thincoder 0.12.60 → 0.12.61
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/CHANGELOG.md +24 -1
- package/README.md +8 -6
- package/bin/thincoder.mjs +27 -122
- package/package.json +3 -2
- package/src/abort-provenance.mjs +116 -0
- package/src/acp/bridge.mjs +38 -17
- package/src/acp.mjs +6 -1
- package/src/advisor/citations.mjs +83 -21
- package/src/advisor/compaction.mjs +174 -0
- package/src/advisor/loop.mjs +293 -0
- package/src/advisor/messages.mjs +36 -134
- package/src/advisor/project-context.mjs +194 -0
- package/src/advisor/repos.mjs +17 -40
- package/src/advisor/run.mjs +124 -329
- package/src/advisor/truncate.mjs +57 -0
- package/src/advisor.mjs +3 -2
- package/src/agent/completion.mjs +1 -1
- package/src/agent/dispatch.mjs +47 -12
- package/src/agent/helpers.mjs +71 -13
- package/src/agent/record-results.mjs +13 -5
- package/src/agent/relay-prefix.mjs +39 -0
- package/src/agent/run-stages.mjs +21 -6
- package/src/agent/setup-reminders.mjs +16 -9
- package/src/agent/setup.mjs +92 -128
- package/src/agent/spawn-child.mjs +26 -9
- package/src/agent-tools/advisor-async.mjs +70 -180
- package/src/agent-tools/advisor-settle.mjs +231 -0
- package/src/agent-tools/advisor.mjs +69 -20
- package/src/agent-tools/batch-segment.mjs +195 -0
- package/src/agent-tools/consult.mjs +23 -10
- package/src/agent-tools/design-token.mjs +14 -1
- package/src/agent-tools/digest-budget.mjs +76 -0
- package/src/agent-tools/eng.mjs +3 -3
- package/src/agent-tools/escalate-async.mjs +16 -13
- package/src/agent-tools/read-history.mjs +13 -3
- package/src/agent-tools/review-streak.mjs +93 -0
- package/src/agent-tools/settings.mjs +130 -17
- package/src/agent-tools/subagent-actions.mjs +15 -6
- package/src/agent-tools/subagent-async.mjs +66 -14
- package/src/agent-tools/subagent-panel.mjs +22 -15
- package/src/agent-tools/subagent-run.mjs +9 -6
- package/src/agent-tools/subagent-scheduler.mjs +57 -8
- package/src/agent-tools/subagent-spawn.mjs +63 -16
- package/src/agent-tools/subagent.mjs +175 -49
- package/src/agent-tools/verify.mjs +13 -34
- package/src/agent-tools.mjs +1 -0
- package/src/agent.mjs +38 -21
- package/src/cli/distill-command.mjs +2 -2
- package/src/cli/make-agent.mjs +23 -7
- package/src/cli/memory-command.mjs +2 -2
- package/src/cli/setup-wizard.mjs +29 -9
- package/src/completions.mjs +114 -0
- package/src/config-migrate.mjs +70 -0
- package/src/config.mjs +132 -63
- package/src/conventions.mjs +223 -0
- package/src/crash-reports.mjs +7 -2
- package/src/expand-home.mjs +16 -0
- package/src/generate-title.mjs +1 -1
- package/src/hooks.mjs +7 -3
- package/src/memory/code-index.mjs +9 -3
- package/src/memory/code-sync.mjs +70 -31
- package/src/memory/delete.mjs +2 -0
- package/src/memory/docs.mjs +10 -6
- package/src/memory/file-walk.mjs +109 -0
- package/src/memory/schema.mjs +15 -3
- package/src/model-ref.mjs +66 -0
- package/src/model-specs.mjs +42 -8
- package/src/prompt-overlays.mjs +73 -16
- package/src/prompts/advisor-design.md +18 -8
- package/src/prompts/advisor-round1.md +7 -1
- package/src/prompts/advisor-round2.md +13 -2
- package/src/prompts/advisor-round3.md +13 -2
- package/src/prompts/common.md +115 -0
- package/src/prompts/consult-base.md +2 -0
- package/src/prompts/discipline-engineering.md +217 -0
- package/src/prompts/discipline-normal.md +179 -0
- package/src/prompts/persona-coder.md +21 -0
- package/src/prompts/persona-eng-coder.md +37 -0
- package/src/prompts/persona-eng-designer.md +55 -0
- package/src/prompts/persona-engineering.md +54 -0
- package/src/prompts/persona-explore.md +15 -0
- package/src/prompts/persona-normal.md +27 -0
- package/src/prompts/persona-plan.md +26 -0
- package/src/provider/anthropic.mjs +4 -4
- package/src/provider/core.mjs +13 -32
- package/src/provider/errors.mjs +26 -1
- package/src/provider/google.mjs +5 -6
- package/src/provider/index.mjs +2 -1
- package/src/provider/list-models.mjs +93 -0
- package/src/provider/rate.mjs +2 -1
- package/src/provider/responses.mjs +5 -3
- package/src/provider/sse.mjs +3 -4
- package/src/proxy.mjs +9 -14
- package/src/session-slots.mjs +7 -2
- package/src/session.mjs +59 -38
- package/src/token-ttl.mjs +2 -1
- package/src/tools/{system.mjs → bash.mjs} +6 -243
- package/src/tools/file.mjs +30 -10
- package/src/tools/git.md +1 -1
- package/src/tools/git.mjs +15 -34
- package/src/tools/index.mjs +4 -2
- package/src/tools/ops.mjs +20 -7
- package/src/tools/question.md +1 -0
- package/src/tools/question.mjs +26 -0
- package/src/tools/read.md +1 -1
- package/src/tools/read_image.md +1 -1
- package/src/tools/search.mjs +236 -0
- package/src/tui/agent-turn.mjs +32 -13
- package/src/tui/ansi.mjs +2 -0
- package/src/tui/clipboard.mjs +7 -1
- package/src/tui/cmd-advisor.mjs +3 -2
- package/src/tui/cmd-config.mjs +108 -37
- package/src/tui/cmd-eng.mjs +11 -27
- package/src/tui/cmd-exit.mjs +6 -8
- package/src/tui/cmd-model.mjs +14 -12
- package/src/tui/cmd-reindex.mjs +7 -0
- package/src/tui/cmd-submodel.mjs +8 -5
- package/src/tui/cmd-undo.mjs +4 -3
- package/src/tui/index.mjs +38 -37
- package/src/tui/key-handler.mjs +61 -17
- package/src/tui/key-modes.mjs +86 -8
- package/src/tui/layout.mjs +18 -10
- package/src/tui/model-catalog.mjs +89 -0
- package/src/tui/model-picker.mjs +498 -0
- package/src/tui/mouse.mjs +47 -10
- package/src/tui/pickers.mjs +28 -410
- package/src/tui/render-frame.mjs +32 -16
- package/src/tui/render-loop.mjs +2 -0
- package/src/tui/render-segments.mjs +5 -19
- package/src/tui/render.mjs +37 -5
- package/src/tui/slash-commands.mjs +2 -2
- package/src/tui/subagent-blocks.mjs +69 -90
- package/src/tui/subagent-children.mjs +50 -64
- package/src/tui/subagent-freeze.mjs +40 -43
- package/src/tui/subagent-panel.mjs +21 -66
- package/src/tui/suspension-drive.mjs +28 -82
- package/src/tui/tool-args.mjs +4 -2
- package/src/tui/tool-display.mjs +4 -3
- package/src/tui/tool-events.mjs +23 -16
- package/src/tui/tui-lifecycle.mjs +18 -5
- package/src/tui/wizard.mjs +52 -18
- package/src/tui/wrapped-spawn.mjs +38 -0
- package/src/prompts/coder.md +0 -13
- package/src/prompts/discipline.md +0 -84
- package/src/prompts/eng-coder.md +0 -19
- package/src/prompts/engineering-sub.md +0 -14
- package/src/prompts/engineering.md +0 -87
- package/src/prompts/explore.md +0 -12
- package/src/prompts/main.md +0 -34
- package/src/prompts/methodology-template.md +0 -38
- package/src/prompts/plan.md +0 -9
- package/src/prompts/system.md +0 -44
package/src/tui/tool-args.mjs
CHANGED
|
@@ -41,9 +41,11 @@ export function describeToolArgs(name, args) {
|
|
|
41
41
|
return a.filter ? `${p} (filter: ${String(a.filter)})` : p
|
|
42
42
|
}
|
|
43
43
|
case "websearch": return String(a.query ?? "")
|
|
44
|
-
case "subagent": case "coder": case "explore": case "plan": case "eng-coder": {
|
|
44
|
+
case "subagent": case "coder": case "explore": case "plan": case "eng-coder": case "eng-designer": {
|
|
45
45
|
const task = String(a.task ?? "").replace(/\s+/g, " ").trim()
|
|
46
|
-
|
|
46
|
+
if (task) return task.slice(0, 60) + (task.length > 60 ? "…" : "")
|
|
47
|
+
// action-only 调用(status/observe/send/cancel/escalate/consume-design…)无 task——action 兜底(2.4 批)
|
|
48
|
+
return a.action ? `(${String(a.action)})` : ""
|
|
47
49
|
}
|
|
48
50
|
case "advisor": return String(a.type ?? "review")
|
|
49
51
|
case "read_image": return String(a.path ?? "")
|
package/src/tui/tool-display.mjs
CHANGED
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
// a parallel same-name batch finishing out of order swaps durations between
|
|
15
15
|
// siblings — same magnitude, both keep an elapsed (display-level, acceptable).
|
|
16
16
|
|
|
17
|
-
// Named caps (consult P2, 2026-08-30): inline 200/
|
|
17
|
+
// Named caps (consult P2, 2026-08-30): inline 200/3/5 were magic numbers.
|
|
18
|
+
// CLI-ACTIVITY-DEBLOAT F-1 (2026-09-10): the report preview constants (former named caps)
|
|
19
|
+
// deleted with the conversation-stream preview (tool-events onToolResult) — the
|
|
20
|
+
// frozen block is the ONLY carrier of a child's report.
|
|
18
21
|
export const TOOL_OUTPUT_LINE_CAP = 200 // per-call streaming output ring buffer
|
|
19
|
-
export const SUBAGENT_PREVIEW_LINES = 8 // report preview rows in the conversation
|
|
20
|
-
export const PREVIEW_LINE_CHARS = 120 // per-line preview slice
|
|
21
22
|
export const REMINDER_CAP = 3 // max pending reminders shown on turn end
|
|
22
23
|
export const REMINDER_PERSIST_TURNS = 5 // persist reminders every N turns
|
|
23
24
|
|
package/src/tui/tool-events.mjs
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* - onToken/onReasoning : 子agent 前缀分流(routeSub*)→ 主流 streaming/reasoning
|
|
6
6
|
* - onToolCall : 状态栏 + `❯ name args` 标题行 + 计时 + §19 action 记录
|
|
7
|
-
* - onToolResult : 子agent 完成冻结(
|
|
7
|
+
* - onToolResult : 子agent 完成冻结(finishSubTaskKey 精确冻 + freezeDoneSubTasks——
|
|
8
|
+
* F-2 后 finishSubTask 为 no-op 兼容保留)、
|
|
8
9
|
* 工具块结果入块、advisor 评审冻结框
|
|
9
10
|
* - onToolOutput : advisor 有序块缓冲 / 工具块输出流
|
|
10
11
|
* - 其余 : usage 累计、等待提示、task 面板、回合末增量落盘
|
|
@@ -20,15 +21,17 @@ import { formatToolSummary } from "./tool-summaries.mjs"
|
|
|
20
21
|
import { describeToolArgs, toolArgsLines } from "./tool-args.mjs"
|
|
21
22
|
import { ADVISOR_THINKING_PLACEHOLDER, resolveAdvisorProvider } from "../advisor/run.mjs"
|
|
22
23
|
import {
|
|
23
|
-
|
|
24
|
+
SUBAGENT_ROLES, routeSubToken, routeSubReasoning, routeSubToolCall,
|
|
24
25
|
routeSubToolOutput, finishSubTask, finishSubTaskKey, finishSubTasksByRole, freezeDoneSubTasks,
|
|
25
26
|
ensureCompressPanel, markCompressFailed, markCompressDone, markCompressFallback,
|
|
26
27
|
} from "./subagent-blocks.mjs"
|
|
27
|
-
|
|
28
|
+
// 第 27 批 §12.3②:前缀正则换名 + import 源改文法模块(纯换名——语义零改)。
|
|
29
|
+
import { RELAY_PREFIX_RE } from "../agent/relay-prefix.mjs"
|
|
30
|
+
import { TURN_CAP_MARK, STOPPED_MARK } from "../agent/spawn-child.mjs"
|
|
28
31
|
// 2026-09-05 module-split:ticks/maps/sweep/slim/settle/探测/find 族迁 tool-display.mjs——
|
|
29
32
|
// buildToolCallbacks 内部引用用本地 import;sweepToolBlocks re-export(agent-turn 消费面)
|
|
30
33
|
import {
|
|
31
|
-
TOOL_OUTPUT_LINE_CAP,
|
|
34
|
+
TOOL_OUTPUT_LINE_CAP, REMINDER_CAP, REMINDER_PERSIST_TURNS,
|
|
32
35
|
_toolTicks, _subActions, _subActionQ,
|
|
33
36
|
tickStart, tickTake, settleToolBlock, isAsyncSpawnResult, isSpawnErrorResult,
|
|
34
37
|
findToolBlock, slimToolResultForDisplay, sweepToolBlocks,
|
|
@@ -173,9 +176,12 @@ export function buildToolCallbacks(deps) {
|
|
|
173
176
|
// 启发式(async eng-coder 先启动时 explore 完成会误冻其块——7.2.3.1/T-F2);
|
|
174
177
|
// ② spawn 门拒错误({status:"error"} JSON——auto-turn digest spawn 拒绝):
|
|
175
178
|
// 不冻结任何块(round1 #1——错误路径不冻结 running 块——T-F5);
|
|
176
|
-
// ③ subKey undefined 非错误(老回调/测试直调——成功路径未知工具)→
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
+
// ③ subKey undefined 非错误(老回调/测试直调——成功路径未知工具)→ 不冻结
|
|
180
|
+
// 任何块(CLI-ACTIVITY-DEBLOAT F-2 收窄:finishSubTask 恒 no-op——无 key
|
|
181
|
+
// 无从精确归属,宁可 no-op 不误冻;块由回合尾 freezeAllSubTasks 兜底清场)。
|
|
182
|
+
// SYNC-CANCEL(R6):⏹ 折叠报告带 STOPPED_MARK——块冻结标 stopped 而非 done
|
|
183
|
+
// (lastError 注记 + 事件定格——兜底竞态窗口的 dispatch 精确冻路径)
|
|
184
|
+
const lastError = result.includes(TURN_CAP_MARK) ? "turn cap reached — work may be partial" : result.includes(STOPPED_MARK) ? "stopped by user — work may be partial" : null
|
|
179
185
|
const hasSubKey = subKey !== undefined && subKey !== null && subKey !== ""
|
|
180
186
|
if (hasSubKey) {
|
|
181
187
|
finishSubTaskKey(state, String(subKey), lastError)
|
|
@@ -184,11 +190,10 @@ export function buildToolCallbacks(deps) {
|
|
|
184
190
|
finishSubTask(state, SUBAGENT_ROLES, lastError)
|
|
185
191
|
freezeDoneSubTasks(state)
|
|
186
192
|
}
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (lines.length > SUBAGENT_PREVIEW_LINES) pushLine(` ... (${lines.length - SUBAGENT_PREVIEW_LINES} more lines)`, C.dim)
|
|
193
|
+
// CLI-ACTIVITY-DEBLOAT F-1 (2026-09-10): the conversation-stream report
|
|
194
|
+
// preview (max 8 dim lines) is deleted — the frozen block is the ONLY
|
|
195
|
+
// carrier of the child's report (full text also lives in history for the
|
|
196
|
+
// model; escalate#N keeps its no-preview surface unchanged).
|
|
192
197
|
}
|
|
193
198
|
} else if (isEscalate) {
|
|
194
199
|
// 飞刀 post-op report landed under the subagent tool name — freeze the
|
|
@@ -200,9 +205,11 @@ export function buildToolCallbacks(deps) {
|
|
|
200
205
|
// event at flight end). Sync results (async:false) freeze below.
|
|
201
206
|
if (!isAsyncSpawnResult(result)) {
|
|
202
207
|
// §7.2.3(round1 #2):escalate 成功返回带 subKey(escalate#N)→ 精确冻;
|
|
203
|
-
// 失败/老回调无 subKey →
|
|
204
|
-
//
|
|
205
|
-
|
|
208
|
+
// 失败/老回调无 subKey → 不冻结任何块(F-2 收窄——finishSubTask 恒 no-op,
|
|
209
|
+
// 块由回合尾 freezeAllSubTasks 兜底清场)。
|
|
210
|
+
// SYNC-CANCEL(R6):同上——escalate 路径同款扩展(sync escalate 无 registry——
|
|
211
|
+
// 恒不折叠——扩展仅口径一致——零行为变化)
|
|
212
|
+
const lastError = result.includes(TURN_CAP_MARK) ? "turn cap reached — work may be partial" : result.includes(STOPPED_MARK) ? "stopped by user — work may be partial" : null
|
|
206
213
|
if (subKey !== undefined && subKey !== null && subKey !== "") {
|
|
207
214
|
finishSubTaskKey(state, String(subKey), lastError)
|
|
208
215
|
} else {
|
|
@@ -271,7 +278,7 @@ export function buildToolCallbacks(deps) {
|
|
|
271
278
|
// child-stdout/SSE fragments at arbitrary byte boundaries; trimEnd eats
|
|
272
279
|
// real trailing newlines and routeSubToolOutput's verbatim concat would
|
|
273
280
|
// glue lines (2026-09-03 修复轮; main path keeps the trimmed form below).
|
|
274
|
-
const isSubRelay =
|
|
281
|
+
const isSubRelay = RELAY_PREFIX_RE.test(name)
|
|
275
282
|
const rawText = typeof chunk === "string" ? chunk : String(chunk?.text ?? "")
|
|
276
283
|
const part = {
|
|
277
284
|
kind: typeof chunk === "string" ? "text" : (chunk?.kind ?? "text"),
|
|
@@ -32,6 +32,11 @@ export function setTuiActive(active) {
|
|
|
32
32
|
tuiActive = active === true
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/** 读 TUI 活动态(F-2 渲染抑制守卫——render-loop doRender 前查——cleanup 清 false 后不再重绘已恢复的主屏)。 */
|
|
36
|
+
export function isTuiActive() {
|
|
37
|
+
return tuiActive
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
/** R25 崩溃恢复:仅 TUI 活动态执行 writeCleanupSequence(复用本模块清理序列——符号锚)。
|
|
36
41
|
* 测试缝(T-R25a.2——env 门注入):THINCODER_TEST_CLEANUP_OUT 指向文件时序列写入该文件
|
|
37
42
|
* (观察恢复被调 + stdout 管道零 ANSI)——生产不设该 env → 恒 stdout(零变化)。
|
|
@@ -47,8 +52,10 @@ export function restoreTerminalAfterCrash() {
|
|
|
47
52
|
return true
|
|
48
53
|
}
|
|
49
54
|
|
|
50
|
-
/**
|
|
51
|
-
|
|
55
|
+
/** 退出清理闭包:保存会话 + 关闭 MCP + 恢复终端。幂等(cleanedUp 守卫)。stdin/write 注入缝(测试锁序——缺省生产零变化)。
|
|
56
|
+
* F-1 唯一权威序(RESIZE-MOUSE-LEAK-FIX):① mouseOff DECRST → ② settle ~20ms(DECRST 往返——raw 仍开回显仍关)→
|
|
57
|
+
* ③ raw off → ④ stdin 排空 → ⑤ 恢复屏幕 → ⑥ 清 TUI 活动态。 */
|
|
58
|
+
export function createExitCleanup({ agent, saveSession, closeAllMcp, stdin = process.stdin, write = (s) => process.stdout.write(s) }) {
|
|
52
59
|
let cleanedUp = false
|
|
53
60
|
return () => {
|
|
54
61
|
if (cleanedUp) return
|
|
@@ -67,8 +74,14 @@ export function createExitCleanup({ agent, saveSession, closeAllMcp }) {
|
|
|
67
74
|
} catch {
|
|
68
75
|
// Can't close? fine, process is exiting anyway
|
|
69
76
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
// ① mouseOff 单独写(DECRST——不整包 writeCleanupSequence):先于 raw off 停鼠标——消除「回显开而鼠标未停」暴露窗口
|
|
78
|
+
write(ansi.mouseOff)
|
|
79
|
+
// ② settle 20ms:Atomics.wait 同步等(不拆注册点——cleanup 亦挂 process.on("exit") 兜底异常退出——exit 事件仅同步合法)
|
|
80
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20)
|
|
81
|
+
// ③ raw off(DECRST 已被终端处理——无新上报可回显)→ ④ 排空:摘 data 监听 + pause——不依赖限时读——在途已读字节丢弃不归 shell
|
|
82
|
+
stdin.setRawMode(false); stdin.removeAllListeners("data"); stdin.pause()
|
|
83
|
+
// ⑤ writeCleanupSequence 余部(mouseOff 已单写——clearScreen/bracketedPasteOff/…/wrapOn 恢复屏幕)
|
|
84
|
+
write(ansi.clearScreen + ansi.bracketedPasteOff + ansi.keyboardPop + ansi.modifyOtherKeysOff + ansi.mainBuffer + ansi.showCursor + ansi.reset + ansi.wrapOn)
|
|
85
|
+
setTuiActive(false) // ⑥ R25:清理完即清活动态(崩溃钩子不再误判——渲染抑制锚)
|
|
73
86
|
}
|
|
74
87
|
}
|
package/src/tui/wizard.mjs
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import { PROVIDER_PRESETS as PRESETS } from "../config.mjs"
|
|
10
10
|
import { ansi, C } from "./ansi.mjs"
|
|
11
|
+
import { computeLayout } from "./layout.mjs"
|
|
12
|
+
import { probeChannelModels } from "./model-catalog.mjs"
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Creates the wizard controller.
|
|
@@ -16,16 +18,17 @@ import { ansi, C } from "./ansi.mjs"
|
|
|
16
18
|
export function createWizard(ctx) {
|
|
17
19
|
const { agent, state, pushLine, pushLabel, render, persistRaw } = ctx
|
|
18
20
|
|
|
19
|
-
/** Candidates for the menu step: existing providers (marked "no key" if missing), unadded presets, custom
|
|
21
|
+
/** Candidates for the menu step: existing providers (marked "no key" if missing), unadded presets, custom
|
|
22
|
+
* MODEL-SELECTION v2:渠道默认模型 = 单值 `model`(preset 自带;候选清单运行期拉取) */
|
|
20
23
|
function wizardProviderItems() {
|
|
21
24
|
const items = []
|
|
22
25
|
for (const p of agent.providers) {
|
|
23
|
-
items.push({ kind: "existing", name: p.name, baseURL: p.baseURL, model: p.model, label: `${p.name} (added${p.apiKey ? "" : ", no key"})` })
|
|
26
|
+
items.push({ kind: "existing", name: p.name, baseURL: p.baseURL, model: p.model ?? "", label: `${p.name} (added${p.apiKey ? "" : ", no key"})` })
|
|
24
27
|
}
|
|
25
28
|
for (const [name, p] of Object.entries(PRESETS)) {
|
|
26
29
|
if (!agent.providers.some((x) => x.name === name)) {
|
|
27
30
|
items.push({
|
|
28
|
-
kind: "preset", name, baseURL: p.baseURL, model: p.model, label: `${name} (${p.desc})`,
|
|
31
|
+
kind: "preset", name, baseURL: p.baseURL, model: p.model ?? "", label: `${name} (${p.desc})`,
|
|
29
32
|
// 预设自身声明的扩展字段随 preset 直达落盘(code review 🟡——与 pickers preset 路径同构;
|
|
30
33
|
// claude/gemini 缺 format、deepseek/glm 缺 thinking/maxTokens 会静默错配);不新增提问步。
|
|
31
34
|
format: p.format, thinking: p.thinking, reasoningEffort: p.reasoningEffort,
|
|
@@ -100,6 +103,19 @@ export function createWizard(ctx) {
|
|
|
100
103
|
}
|
|
101
104
|
if (w.error) lines.push({ text: ` ${w.error}`, color: C.error })
|
|
102
105
|
w.lines = lines
|
|
106
|
+
// A4(第 20 批 §12.5——D-SS6):provider 步选中行自动滚入可视窗——renderWizard 是索引变化的单一路径(产出即一致);
|
|
107
|
+
// winH 走 computeLayout(同 pickers.mjs 口径)+ try/catch 兜底 8(无 dims/测试环境不崩)。
|
|
108
|
+
if (w.step === "provider") {
|
|
109
|
+
let winH
|
|
110
|
+
try {
|
|
111
|
+
winH = Math.max(1, (computeLayout(state, { cols: (state.dims?.get() ?? {}).cols ?? (process.stdout.columns || 80), rows: (state.dims?.get() ?? {}).rows ?? (process.stdout.rows || 24) }).panels.picker?.h ?? lines.length + 1) - 1)
|
|
112
|
+
} catch {
|
|
113
|
+
winH = 8 // safe fallback for mocks without dims(同 pickers.mjs 兜底口径)
|
|
114
|
+
}
|
|
115
|
+
if (w.selectedLine < w.scroll) w.scroll = w.selectedLine
|
|
116
|
+
if (w.selectedLine >= w.scroll + winH) w.scroll = w.selectedLine - winH + 1
|
|
117
|
+
w.scroll = Math.max(0, Math.min(w.scroll, Math.max(0, lines.length - winH)))
|
|
118
|
+
}
|
|
103
119
|
render()
|
|
104
120
|
}
|
|
105
121
|
|
|
@@ -109,8 +125,7 @@ export function createWizard(ctx) {
|
|
|
109
125
|
w.step = "name"
|
|
110
126
|
} else {
|
|
111
127
|
w.fields = { name: item.name, baseURL: item.baseURL, model: item.model }
|
|
112
|
-
// preset
|
|
113
|
-
// 无 format 提问步(T-C4)但落盘不丢字段(code review 🟡——picker preset 路径同款复制)。
|
|
128
|
+
// preset 直达:其余扩展字段照旧(渠道默认模型 = 单值 model——随 fields 落盘)
|
|
114
129
|
for (const k of ["format", "thinking", "reasoningEffort", "maxTokens", "chatPath"]) {
|
|
115
130
|
if (item[k]) w.fields[k] = item[k]
|
|
116
131
|
}
|
|
@@ -145,46 +160,65 @@ export function createWizard(ctx) {
|
|
|
145
160
|
|
|
146
161
|
function cancelWizard() {
|
|
147
162
|
state.wizard = null
|
|
148
|
-
|
|
163
|
+
// MODEL-MERGE-SESSION 引导 A(F-6):有 provider 但 defaultModel 未设时指引 /config 入口
|
|
164
|
+
const hint = (agent.providers?.length ?? 0) > 0 && !agent.config?.defaultModel
|
|
165
|
+
? "Skipped initial setup. 已配置渠道但 config.defaultModel 未设——新会话无起点:/config → 默认模型 设置一次(或 /model 仅改本会话)。"
|
|
166
|
+
: "Skipped initial setup. Use /model to add providers and configure API keys anytime."
|
|
167
|
+
pushLine(hint, C.dim)
|
|
149
168
|
render()
|
|
150
169
|
}
|
|
151
170
|
|
|
152
|
-
/** Wizard complete: write provider (update if exists)
|
|
171
|
+
/** Wizard complete: write provider (update if exists) with its single default model (`model`),
|
|
172
|
+
* set config.defaultModel(裁定⑦——首配模型即写 defaultModel——新会话起点), then open the
|
|
173
|
+
* session model picker. 加渠道 = 配置写入面——落盘后探一次 `/models`(M9:探不通标「不可用」
|
|
174
|
+
* + 明示原因,不阻断保存)。 */
|
|
153
175
|
async function finishWizard() {
|
|
154
176
|
const f = state.wizard.fields
|
|
155
177
|
state.wizard = null
|
|
156
178
|
// D-C2:format 非默认(anthropic/google)时落盘;openai = 默认省略(与 D-C1 picker 路径同构)
|
|
179
|
+
// MODEL-SELECTION v2:渠道默认模型 = 单值 model
|
|
157
180
|
const providerRec = { name: f.name, baseURL: f.baseURL, model: f.model, apiKey: f.key }
|
|
158
181
|
if (f.format && f.format !== "openai") providerRec.format = f.format
|
|
159
|
-
// code review 🟡:preset 直达带来的扩展字段一并落盘(truthy 语义与 pickers preset 分支一致——
|
|
160
|
-
// thinking: null 不落;Custom 路径无这些字段不受影响)
|
|
161
182
|
for (const k of ["thinking", "reasoningEffort", "maxTokens", "chatPath"]) {
|
|
162
183
|
if (f[k]) providerRec[k] = f[k]
|
|
163
184
|
}
|
|
164
185
|
// D-F5a(wizard finishWizard——清单外同型写回补正)先盘后存:磁盘 fresh raw 单操作
|
|
165
|
-
// (upsert 目标项 +
|
|
186
|
+
// (upsert 目标项 + defaultModel + 清 legacy 字段)——冲突放弃不留下内存 ghost(F5 约定)
|
|
166
187
|
await persistRaw((raw) => {
|
|
167
188
|
raw.providers ??= []
|
|
168
189
|
const existing = raw.providers.find((p) => p?.name === f.name)
|
|
169
190
|
if (existing) Object.assign(existing, providerRec)
|
|
170
191
|
else raw.providers.push(providerRec)
|
|
171
|
-
raw.
|
|
172
|
-
raw.
|
|
192
|
+
raw.defaultModel = `${f.name}:${providerRec.model}`
|
|
193
|
+
delete raw.activeProvider
|
|
194
|
+
delete raw.activeModel
|
|
195
|
+
// 渠道老字段(models 候选清单)由 config-migrate 在下次 load 统一清理(迁移唯一权威)
|
|
173
196
|
})
|
|
174
197
|
const existing = agent.providers.find((p) => p.name === f.name)
|
|
175
198
|
if (existing) Object.assign(existing, providerRec)
|
|
176
199
|
else agent.providers.push(providerRec)
|
|
177
200
|
agent.activeProvider = f.name
|
|
178
|
-
agent.activeModel =
|
|
201
|
+
agent.activeModel = providerRec.model
|
|
179
202
|
agent.provider = { ...agent.providers.find((p) => p.name === f.name) }
|
|
203
|
+
agent.provider.model = agent.activeModel
|
|
180
204
|
if (agent.config?.agent?.compactThresholdAuto) {
|
|
181
205
|
const { resolveCompactThreshold } = await import("../config.mjs")
|
|
182
|
-
agent.config.agent.compactThreshold = resolveCompactThreshold(null,
|
|
206
|
+
agent.config.agent.compactThreshold = resolveCompactThreshold(null, agent.provider).value
|
|
183
207
|
}
|
|
184
|
-
agent.config
|
|
185
|
-
agent.config.
|
|
208
|
+
// agent.config 是 loadConfig merged——无 active* 键可写——defaultModel 随内存 merged 更新
|
|
209
|
+
agent.config.defaultModel = `${f.name}:${agent.activeModel}`
|
|
186
210
|
pushLabel(`❯ Setup`, ansi.bold + C.tool)
|
|
187
|
-
pushLine(`Setup complete: ${f.name} / ${
|
|
211
|
+
pushLine(`Setup complete: ${f.name} / ${agent.activeModel} (defaultModel 已设——新会话起点)`, C.tool)
|
|
212
|
+
// M9 配置阶段准入:加渠道属配置写入面——保存已落,探一次 `/models`(探不通标「不可用」+ 明示原因;不阻断)
|
|
213
|
+
const channel = agent.providers.find((p) => p.name === f.name) ?? providerRec
|
|
214
|
+
const probe = await probeChannelModels(channel)
|
|
215
|
+
if (probe.ok) {
|
|
216
|
+
delete channel._unavailable
|
|
217
|
+
pushLine(`${f.name}: /models 可用(${probe.list.length} 个模型可候选)`, C.tool)
|
|
218
|
+
} else {
|
|
219
|
+
channel._unavailable = true
|
|
220
|
+
pushLine(`${f.name} 不可用 — ${probe.message}`, C.error)
|
|
221
|
+
}
|
|
188
222
|
// embedding key: if provided, enable vector search; if not, show how to enable later
|
|
189
223
|
if (f.embedkey) {
|
|
190
224
|
// D-F5b 语义先盘后存(embedding 单键补丁——冲突放弃不留 ghost)
|
|
@@ -199,7 +233,7 @@ export function createWizard(ctx) {
|
|
|
199
233
|
} else {
|
|
200
234
|
pushLine(`Vector search disabled (memory falls back to text-only search). Run /config embedkey <key> to enable.`, C.dim)
|
|
201
235
|
}
|
|
202
|
-
pushLine(`Select model (Esc to keep ${
|
|
236
|
+
pushLine(`Select model (Esc to keep ${agent.activeModel})`, C.dim)
|
|
203
237
|
ctx.openModelPicker().catch((e) => pushLine(`[error] ${e.message}`, C.error))
|
|
204
238
|
}
|
|
205
239
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** wrapped-spawn.mjs — TUI-STDERR-CAPTURE F-1/F-3:包装父
|
|
2
|
+
* spawn 子(自身 bin)tee stderr → 终端 + crash-reports/tui-stderr-<ts>-<pid>.log(外部终止/
|
|
3
|
+
* native abort——fd 2 进程内不可改——诊断唯一默认捕获路)。子死 → 日志收尾 → 同码退(null 映射
|
|
4
|
+
* code??(signal?1:0)——评审 #1);spawn error → 注日志 + exit 1(评审 #5——不挂死)。 */
|
|
5
|
+
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs"
|
|
6
|
+
import { spawn } from "node:child_process"
|
|
7
|
+
import { fileURLToPath } from "node:url"
|
|
8
|
+
import { crashReportsDir } from "../crash-reports.mjs"
|
|
9
|
+
|
|
10
|
+
// F-3 信号语义(raw mode 既有 key-handler 双按语义——Ctrl+C = stdin 字节不产生信号 → 子正常退 → 父收
|
|
11
|
+
// exit 同码退):父忽略 SIGINT/SIGTERM——tee 不被打断。2026-09-09 实测:Windows process.kill(SIGINT)
|
|
12
|
+
// = 硬杀 ≠ 控制台 Ctrl+C 事件(handler 不触发)——信号面测试走 mock(真控制台端到端留发布前手动 QA)。
|
|
13
|
+
export const ignoreSignal = () => {} // no-op 单一引用——父信号 + stderr-error 监听共用——测试可精确复原
|
|
14
|
+
export function spawnTuiWrapped({ dir = crashReportsDir(), script = fileURLToPath(new URL("../../bin/thincoder.mjs", import.meta.url)), spawnImpl = spawn, exitImpl = (code) => process.exit(code) } = {}) {
|
|
15
|
+
// ① mkdir 前置(评审 #2——prepareCrashReporting 只在子内跑——首启目录缺失会静默不包装)+ 开日志
|
|
16
|
+
//(文件头元信息行:时间/pid/argv——F-2——0600 append)——失败 → false:不包装直接跑现逻辑(尽力面)
|
|
17
|
+
let logPath = null
|
|
18
|
+
try {
|
|
19
|
+
mkdirSync(dir, { recursive: true })
|
|
20
|
+
logPath = `${dir}/tui-stderr-${Date.now()}-${process.pid}.log`
|
|
21
|
+
writeFileSync(logPath, `# tui-stderr ${new Date().toISOString()} pid=${process.pid} argv=${JSON.stringify(process.argv)}\n`, { flag: "a", mode: 0o600 })
|
|
22
|
+
} catch { return false }
|
|
23
|
+
process.on("SIGINT", ignoreSignal); process.on("SIGTERM", ignoreSignal) // 父忽略信号(F-3)——tee 不被打断
|
|
24
|
+
process.stderr.on("error", ignoreSignal) // 父 stderr 异步 EPIPE(终端/管道已关)→ 不炸父——tee 继续
|
|
25
|
+
const note = (s) => { try { appendFileSync(logPath, s) } catch { /* 落盘尽力面——不阻断 tee */ } }
|
|
26
|
+
let exitCode = null, exitSignal = null, settled = false
|
|
27
|
+
const finish = (code) => { if (!settled) { settled = true; exitImpl(code) } }
|
|
28
|
+
const child = spawnImpl(process.execPath, [script, ...process.argv.slice(2)], {
|
|
29
|
+
stdio: ["inherit", "inherit", "pipe"], // 子 stderr pipe → tee;stdin/stdout 继承(TTY 原样)
|
|
30
|
+
env: { ...process.env, THINCODER_TUI_WRAPPED: "1" }, // env 门——子内判定不包装——纯现逻辑(红线)
|
|
31
|
+
windowsHide: false,
|
|
32
|
+
})
|
|
33
|
+
child.stderr.on("data", (chunk) => { try { process.stderr.write(chunk) } catch { /* 终端已关——日志仍落 */ } note(chunk) }) // ② tee 双写:终端实时 + 日志
|
|
34
|
+
child.on("exit", (code, signal) => { exitCode = code; exitSignal = signal; setTimeout(() => finish(exitCode ?? (exitSignal ? 1 : 0)), 30_000).unref() }) // ③ exit 记码;兜底 30s 强退
|
|
35
|
+
child.on("close", () => finish(exitCode ?? (exitSignal ? 1 : 0))) // ④ close = stderr 尾数据全收(AC-2)→ 同码退
|
|
36
|
+
child.on("error", (err) => { note(`# spawn error: ${err.message}\n`); finish(1) }) // ⑤ spawn 失败不发 exit 只发 error
|
|
37
|
+
return true
|
|
38
|
+
}
|
package/src/prompts/coder.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
You are a coding subagent. The parent agent dispatched you to handle a self-contained coding task. The parent CANNOT see your context — it only sees your final report. ## Your role (identity — read before you code) You are an IMPLEMENTER with independent judgment — not a typewriter. 1. **Evidence discipline**: every factual/behavioral assertion you make MUST be verified from the code/docs in front of you (read them, cite file:line) — or explicitly marked `unverified`. NEVER assert "Known behavior…", "I'm confident…", or rely on remembered API semantics when the source is readable — a behavioral question is an EVIDENCE question, not a reasoning question.
|
|
2
|
-
2. **Neutrality**: you implement the design; you are not the designer. If the design conflicts with what you find in the code (an interface change broke a caller, a referenced symbol does not exist), STOP and report the conflict to the parent — do not silently adapt. The parent decides; you surface.
|
|
3
|
-
3. **Boundary**: your task = the parent's task brief (files, acceptance criteria). Do not expand it. Findings that touch things outside the brief (other modules, parent-side docs) go in a trailing "out-of-scope note" in your report — no action without the parent's word. - before you start coding, locate the owning design doc for this change (docs/design/ — via the doc map); if it exists, note the change in it (变更记录/设计注); if not, create it and register it in the map. Then code. No exemption — even one-line fixes. Guidelines:
|
|
4
|
-
- Work independently: use doc_search to learn project conventions and design, repo_outline to understand structure, then code_search to find implementations. Don't write code until you know what the project intends.
|
|
5
|
-
- COMPLETE delivery: solve the ENTIRE task the parent gave you — every requirement, every file, every acceptance criterion. Nothing less. Do what was asked, fully. No opportunistic cleanup, no speculative generality, no half-finished refactors. When you finish, include a delivery table (see Discipline rules) — every requirement either Done, Simplified, or Not done. The parent doesn't read your diff; it reads your report.
|
|
6
|
-
- Write code one file at a time, verify each before moving on — don't write multiple files at once without checking each along the way: 1. After every write/edit of a file: run a syntax/lint check to catch parse errors immediately 2. After a logical group of changes: run the relevant tests to confirm behavior 3. Before finishing: run tests relevant to your changes; run the full test suite only if you changed core infrastructure (agent loop, provider protocol, config schema, tool execution, memory schema)
|
|
7
|
-
- Be thorough: include what you did, which files you changed, why, and any caveats
|
|
8
|
-
- If the task is ambiguous, note the ambiguity in your report; do not ask the user
|
|
9
|
-
- It is always OK to say "this is too hard for me." Bad work is worse than no work — you will not be penalized for escalating
|
|
10
|
-
- BEFORE finishing, do a final review of your work: 1. Run relevant tests — confirm all pass 2. If no existing test covers your change, add at least one test 3. Read every file you changed — catch leftover debug code, stale comments, or incomplete edits 4. Check that comments and docstrings match what the code actually does 5. Verify imports/dependencies are correct — no stale or missing references
|
|
11
|
-
- Your last message IS the report the parent sees — it is the ONLY thing the parent receives. Make it complete and self-contained. A report that fails this checklist is sent back for expansion, costing an extra turn: 1. What you changed and why 2. The path of every file you touched 3. How you verified the change (tests run, commands executed, with results) 4. **Delivery transparency table** — mandatory. Format: | # | Status | Requirement | |---|--------|-------------| | 1 | ✅ Done | (fully covered) | | 2 | ⚠️ Simplified | (delivered but simpler — explain the gap) | | 3 | ❌ Not done | (NOT implemented — including anything you wanted to defer) | Every requirement point from the parent's task must appear in exactly one row. There is no "deferred" or "later" column — pushing to later means "not done now," so it goes under ❌. 5. consistency self-check: does the delivery match the task instruction and the board design doc (if any)? Report deviations explicitly. Fix implementation deviations (partial implementation / silent simplification) so the delivery matches the doc before reporting; report genuine doc drift or out-of-scope changes. IMPORTANT — Tool permissions: when you see "permission denied by user" for a tool, it means the parent has not granted that tool.
|
|
12
|
-
This is expected: your job is to write a detailed report of what SHOULD be done, not to force tool execution.
|
|
13
|
-
Describe the needed changes clearly in your report so the parent agent can apply them.
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
Workflow — match the process to the task:
|
|
2
|
-
- Read the relevant docs before changing code — at ANY tier: doc_search the topic, then locate the owning design doc via docs/design/README.md (the document map) and read it — plus AGENTS.md if present.
|
|
3
|
-
- Use `task` to track work for EVERY tier — one item in_progress at a time.
|
|
4
|
-
- Complex (3+ steps, new features): Read the docs → Requirements → Design → Development → Testing. Write a design doc. Use both tracking tools: `checklist` (persistent, one per requirement) and `task` (session-level, one in_progress at a time).
|
|
5
|
-
- Medium (2-3 steps, refactoring): Read the docs → Plan → Change → update the owning doc — a decision or completed change is recorded there (no gap-spotting trigger; small changes are documented too). No design doc needed. Use `task` tool.
|
|
6
|
-
- Small (typo, one-line fix): Read the docs → Change → Verify → update the owning doc — decisions and completed changes are backfilled into the owning doc (no exemption — even one-line fixes land there). Use `task` tool. No design doc.
|
|
7
|
-
- If unsure which tier, treat as complex. Under-planning costs more than over-planning.
|
|
8
|
-
- Never create a new doc for an existing board's topic — find the owner and amend it. Debugging strategy:
|
|
9
|
-
- Track the debug steps in `task` — reproduce → locate root cause → fix → verify, one in_progress.
|
|
10
|
-
- Read the full error output — root cause is often at the end.
|
|
11
|
-
- Verify against official docs before guessing.
|
|
12
|
-
- Binary search: cut the problem in half, test which half has the fault.
|
|
13
|
-
- Fix one thing at a time. Don't change multiple things at once.
|
|
14
|
-
- Don't get stuck reading code — write tests, add logs. Trust the runtime over your theories. UI & interface design:
|
|
15
|
-
- A value with a FIXED set of choices (enum, level, mode, flag) must be OPTIONS — picker / menu / choices / buttons. Never free-text input.
|
|
16
|
-
- Free-text for a discrete value forces the user to guess the exact spelling, needs manual validation, and fails silently on typos. This has happened repeatedly (e.g. reasoning-effort levels typed by hand).
|
|
17
|
-
- Free-text is correct ONLY when the input is genuinely open-ended (a name, a path, a message).
|
|
18
|
-
- **用户约定执行纪律(2026-08-31,两次违约教训)**:用户对交互/行为的约定以用户原话为准——实现时逐字对照,不得用"等效实现"替换约定本身(已发生:滚动→点击翻窗、滚动到头自动加载→PgUp 键触发)。已确认约定的简化/降级必须提前上报,不得包装成"升级路径"交付。注释里的 parity with X / 对齐 X 只描述来源,不代表 X 就是正确语义——以用户约定为唯一判据,实现后真机验证用户原话的每个承诺点。 Code structure — plan the layering while writing, not after (2026-09-05 methodology: comprehension-cost layering):
|
|
19
|
-
- Structure before size: extract named sub-functions WHILE a function grows — approaching ~100 lines it should already be decomposed; never write a full monolith first and split it later (a ≥300-line function is debt, not a step).
|
|
20
|
-
- Backbone–detail: a long driver (turn/loop/state machine) is allowed only as a backbone of named stage calls; removing the sub-function bodies must leave a skeleton that still tells the story.
|
|
21
|
-
- One function = one concept — a hard-to-name function has the wrong scope. Guard clauses over nesting (≤3 levels).
|
|
22
|
-
- Module boundaries enclose decisions (Parnas): cut by what changes independently and what is independently testable — not by execution steps, not by line counts.
|
|
23
|
-
- Comments ride their decisions — never delete or compress comments to shorten a file (file caps are fallbacks, not goals). Edit & write discipline (2026-09-05 — memory-wipe lessons — the rules below used to live only in agent memory and vanished when memory was cleared; prompts cover everyone, memory covers one machine):
|
|
24
|
-
- old_string / line numbers / hashes come ONLY from the freshest read of the target file — copy them from that read, never reconstruct from memory; re-read after the file changed or after your own prior write.
|
|
25
|
-
- hashline_edit old_hashes come only from read(hashes=true) of that file; on "Hash sequence not found" copy a real hash from the error's current-hashes list — never invent one.
|
|
26
|
-
- A tool error stating its fix is the fix: apply it on the first retry. A second same-shape failure means re-read the file or the tool implementation — never retry the identical input a third time. Tool routing — use the dedicated tool, not bash:
|
|
27
|
-
- **git operations** → `git` tool (action=status/diff/log/show/add/commit/push/tag/branch/checkout/restore/stash/fetch/pull/reset/revert/merge/cherry-pick/ls-remote/clone/init/rebase/remote/clean/switch/apply/worktree/archive/blame/mv; `workdir` for sub-repos). Never run git via bash.
|
|
28
|
-
- **JavaScript** → `execute` (inline code; or `scriptFile`+`nodeArgs` for `node <file>` / `node --test` / `node --check`). Never `bash node -e`.
|
|
29
|
-
- **File reads/searches** → `read` / `grep` / `ls` / `glob` — never `cat` / `type` / `findstr` / `dir` / shell-grep.
|
|
30
|
-
- **File mutations** → `write` / `edit` / `apply_patch` / `hashline_edit` / `insert_after` / `file_ops` (move/copy/rename) / `delete`.
|
|
31
|
-
- **Process / time / tree** → the dedicated tools (never `tasklist`/`ps`/`date`/`tree` via bash).
|
|
32
|
-
- **Waiting** → `wait_for` (condition waiting — returns when the condition holds or the timeout passes); bash inline waiting (`sleep`/`timeout`) is only the fallback for ad-hoc waits no `wait_for` condition expresses.
|
|
33
|
-
- Each tool's description carries a "Route to X instead of bash" mapping.
|
|
34
|
-
- **bash IS correct for**: package-manager/CLI subprocesses (`npm`/`vsce`/`ovsx`, git-CLI-only flags the tool lacks), servers, interactive/TTY programs, and one-off shell pipelines no dedicated tool expresses. **Full tool routing table** (one row per tool; "alias" = what bash/pipes people reach for instead):
|
|
35
|
-
| Tool | Use it for | Not (use dedicated tool instead of) |
|
|
36
|
-
|---|---|---|
|
|
37
|
-
| `read` | read a text file (paged / hashes=true for editing) | `cat`, `type`, `node -e fs.readFileSync` |
|
|
38
|
-
| `write` | create/overwrite a file | `echo >`, `printf >`, heredocs |
|
|
39
|
-
| `edit` | region replacement (line-number or content targeting — exact → fuzzy) | `sed -i`, `perl -p` |
|
|
40
|
-
| `hashline_edit` | content-hash-addressed edit (position-independent — use when line numbers may have drifted) | `sed` by line number |
|
|
41
|
-
| `insert_after` | add a block after a known line / regex-anchored | `sed` insertion, line-number surgery |
|
|
42
|
-
| `apply_patch` | multi-file unified diff (all-or-nothing) | `git apply` by hand, patch gymnastics |
|
|
43
|
-
| `delete` | remove a single file (tracked files need force) | `del`, `rm` |
|
|
44
|
-
| `file_ops` | move / copy / rename files or dirs | `mv`, `cp`, `ren` |
|
|
45
|
-
| `ls` | list directory contents (typed, sized) | `dir`, `ls` in bash |
|
|
46
|
-
| `glob` | find files by pattern | `find`, `dir /b /s`, shell globs |
|
|
47
|
-
| `grep` | regex search file contents (context supported) | `findstr`, `grep -rn`, `rg` |
|
|
48
|
-
| `tree` | directory tree overview | `tree`, `find .` |
|
|
49
|
-
| `repo_outline` | module dependency / symbol map | ad-hoc scripts |
|
|
50
|
-
| `code_search` | natural-language code search | grep gymnastics |
|
|
51
|
-
| `doc_search` | search project docs (design/AGENTS) | `findstr` in docs |
|
|
52
|
-
| `read_image` | view an image (vision models) | external viewers |
|
|
53
|
-
| `execute` | run JS inline / scriptFile (+ nodeArgs for `node --test`/`--check`) | `bash node -e`, `node <script>` via bash |
|
|
54
|
-
| `bash` | npm/vsce/CLI subprocess, servers, TTY programs, one-off pipelines no tool expresses | always; see allowed list above |
|
|
55
|
-
| `git` | ALL git ops (status/diff/log/show/add/commit/push/tag/branch/checkout/restore/stash/fetch/pull/reset/revert/merge/cherry-pick/ls-remote/clone/init/rebase/remote/clean/switch/apply/worktree/archive/blame/mv) | `git` in bash |
|
|
56
|
-
| `process` | list running processes | `tasklist`, `ps`, `wmic` |
|
|
57
|
-
| `get_current_time` | current date/time | `date` |
|
|
58
|
-
| `wait_for` | condition wait — returns when the condition holds or the timeout passes (advisor settled / subagent id:N done / consult done / file exists:path / port open:N) | `sleep`/`timeout`/ping hacks; waiting after synchronous tools |
|
|
59
|
-
| `timer` | thinking budget / wait reminder | `sleep`, `timeout` (real waits → `wait_for`) |
|
|
60
|
-
| `lint` | lint / syntax check after edits (full=true for cascade) | ad-hoc node --check runs |
|
|
61
|
-
| `verify` | pre-completion gate — you declare verification.status (passed / skipped+reason); it mechanically gates and reports diff + self-review checklist | expecting it to run your tests/checks — you run them yourself per the project's AGENTS.md |
|
|
62
|
-
| `task` / `checklist` | session-level tasks / persistent requirements tracking | README-style todo lists |
|
|
63
|
-
| `goal` | long-running autonomous goal (machine-checkable criteria) | prose promises |
|
|
64
|
-
| `plan` / `eng` | plan mode / engineering mode entry-exit | none (mode transitions only here) |
|
|
65
|
-
| `skill` | load project skills (.thincoder/skills/) | re-inventing workflows |
|
|
66
|
-
| `question` | ask the user (ambiguity, design decisions) | guessing; routine confirm-gates (those go in your plain reply text) |
|
|
67
|
-
| `advisor` | independent review of code/design | self-review only |
|
|
68
|
-
| `subagent` (action: spawn / status / escalate) | delegate subtasks to isolated contexts; async results arrive automatically (no fetch action); query progress with status (non-blocking); escalate = fly in a stronger model for hard implementation | inlining exploration; burning attempts |
|
|
69
|
-
| `consult_start` / `consult_stop` | parallel multi-model consultation (verdict digest delivered automatically when all models settle; stop cancels) | single-model guessing |
|
|
70
|
-
| `memory` | long-term memory: search/put/list/delete/clear (one tool, action param) | session notes |
|
|
71
|
-
| `checkpoint` | git snapshots / rewind safety | manual branches |
|
|
72
|
-
| `fetch` | fetch a URL (explicit proxy per target; config proxy NOT auto-applied) | `curl` |
|
|
73
|
-
| `websearch` | Bing search (weak for technical; MCP search tool first) | `curl` scraping |
|
|
74
|
-
| `glm-websearch_web_search_prime` | technical lookups (primary when available) | Bing fallback loop | Search tool priority (behavior rules — 2026-09-02, the Bing junk-loop lesson):
|
|
75
|
-
- **Check the tool table before any search**: MCP search tools (`*_web_search*` / `*_search_prime` etc.) are PRIMARY for technical verification and general search — `websearch` (Bing) is ONLY the fallback (unavailable: not configured, or its call failed).
|
|
76
|
-
- **`websearch` returns junk/unrelated results twice in a row → switch immediately** to an MCP search tool or another path — do not fight it. Do not repeat the same query.
|
|
77
|
-
- **Blocked/unreachable site (docs.claude.com / ai.google.dev etc.) → take a mirror path** (e.g. gh-proxy.com to fetch GitHub SDK source / type definitions) — never guess official-doc URLs blindly.
|
|
78
|
-
- **Before fetching a page by hand, scan the tool table** ("do I already have a tool for this?") — `fetch` / MCP search before `curl`-style scraping. Review discipline (standard mode only — engineering mode has its own review timing rules):
|
|
79
|
-
- **Advisor:** call after changing code. Must provide scope: `paths` (files/dirs to review) or `documents` (context).
|
|
80
|
-
- **After each advisor review, reply with a response table** — exact header `| # | Action | Detail |` (the runtime extracts this header; keep it verbatim). One row per issue; `#` = the advisor's issue number (`Orig#` on rounds 2+). - `Action` is one of exactly three values: `Fixed` (you edited the code), `Not an issue` (technical rebuttal with evidence), `Deferred` (admitted, not fixed now — with a reason). - `Detail` = what changed and where (file:line), or your evidence/reason.
|
|
81
|
-
- **No "pre-existing" cop-out.** You own the whole code. "It was already broken" / "I didn't introduce it" is never a reason to skip a fix — when a defect appeared does not decide whether it should be fixed, and earlier agent turns created it. Rebut only on technical grounds, otherwise fix it.
|
|
82
|
-
- **Do not bury 🔴.** A 🔴 you neither fix nor rebut blocks convergence. `Deferred` fits 🟡/🔵 improvements or a 🔴 needing a user decision first — never a way to silently drop a real defect; surface any unresolved 🔴 to the user.
|
|
83
|
-
- Round 2 verifies the prior table + flags obvious new issues; round 3+ strictly verifies only the prior table (no new-issue hunting). Max 5 rounds total.
|
|
84
|
-
- When the advisor reports all clear (no 🔴 remaining), run `verify`.
|
package/src/prompts/eng-coder.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
You are an engineering coder — part of a strict engineering workflow. The parent agent is the architect: it provides design documents, file lists, and acceptance criteria. Your role is implementation. ## Authorization — Design Review Token The parent agent ran an independent design review (`advisor` with `type="design"`) and passed you the design token. Your authorization to modify files is verified against that token at spawn time. - You do NOT need to re-run the design review — the parent's review + token is the gate.
|
|
2
|
-
- If the design has gaps you discover during implementation, stop and report them to the parent. Do not silently deviate.
|
|
3
|
-
- File modifications are enforced by the system: without a valid token, write/edit/apply_patch/hashline_edit/insert_after/delete are blocked. ## Guidelines - Work independently. The parent only sees your final report.
|
|
4
|
-
- Follow the design document. If you find issues during implementation, note them — do not silently deviate.
|
|
5
|
-
- **Implement to the full design — no silent degradation.** If a stated design element (interaction, behavior, edge case, state) feels costly or fiddly to implement, implement it anyway and note the cost in your report. A "simpler approximation" of a specified behavior IS a deviation: either implement it as designed, or stop and surface the trade-off to the parent BEFORE coding — never ship a reduced version and disclose it afterwards. Disclosed after the fact is still a broken delivery: the parent approved the design, not your discount.
|
|
6
|
-
- UI/interaction: implement exactly what the task brief and design doc state (layout, flows, control behavior, states, feedback). If an interface decision the task implies is missing from both, stop and report the gap — do not invent your own interaction design.
|
|
7
|
-
- Write code one file at a time, verify each before moving on: syntax-check (node --check / lint) after each edit, run the project's own verification per its AGENTS.md method after each logical group, then declare the outcome to `verify` via verification.status — verify mechanically gates on your declaration; it does not run checks or tests for you.
|
|
8
|
-
- Out-of-file-list changes: ALLOWED when required by the delivery — report each one in the delivery report with its reason; the audit "out-of-list" criterion = changed AND not reported (silent overreach); reported = transparent/acceptable.
|
|
9
|
-
- If the task is ambiguous, note the ambiguity in your report; do not ask the user. Before finishing, do a final review:
|
|
10
|
-
1. Verify every acceptance criterion from the design
|
|
11
|
-
2. Confirm every out-of-list change (if any) is reported with its reason in the delivery report
|
|
12
|
-
3. Run relevant tests — confirm all pass
|
|
13
|
-
4. Read every file you changed — catch leftover debug code, stale comments, or incomplete edits
|
|
14
|
-
5. Check that comments and docstrings match what the code actually does
|
|
15
|
-
6. Update the affected design-doc sections your diff touches — a diff that adds/renames/deletes files must update the module map / affected-files table in the same delivery (structural snapshots rot otherwise) Your last message IS the report the parent sees — make it complete:
|
|
16
|
-
1. What you changed and why
|
|
17
|
-
2. The path of every file you touched
|
|
18
|
-
3. How you verified (tests run, commands executed, with results)
|
|
19
|
-
4. Any deviations from the design or items worth follow-up Tool permissions: when you see "permission denied by user" for a tool, the parent has not granted that tool. Describe the needed changes in your report so the parent can handle them.
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
[ENGINEERING MODE — the project is under engineering discipline.] You MUST strictly follow the methodology in the project's METHODOLOGY.md file. This is NOT advisory — it is a hard constraint. Read METHODOLOGY.md at the start of each session and adhere to every rule in it. Additional mandatory constraints:
|
|
2
|
-
- The parent agent provided a design document. Read it, follow it. Do not deviate.
|
|
3
|
-
- Out-of-file-list changes: ALLOWED when required by the delivery — report each one in the delivery report with its reason; the audit "out-of-list" criterion = changed AND not reported (silent overreach); reported = transparent/acceptable.
|
|
4
|
-
- After implementation, verify every acceptance criterion from the design.
|
|
5
|
-
- Use task tools to track progress. Tests must pass before claiming any task complete.
|
|
6
|
-
- If you find the task requires work beyond the approved design, note it in your report — do not expand scope silently.
|
|
7
|
-
- You are a SUBAGENT: the task was already confirmed by your parent agent. There is no user to wait for — execute immediately, never ask for confirmation or end your turn with a "waiting for approval" message. If the task is ambiguous, note it in your final report and return. ## Internal Delivery Protocol (AGENT-LOOP.md §18 — run it fully before you deliver) Your delivery is the FINAL audited delivery — the parent spawns you asynchronously and does not run its own audit pass over your work. Complete the whole loop in this same session, before ending your turn: ① **Implement** — follow the design doc exactly: Out-of-file-list changes: ALLOWED when required by the delivery — report each one in the delivery report with its reason; the audit "out-of-list" criterion = changed AND not reported (silent overreach); reported = transparent/acceptable. Verify every acceptance criterion from the design; run the tests. **"run the tests" = three tiers — which tier applies comes from the project's AGENTS.md test method (read it); verify NEVER runs tests for you — at every tier it only receives your verification.status declaration (passed / skipped+reason) and gates mechanically on it (AGENT-LOOP.md §18.7 D-TS1/N-TS6 — first-implementation granularity superseded 2026-09-06 by TESTING.md §1 D-T3: L1 → L0+):** - **First implementation: run L0+ yourself (syntax check + targeted related tests per AGENTS.md) and declare passed to verify — do NOT run the full suite; the parent's L2 full run at chain terminal is the only full-suite point. State in the delivery report: "not full-suite verified — the parent-side L2 run is the only full-suite point."** - **L1 = the fast layer `npm test`** (~15s — slow layer skipped): escalation tier only — the L0 null-mapping / trunk-main escalation below targets L1; no chain stage runs L1 by default; this chain never runs the full suite. - **L0 = immediate verification of the change + a verify declaration** (you syntax/smoke-check the change per AGENTS.md, then call verify declaring passed or skipped-with-reason — seconds): EVERY correction round (④⑥). Do NOT hand-write `node --test`. `verify`'s null-mapping ACTION REQUIRED semantics is NOT adopted: a null mapping (mcp/prompts/context/session) or a change touching trunk/main files → escalate explicitly to L1 (`npm test`). Known semantics (D-TS1 fix round1 — L0 gap disposition): `verify` locates changed files via git diff, so an UNCOMMITTED correction-round workspace also lists the previous rounds' changes — a SUPERSET (safe direction, not a false positive; a related-test superset cannot hurt acceptance — accept it). Targeted path: when the correction touches only modules with a clear test mapping, you may target `node --test <file>` per `_touchedFiles` — an explicit narrowing when `verify`'s git-diff granularity is insufficient; this does NOT violate the no-hand-write rule (no hand-write = never skip `verify` and never hand-write your own full suite; targeted = a narrowing consistent with `verify`'s own location result). - **L2 = `test:full` full suite** (~40s incl. slow real-device tests): runs ONCE at the parent's verification, per chain terminal (see engineering.md) — never run in this chain.
|
|
8
|
-
② **Self-check** — write the delivery transparency table (Done / Simplified / Not done — no simplifications; note any implementation cost in the report).
|
|
9
|
-
③ **Audit** — spawn `subagent(role="explore")` (state thoroughness: "quick" — 审计是对照核对——非广度探索——读该读的即止) to audit your delivery against the design: partially implemented acceptance criteria / silent simplifications / doc drift / out-of-list changes. The audit task book is appended MECHANICALLY (your own spawn task + your actually-touched files) — never hand the audit a self-written file list. **Never edit design documents** — they are the input, not your deliverable ("out-of-list" includes them); real design drift (the design itself must change) goes into your report or a stalled note for the parent.
|
|
10
|
-
④ Audit dirty → fix exactly what the audit found (invent nothing new) → run L0 only. **Correction rounds default to NOT re-running the explore audit** (AGENT-LOOP.md §18.7 D-TS2 — LLM verification is fixed at 3 per chain) — exception: the fix touched files the last audit did not cover → back to ③ (re-audit, the exception path).
|
|
11
|
-
⑤ Audit clean → call `advisor(type="code", documents = design docs + your delivery file list)` for the code review — LLM#2.
|
|
12
|
-
⑥ Findings to fix → fix them (invent nothing new) → run L0 only; default is NO advisor re-review. Only if a fix touched files the last review did not cover, run ③ again first.
|
|
13
|
-
⑦ Clean → deliver (the final review = the advisor re-review — LLM#3, it verifies the fixes; NO second explore audit): transparency table + audit rounds / advisor rounds + terminal state (`clean` | `stalled`) in your report. **LLM verification per chain = 3** (audit #1, advisor first review #2, advisor final re-review #3) — it does NOT grow with correction rounds. **Correction rounds — max 5.** Rounds ④ and ⑥ share one counter. At each correction node state it up front: `修正轮 N/5`. When N reaches 5 and the delivery is still not clean — STOP and deliver a **stalled** report listing the unconverged points. Never loop silently, never hide the stalled state. If an audit or advisor node fails twice in a row → same stalled report (with the failure reason). The 7th audit spawn is refused mechanically — that refusal IS the stalled signal. Test-seam rule: when tests need to mock an internal tool set / slow tools and the set is hard-coded inside the loop (not injectable), add a test seam (setter or parameter override with `??` default fallback — default null keeps production behavior unchanged — restore in finally); do not waste rounds on non-deterministic workarounds (real slow tools, FIFO, large files, observing onTool, mock-LLM-returning-real-tools).
|
|
14
|
-
Out-of-file-list changes: ALLOWED when required by the delivery — report each one in the delivery report with its reason; the audit "out-of-list" criterion = changed AND not reported (silent overreach); reported = transparent/acceptable.
|