@thincoder/core 0.10.12 → 0.11.1

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.
Files changed (174) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/advisor/messages.mjs +4 -2
  3. package/advisor/notice.mjs +1 -1
  4. package/advisor/project-context.mjs +121 -11
  5. package/agent/completion.mjs +13 -1
  6. package/agent/digest-account.mjs +73 -46
  7. package/agent/dispatch-run.mjs +1 -1
  8. package/agent/dispatch.mjs +29 -21
  9. package/agent/family-tools.mjs +9 -5
  10. package/agent/helpers.mjs +1 -94
  11. package/agent/offload.mjs +88 -0
  12. package/agent/record-results.mjs +1 -6
  13. package/agent/run-stages.mjs +74 -49
  14. package/agent/run-start.mjs +8 -8
  15. package/agent/setup.mjs +6 -11
  16. package/agent/suspension.mjs +1 -1
  17. package/agent/turn-loop.mjs +35 -3
  18. package/agent/write-face.mjs +99 -0
  19. package/agent/write-gate.mjs +354 -5
  20. package/agent-tools/advisor.mjs +127 -11
  21. package/agent-tools/async-settle.mjs +2 -2
  22. package/agent-tools/bash-async.mjs +8 -2
  23. package/agent-tools/batch-closeout.mjs +206 -0
  24. package/agent-tools/batch-gate.mjs +287 -73
  25. package/agent-tools/batch-ledger-bind.mjs +183 -0
  26. package/agent-tools/batch-ledger-link.mjs +73 -0
  27. package/agent-tools/batch-lifecycle.mjs +113 -354
  28. package/agent-tools/batch-paths.mjs +45 -40
  29. package/agent-tools/batch-read.mjs +1 -1
  30. package/agent-tools/batch-reopen.mjs +142 -0
  31. package/agent-tools/batch-skeleton.mjs +283 -80
  32. package/agent-tools/batch-unit-test-gate.mjs +107 -0
  33. package/agent-tools/batch.mjs +243 -50
  34. package/agent-tools/context.mjs +6 -3
  35. package/agent-tools/design-slots.mjs +24 -24
  36. package/agent-tools/design-token.mjs +96 -15
  37. package/agent-tools/doc-check.mjs +18 -2
  38. package/agent-tools/eng.mjs +1 -1
  39. package/agent-tools/escalate-async.mjs +3 -3
  40. package/agent-tools/manifest.mjs +8 -5
  41. package/agent-tools/read-history.mjs +5 -5
  42. package/agent-tools/review-facts.mjs +31 -13
  43. package/agent-tools/spawn-gates.mjs +10 -14
  44. package/agent-tools/ssh-session.mjs +120 -111
  45. package/agent-tools/ssh-transfer.mjs +210 -0
  46. package/agent-tools/ssh-transport.mjs +277 -46
  47. package/agent-tools/subagent-actions-query.mjs +1 -1
  48. package/agent-tools/subagent-actions.mjs +1 -1
  49. package/agent-tools/subagent-async.mjs +3 -2
  50. package/agent-tools/subagent-scheduler.mjs +43 -9
  51. package/agent-tools/subagent-spawn.mjs +68 -9
  52. package/agent-tools/subagent.mjs +16 -14
  53. package/agent.mjs +1 -0
  54. package/client-identity.mjs +70 -0
  55. package/config-presets.mjs +5 -3
  56. package/config.mjs +19 -13
  57. package/context.mjs +39 -8
  58. package/declaration.mjs +2 -2
  59. package/doc-check/anchors.mjs +128 -28
  60. package/doc-check/index.mjs +93 -27
  61. package/doc-check/line-refs.mjs +112 -0
  62. package/doc-check/paragraph.mjs +24 -8
  63. package/embedding.mjs +2 -0
  64. package/explore-distill.mjs +1 -1
  65. package/generate-title.mjs +3 -2
  66. package/i18n.mjs +1 -1
  67. package/index-discover.mjs +4 -1
  68. package/ledger-close-proof.mjs +62 -0
  69. package/ledger-cmd.mjs +79 -194
  70. package/ledger-db.mjs +29 -28
  71. package/ledger-migrate.mjs +4 -4
  72. package/ledger-read.mjs +10 -8
  73. package/ledger-surface.mjs +1 -1
  74. package/ledger-tools.mjs +10 -10
  75. package/ledger-variant-notice.mjs +1 -1
  76. package/ledger-write-ops.mjs +373 -0
  77. package/ledger.mjs +2 -2
  78. package/manifest-discovery.mjs +2 -2
  79. package/manifest-schema.mjs +4 -0
  80. package/memory/memory-tool.mjs +5 -2
  81. package/memory/schema.mjs +5 -26
  82. package/model-specs.mjs +6 -5
  83. package/package.json +1 -1
  84. package/path-resolve.mjs +217 -0
  85. package/permission.mjs +4 -0
  86. package/prompt-overlays.mjs +5 -0
  87. package/prompts/advisor-design.md +9 -6
  88. package/prompts/advisor-round1.md +4 -3
  89. package/prompts/advisor-round2.md +6 -5
  90. package/prompts/advisor-round3.md +6 -5
  91. package/prompts/common.md +23 -10
  92. package/prompts/consult-base.md +2 -1
  93. package/prompts/discipline-engineering.md +29 -48
  94. package/prompts/discipline-normal.md +10 -5
  95. package/prompts/persona-eng-coder.md +8 -0
  96. package/prompts/persona-eng-designer.md +22 -12
  97. package/prompts/persona-engineering.md +81 -66
  98. package/prompts/persona-plan.md +2 -1
  99. package/prompts/persona-tester.md +55 -0
  100. package/provider/anthropic.mjs +12 -7
  101. package/provider/core.mjs +21 -5
  102. package/provider/errors.mjs +41 -0
  103. package/provider/google.mjs +25 -9
  104. package/provider/list-models.mjs +4 -3
  105. package/provider/rate.mjs +11 -3
  106. package/provider/responses.mjs +17 -7
  107. package/provider/sse.mjs +15 -4
  108. package/session-identity.mjs +21 -0
  109. package/session-lifecycle.mjs +12 -4
  110. package/session-slots.mjs +9 -14
  111. package/session.mjs +5 -0
  112. package/skip-dirs.mjs +48 -0
  113. package/team-ledger.mjs +4 -4
  114. package/text-budget.mjs +4 -0
  115. package/token-window.mjs +107 -10
  116. package/tool-docs/advisor.md +2 -2
  117. package/tool-docs/apply_patch.md +2 -0
  118. package/tool-docs/bash.md +4 -0
  119. package/tool-docs/batch.md +85 -1
  120. package/tool-docs/browser.md +3 -1
  121. package/tool-docs/code_search.md +3 -1
  122. package/tool-docs/consult_start.md +6 -2
  123. package/tool-docs/consult_stop.md +3 -1
  124. package/tool-docs/delete.md +3 -0
  125. package/tool-docs/doc_check.md +43 -8
  126. package/tool-docs/doc_search.md +5 -1
  127. package/tool-docs/edit.md +22 -1
  128. package/tool-docs/execute.md +7 -1
  129. package/tool-docs/fetch.md +3 -1
  130. package/tool-docs/file_ops.md +3 -1
  131. package/tool-docs/git.md +9 -1
  132. package/tool-docs/goal.md +2 -0
  133. package/tool-docs/grep.md +1 -1
  134. package/tool-docs/hashline_edit.md +2 -0
  135. package/tool-docs/insert_after.md +3 -1
  136. package/tool-docs/ledger.md +5 -5
  137. package/tool-docs/lint.md +1 -0
  138. package/tool-docs/manifest-read.md +2 -1
  139. package/tool-docs/manifest.md +9 -2
  140. package/tool-docs/memory.md +11 -1
  141. package/tool-docs/notify_parent.md +1 -1
  142. package/tool-docs/plan.md +2 -0
  143. package/tool-docs/question.md +1 -1
  144. package/tool-docs/read_history.md +15 -3
  145. package/tool-docs/read_image.md +3 -1
  146. package/tool-docs/recent_changes.md +3 -1
  147. package/tool-docs/repo_outline.md +3 -1
  148. package/tool-docs/settings.md +10 -2
  149. package/tool-docs/skill.md +2 -0
  150. package/tool-docs/ssh.md +30 -17
  151. package/tool-docs/subagent.md +50 -13
  152. package/tool-docs/task.md +2 -0
  153. package/tool-docs/team_ledger.md +1 -1
  154. package/tool-docs/timer.md +7 -0
  155. package/tool-docs/verify.md +13 -1
  156. package/tool-docs/websearch.md +3 -1
  157. package/tool-docs/write.md +2 -0
  158. package/tools/bash.mjs +12 -2
  159. package/tools/edit-batch.mjs +40 -33
  160. package/tools/edit-diff.mjs +69 -1
  161. package/tools/execute.mjs +10 -4
  162. package/tools/file.mjs +17 -27
  163. package/tools/git.mjs +27 -1
  164. package/tools/ops.mjs +2 -1
  165. package/tools/patch.mjs +12 -3
  166. package/tools/process-doc-mark.mjs +139 -0
  167. package/tools/search.mjs +15 -6
  168. package/tools/shared.mjs +24 -1
  169. package/tools/ssh.mjs +48 -7
  170. package/tools/tree.mjs +4 -1
  171. package/tools/write-path.mjs +64 -3
  172. package/traces/trace-store.mjs +4 -3
  173. package/agent/light-round.mjs +0 -63
  174. package/agent/unbooked-pen.mjs +0 -62
