repl: configurable prompt template via config.shell.prompt (closes #10)

At-a-glance situational awareness: see the active model, context fill,
mode flags, and cwd in the prompt itself — prevents "wait, am I still
in plan mode?" surprises.

Example config:

    shell = {
        prompt = "[{model} {ctx_used}/{ctx_max}t T{turn} {mode}] {cwd_short} > ",
    }

Variables (substituted via {name}):
  {model}        active preset name
  {ctx_used}     char/4 token heuristic (Phase 0 §8; accurate is Q1)
  {ctx_max}      config.context.token_budget
  {turn}         #ctx.turns
  {cwd}          libc.getcwd() (chdir-aware; PWD env may drift)
  {cwd_short}    cwd with $HOME -> ~
  {last_status}  last exec exit code, "" if none yet
  {mode}         "norris" | "plan" | "normal"

Default behavior unchanged when shell.prompt is unset — keeps the
"[aish:<model>]>" form with norris  and plan markers.

Side wiring:
  - ffi/libc.lua gains getcwd() (chdir() doesn't update PWD).
  - run_shell records exit code into last_exec_code for {last_status}.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 21:14:43 +00:00
parent 10d2501cff
commit d738f339cb
3 changed files with 57 additions and 0 deletions
+34
View File
@@ -449,7 +449,40 @@ function M.run(config)
if session then session:append(turn) end
end
-- Issue #10: configurable prompt template. When config.shell.prompt is
-- set, substitute {model}/{ctx_used}/{ctx_max}/{turn}/{cwd}/{cwd_short}
-- /{last_status}/{mode}. Otherwise fall back to the default with the
-- norris ⚡ + plan markers.
local libc = require("ffi.libc")
local last_exec_code = nil
local function _cwd_short()
local c = libc.getcwd() or os.getenv("PWD") or "?"
local home = os.getenv("HOME")
if home and c:sub(1, #home) == home then
c = "~" .. c:sub(#home + 1)
end
return c
end
local function _mode()
if ctx.norris_active then return "norris" end
if plan_mode then return "plan" end
return "normal"
end
local function prompt()
local tmpl = config.shell and config.shell.prompt
if tmpl then
local vars = {
model = active_name,
ctx_used = tostring(ctx:estimate_tokens()),
ctx_max = tostring(ctx.token_budget),
turn = tostring(#ctx.turns),
cwd = libc.getcwd() or "?",
cwd_short = _cwd_short(),
last_status = last_exec_code and tostring(last_exec_code) or "",
mode = _mode(),
}
return (tmpl:gsub("{(%w+)}", function(k) return vars[k] or "" end))
end
if ctx.norris_active then
return ("[aish:%s \xE2\x9A\xA1]> "):format(active_name)
end
@@ -544,6 +577,7 @@ function M.run(config)
end
renderer.exec_begin()
local out, code = executor.exec(cmd)
last_exec_code = code
renderer.exec_end(code)
if config.shell and config.shell.capture_output then
ctx:append_exec_output(out)