thincoder 0.12.34 → 0.12.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,7 @@ Design philosophy (the entire meaning of the name): if the Node standard library
15
15
  - **Fix-verify loop**: file changes without `verify` get pushed back — syntax check + tests must pass before the agent can claim completion (auto-repair up to 3 rounds)
16
16
  - **Checkpoint system**: auto-snapshot before every user task, `list`/`create`/`rewind` tools for the model, single-file restore — rewinding itself is reversible (pre-rewind state auto-saved)
17
17
  - **Codebase understanding** ⭐0.5.0: `repo_outline` (dependency outline, auto-injected at startup), `code_search` (source FTS5 + vectors + JSDoc extraction), `doc_search` (docs chunked by ## headings) — background indexing, auto-incremental updates on file writes, three tools guided by "structure → intent → details"
18
- - **Model adaptation** ⭐: top-tier only, latest only. Built-in flagship models from seventeen providers — DeepSeek / Kimi / Kimi For Coding / GLM / Qwen / Qwen Token Plan / MiniMax / OpenAI / Claude / Gemini / Grok / Mistral / Volcengine Ark (豆包) / Hunyuan (腾讯混元) / SiliconFlow (硅基流动) / OpenRouter / Groq. No legacy model compatibility, no local model support. Auto-matched context windows, truncation-resume protocols (prefix/partial), thinking-mode APIs (thinking.type / reasoning_effort), reasoning_content echo strategies (reasoningEcho), output limits, temperature range clamping — all deeply adapted.
18
+ - **Model adaptation** ⭐: top-tier only, latest only. Built-in flagship models from seventeen providers — DeepSeek / Kimi / Kimi For Coding / GLM / Qwen / Qwen Token Plan / MiniMax / OpenAI / Claude / Gemini / Grok / Mistral / Volcengine Ark (豆包) / Hunyuan (腾讯混元) / SiliconFlow (硅基流动) / OpenRouter / Groq. No legacy model compatibility. Auto-matched context windows, truncation-resume protocols (prefix/partial), thinking-mode APIs (thinking.type / reasoning_effort), reasoning_content echo strategies (reasoningEcho), output limits, temperature range clamping — all deeply adapted.
19
19
  - **Toolset**: `read` / `write` / `edit` / `bash` / `glob` (supports `**`) / `grep` / `websearch` / `ls` / `fetch` + `read_image` (image/video paste) + three retrieval tools + MCP — all zero-dependency, file tools confined to the working directory
20
20
  - **Memory system**: three layers (personal/project/team), FTS5 + vector RRF hybrid retrieval, git-friendly markdown format
21
21
  - **Two-phase tool scheduling**: permission prompts serialized, read-only tools parallelized, side-effect tools serialized
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thincoder",
3
- "version": "0.12.34",
3
+ "version": "0.12.36",
4
4
  "description": "Thin coding agent - zero dependencies, no build step, Node.js native. Sharp code, zero bloat.",
5
5
  "keywords": [
6
6
  "ai",
@@ -185,13 +185,27 @@ export function buildAdvisorUserMessage(agent, prior, reviewType, designToken =
185
185
  } catch { /* file doesn't exist — skip */ }
186
186
  }
187
187
 
188
+ // Document map (docs/design/README.md) — inject when the discovered
189
+ // project root has one: the reviewer checks document ownership against it
190
+ // (a change for an existing section must amend that section's document,
191
+ // not spawn a new file for it). Absent map → skip (nothing to check against).
192
+ try {
193
+ const mapPath = resolve(guideRoot ?? agent.cwd, "docs", "design", "README.md")
194
+ if (existsSync(mapPath)) {
195
+ parts.push("## Document Map")
196
+ parts.push("The document map below registers which document files exist per section. Use it for the Document ownership criterion: a change for an existing section must amend that section's document, not create a new file.")
197
+ parts.push(readFileSync(mapPath, "utf8"))
198
+ parts.push("")
199
+ }
200
+ } catch { /* file doesn't exist or is unreadable — skip */ }
201
+
188
202
  parts.push("## Instructions")
189
203
  if (docList.length > 0) {
190
204
  parts.push("1. Read every document in the Documents to Review list in full — review ONLY those files. Read METHODOLOGY.md to understand the project's standards.")
191
205
  } else {
192
206
  parts.push("1. Read the design document fully. Read METHODOLOGY.md to understand the project's standards.")
193
207
  }
194
- parts.push("2. Review against: completeness (all requirements covered?), feasibility (can this be built?), clarity (specific enough?), acceptance criteria (verifiable?), scope (appropriate?).")
208
+ parts.push("2. Review against: completeness (all requirements covered?), feasibility (can this be built?), methodology compliance (does it follow the project's METHODOLOGY.md?), clarity (specific enough?), acceptance criteria (verifiable?), scope (appropriate?).")
195
209
  parts.push("3. If the ## Project Guide (AGENTS.md) section above is present, also check requirement fit: does the design match what the requirements documents it points to actually ask for?")
196
210
  parts.push("4. Do NOT run git diff or look for code changes — there are none at this stage.")
197
211
  parts.push("5. If you find issues, produce your review table with the format: | # | Category | Severity | Issue | Suggestion |. If the design passes, no table is needed.")
@@ -361,13 +361,11 @@ function extractUnfixedIssues(priorText) {
361
361
  export async function runAdvisorReview(agent, reviewType, callbacks, designToken = null, documents = null, paths = null) {
362
362
  const onOutput = callbacks?.onOutput
363
363
  const signal = callbacks?.signal
364
- const cfg = agent.config?.advisor
365
364
  const startTime = Date.now()
366
-
367
- // Engineering mode overrides advisor toggle reviews are mandatory regardless
368
- if (!cfg?.enabled && !agent.config?.agent?.engineering) {
369
- return "Advisor: not enabled (set advisor.enabled in config.json)."
370
- }
365
+
366
+ // Advisor reviews are ALWAYS available (2026-08-21 semantic refactor): the
367
+ // former advisor.enabled gate is removed — review capability has no off
368
+ // switch; only the guard (completion pushback) is opt-in via advisor.guard.
371
369
 
372
370
  // Mechanical convergence cap — refuse further reviews once the protocol has run
373
371
  // its rounds. _advisorRound counts completed advisor calls (incremented by the
package/src/advisor.mjs CHANGED
@@ -72,14 +72,11 @@ const ADVISOR_ROUND1 = loadPrompt("advisor-round1.md", "advisor-round1.md")
72
72
  // buildAdvisorSystemPrompt when _advisorRound > 0.
73
73
  const ADVISOR_ROUND2 = loadPrompt("advisor-round2.md", "advisor-round2.md")
74
74
  const ADVISOR_ROUND3 = loadPrompt("advisor-round3.md", "advisor-round3.md")
75
- // Fallback when advisor-design.md is missing keep in sync with the real
76
- // file (table format + workflow steps).
77
- const ADVISOR_DESIGN_FALLBACK = `You are an independent design reviewer for an engineering-mode project. Review the design document in the changes below. Evaluate: completeness, feasibility, clarity, scope, acceptance criteria. Read METHODOLOGY.md if provided. Produce a review table with | # | Category | Severity | Issue | Suggestion | format.`
78
- let ADVISOR_DESIGN = ""
79
- // Design review is OPTIONAL (engineering mode only) — silent fallback to the
80
- // in-code constant is intentional, unlike the mandatory round prompts which
81
- // must exist for every review (loadPrompt throws a descriptive error there).
82
- try { ADVISOR_DESIGN = readFileSync(join(__dirname, "prompts", "advisor-design.md"), "utf8") } catch { /* fallback below */ }
75
+ // Design-review prompthard-loaded like the round prompts (decision
76
+ // 2026-08-21): a missing file means a broken installation, and silently
77
+ // degrading to a lesser in-code prompt would quietly strip the approval-signal
78
+ // and citation rules, disabling design approval entirely. loadPrompt throws.
79
+ const ADVISOR_DESIGN = loadPrompt("advisor-design.md", "advisor-design.md")
83
80
 
84
81
  // ────────────────────────────────────────
85
82
  // System prompt building
@@ -109,7 +106,7 @@ export function buildAdvisorSystemPrompt(agent, prior, reviewType) {
109
106
  // approval token); rounds 2+ converge like code reviews (verify agent fix claims).
110
107
  if (reviewType === "design") {
111
108
  if (!hasPrior) {
112
- return ADVISOR_DESIGN || ADVISOR_DESIGN_FALLBACK
109
+ return ADVISOR_DESIGN
113
110
  }
114
111
  const round = (agent._advisorRound || 0) + 1
115
112
  if (round === 2) return ADVISOR_ROUND2
@@ -111,10 +111,11 @@ export function handleCompletion(agent, response, depth, turn, guardPushbacks, h
111
111
  }
112
112
 
113
113
  // --- advisor guard: review of mutated files before completion ---
114
- // Active by default when advisor.enabled is set (opt-out via guard: false),
115
- // and NEVER in engineering mode.
114
+ // OPT-IN ONLY (advisor.guard === true, default OFF 2026-08-21 semantic
115
+ // refactor), and NEVER in engineering mode. The advisor tool itself is always
116
+ // available; this guard only controls whether completion is pushed back.
116
117
  const cfg = agent.config?.advisor
117
- const advisorReview = cfg?.enabled && cfg?.guard !== false
118
+ const advisorReview = cfg?.guard === true
118
119
  if (depth === 0 && advisorReview && !agent.config?.agent?.engineering) {
119
120
  // Cap sync: beyond MAX_ADVISOR_ROUNDS the advisor tool refuses to review
120
121
  // (run.mjs convergence cap) — pushing back further would loop forever
@@ -4,7 +4,7 @@
4
4
  import { configDir } from "../config.mjs"
5
5
  import { readFileSync, readdirSync, existsSync } from "node:fs"
6
6
  import { homedir } from "node:os"
7
- import { writeFile, mkdir } from "node:fs/promises"
7
+ import { writeFile, mkdir, readdir, stat, unlink } from "node:fs/promises"
8
8
  import { join } from "node:path"
9
9
  import { execSync } from "node:child_process"
10
10
 
@@ -23,6 +23,9 @@ export const REPORT_CONTINUATION =
23
23
  const TOOL_RESULT_OFFLOAD_LIMIT = 16_000
24
24
  const TOOL_RESULT_PREVIEW = 2_000
25
25
 
26
+ /** Offload-dir write-time self-cleanup retention window (2026-08-21): files older than 3 days are deleted on the next offload. */
27
+ export const TMP_RETENTION_MS = 3 * 24 * 3600 * 1000
28
+
26
29
  const GIT_TIMEOUT_MS = 5000
27
30
  const MAX_GIT_CHANGES_DISPLAY = 20
28
31
 
@@ -39,11 +42,35 @@ export function tryCanonicalize(name, args) {
39
42
  try { return name + ":" + JSON.stringify(JSON.parse(args)) } catch { return name + ":" + args }
40
43
  }
41
44
 
42
- /** Offload oversized tool results (>16k chars) to disk, returning a preview + file path */
43
- export async function offloadToolResult(text, callId) {
45
+ /**
46
+ * Best-effort write-time self-cleanup: delete files in dir whose mtime exceeds TMP_RETENTION_MS.
47
+ * Subdirectories are never touched; every failure is silent — cleanup must not affect offload.
48
+ */
49
+ export async function cleanupOldToolResults(dir) {
50
+ const now = Date.now()
51
+ let entries
52
+ try {
53
+ entries = await readdir(dir, { withFileTypes: true })
54
+ } catch {
55
+ return // dir missing or unreadable → nothing to clean
56
+ }
57
+ for (const entry of entries) {
58
+ if (!entry.isFile()) continue // subdirectories untouched
59
+ try {
60
+ const st = await stat(join(dir, entry.name))
61
+ if (now - st.mtimeMs > TMP_RETENTION_MS) await unlink(join(dir, entry.name))
62
+ } catch {
63
+ /* entry vanished concurrently or I/O error — best effort, keep going */
64
+ }
65
+ }
66
+ }
67
+
68
+ /** Offload oversized tool results (>16k chars) to disk, returning a preview + file path.
69
+ * Writes trigger write-time self-cleanup of the offload dir first (dir param overridable for tests). */
70
+ export async function offloadToolResult(text, callId, dir = join(configDir, "tool-results")) {
44
71
  if (text.length <= TOOL_RESULT_OFFLOAD_LIMIT) return text
45
72
  try {
46
- const dir = join(configDir, "tool-results")
73
+ await cleanupOldToolResults(dir)
47
74
  await mkdir(dir, { recursive: true })
48
75
  const file = join(dir, `${Date.now()}-${String(callId).replace(/[^a-zA-Z0-9_-]/g, "_")}.log`)
49
76
  await writeFile(file, text, "utf8")
@@ -53,7 +53,7 @@ export const subagentTool = {
53
53
  name: "subagent",
54
54
  description:
55
55
  "Spawn a sub-agent to handle an independent subtask in an isolated context. The sub-agent returns only its final report. Spawn MULTIPLE subagents in the SAME response for parallel work—they run concurrently.\n" +
56
- "Use role='explore' for codebase search/analysis (read-only, fast), role='plan' for read-only implementation planning (returns a step-by-step plan, never edits), role='coder' for self-contained implementation tasks. Do not give parallel subagents tasks that edit the same files.\n\n" +
56
+ "Use role='explore' for codebase search/analysis (read-only, fast — specify thoroughness in the task: quick / medium / thorough (default medium)), role='plan' for read-only implementation planning (returns a step-by-step plan, never edits), role='coder' for self-contained implementation tasks. Do not give parallel subagents tasks that edit the same files.\n\n" +
57
57
  "Writing the prompt:\n" +
58
58
  "- The sub-agent starts with zero context — it has not seen this conversation. Brief it like a colleague who just walked into the room: state the goal, list what you already know, hand over the specifics.\n" +
59
59
  "- Put exact paths and commands in the prompt when you know them. The sub-agent should not search for things you already know.\n" +
package/src/config.mjs CHANGED
@@ -52,7 +52,7 @@ const DEFAULTS = {
52
52
  consultTurns: 40, // per-consultant tool-turn budget (diagnosis tasks)
53
53
  consultTimeoutMs: 600000, // wall-clock ceiling per consultant (10min)
54
54
  streamRules: [], // time-traveling stream rules: [{ pattern: "regex", message: "reminder", action: "abort"|"warn", repeat: "always"|"once" }]
55
- advisor: { enabled: false }, // code review; { enabled: true, provider: "deepseek", model: "deepseek-chat", thinking: { type: "enabled" }, reasoningEffort: "max", guard: true }
55
+ advisor: { guard: false }, // code review is always available; guard: true pushes completion back until reviewed (opt-in). Also accepts provider/model/thinking/reasoningEffort overrides. Deprecated: enabled (2026-08-21)
56
56
  autoThink: false, // auto-classify task difficulty and set reasoning effort per-turn
57
57
  engineering: false, // strict methodology enforcement — read METHODOLOGY.md, design-before-code
58
58
  },
@@ -12,6 +12,7 @@ Evaluate the design against these dimensions:
12
12
  4. **Clarity** — Is the design specific enough to implement? Are the affected files identified?
13
13
  5. **Acceptance criteria** — Are they verifiable? Do they cover normal paths, edge cases, and error conditions?
14
14
  6. **Scope** — Is the scope appropriate? Are there opportunities to simplify? Is there scope creep?
15
+ 7. **Document ownership** — Does the change amend the design document that already owns its topic (per the document map in `docs/design/README.md`), or does it fragment by creating a new file for an existing section? Does the wording duplicate or contradict existing documents?
15
16
 
16
17
  ## Output Format
17
18
 
@@ -27,6 +28,14 @@ Severity levels:
27
28
  - 🟡 Advisory — design could be improved; NOT a blocker for approval
28
29
  - 🔵 Note — optional observation; NOT a blocker
29
30
 
31
+ Document ownership severity:
32
+ - Wording that CONTRADICTS an existing document (same mechanism described differently in two places) → 🔴
33
+ - Creating a new file for an existing section, or duplicating a description that already exists elsewhere → 🟡
34
+
35
+ ## Citation Discipline
36
+
37
+ When you cite design-document text, use the exact `file:line` format (e.g. `docs/design/AGENT-LOOP.md:180`) — host-side verification will check the citation against the current disk state. If you have not read/verified the cited content, mark it `unverified` instead of presenting it as fact.
38
+
30
39
  ## Approval Signal
31
40
 
32
41
  The user message contains an exact token in an `## Approval Signal` section (format `[DESIGN-TOKEN:...]`).
@@ -19,6 +19,16 @@ subagents only.
19
19
  non-functional standards. Clarification is DONE when each layer is concrete
20
20
  enough to design against (the user confirms, or the answers stop changing
21
21
  the requirement). Do NOT start the design before this.
22
+ - **Plan confirmation before writing any doc — no exemptions.** When
23
+ clarification is DONE, and before writing the requirements doc (or the
24
+ design doc), state in plain text your understanding of the requirement
25
+ plus your next-step plan, and WAIT for the user's explicit confirmation
26
+ ("OK / 可以 / continue"-type reply) before writing. No confirmation,
27
+ silence, or a new question from the user → do not write. Even if you
28
+ are completely sure you understand, you must still write the plan out
29
+ and wait — "this is obvious enough to skip asking" is never a valid
30
+ reason. Writing docs is a writing action — it is under the same
31
+ discipline.
22
32
  2. **Design.** Write the design document in `docs/` (problem statement,
23
33
  solution approach, full affected-file list, verifiable acceptance criteria).
24
34
  Do NOT open any code file for editing before this document exists.
@@ -15,3 +15,8 @@ Guidelines:
15
15
  - Complete the search efficiently and report findings in a structured format
16
16
  - If the expected pattern doesn't exist, report that explicitly: what you searched for, which tools you used, and that nothing matched. "Probably there" is not a finding — only report what you actually saw.
17
17
  - If something is ambiguous, note it in your report; do not ask the user
18
+
19
+ **Thoroughness levels** — pick the depth the task actually needs (the parent agent may state one in the task description):
20
+ - quick — a single targeted search answering one specific question
21
+ - medium — the default: a moderate multi-pronged search, several probes in parallel
22
+ - thorough — exhaustive analysis across multiple locations and naming conventions; your report must list what you searched for and what you did NOT find
@@ -10,6 +10,7 @@ For tasks that match the Coding discipline's "complex" tier, plan mode is your d
10
10
 
11
11
  Delegate well — spawn subagents for independent subtasks.
12
12
  - Explore agents for parallel codebase search, plan agents for architecture design, coder agents for self-contained implementation.
13
+ - When delegating an explore agent, state the thoroughness in the task description — quick / medium / thorough — graded by need; unspecified means the default.
13
14
  - Delegate breadth-first exploration; do precision edits yourself.
14
15
  - Never give parallel subagents tasks that edit the same files — conflicts waste everyone's time.
15
16
  - When a coder subagent finishes, verify its report: read the files it claims to have changed, run the tests — do not trust subagent reports blindly.
@@ -8,9 +8,12 @@ Programming is collaborative labor between you and the human. The human decides
8
8
 
9
9
  **How you work — before you write any code:**
10
10
  - **Read design docs first.** Use `doc_search` to find relevant design docs, AGENTS.md, and architecture decisions. Code without design context is guesswork. If docs conflict with code, docs are right. If the user's instruction conflicts with the docs, tell the user first — discuss, update the docs, then code.
11
+ - **Document ownership — find the doc that owns the topic before writing.** Before writing to `docs/design/`, check the `docs/design/README.md` document map (no map → check AGENTS.md and the docs directory) to locate the document that owns the topic — if it exists, update it; never create a new file for an existing section. Create a new file only when no section owns the topic, and register it in the map. Describe each mechanism in detail in exactly ONE place (the authoritative source); other documents reference it, never copy it.
11
12
  - **Check existing code.** Search for existing functions, helpers, patterns before writing new ones. Duplicates are technical debt.
12
13
  - **Understand intent.** Ask why this change is needed — the "why" reveals scope the literal request hides.
13
- - **Confirm understanding.** State what you believe the user asked for and what you plan to deliver. Wait for confirmation. No task is too small — a wrong assumption always costs more than the round-trip. Once confirmed, deliver exactly what was agreed — no simplifying, no substituting, no taking shortcuts after the fact. Simplifying a confirmed requirement frustrates the user and wastes time; they will just tell you to do it right anyway.
14
+ - **Confirm understanding.** State what you believe the user asked for and what you plan to deliver, including the most important acceptance criteria. Wait for confirmation. No task is too small — a wrong assumption always costs more than the round-trip. Once confirmed, deliver exactly what was agreed — no simplifying, no substituting, no taking shortcuts after the fact. Simplifying a confirmed requirement frustrates the user and wastes time; they will just tell you to do it right anyway.
15
+ - **Confirm before any file-writing action — no exemptions.** Before ANY file-writing action (write / edit / apply_patch / insert_after / delete / hashline_edit, or any bash that writes files), restate in plain text your understanding of the task plus the key points of your plan, and WAIT for the user's explicit confirmation (an "OK / 可以 / continue"-type reply) before executing. No confirmation, silence, or the user answering with a new question or a new requirement → do not touch anything, no matter how small or obvious the change seems. Even after rounds of clarification, when you are completely sure you understand, you must still write the plan out and wait — "this is obvious enough to skip asking" is never a valid reason to skip, and a new question from the user is not a confirmation; it means the understanding has changed.
16
+ - **Re-confirm when the requirement changes.** If what was confirmed is later changed by a new requirement in the conversation, restate your understanding and plan and wait for fresh confirmation before touching files.
14
17
 
15
18
  **How you work — while coding:**
16
19
  - When you need multiple independent pieces of information, call tools in parallel — read files, search, grep all at once.
@@ -94,7 +94,7 @@ export async function runAgentTurn(ctx, text) {
94
94
  const callbacks = {
95
95
  onToken: (t) => {
96
96
  // Subagent streaming: prefix format role#id/ → extract id, update subTask streaming text
97
- const subMatch = t.match(/^(\w+)#(\d+)\//)
97
+ const subMatch = t.match(/^([\w-]+)#(\d+)\//)
98
98
  if (subMatch) {
99
99
  const key = `${subMatch[1]}#${subMatch[2]}`
100
100
  const payload = t.slice(subMatch[0].length)
@@ -114,7 +114,7 @@ export async function runAgentTurn(ctx, text) {
114
114
  },
115
115
  onReasoning: (t) => {
116
116
  // Subagent reasoning tokens also carry role#id/ prefix, go into subTasks panel
117
- const subMatch = t.match(/^(\w+)#(\d+)\//)
117
+ const subMatch = t.match(/^([\w-]+)#(\d+)\//)
118
118
  if (subMatch) {
119
119
  const key = `${subMatch[1]}#${subMatch[2]}`
120
120
  if (!state.subTasks[key]) {
@@ -129,7 +129,7 @@ export async function runAgentTurn(ctx, text) {
129
129
  },
130
130
  onToolCall: (name, args) => {
131
131
  // Subagent tool call: prefix role#id/toolName → update subTask current tool
132
- const subMatch = name.match(/^(\w+)#(\d+)\//)
132
+ const subMatch = name.match(/^([\w-]+)#(\d+)\//)
133
133
  if (subMatch) {
134
134
  const key = `${subMatch[1]}#${subMatch[2]}`
135
135
  const toolName = name.slice(subMatch[0].length)
@@ -1,4 +1,4 @@
1
- /** /advisor command: toggle advisor on/off, select model, configure thinking.
1
+ /** /advisor command: configure review model/thinking and toggle the review guard.
2
2
  * Interactive loop UX — stays in menu after each action, Esc to exit.
3
3
  * ctx: { agent, showPicker, pushLine, pushLabel, persistRaw } */
4
4
  import { ansi, C } from "./ansi.mjs"
@@ -6,6 +6,9 @@ import { ansi, C } from "./ansi.mjs"
6
6
  export async function handleAdvisorCommand(ctx) {
7
7
  const { agent, showPicker, pushLine, pushLabel } = ctx
8
8
  const cfg = agent.config.advisor ??= {}
9
+ // Deprecated field cleanup (2026-08-21): advisor.enabled is no longer read
10
+ // anywhere; drop it here so persist() never re-writes the stale flag.
11
+ delete cfg.enabled
9
12
 
10
13
  const persist = async () => {
11
14
  if (ctx.persistRaw) {
@@ -21,13 +24,12 @@ export async function handleAdvisorCommand(ctx) {
21
24
 
22
25
  // ── State helpers ──
23
26
  function advisorStatus() {
24
- const enabled = cfg.enabled === true
25
27
  const curModel = cfg.model || agent.provider.model
26
28
  const thinkInfo = cfg.thinking === null ? "off"
27
29
  : cfg.thinking?.type === "disabled" ? "off"
28
30
  : cfg.reasoningEffort ? `on (${cfg.reasoningEffort})`
29
31
  : cfg.thinking ? `on (${cfg.thinking.type})` : "(main)"
30
- return `Advisor: ${enabled ? "ON" : "OFF"} | Model: ${curModel} | Think: ${thinkInfo}`
32
+ return `Advisor: always available | Model: ${curModel} | Think: ${thinkInfo}`
31
33
  }
32
34
 
33
35
  function headerLine() {
@@ -105,14 +107,12 @@ export async function handleAdvisorCommand(ctx) {
105
107
  // ── Main loop ──
106
108
  let mainIdx = 0
107
109
  for (;;) {
108
- const enabled = cfg.enabled === true
109
110
  const curProvider = cfg.provider || "(main)"
110
111
  const curModel = cfg.model || agent.provider.model
111
112
  const guardInfo = cfg.guard === true ? "on" : "off"
112
113
 
113
114
  const entries = [
114
115
  { type: "header", text: headerLine() },
115
- { type: "item", text: `Advisor: ${enabled ? "ON" : "OFF"}`, action: "toggle" },
116
116
  { type: "item", text: `Model: ${curModel}`, action: "model", note: `Provider: ${curProvider}` },
117
117
  { type: "item", text: `Thinking: ${advisorStatus().split("|")[2]?.trim() || "(main)"}`, action: "thinking" },
118
118
  { type: "item", text: `Guard: ${guardInfo}`, action: "guard" },
@@ -125,21 +125,13 @@ export async function handleAdvisorCommand(ctx) {
125
125
 
126
126
  if (choice.action === "view") {
127
127
  pushLabel("❯ Advisor", ansi.bold + C.tool)
128
- pushLine(`Status: ${enabled ? "ON" : "OFF"}`, C.dim)
128
+ pushLine(`Status: always available`, C.dim)
129
129
  pushLine(`Model: ${curModel} (provider: ${curProvider})`, C.dim)
130
130
  pushLine(`Guard: ${guardInfo}`, C.dim)
131
131
  pushLine(`Thinking: ${advisorStatus().split("|")[2]?.trim() || "(main)"}`, C.dim)
132
132
  continue
133
133
  }
134
134
 
135
- if (choice.action === "toggle") {
136
- cfg.enabled = !cfg.enabled
137
- await persist().catch(err => pushLine(`[error] Advisor toggle: ${err.message}`, C.error))
138
- pushLabel("❯ Advisor", ansi.bold + C.tool)
139
- pushLine(`Advisor: ${cfg.enabled ? "ON" : "OFF"}`, C.tool)
140
- continue
141
- }
142
-
143
135
  if (choice.action === "guard") {
144
136
  cfg.guard = !(cfg.guard === true)
145
137
  await persist().catch(err => pushLine(`[error] ${err.message}`, C.error))
@@ -268,9 +268,9 @@ export function renderStatus(state, agent, cols, slashCommands) {
268
268
  const statusLine = buildStatusLine(state, agent, { cols, slashCommands })
269
269
  const autoBanner = agent.autoApprove ? `${C.warn} AUTO${ansi.reset}${ansi.dim}│` : ""
270
270
  const planBanner = agent.planMode ? `${C.tool} PLAN${ansi.reset}${ansi.dim}│` : ""
271
- const advisorBanner = agent.config?.advisor?.enabled ? `${C.advisor} ADVISOR${ansi.reset}${ansi.dim}│` : ""
271
+ const advisorBanner = agent.config?.advisor?.guard === true ? `${C.advisor} GUARD${ansi.reset}${ansi.dim}│` : ""
272
272
  const engBanner = agent.config?.agent?.engineering ? `${C.advisor} ENG${ansi.reset}${ansi.dim}│` : ""
273
- const bannerPrefix = (agent.planMode ? " PLAN│ " : "") + (agent.autoApprove ? " AUTO│ " : "") + (agent.config?.advisor?.enabled ? " ADVISOR│ " : "") + (agent.config?.agent?.engineering ? " ENG│ " : "")
273
+ const bannerPrefix = (agent.planMode ? " PLAN│ " : "") + (agent.autoApprove ? " AUTO│ " : "") + (agent.config?.advisor?.guard === true ? " GUARD│ " : "") + (agent.config?.agent?.engineering ? " ENG│ " : "")
274
274
  const statusMax = cols - 1 - (bannerPrefix ? stringWidth(bannerPrefix) : 0)
275
275
  return `${ansi.dim}${planBanner}${autoBanner}${advisorBanner}${engBanner}${sliceByWidth(statusLine, Math.max(10, statusMax))}${ansi.reset}`
276
276
  }