package/token-window.mjs CHANGED
@@ -4,11 +4,12 @@
4
4
  * 迁出三族(语义零改——原档越限在即,本批按 §6.16.8 拆出):① 计量面(IMAGE_TOKEN_ESTIMATE + `estimateTokens`);
5
5
  * ② 尾族(KEEP_HEAD 头注 + TAIL_BUDGET_FRACTION / SUMMARY_TOKEN_ESTIMATE / TAIL_FLOOR_MESSAGES + `keepTailSize` + `tailBudgetTokens`);
6
6
  * ③ 切分与配对修复族(`splitHistory` + `repairedTailStart` + `tightenTailByBudget`)。
7
- * 新增族:`contextUsage`(与 `compressIfNeeded` 的 token 判定**同一函数**)· `historyPercent`(与两端状态行**同式**)·
7
+ * 新增族:`contextUsage`(与 `compressIfNeeded` 的 token 判定**同一函数**)· `historyReading` / `historyPercent`(状态行
8
+ * 百分比——与 CLI ∥ VSC **同源**:一个实现点,端侧零自算式,消费面全是本档的投影)·
8
9
  * `collectStaleToolOutputs`(陈旧工具输出合格集)+ 门槛常量 `PRUNE_MIN_TOKENS`(**与合格集同住本档** ⇒ 消费方单向取用、零回指)。
9
- * 依赖面 = `provider/rate.mjs`(`estimateText`)+ `config.mjs`(`providerSpec` / `resolveCompactThreshold`)——**零 import 环**。
10
+ * 依赖面 = `provider/rate.mjs`(`estimateText` / `ESTIMATOR_VERSION`)+ `config.mjs`(`providerSpec` / `resolveCompactThreshold`)——**零 import 环**。
10
11
  */
11
- import { estimateText } from "./provider/rate.mjs"
12
+ import { estimateText, ESTIMATOR_VERSION } from "./provider/rate.mjs"
12
13
  import { providerSpec, resolveCompactThreshold } from "./config.mjs"
13
14
 
14
15
  const IMAGE_TOKEN_ESTIMATE = 2000 // rough estimate for image content tokens (CLI legacy 256 underestimated real image costs, delaying compaction)
@@ -40,7 +41,7 @@ const KEEP_HEAD = 0 // No dedicated head: earliest messages may be a COMPLETED e
40
41
  // × window − SUMMARY_TOKEN_ESTIMATE ≈1K, §6.9) tightens it over pair-safe boundaries when compaction runs,
41
42
  // never below TAIL_FLOOR_MESSAGES; ordinary sessions never reach it (D-T4: the trigger ratio lives in `config.mjs` — untouched here).
42
43
  const TAIL_BUDGET_FRACTION = 0.15
43
- const SUMMARY_TOKEN_ESTIMATE = 1000 // §6.9: summary output target ~1K tokens — reserved from the 15%
44
+ export const SUMMARY_TOKEN_ESTIMATE = 1000 // §6.9: summary output target ~1K tokens — reserved from the 15%;§6.17 可达性门的 `note` 项同取本常量
44
45
  const TAIL_FLOOR_MESSAGES = 10 // §6.4④ D-T2: the tail keeps ≥10 verbatim messages — floor beats budget
