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:
@@ -42,6 +42,14 @@ return {
|
|||||||
},
|
},
|
||||||
capture_output = true, -- inject exec output into context
|
capture_output = true, -- inject exec output into context
|
||||||
confirm_cmd = true, -- prompt before executing CMD: suggestions
|
confirm_cmd = true, -- prompt before executing CMD: suggestions
|
||||||
|
|
||||||
|
-- Issue #10: prompt template. When set, replaces the default
|
||||||
|
-- "[aish:<model>]> " prompt. Variables (substituted via {name}):
|
||||||
|
-- {model} {ctx_used} {ctx_max} {turn}
|
||||||
|
-- {cwd} {cwd_short} (cwd with $HOME -> ~)
|
||||||
|
-- {last_status} (last exec exit code, empty if none yet)
|
||||||
|
-- {mode} (norris / plan / normal)
|
||||||
|
-- prompt = "[{model} {ctx_used}/{ctx_max}t T{turn} {mode}] {cwd_short} > ",
|
||||||
},
|
},
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ int flock(int fd, int operation);
|
|||||||
/* TTY detection for non-interactive mode (`aish -p`). Returns 1 if the
|
/* TTY detection for non-interactive mode (`aish -p`). Returns 1 if the
|
||||||
fd refers to a terminal, 0 otherwise (sets errno on error). */
|
fd refers to a terminal, 0 otherwise (sets errno on error). */
|
||||||
int isatty(int fd);
|
int isatty(int fd);
|
||||||
|
|
||||||
|
/* getcwd — chdir() doesn't update PWD env, so prompt {cwd} needs the
|
||||||
|
real cwd. NULL buffer + size 0 is the GNU extension that malloc()s
|
||||||
|
the buffer; we use a fixed-size stack buffer instead. */
|
||||||
|
char *getcwd(char *buf, size_t size);
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local C = ffi.C
|
local C = ffi.C
|
||||||
@@ -183,4 +188,14 @@ function M.isatty(fd)
|
|||||||
return C.isatty(fd) == 1
|
return C.isatty(fd) == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------- getcwd
|
||||||
|
local CWD_BUF = ffi.new("char[?]", 4096)
|
||||||
|
function M.getcwd()
|
||||||
|
local p = C.getcwd(CWD_BUF, 4096)
|
||||||
|
if p == nil then
|
||||||
|
return nil, ffi.string(C.strerror(C.__errno_location()[0]))
|
||||||
|
end
|
||||||
|
return ffi.string(CWD_BUF)
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
@@ -449,7 +449,40 @@ function M.run(config)
|
|||||||
if session then session:append(turn) end
|
if session then session:append(turn) end
|
||||||
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 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
|
if ctx.norris_active then
|
||||||
return ("[aish:%s \xE2\x9A\xA1]> "):format(active_name)
|
return ("[aish:%s \xE2\x9A\xA1]> "):format(active_name)
|
||||||
end
|
end
|
||||||
@@ -544,6 +577,7 @@ function M.run(config)
|
|||||||
end
|
end
|
||||||
renderer.exec_begin()
|
renderer.exec_begin()
|
||||||
local out, code = executor.exec(cmd)
|
local out, code = executor.exec(cmd)
|
||||||
|
last_exec_code = code
|
||||||
renderer.exec_end(code)
|
renderer.exec_end(code)
|
||||||
if config.shell and config.shell.capture_output then
|
if config.shell and config.shell.capture_output then
|
||||||
ctx:append_exec_output(out)
|
ctx:append_exec_output(out)
|
||||||
|
|||||||
Reference in New Issue
Block a user