repl: :plan toggle — CMD: lines become PLAN: notes (closes #5)
Plan mode is a safer entry point than going straight to Norris: the user
iterates with the model on what to do, sees each CMD: as a PLAN: line,
and the would-have-run notes feed back into the next-turn context so the
model can refine without side effects.
Toggle with :plan (flip), :plan on, :plan off. Off by default.
When plan_mode is true:
- CMD: lines extracted from the assistant turn print as "PLAN: <cmd>"
- The note "[plan] would run: <cmd>" is appended via the existing
append_exec_output channel — same context flow as a real exec, so
the model sees its proposed action on the next turn.
- run_shell is NOT called; no executor, no cd intercept, no capture.
The prompt shows "[aish:<model> plan]>" while active (mirrors the
norris ⚡ marker convention).
Orthogonal to Norris: plan_mode only gates the interactive CMD:
extraction path. Norris has its own halt protocol; combining them is
not supported (the planner would be confused by skipped actions).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,8 @@ Meta commands:
|
||||
:mcp disconnect <alias> drop an MCP session
|
||||
:norris <goal> launch Chuck Norris autonomous mode on <goal>
|
||||
:norris off exit Norris mode (rare — usually 'abort' at halt)
|
||||
:plan toggle plan mode (CMD: lines printed, NOT executed)
|
||||
:plan on / :plan off set plan mode explicitly
|
||||
:safety patterns list active destructive-op patterns
|
||||
:safety check <cmd> probe is_destructive against <cmd> without running
|
||||
:remember <text> shortcut: :memory add fact <text>
|
||||
@@ -60,6 +62,13 @@ function M.run(config)
|
||||
.. "' not found in config.models")
|
||||
end
|
||||
|
||||
-- Plan mode (issue #5): when true, CMD: lines are NOT executed; they
|
||||
-- are echoed as "PLAN:" and fed back to the next-turn context as
|
||||
-- would-have-run notes so the model can iterate without side effects.
|
||||
-- Off by default; toggle with :plan / :plan on / :plan off. Orthogonal
|
||||
-- to Norris mode (Norris has its own halt protocol).
|
||||
local plan_mode = false
|
||||
|
||||
-- Phase 5: render the evicted turns into a compact transcript for
|
||||
-- the summarizer prompt. Same shape as :memory summarize uses.
|
||||
local function render_evicted(turns)
|
||||
@@ -358,6 +367,9 @@ function M.run(config)
|
||||
if ctx.norris_active then
|
||||
return ("[aish:%s \xE2\x9A\xA1]> "):format(active_name)
|
||||
end
|
||||
if plan_mode then
|
||||
return ("[aish:%s plan]> "):format(active_name)
|
||||
end
|
||||
return ("[aish:%s]> "):format(active_name)
|
||||
end
|
||||
|
||||
@@ -581,14 +593,22 @@ function M.run(config)
|
||||
|
||||
-- CMD: extraction on the final pure-text response only.
|
||||
for _, cmd in ipairs(executor.extract_cmd_lines(final_resp)) do
|
||||
local doit
|
||||
if config.shell and config.shell.confirm_cmd then
|
||||
local ans = rl.readline(("execute '%s'? [y/N] "):format(cmd)) or ""
|
||||
doit = (ans:lower():sub(1, 1) == "y")
|
||||
if plan_mode then
|
||||
-- Issue #5: print PLAN: and feed back as a would-have-run
|
||||
-- note. Same context flow as a real exec output so the
|
||||
-- model can iterate on the plan turn by turn.
|
||||
renderer.status(("PLAN: %s"):format(cmd))
|
||||
ctx:append_exec_output(("[plan] would run: %s"):format(cmd))
|
||||
else
|
||||
doit = true
|
||||
local doit
|
||||
if config.shell and config.shell.confirm_cmd then
|
||||
local ans = rl.readline(("execute '%s'? [y/N] "):format(cmd)) or ""
|
||||
doit = (ans:lower():sub(1, 1) == "y")
|
||||
else
|
||||
doit = true
|
||||
end
|
||||
if doit then run_shell(cmd) end
|
||||
end
|
||||
if doit then run_shell(cmd) end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -725,6 +745,19 @@ function M.run(config)
|
||||
active_name, active_cfg = name, config.models[name]
|
||||
renderer.status("model -> " .. name)
|
||||
end,
|
||||
plan = function(args)
|
||||
local sub = (args:match("^%s*(%S*)") or ""):lower()
|
||||
if sub == "" then
|
||||
plan_mode = not plan_mode
|
||||
elseif sub == "on" then
|
||||
plan_mode = true
|
||||
elseif sub == "off" then
|
||||
plan_mode = false
|
||||
else
|
||||
renderer.status("usage: :plan [on|off]"); return
|
||||
end
|
||||
renderer.status("plan mode " .. (plan_mode and "on" or "off"))
|
||||
end,
|
||||
models = function()
|
||||
renderer.status(("models (active: %s):"):format(active_name))
|
||||
for name, cfg in pairs(config.models) do
|
||||
|
||||
Reference in New Issue
Block a user