45
46
  export function keepTailSize(provider, historyLen) {
46
47
  // provider is guaranteed at every call site (runAgent always builds one); providerSpec
@@ -138,11 +139,13 @@ export function contextUsage(agent, extras = {}) {
138
139
  const overhead = system + tools
139
140
  const historyTokens = estimateTokens(history)
140
141
  const measured = agent?._lastPromptTokens != null
142
+ // §6.3 / §6.17 估算自校准:估算值乘 `_tokRatio`;`_lastPromptTokens` 是服务端真值,不乘。
143
+ const ratio = tokenRatioOf(agent) // 取比单源(同下 `historyReading` 的第三参来源)
141
144
  return {
142
145
  basis: measured ? "measured" : "estimated",
143
146
  total: measured
144
- ? agent._lastPromptTokens + estimateTokens(history.slice(agent._usageAtLen ?? history.length))
145
- : historyTokens + overhead,
147
+ ? agent._lastPromptTokens + Math.round(estimateTokens(history.slice(agent._usageAtLen ?? history.length)) * ratio)
148
+ : Math.round((historyTokens + overhead) * ratio),
146
149
  history: historyTokens,
147
150
  system,
148
151
  tools,
@@ -153,12 +156,106 @@ export function contextUsage(agent, extras = {}) {
153
156
  }
154
157
  }
155
158
 
159
+ /** 估算样本键(§6.17.1 链二校验③单源):`format:model`——采样面与恢复面同一判据(切模型自清)。 */
160
+ export function tokenKey(provider) {
161
+ return `${provider?.format ?? "openai"}:${provider?.model ?? ""}`
162
+ }
163
+
164
+ /**
165
+ * 估算自校准采样(§6.17「估算自校准」)——池化比:`num += usage.prompt_tokens`、`den += estPrompt`
166
+ * (分子分母同取输入侧口径;`estPrompt` 由发送面随 result 带回,与净判同一个值)。
167
+ * `tokenKey()` 变 ⇒ 归零重采(切模型自清,不接四个刷新点);`_tok` / `_tokRatio` 的**采样侧**只由本函数写
168
+ * (恢复侧 = `restoreTokenSample`,清空 = `/new`——`session-lifecycle.mjs`);run 装配**不复位**
169
+ * (池化比要跨消息累计,见 `agent/setup.mjs`)。
170
+ * 无样本 ⇒ 比 = 1(估算维持原口径)。作用面 = `contextUsage` 两支承(§6.3);实测基线 `_lastPromptTokens` 不乘。
171
+ * 落槽形见 §6.17.1 链二:`est` = 估算口径版本位,跨进程恢复时与本值不符的样本不复用。
172
+ */
173
+ export function recordTokenSample(agent, provider, estPrompt, usage) {
174
+ const prompt = Number(usage?.prompt_tokens)
175
+ if (!agent || !(prompt > 0) || !(estPrompt > 0)) return
176
+ const key = tokenKey(provider)
177
+ if (agent._tok?.key !== key) agent._tok = { key, num: 0, den: 0, est: ESTIMATOR_VERSION }
178
+ agent._tok.num += prompt
179
+ agent._tok.den += estPrompt
180
+ // 下界 = 1:已知偏差方向是估算偏低;比 < 1 会把全部估算支缩小、重开「该压没压」原始故障,
181
+ // 保守方向不取(仅一侧校准,零新魔数)。
182
+ agent._tokRatio = Math.max(1, agent._tok.num / agent._tok.den)
183
+ }
184
+
185
+ /**
186
+ * 样本写出面(§6.17.1 链二写面):会话槽 `estimateSample` 的值——`agent._tok` 为空 ⇒ `null`
187
+ * (落 null,不造空对象);`est` 缺省补当前口径版本(老进程内的样本对象无该位)。
188
+ */
189
+ export function tokenSampleOf(agent) {
190
+ const tok = agent?._tok
191
+ if (!tok || typeof tok.key !== "string") return null
192
+ return { key: tok.key, num: tok.num, den: tok.den, est: tok.est ?? ESTIMATOR_VERSION }
193
+ }
194
+
195
+ /**
196
+ * 样本读回面(§6.17.1 链二):槽里是盘上用户数据 ⇒ 判定走 `validSample`(同一份校验,不在此复述),
197
+ * 不过则回到未校准档(`_tok = null` / `_tokRatio = 1`,不抛——坏样本不得拦住会话恢复)。
198
+ * 返回是否复用了样本。
199
+ */
200
+ export function restoreTokenSample(agent, sample, provider) {
201
+ const ok = validSample(sample, provider)
202
+ agent._tok = ok
203
+ agent._tokRatio = ok ? Math.max(1, ok.num / ok.den) : 1
204
+ return ok !== null
205
+ }
206
+
207
+ /**
208
+ * 样本判定(§6.17.1 链二四条校验——**唯一实现面**):形状与数值域(`num` / `den` / `est` 有限、`den > 0`)·
209
+ * 口径版本(`est === ESTIMATOR_VERSION`——口径变了,旧样本的 `den` 与新估算不同量纲)· 键匹配
210
+ * (`key === tokenKey(provider)`——恢复后模型已换 ⇒ 不复用)。全过 ⇒ `{ key, num, den, est }`;任一不过 ⇒ `null`
211
+ * (回到未校准档,不抛——坏样本不得拦住会话恢复 / 读数)。恢复面 `restoreTokenSample` 与读数面 `sampleRatio`
212
+ * 同取本函数:同一判定,不写第二份。④ 比 < 1 不必单独判——由 `max(1, …)` 兜底。
213
+ */
214
+ function validSample(sample, provider) {
215
+ if (!sample || typeof sample !== "object" || Array.isArray(sample)) return null
216
+ const { key, num, den, est } = sample
217
+ if (typeof key !== "string" || !Number.isFinite(num) || !Number.isFinite(den) || !Number.isFinite(est) || !(den > 0)) return null
218
+ if (est !== ESTIMATOR_VERSION) return null
219
+ if (key !== tokenKey(provider)) return null
220
+ return { key, num, den, est }
221
+ }
222
+
156
223
  /**
157
- * 状态行百分比(§6.16.4「与状态行 ctx% 的单源关系」)——与 CLI 状态行(`render-frame.mjs` 式)与 VSC `ctxPercentForHistory` **同式**;
158
- * stats 报出的就是用户看到的那个百分比(不新增第三口径);空历史 ⇒ 0(状态行的 `ctxPct > 0` 显示门在端侧)。
224
+ * 核读数(§6.16.4「与状态行 ctx% 的单源关系」)——这个量**只有本函数一个实现点**,五处消费面全是它的投影:
225
+ * stats 第 6 行 · 打开态读数 · 桌面 `ev:usage` · VSC `specs.mjs` · CLI 状态行(经 `ctxCacheOf` 缓存)。
226
+ * 一点出两值:`tokens` = 校准后的历史估算 `round(estimateTokens(history) × ratio)`、`pct` = 它 ÷ 窗口、
227
+ * `window` = `providerSpec(provider).context`——同屏「context X% Yk」必须同尺,两值各自算一次就是两把尺;
228
+ * `window` 随读带出,消费面不再二次派生同一个量。
229
+ * `ratio` **无缺省**:忘传 ⇒ `estimateTokens × undefined` = `NaN` ⇒ `tokens` / `pct` 皆 `NaN`。三面显示门
230
+ * 都把非正值读作「不显示」(CLI `ctxPct > 0` · VSC 分子为假回 `null` · 桌面 `usageFrameWanted` 不发帧)
231
+ * ⇒ 读数**缺席**(不会是错数);给缺省 1 则相反——忘传处静默显示未校准读数,看着完全正常而差一整个校准比。
232
+ * 两种形状里只有后者会被当作事实读走,所以宁可缺席。
233
+ * 空历史 ⇒ 0(状态行的 `ctxPct > 0` 显示门在端侧,不入本函数面)。
159
234
  */
160
- export function historyPercent(history, provider) {
161
- return Math.round((estimateTokens(history ?? []) / providerSpec(provider).context) * 100)
235
+ export function historyReading(history, provider, ratio) {
236
+ const window = providerSpec(provider).context
237
+ const tokens = Math.round(estimateTokens(history ?? []) * ratio)
238
+ return { tokens, pct: Math.round((tokens / window) * 100), window }
239
+ }
240
+
241
+ /** 状态行百分比薄转口(名面与 export 不变):`historyReading` 的 `pct`——与 stats ∥ CLI ∥ VSC **同源**
242
+ * (各处全是这一实现点的投影,端侧零自算式)。第三参 `ratio` 同 `historyReading`:无缺省。 */
243
+ export function historyPercent(history, provider, ratio) {
244
+ return historyReading(history, provider, ratio).pct
245
+ }
246
+
247
+ /** 估算校准比(§6.17)——`agent._tokRatio` 的**唯一**读出点(中性值 1 的唯一出处):
248
+ * 无样本 ⇒ 1。合法的 1 只有两种:新会话无样本(`_tok` 为 `null`)· 打开态槽无 `estimateSample`;
249
+ * agent 不在场(`undefined` / `null`)也回 1,该档 = 未校准读数,由消费面显式断言,不靠默认值蒙过。 */
250
+ export function tokenRatioOf(agent) {
251
+ return agent?._tokRatio ?? 1
252
+ }
253
+
254
+ /** 槽样本 ⇒ 校准比(§6.17.1 链二读数面):判定与 `restoreTokenSample`(恢复面)同一份 `validSample`,
255
+ * 好样本 ⇒ `max(1, num / den)`,坏样本 ⇒ `1`(下界 1 与 `recordTokenSample` 同——偏差方向已知是估算偏低)。 */
256
+ export function sampleRatio(sample, provider) {
257
+ const s = validSample(sample, provider)
258
+ return s ? Math.max(1, s.num / s.den) : 1
162
259
  }
163
260
 
164
261
  /** 陈旧工具输出门槛(§6.16.3 判据③):低于门槛的替换是净增 token(stub 本身有长度)——常量与合格集同住本档。 */
@@ -1,9 +1,9 @@
1
1
  Run an independent review of your work. `type` is REQUIRED — one of exactly two values, no default and no silent fallback.
2
2
 
3
- - `type="design"` — design review: reviews design / requirement documents BEFORE implementation. Pass `documents=[...]` (the explicit list), plus `batchDoc` when a batch record is in flight.
3
+ - `type="design"` — design review: reviews design / requirement documents BEFORE implementation. Pass `documents=[...]` and `batchDoc` — the batch record this review belongs to. **`batchDoc` is required**: a design review without it is refused before launch, because the token it would mint has no batch to close it.
4
4
  - `type="code"` — code review: reviews the code you changed AFTER implementation. Pass `paths=[...]` to scope files/directories; `documents=[...]` adds acceptance-criteria context.
5
5
 
6
- `documents` — one rule: a relative path resolves against the session project root (e.g. `docs/core/design/ADVISOR-GUARDS.md`); an absolute path is used as-is.
6
+ `documents` — one rule: a relative path resolves against the session project root (e.g. `docs/<area>/<name>.md`); an absolute path is used as-is.
7
7
 
8
8
  The relative form needs the session anchor to be inside a project. When the anchor is a workspace root (several projects below it, no manifest of its own) there is no single project root to resolve against, and a relative path is refused with `no-project-root` — write the absolute path there.
9
9
 
@@ -13,3 +13,5 @@ Notes:
13
13
  - If a hunk's context matches multiple locations it is rejected — add more surrounding context lines
14
14
  - Deleting files is not supported — use the delete tool
15
15
  - For single-file edits, edit is simpler; for full rewrites, write is simpler
16
+
17
+ **`ledger`** — the in-flight ledger row this call serves: `"#N"` or `"N"`. In engineering mode the parent agent must pass it — a call that does not say which row it serves is refused.
package/tool-docs/bash.md CHANGED
@@ -37,3 +37,7 @@ Notes:
37
37
  - After commands that change files (git checkout, npm install, etc.), repo_outline and code_search may be stale — re-run them to get current results.
38
38
  - Prefer read/glob/grep/ls for file operations inside the project — file tools and bash reach the same paths (no directory restriction).
39
39
  - NEVER use bash to write or modify files (echo/sed/printf > file, cat << EOF, etc.). Use write/edit/insert_after/apply_patch instead — they handle encoding, escaping, and EOL conventions correctly.
40
+
41
+ **`ledger`** — the in-flight ledger row this call serves: `"#N"` or `"N"`. In engineering mode the parent agent must pass it — a call that does not say which row it serves is refused, and read-only commands are judged the same way (no command-line classification).
42
+
43
+ **`project`** — the project this call belongs to: the absolute path of the project root (the directory carrying PROJECT-MANIFEST.json). Engineering mode: pass it together with `ledger`. Pure declaration: it does not change the execution directory.
@@ -1 +1,85 @@
1
- Batch-record lifecycle tool (一段一作者 — six append-only sections, one author each). actions: create (depth-0 only — write a new six-section skeleton record; pass path + topic + source, plus optional ledger / board that fill the record's 台账 header line at creation) · append (add text to the END of YOUR section — existing lines are never rewritten or deleted) · status (update YOUR section's `**状态行**:` line — the value must carry exactly ONE legal keyword of your section's STATUS_WORDS entry, decorated only by surrounding symbols / a trailing ISO date / whitespace; prose goes to the separate `note` field, which lands as a parenthetical). Writing §1 已收口 closes the record and runs the closeout-chain gate: §2 / §3 / §4 / §6 must each carry content, or §1 declares it inapplicable with `> 不适用:§N —— <reason>` (§2 / §3 / §4 only; §5 is exempt; §6 never). Reopening a frozen record = §1 → 进行中 with `note` = the reason: the tool rewrites the §1 status line and appends a 「重开记录」 line — no other existing line is touched. · close (depth-0 only — freeze the record: §1 → 「已收口 <date>」, every further write is refused except the reopen conjunction; runs the same closeout-chain gate as status — an incomplete chain is refused, naming the missing sections). Closing a record — the close action, or status writing §1 已收口 — also writes the ledger: this record's unsettled rows (task_book → this record, status 待讨论 / 待设计 / 在途, trigger ≠ 认账不排期 — a policy call leaves its row untouched, and a row already 条件 is skipped with zero writes) get trigger → 条件, with the resume sentence 「原批 <record> 已收口,须另起批次接手」 landing in resume_when (a non-empty resume_when keeps its value — the sentence is appended to evidence instead); each changed / skipped row is named in a `[ledger reanchor]` receipt block (a ledger write failure — busy store / no derivable project root — is named there too and never blocks the freeze). Identity fixes what you may write (段白名单): main agent (depth 0) — create/close; append/status domains are mode-dependent: engineering — append §1/§4/§6, status §1; normal — append §1/§2/§4/§5/§6, status §1/§2/§5 (the main agent holds the designer seat and records direct implementation); eng-designer — §2; eng-coder — §5; a normal-mode coder bound via batchDoc — §5; design review (review binding) — §3. A write outside your own section is refused. Skeleton placeholders (dead literals — `#<编号>` / `<板块>` and the older `<BATCH-ID>` / `<讨论来源>`) in the record header's structural lines block append/status until filled: create with ledger / board fills them at creation; if omitted, the main agent fills the header by file edit right after create (citing these literals in a section's prose is fine — only the header is checked); `<§N 模板占位:…>` template lines are legal and never blocked. Target record: sub-agents and design reviews have NO path parameter in practice — the record arrives via the spawn binding / the review instance key (passing path is refused); depth-0 MAY pass path (若传则须可读), omitting it picks the unique in-flight record (0 or ≥2 in flight ⇒ refuse — pass path). A design review's append is stamped by the tool with a `### 轮次 N(评审子代理)` heading — N is tool-counted; do not write your own heading (it would be dropped). Credential values are stripped mechanically before writing (never write a token or designId value). Document-check gate: appending §2 / §5 as a sub-agent and closing a record (or setting §1 to 已收口) run the doc-check gate before the write — REFUSED ⇒ the write is refused and the record's bytes stay unchanged (do not close a record after a refused append); ERROR ⇒ a closeout is refused while an append still lands with an ERROR receipt line; no PROJECT-MANIFEST.json ⇒ the gate does not run and the receipt says so. Failures are hard and visible (no silent fallback): if a write is refused or fails, say so in your report — “§× 未写入”.
1
+ Batch-record lifecycle tool (一段一作者 — six sections, one author each; the author has two actions on that one section: append adds text at its end, edit rewrites its lines in place; the section's status line has a single writer, `status`).
2
+
3
+ actions: create (depth-0 only — write a new six-section skeleton record; pass path + topic + source, plus optional ledger / board that fill the record's 台账 header line at creation;
4
+
5
+ ledger:"#N" takes over an existing row, and omitting ledger opens a new row only when no unsettled row exists on the same board — otherwise create is refused with the candidate list and you pass newDebt:"<why this is a new debt>" or ledger:"#<id>")
6
+
7
+ · append (add text to the END of YOUR section — the lines already there are left byte-for-byte as they stand)
8
+
9
+ A §3 append must carry exactly one verdict on its own line — `VERDICT: pass` or `VERDICT: changes-required`; the localized whole-line forms count too (`结论:通过` / `结论:不通过`, and their 需修改 variants), and the Chinese forms take an optional trailing 。.
10
+
11
+ A §3 write that reads as no verdict line, or as both lines at once, is refused with nothing written — no round number is consumed, so resend the same text with the verdict line added.
12
+
13
+
14
+ · edit — replace one anchored range inside YOUR OWN section (any section you may append to; your identity decides which)
15
+
16
+ oldText must match exactly once, byte for byte (line endings included), inside the section body; newText replaces that range, and an empty newText deletes it.
17
+
18
+ Refused with nothing written: a blank oldText, zero hits, two or more hits (the matching lines are named), an anchor crossing the section boundary, and any anchor or replacement that touches or introduces a `**状态行**:` line (the status line has one writer: `status`).
19
+
20
+ On §3 an incoming `### 轮次 N(评审子代理)` line is dropped from newText — the round stamp is tool-generated only.
21
+
22
+ Two gates hang on `append` alone and do not apply to `edit`: the §5 ordering gate (an edit needs an existing anchor, so it cannot write what does not exist yet) and the §3 verdict line (that gate reads the block you append; rewriting words inside an existing block changes no machine criterion).
23
+
24
+
25
+ · status (update YOUR section's `**状态行**:` line — the value must carry exactly ONE legal keyword of your section's STATUS_WORDS entry, decorated only by surrounding symbols / a trailing ISO date / whitespace).
26
+
27
+ Prose goes to the separate `note` field, which lands as a parenthetical.
28
+
29
+ A §3 pass round sets that section's status line in the same write — `status` on §3 is an optional rewrite, never a required step.
30
+
31
+ Writing §1 已收口 closes the record and runs the closeout-chain gate: every section must carry content (§5 exempt) — the `不适用` declaration is retired, so any `> 不适用:§N` line in §1 is refused outright.
32
+
33
+ Reopening a frozen record = §1 → 进行中 with `note` = the reason: the tool rewrites the §1 status line and appends a 「重开记录」 line — no other existing line is touched.
34
+
35
+ · close (depth-0 only — freeze the record: §1 → 「已收口 <date>」, every further write is refused except the reopen conjunction; runs the same closeout-chain gate as status — an incomplete chain is refused, naming the missing sections).
36
+
37
+ Closing a record — the close action, or status writing §1 已收口 — also writes the ledger: this record's unsettled rows (task_book → this record, status 待讨论 / 待设计 / 在途, trigger ≠ 认账不排期 — a policy call leaves its row untouched, and a row already 条件 is skipped with zero writes) get trigger → 条件,
38
+
39
+ with the resume sentence 「原批 <record> 已收口,须另起批次接手」 landing in resume_when (a non-empty resume_when keeps its value — the sentence is appended to evidence instead);
40
+
41
+ each changed / skipped row is named in a `[ledger reanchor]` receipt block (a ledger write failure — busy store / no derivable project root — is named there too and never blocks the freeze).
42
+
43
+ Identity fixes what you may write (段白名单): main agent (depth 0) — create/close;
44
+
45
+ append/status domains are mode-dependent: engineering — append §1/§4/§6, status §1; normal — append §1/§2/§4/§5/§6, status §1/§2/§5 (the main agent holds the designer seat and records direct implementation);
46
+
47
+ edit reads the same whitelist as append — one table, two actions: the section you may append to is the section you may rewrite in place. Depth-0 gains §2 and §5 on edit in normal mode for the reason it gains them on append (the main agent holds the designer and implementer seats).
48
+
49
+ eng-designer — §2; eng-coder — §5; a normal-mode coder bound via batchDoc — §5; design review (review binding) — §3.
50
+
51
+ A write outside your own section is refused.
52
+
53
+ Skeleton placeholders (dead literals — `#<编号>` / `<板块>` and the older `<BATCH-ID>` / `<讨论来源>`) in the record header's structural lines block append/status until filled:
54
+
55
+ create with ledger / board fills them at creation; if omitted, the main agent fills the header by file edit right after create (citing these literals in a section's prose is fine — only the header is checked);
56
+
57
+ `<§N 模板占位:…>` template lines are legal and never blocked.
58
+
59
+ Target record: sub-agents and design reviews have NO path parameter in practice — the record arrives via the spawn binding / the review instance key (passing path is refused); depth-0 MAY pass path (若传则须可读), omitting it picks the unique in-flight record (0 or ≥2 in flight ⇒ refuse — pass path).
60
+
61
+ A design review's append is stamped by the tool with a `### 轮次 N(评审子代理)` heading — N is tool-counted; do not write your own heading (it would be dropped).
62
+
63
+ Credential values are stripped mechanically before writing (never write a token or designId value).
64
+
65
+ Document-check gate: every append and every edit runs it before the write — any section (§1–§6), depth-0 and sub-agent and design review alike — and so does closing a record (or setting §1 to 已收口).
66
+
67
+ On `edit` the judged set splits by hit class: paragraph / line length is judged on the newText you pass; the anchor classes (档缺 / 节缺 / 引缺) are judged on the whole record as it will stand after your edit.
68
+
69
+ A dead citation anywhere in the record therefore refuses an unrelated edit — and replacing that very line is the way through, which is why the record is judged as it will stand, not as it sits.
70
+
71
+ On append it judges three things: the text you are appending now, the markdown files already touched in this run, and the record itself — but only the first two can refuse the write.
72
+
73
+ A hit in the text you are appending or in a touched file ⇒ REFUSED: nothing lands, the record's bytes stay unchanged (do not close a record after a refused append).
74
+
75
+ A hit in a section that has already landed is named in the receipt with its section number and author and never blocks the write — another author's landed lines cannot be rewritten here; the close face is where it is enforced.
76
+
77
+ On close (and on status writing §1 已收口) every hit in the range refuses, each one named as `§N(作者:…)` when it sits inside the record.
78
+
79
+ An append receipt reads `[doc-check gate] DOC-CHECK GATE: PASS · 范围 N 档` — N = the documents actually judged, the record itself always one of them.
80
+
81
+ ERROR ⇒ a closeout is refused while an append still lands with an ERROR receipt line;
82
+
83
+ no PROJECT-MANIFEST.json ⇒ the gate does not run and the receipt says so.
84
+
85
+ Failures are hard and visible (no silent fallback): if a write is refused or fails, say so in your report — “§× 未写入”.
@@ -1,4 +1,6 @@
1
- Drive a real browser the tool starts itself: a headless (default) or visible system Edge/Chrome in an isolated profile under `~/.thincoder/browser/profile`. Multi-step and stateful: `navigate`, `snapshot`, then act on the element references the snapshot returns. Not a takeover — it never attaches to a browser you already have open and needs no extension.
1
+ Drive a real browser the tool starts itself: a headless (default) or visible system Edge/Chrome in an isolated profile under `~/.thincoder/browser/profile`.
2
+
3
+ Multi-step and stateful: `navigate`, `snapshot`, then act on the element references the snapshot returns. Not a takeover — it never attaches to a browser you already have open and needs no extension.
2
4
 
3
5
  **Route to browser instead of fetch:**
4
6
  - content that only exists after JS runs, or behind interaction (login, click, form, scroll) → browser
@@ -1 +1,3 @@
1
- Search the project's source code for relevant code. Use this to find functions, classes, or code patterns across the codebase. Returns matching code chunks with file paths and line numbers. Prefer doc_search for the intended design (design docs, conventions); code_search for the implementation as written. For what was said in a session (conversation/chat history), use read_history.
1
+ Search the project's source code for relevant code. Use this to find functions, classes, or code patterns across the codebase. Returns matching code chunks with file paths and line numbers.
2
+
3
+ Prefer doc_search for the intended design (design docs, conventions); code_search for the implementation as written. For what was said in a session (conversation/chat history), use read_history.
@@ -1,5 +1,9 @@
1
1
  Start a parallel multi-model consultation (会诊) for a hard problem you are stuck on (repeated failures, no headway). Call it directly when the user asks for 会诊 / consult — an explicit user request applies even if you are not 'stuck'.
2
2
 
3
- Several configured models (agent.consultModels — at most 5; an unconfigured pool is an explicit error) analyze the same problem INDEPENDENTLY and in parallel. Non-blocking: returns immediately with a consult id; the consultants keep running in the background across turns. When EVERY model has replied (or failed), the full verdict text is delivered to you automatically — as a system reminder at the next run start, or digested on its own while the session is idle — judge and adopt each opinion yourself with your own tools (opinions are suggestions, not gates). To stop a session early (user changed their mind / wants the tokens back), call consult_stop(id) — a stopped session delivers no digest.
3
+ Several configured models (agent.consultModels — at most 5; an unconfigured pool is an explicit error) analyze the same problem INDEPENDENTLY and in parallel. Non-blocking: returns immediately with a consult id; the consultants keep running in the background across turns.
4
4
 
5
- The brief should carry the symptom, what you already tried (the failure trail), and entry-point files; do NOT paste raw error logs — consultants pull the main session history themselves via their main_history tool.
5
+ When EVERY model has replied (or failed), the full verdict text is delivered to you automatically — as a system reminder at the next run start, or digested on its own while the session is idle — judge and adopt each opinion yourself with your own tools (opinions are suggestions, not gates).
6
+
7
+ To stop a session early (user changed their mind / wants the tokens back), call consult_stop(id) — a stopped session delivers no digest.
8
+
9
+ The brief should carry the symptom, what you already tried (the failure trail), and entry-point files; do NOT paste raw error logs — consultants pull the main session history themselves via their main_history tool.
@@ -1,3 +1,5 @@
1
- Cancel a still-running consultation session (会诊) — the user changed their mind, the problem resolved, or you want the tokens back. Aborts every consultant that is still running; a stopped session delivers NO digest (its already-collected partial replies are dropped). Sessions that finished on their own are no longer cancellable — their verdict text is delivered automatically.
1
+ Cancel a still-running consultation session (会诊) — the user changed their mind, the problem resolved, or you want the tokens back. Aborts every consultant that is still running; a stopped session delivers NO digest (its already-collected partial replies are dropped).
2
+
3
+ Sessions that finished on their own are no longer cancellable — their verdict text is delivered automatically.
2
4
 
3
5
  Returns JSON {abandoned: <pending count>, cancelled: true} — or {error: "unknown consult id"} (already finished/cancelled).
@@ -1,4 +1,5 @@
1
1
  Delete a file, or an empty directory. Use when the agent created a temporary or junk file or empty directory that should be cleaned up, or when the user explicitly asks to delete something.
2
+
2
3
  Refuses to delete git-tracked files as a safety measure — tracked files should be edited or removed via bash with explicit user confirmation.
3
4
 
4
5
  **Route to delete instead of bash:** `del file` / `rm file` → delete (single files); an empty directory → delete (empty directories only). Non-empty directories have no delete path — stop and report to the user instead of hand-rolling a recursive shell delete.
@@ -13,3 +14,5 @@ Notes:
13
14
  - Directories: empty ones only — a non-empty directory is refused (a natural fuse; nothing recursive is ever removed); delete nested empty trees bottom-up
14
15
  - Symbolic links are removed as links — the link itself is deleted, never followed (its target is untouched)
15
16
  - Returns `Deleted <path>` (or `Error: ...` on failure / tracked / non-empty refusal).
17
+
18
+ **`ledger`** — the in-flight ledger row this call serves: `"#N"` or `"N"`. In engineering mode the parent agent must pass it — a call that does not say which row it serves is refused.
@@ -2,12 +2,20 @@ Run a built-in documentation-discipline check on a project and report its findin
2
2
  Read-only. In the default `report` mode it is non-gating: it reports, it never blocks, never sets an exit code, and its output is never a reason to refuse a task or a user instruction.
3
3
 
4
4
  What it checks — exactly two criteria, both built into the engine:
5
- - Anchors — every documentation pointer (a `*.md` token, optionally carrying directory segments, optionally followed by a section
5
+ - Anchors — a documentation pointer (a `*.md` token, optionally carrying directory segments, optionally followed by a section
6
6
  indicator `§N` / `§N.M` / `§N.M.K`) must resolve: the file must exist on disk (`档缺` when it does not), and when a section
7
7
  indicator is given the target file must carry a heading numbered with it (`节缺` when it does not). Line-number coordinates
8
8
 
9
9
  (`<doc>:12`) are not section references and are not judged as such. Files inside fenced code blocks and executable lines
10
10
  (command / pattern code spans) are skipped whole.
11
+ - **The reach is what survives extraction, not every `.md` token.** Three name families are never extracted, so they are never
12
+ judged: **conventional file names** used to say which files get read (`AGENTS.md`, `SKILL.md`, `project_rules.md`, `todo.md`
13
+ and the rest of that closed set),
14
+
15
+ **placeholder stems** (single letters plus the usual sample words — `x.md`, `file.md`, `foo.md`, `example.md`), and — on the
16
+ prompt face only — the seven operand names the `Operand names` block below lists.
17
+
18
+ A `.md` token outside those families is judged even when misspelled: a wrong name reports `档缺`, never a pass.
11
19
 
12
20
  ~~~text
13
21
  **Operand names.** Seven names count as operands rather than documents: the model writes them, or reads a file by that name. On
@@ -21,15 +29,41 @@ What it checks — exactly two criteria, both built into the engine:
21
29
  ```
22
30
  ~~~
23
31
 
24
- - Paragraph length — on a document face, adjacent non-blank body lines merge into one paragraph whose total character
32
+ - Citation check — the same criterion, one more hit class `引缺` (a line that claims its own basis).
33
+
34
+ A claim line writes `依据 <document path>「<verbatim quotation>」`, and the quoted text must appear as a contiguous substring
35
+ of the file that path resolves to; when it does not, the hit is `引缺` and the report names the path plus the first 40 characters.
36
+
37
+ The path is judged for existence first: a path that does not resolve reports `档缺`, never `引缺` — the fix differs
38
+ (check the path ∥ check the words). Both classes count into the one `锚` total: no third criterion, no new verdict word.
39
+
40
+ Between the path token and `「` only the optional separator (`:` ∥ `:` ∥ `=`) and an optional backtick may sit. Anything else
41
+ (a space, a prose word, a punctuation mark) means the line is not a basis marker and nothing is judged; an empty `「」` is not a marker either.
42
+
43
+ A path whose name falls in the exempt families above is not a marker path either — the same exemption decides both faces.
44
+
45
+ Existence only: no whitespace folding, no case or width folding. A verbatim quotation taken from the wrong place still passes —
46
+ whether it is the *right* basis (a contract rather than a comment) belongs to review, and this tool does not claim to judge it.
47
+
48
+
49
+ - Paragraph length — on every non-prompt face, adjacent non-blank body lines merge into one paragraph whose total character
25
50
  count (CJK counts as 1) is judged against the built-in threshold; blank lines, table rows, headings, list items,
26
51
  quote-block lines and fenced code blocks do not count into a paragraph and break it, and lines inside an exempt zone
27
52
 
28
- are excluded. Breaking a paragraph into more lines changes nothing — only splitting it into more paragraphs or
29
- writing it shorter passes.
53
+ are excluded. No face is exempt: a tool-description file (`thincoder-core/tool-docs/**`, this file included) is judged on
54
+ this criterion too — a long paragraph hides its later points from every reader, the model included.
55
+
56
+ Breaking a paragraph into more lines changes nothing, and clearing the length check is not the same as fixing it: splitting a
57
+ paragraph into more paragraphs, or writing it shorter, clears the count while the reader still faces the same packed paragraph.
58
+
59
+ What fixes it is fewer and shorter sentences, a list of named items where the paragraph holds parallel assertions, or a table
60
+ where it holds enumerated readings.
30
61
  - Line length — a prompt-face file (`thincoder-core/prompts/**`) is judged line by line: the model reads raw text, so
31
62
  one line is one block, and any single line over the threshold counts with no structural exemption.
32
63
 
64
+ `report` mode also prints one extra block: an inventory of the line-number coordinates found in the documents you pass
65
+ (`<doc>:12` shape). That block is a reading, not a criterion — `gate` never produces it, and it cannot change a verdict or a hit count.
66
+
33
67
  It cannot — in `report` mode — write anything, gate anything, or turn a finding into a refusal.
34
68
 
35
69
  Arguments — `target` (optional): the project root to check; defaults to the session anchor, relative paths resolve against it.
@@ -46,10 +80,11 @@ is judged; in `gate` mode such paths are listed as skipped instead. A pointer re
46
80
 
47
81
  against the pointing document's own directory — bare names are not resolved across other directories.
48
82
 
49
- Delivery gate — `mode: "gate"`, for the end of a design or development round. Every listed doc is judged **whole**: a
50
- pre-existing dangling pointer or overlong paragraph / line anywhere in it counts, not only this round's edited lines. The first line is the
83
+ Delivery gate — `mode: "gate"`, for the end of a design or development round. Every listed doc is judged **whole**:
84
+ a pre-existing dangling pointer or overlong paragraph / line anywhere in it counts, not only this round's edited lines.
85
+
86
+ The first line is the machine-readable verdict — `DOC-CHECK GATE: PASS` / `DOC-CHECK GATE: REFUSED(命中 K 条)` / `DOC-CHECK GATE: ERROR(reason)` —
51
87
 
52
- machine-readable verdict — `DOC-CHECK GATE: PASS` / `DOC-CHECK GATE: REFUSED(命中 K 条)` / `DOC-CHECK GATE: ERROR(reason)` —
53
88
  and consumers judge by that prefix; the hits are fixed in the same round and are never booked to the ledger. The gate refuses
54
89
 
55
90
  **one delivery**, not a user instruction: an explicit user go-ahead is followed, with the hits written into the delivery.
@@ -64,7 +99,7 @@ a reader: use `doc_search` / `read` to read documents and `grep` to search them;
64
99
  Side effects and permissions — none. The tool is read-only: no writes, no approval prompt, safe to run in parallel with other calls.
65
100
 
66
101
  Parameters and scope — both criteria run on built-in constants (`DOC_CHECK_BUILTINS`: threshold 300 characters, no exempt zones,
67
- no exemptions), and the judged set is the document list the caller passes — the judged set and the resolution set are the same
102
+ no registration-based exemption list), and the judged set is the document list the caller passes — the judged set and the resolution set are the same
68
103
 
69
104
  documents, so there is no scan-dir / domain / root / exclude surface and no whole-repository scan. Changing a criterion or a
70
105
  threshold is a code change and goes through review; no project declaration changes these readings.
@@ -1 +1,5 @@
1
- Search the project's documentation (README, design docs, guides, markdown files) for relevant information. Use this to find design decisions, coding conventions, architecture docs, or project rules. Prefer this over code_search when you need to understand the project's intended design rather than existing implementation. Returns matching doc chunks: path, heading, line range, relevance score, content excerpt. For what was said in sessions (conversation/chat history — decisions, rulings), use read_history.
1
+ Search the project's documentation (README, design docs, guides, markdown files) for relevant information. Use this to find design decisions, coding conventions, architecture docs, or project rules.
2
+
3
+ Prefer this over code_search when you need to understand the project's intended design rather than existing implementation. Returns matching doc chunks: path, heading, line range, relevance score, content excerpt.
4
+
5
+ For what was said in sessions (conversation/chat history — decisions, rulings), use read_history.
package/tool-docs/edit.md CHANGED
@@ -1,4 +1,20 @@
1
- Edit a file as a patch — targeted by content or by line number. Two targeting forms (mutually exclusive — use one): ① line-based: `line: N` replaces that single line, `startLine: N, endLine: M` replaces the inclusive 1-based line range — no old_string needed when you know the line number; give new_string to replace, or OMIT new_string to delete the line/range; ② content-based: old_string is the current content of the region to change (must match exactly once); new_string is the desired result of that region. old_string matching is tolerant: exact match first, then a unique whitespace-only variant, then fuzzy match (unique window with ≥90% of lines identical after trimming, tab→space indent and quote normalization — ASCII single, curly single/double and backtick quotes all unify to straight double quotes). Lines shared by old/new are kept; lines only in new_string take their position relative to the shared lines (LCS order); lines only in old_string are deleted — a replacement never leaves old lines behind (zero overlap → old lines are replaced by new_string, not kept). A unique single-line old_string paired with a single-line new_string replaces that exact line in place (line count unchanged). To add a new line without removing anything use insert_after. replace_all applies to content-based edits only: literal replacement of every occurrence — the diff rules do not apply.
1
+ Edit a file as a patch — targeted by content or by line number.
2
+
3
+ Two targeting forms (mutually exclusive — use one):
4
+
5
+ ① line-based: `line: N` replaces that single line, `startLine: N, endLine: M` replaces the inclusive 1-based line range — no old_string needed when you know the line number; give new_string to replace, or OMIT new_string to delete the line/range;
6
+
7
+ ② content-based: old_string is the current content of the region to change (must match exactly once); new_string is the desired result of that region.
8
+
9
+ old_string matching is tolerant: exact match first, then a unique whitespace-only variant, then fuzzy match (unique window with ≥90% of lines identical after trimming,
10
+
11
+ tab→space indent and quote normalization — ASCII single, curly single/double and backtick quotes all unify to straight double quotes).
12
+
13
+ Lines shared by old/new are kept; lines only in new_string take their position relative to the shared lines (LCS order); lines only in old_string are deleted — a replacement never leaves old lines behind (zero overlap → old lines are replaced by new_string, not kept).
14
+
15
+ A unique single-line old_string paired with a single-line new_string replaces that exact line in place (line count unchanged). To add a new line without removing anything use insert_after.
16
+
17
+ replace_all applies to content-based edits only: literal replacement of every occurrence — the diff rules do not apply.
2
18
 
3
19
  **Routing — pick the right edit tool:**
4
20
  - Delete a line/range by number → omit new_string: edit with `line: N` / `startLine: N, endLine: M`
@@ -11,9 +27,14 @@ Edit a file as a patch — targeted by content or by line number. Two targeting
11
27
 
12
28
  **Batch multiple changes into ONE call via the `edits` array** (preferred over N single edit calls) — same file or across files; atomic: any failure writes nothing. One permission ask, one undo unit, and one turn instead of N. Per-entry shape and targeting are in the schema.
13
29
 
30
+ Line-number entries in one `edits` array apply first, in descending `startLine` order, so each is anchored on the numbers you read; their ranges must not overlap (overlap ⇒ the whole batch is refused, nothing written). Content entries then apply in array order. The receipt stays in call order.
31
+
14
32
  Notes:
15
33
  - Prefer this over write for targeted edits — it's safer and keeps changes targeted
16
34
  - If old_string matches zero times (even fuzzy): error. If it matches multiple times without replace_all: error — add more surrounding context to make it unique
17
35
  - Returns `Edited <path>: replaced N occurrence(s)` (delete-mode: `Deleted line N of <path>` / `Deleted lines N-M of <path>`) + git diff + syntax-check note + context block (L..-L..).
18
36
  - Never fabricate the old_string — copy it verbatim from the actual file using read first
37
+ - **Line-number anchoring**: `line` / `startLine`+`endLine` are judged against the last write to that file: inside or below that write's affected range, or when the file was written after it without updating that anchor, the call is refused — naming the path, the drift (`L<startLine>`, `±shift`) and two ways on (re-read, or address the region without line numbers).
19
38
  - use the most recent read of the file as the source of old_string / line numbers / hashes — re-read after the file changed
39
+
40
+ **`ledger`** — the in-flight ledger row this call serves: `"#N"` or `"N"`. In engineering mode the parent agent must pass it — a call that does not say which row it serves is refused.
@@ -1,4 +1,6 @@
1
- Execute JavaScript — either inline `code` or a `scriptFile`. Runs in a real child `node` process — a pure node ESM environment: top-level `await` and dynamic `import()` are available, no globals are injected. File reads/writes/searches belong to the dedicated read/ls/glob/grep/write/edit tools — not to execute. If a script genuinely needs fs/path, `import` the `node:` module inside the code (one explicit import line).
1
+ Execute JavaScript — either inline `code` or a `scriptFile`. Runs in a real child `node` process — a pure node ESM environment: top-level `await` and dynamic `import()` are available, no globals are injected.
2
+
3
+ File reads/writes/searches belong to the dedicated read/ls/glob/grep/write/edit tools — not to execute. If a script genuinely needs fs/path, `import` the `node:` module inside the code (one explicit import line).
2
4
 
3
5
  **Route to execute instead of bash:**
4
6
  - `node -e "…"` → execute (inline code; top-level await + import() + console all work)
@@ -11,3 +13,7 @@ Notes:
11
13
  - Output is capped at ~50KB; when a script overruns it, an explicit `[output truncated]` marker is appended — print large results in chunks, or have the script write them to a file (node:fs) and read that file back with the `read` tool.
12
14
  - Use `write`/`edit`/`apply_patch` for source edits. Still use `bash` for package-manager/CLI subprocesses (`npm test`/`npm publish`/`vsce`), servers, and interactive/TTY programs — execute covers in-process JS and `node <script>`/`node --test`/`node --check`, not arbitrary CLI or long-running programs.
13
15
  - Irreversibility: it runs with full filesystem access and no automatic undo — script side effects are permanent; checkpoint (git) before risky bulk operations.
16
+
17
+ **`ledger`** — the in-flight ledger row this call serves: `"#N"` or `"N"`. In engineering mode the parent agent must pass it — a call that does not say which row it serves is refused, and read-only scripts are judged the same way (no command-line classification).
18
+
19
+ **`project`** — the project this call belongs to (absolute path of the project root). Read only when `workdir` is absent — `workdir` wins when both are given. Pure declaration: it does not change the execution directory (`workdir` does).
@@ -1,4 +1,6 @@
1
- Fetch a URL and return its content. With a DeepSeek provider configured the URL is fetched through DeepSeek's servers and returned as Markdown ([deepseek] title + content); otherwise it is fetched locally and HTML is stripped to readable text. Private/metadata URLs are never sent. Use after websearch to read full documents.
1
+ Fetch a URL and return its content. With a DeepSeek provider configured the URL is fetched through DeepSeek's servers and returned as Markdown ([deepseek] title + content); otherwise it is fetched locally and HTML is stripped to readable text.
2
+
3
+ Private/metadata URLs are never sent. Use after websearch to read full documents.
2
4
 
3
5
  Parameters:
4
6
  - url (required): http/https URL
@@ -14,4 +14,6 @@ Notes:
14
14
  - Paths resolve relative to cwd — no directory restriction (same boundary as bash; the approval gate is the guard).
15
15
  - `dest` is overwritten if it already exists. `copy` is recursive for directories.
16
16
  - To create a directory, use `write` (creates parent dirs) or `bash mkdir`.
17
- - Returns `Moved|Copied|Renamed <source> -> <dest>` or `Error: ...` (same-path and missing-path errors).
17
+ - Returns `Moved|Copied|Renamed <source> -> <dest>` or `Error: ...` (same-path and missing-path errors).
18
+
19
+ **`ledger`** — the in-flight ledger row this call serves: `"#N"` or `"N"`. In engineering mode the parent agent must pass it — a call that does not say which row it serves is refused.
package/tool-docs/git.md CHANGED
@@ -1,6 +1,8 @@
1
1
  Run a git command. Only works inside a git repository.
2
2
 
3
- **Route to git instead of bash** — every sub-command maps to the same-named action: status / log / diff / show / add / rm / 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.
3
+ **Route to git instead of bash** — every sub-command maps to the same-named action:
4
+
5
+ status / log / diff / show / add / rm / 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.
4
6
 
5
7
  - diff: unified diff since last commit (`staged` — staged only; `ref` — compare a commit/branch; `path` — scope).
6
8
  - status: working tree state — staged / unstaged / untracked / conflicts.
@@ -26,3 +28,9 @@ Run a git command. Only works inside a git repository.
26
28
  - Destructive ops (checkout -- path / restore / reset --hard / stash pop / branch|tag delete / clean / rebase) auto-snapshot first; restore a snapshot via `checkpointAction=rewind`.
27
29
  - Failures come back as errors — `git <action> failed: <reason>` — nothing is silently swallowed.
28
30
  - `filter` narrows any read-only action's output (case-insensitive regex); `config` (git -c overrides, e.g. a proxy) applies to the network actions (push / fetch / pull / ls-remote / clone). Other parameter defaults and applicability live in the schema.
31
+
32
+ **`ledger`** — the in-flight ledger row this call serves: `"#N"` or `"N"`. In engineering mode the parent agent must pass it for a write action; omitted = refused.
33
+
34
+ **`project`** — the project this call belongs to (absolute path of the project root). Read only when neither `path` nor `workdir` is given — those win when present. Pure declaration: it does not change where git runs (`workdir` does).
35
+
36
+ Read-only actions do not need it: `diff` / `status` / `log` / `show` / `ls-remote` / `blame`, and each `*Action: "list"` (`checkpoint` `list` / `cat` included).
package/tool-docs/goal.md CHANGED
@@ -1,6 +1,8 @@
1
1
  Manage a long-running autonomous goal.
2
2
  action='set': create or replace the goal — must have a verifiable completion criterion (a machine-checkable proof, not vague effort).
3
+
3
4
  action='complete': mark achieved — only after the criterion's check has actually passed.
4
5
  action='blocked': report an impasse (requires 'reason') — only after 3 genuine attempts.
5
6
  action='cancel': abandon the goal.
7
+
6
8
  Returns a status line — the goal set/updated/completed/blocked/cancelled confirmation, or Error: ... with the reason.
package/tool-docs/grep.md CHANGED
@@ -1,4 +1,4 @@
1
- Search file contents with a regex. Returns matching lines as path:line: content.
1
+ Search file contents with a regex. Returns matching lines, each preceded by its file path and line number.
2
2
 
3
3
  **Route to grep instead of bash:** `findstr /c:"pat" file` / `grep -rn pat .` → grep. Searching file contents is a read — never shell out for it.
4
4
 
@@ -12,3 +12,5 @@ Notes:
12
12
  - Prefer this over edit when: 1) the file may have mixed whitespace/encoding, 2) you want to edit a block of lines with a single call
13
13
  - Replacement text replaces the lines identified by the hashes — content not present in new_content is deleted. For a new line after a known line, use insert_after. For a single simple string swap, use edit.
14
14
  - use the most recent read of the file as the source of old_string / line numbers / hashes — re-read after the file changed
15
+
16
+ **`ledger`** — the in-flight ledger row this call serves: `"#N"` or `"N"`. In engineering mode the parent agent must pass it — a call that does not say which row it serves is refused.
@@ -11,5 +11,7 @@ Notes:
11
11
  - Use this instead of edit when you're adding a new line — a checklist item, a doc heading, a line of prose, a function, an import, or a block — no need to fabricate surrounding context for exact matching.
12
12
  - The inserted content becomes its own line; it's equivalent to `lines.splice(targetLine, 0, content)`.
13
13
  - Returns a diff of the change.
14
- - **Read-before-insert guard**: if the file was modified by any write tool (write/edit/insert_after/hashline_edit/apply_patch/delete) since your last `read`, this tool REFUSES with an error — line numbers may be stale. Read the file again, then retry. This prevents after_line from silently landing at a drifted position.
14
+ - **Read-before-insert guard**: `after_line` is judged against the last write to that file — a line at or above that write's write point is allowed; inside or below it, or when the file was written after it without updating that anchor, the call is refused with the path, the drift and two ways on (re-read, or address the region without line numbers).
15
15
  - use the most recent read of the file as the source of old_string / line numbers / hashes — re-read after the file changed
16
+
17
+ **`ledger`** — the in-flight ledger row this call serves: `"#N"` or `"N"`. In engineering mode the parent agent must pass it — a call that does not say which row it serves is refused.