@iowarp/clio-coder 0.3.4 → 0.3.6

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 (265) hide show
  1. package/CHANGELOG.md +37 -2
  2. package/CONTRIBUTING.md +6 -6
  3. package/README.md +2 -2
  4. package/dist/{acp-S5R4RR5B.js → acp-2BEHC4DL.js} +4 -4
  5. package/dist/{agents-P6DMMVZY.js → agents-LNNFTM53.js} +13 -11
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-2XCZLPKS.js → auth-KXXFI2VS.js} +6 -6
  8. package/dist/{chunk-YCWGATWI.js → chunk-24I7BN55.js} +2 -2
  9. package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
  10. package/dist/chunk-3BPUFZDL.js +37 -0
  11. package/dist/{chunk-WPQLXFOZ.js → chunk-43AOLP7E.js} +2 -2
  12. package/dist/{chunk-N4CZJQRK.js → chunk-5JGRAMKL.js} +4 -4
  13. package/dist/{chunk-BRXQQJFP.js → chunk-6US73PDB.js} +568 -47
  14. package/dist/{chunk-K6WL7QZT.js → chunk-6XXKFVSN.js} +2 -2
  15. package/dist/{chunk-QQK64KLB.js → chunk-CJUB2JJ2.js} +138 -20
  16. package/dist/{chunk-HV5X7OR2.js → chunk-CKXWIANG.js} +12 -12
  17. package/dist/{chunk-UZHIZC5S.js → chunk-CYQKWTG3.js} +61 -76
  18. package/dist/{chunk-QWU7ZBO7.js → chunk-DJVECN66.js} +204 -45
  19. package/dist/{chunk-ZWMF7253.js → chunk-E2ER4LJF.js} +304 -9
  20. package/dist/{chunk-7RXG6QRZ.js → chunk-EKY57CSP.js} +2 -75
  21. package/dist/{chunk-EDRHSCIE.js → chunk-EYPA3EGJ.js} +10 -2
  22. package/dist/{chunk-TTNYS3EA.js → chunk-G7MUEIGA.js} +1 -1
  23. package/dist/{chunk-BPGS2WCQ.js → chunk-GEYXPTRF.js} +2 -1
  24. package/dist/{chunk-BEY543CS.js → chunk-GOXNB3AO.js} +5 -2
  25. package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
  26. package/dist/chunk-HWUFFB6L.js +83 -0
  27. package/dist/{chunk-35MKKU5R.js → chunk-K7T3E2SR.js} +15 -8
  28. package/dist/{chunk-VAWWTKDP.js → chunk-KHSFENX2.js} +2 -2
  29. package/dist/chunk-LCGCVYZ4.js +57 -0
  30. package/dist/{chunk-X6COSD2O.js → chunk-LYF7OHWH.js} +41 -14
  31. package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
  32. package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
  33. package/dist/{chunk-5M54SPOL.js → chunk-ODFEOB4F.js} +161 -5
  34. package/dist/{chunk-3JLKSKD7.js → chunk-OH3TOQTB.js} +5 -1
  35. package/dist/{chunk-MEQ45TQ4.js → chunk-PBTHKCPN.js} +18 -4
  36. package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
  37. package/dist/{chunk-QQL5RT5M.js → chunk-QM3F2GKX.js} +94 -36
  38. package/dist/{chunk-A2GZF7DC.js → chunk-QNQHSOLF.js} +4 -4
  39. package/dist/{chunk-KRPY7NTG.js → chunk-R46L2BIR.js} +3 -3
  40. package/dist/{chunk-BP4OYD6A.js → chunk-RY3LY4J5.js} +20 -2
  41. package/dist/{chunk-34475P3I.js → chunk-TSHXZTOQ.js} +5 -4
  42. package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
  43. package/dist/{chunk-2TZWSW76.js → chunk-WHGPSPT5.js} +2 -2
  44. package/dist/{chunk-TW3WDMVS.js → chunk-WHJYKASB.js} +2 -2
  45. package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
  46. package/dist/{chunk-HXG4IURW.js → chunk-XE2VEJHX.js} +2 -2
  47. package/dist/{chunk-3HZ5RWN2.js → chunk-XF5N4U5A.js} +7 -6
  48. package/dist/{chunk-ZYKPLLNQ.js → chunk-XXQNGV4M.js} +590 -32
  49. package/dist/{chunk-4JUF2NNX.js → chunk-XYDYPRZI.js} +4 -4
  50. package/dist/{chunk-VMNQ6OZA.js → chunk-ZRGEBJ4T.js} +971 -794
  51. package/dist/{chunk-2LZI5CAG.js → chunk-ZXF4XRKW.js} +75 -33
  52. package/dist/{chunk-VSNATDE6.js → chunk-ZZMN5OM4.js} +2 -2
  53. package/dist/cli/index.js +31 -31
  54. package/dist/{clio-J5JIOIDS.js → clio-M2KGYUFZ.js} +2 -2
  55. package/dist/{code-nav-AXCXSBHX.js → code-nav-GQNL7XA6.js} +5 -5
  56. package/dist/codewiki/build-worker.js +4 -4
  57. package/dist/{components-KELWS457.js → components-5TTYYX6G.js} +3 -3
  58. package/dist/{config-OEBMIN2U.js → config-XUUYQIWO.js} +27 -25
  59. package/dist/{configure-PUQOSIXQ.js → configure-IHJ7YOMV.js} +7 -7
  60. package/dist/{context-URSXPBCK.js → context-74JLXAWD.js} +12 -12
  61. package/dist/{context-MGSE4Z2T.js → context-75MIWW3U.js} +24 -22
  62. package/dist/{context-EKDCKUUZ.js → context-ZQ7SIFJV.js} +8 -7
  63. package/dist/{context-clear-KDAJRNUK.js → context-clear-GYKWNUML.js} +24 -22
  64. package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
  65. package/dist/{context-working-set-SBKMPPI2.js → context-working-set-UX5KEP4J.js} +11 -10
  66. package/dist/{dispatch-runner-MSWN72NK.js → dispatch-runner-GIJBHNFL.js} +21 -20
  67. package/dist/{docs-2C2LTVT2.js → docs-6FZSCG5B.js} +3 -3
  68. package/dist/{doctor-7BSE27PJ.js → doctor-SVJ5BZCW.js} +4 -4
  69. package/dist/{eval-IZGDOO4H.js → eval-CG6LLBLD.js} +47 -232
  70. package/dist/{evidence-SR7WXB5B.js → evidence-ZYFIEN42.js} +19 -18
  71. package/dist/{evolve-K7VE2CBX.js → evolve-QGEXEMDW.js} +19 -18
  72. package/dist/{extensions-QVDOHDGJ.js → extensions-ADGNCJJD.js} +3 -3
  73. package/dist/{fleet-7XMJNQNF.js → fleet-S5R4ZOQY.js} +49 -30
  74. package/dist/{fleet-preflight-AQNAH644.js → fleet-preflight-BHSNPBMH.js} +2 -2
  75. package/dist/{init-JGNPAYXT.js → init-5DRU55YR.js} +31 -29
  76. package/dist/memory-7YKKR6UC.js +467 -0
  77. package/dist/{models-ZMMLFJNN.js → models-ZPOLRU2C.js} +10 -10
  78. package/dist/{monitor-2F3T5KHP.js → monitor-US5F5YGZ.js} +33 -18
  79. package/dist/{orchestrator-ORHT43JB.js → orchestrator-E2AL4T5N.js} +1092 -659
  80. package/dist/{paths-UXLN5YYZ.js → paths-E7KYAQWE.js} +3 -3
  81. package/dist/{reset-NXGTYNUO.js → reset-KZ652EK6.js} +3 -3
  82. package/dist/{run-RF4WJGMT.js → run-SRNBKDWD.js} +52 -40
  83. package/dist/{share-UT3W6E4M.js → share-CGZE33UP.js} +3 -3
  84. package/dist/{skills-PSACKC5Q.js → skills-S2X4DLY5.js} +4 -4
  85. package/dist/{skills-eval-WJSI55RZ.js → skills-eval-W2GGIC4R.js} +19 -18
  86. package/dist/{targets-PIIRAOYS.js → targets-54SWINWB.js} +14 -12
  87. package/dist/{terminal-lease-ULWXWNVY.js → terminal-lease-SAIF2OGY.js} +5 -4
  88. package/dist/{uninstall-FZCQCDKC.js → uninstall-BVLWXKBT.js} +3 -3
  89. package/dist/{upgrade-346TZ6AV.js → upgrade-JKAR27XC.js} +8 -8
  90. package/dist/{usage-6KKXR32N.js → usage-MSAWCLX4.js} +60 -27
  91. package/dist/{verifiers-4UUM6TEE.js → verifiers-NCBTHHN2.js} +60 -54
  92. package/dist/{wiki-generate-7STOCIFZ.js → wiki-generate-GUSOQ6ZP.js} +30 -28
  93. package/dist/worker/entry.js +69 -58
  94. package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
  95. package/docs/README.md +3 -3
  96. package/docs/acp.md +1 -1
  97. package/docs/alcf-provider.md +1 -1
  98. package/docs/architecture.md +2 -2
  99. package/docs/artifact-placement.md +1 -2
  100. package/docs/artifact-versions.md +1 -1
  101. package/docs/built-in-agents.md +1 -1
  102. package/docs/capacity-and-scheduling.md +1 -1
  103. package/docs/commands-and-modes.md +9 -7
  104. package/docs/configuration-and-targets.md +12 -1
  105. package/docs/context-engine.md +4 -2
  106. package/docs/context-working-set.md +4 -4
  107. package/docs/development-pipeline.md +1 -1
  108. package/docs/documentation-coverage.md +3 -3
  109. package/docs/documentation-guide.md +2 -2
  110. package/docs/eval-runner.md +1 -1
  111. package/docs/evals-internal.md +4 -45
  112. package/docs/evidence-and-memory.md +67 -7
  113. package/docs/evolution.md +1 -1
  114. package/docs/exit-codes-and-output.md +1 -1
  115. package/docs/extensions-and-sharing.md +2 -2
  116. package/docs/fleet-dispatch.md +28 -2
  117. package/docs/installation-and-lifecycle.md +2 -2
  118. package/docs/middleware-and-components.md +19 -2
  119. package/docs/model-catalog.md +1 -1
  120. package/docs/observability.md +3 -3
  121. package/docs/proactive-memory.md +26 -16
  122. package/docs/prompt-envelope-and-tools.md +4 -2
  123. package/docs/provider-adapter-cookbook.md +1 -1
  124. package/docs/release-cut-checklist.md +38 -35
  125. package/docs/safety-model.md +29 -7
  126. package/docs/scientific-validation.md +3 -3
  127. package/docs/session-lifecycle.md +1 -1
  128. package/docs/skills-marketplace.md +1 -1
  129. package/docs/tool-usage.md +2 -2
  130. package/docs/trace-store.md +1 -1
  131. package/docs/troubleshooting.md +1 -1
  132. package/docs/tui-design.md +38 -4
  133. package/docs/worker-dispatch-mechanics.md +1 -1
  134. package/package.json +7 -4
  135. package/src/cli/agents.ts +2 -3
  136. package/src/cli/argv.ts +14 -1
  137. package/src/cli/fleet.ts +15 -0
  138. package/src/cli/index.ts +1 -1
  139. package/src/cli/memory.ts +272 -10
  140. package/src/cli/modes/json-stream.ts +2 -2
  141. package/src/cli/modes/print.ts +12 -1
  142. package/src/cli/run.ts +22 -2
  143. package/src/cli/targets.ts +12 -3
  144. package/src/cli/usage.ts +55 -7
  145. package/src/core/bus-events.ts +3 -0
  146. package/src/core/response-model-id.ts +134 -0
  147. package/src/core/toml.ts +62 -0
  148. package/src/core/workspace-files.ts +0 -1
  149. package/src/domains/agents/builtins/architect.md +1 -1
  150. package/src/domains/agents/catalog.ts +5 -4
  151. package/src/domains/agents/recipe.ts +54 -14
  152. package/src/domains/agents/result-contract.ts +7 -4
  153. package/src/domains/context/bootstrap.ts +36 -27
  154. package/src/domains/context/project-metadata.ts +19 -63
  155. package/src/domains/context/prompt-context.ts +8 -0
  156. package/src/domains/context/working-set/policies/index.ts +3 -4
  157. package/src/domains/dispatch/budget-envelope.ts +396 -0
  158. package/src/domains/dispatch/contract.ts +2 -0
  159. package/src/domains/dispatch/extension.ts +81 -27
  160. package/src/domains/dispatch/orphan-recovery.ts +1 -0
  161. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  162. package/src/domains/dispatch/state.ts +1 -0
  163. package/src/domains/dispatch/types.ts +10 -3
  164. package/src/domains/dispatch/validation.ts +14 -0
  165. package/src/domains/dispatch/worker-spawn.ts +14 -3
  166. package/src/domains/eval/metrics/evidence.ts +0 -116
  167. package/src/domains/eval/metrics/invariants.ts +1 -1
  168. package/src/domains/eval/runners/clio-run.ts +1 -10
  169. package/src/domains/eval/runners/external-command.ts +2 -29
  170. package/src/domains/eval/schema/suite.ts +0 -7
  171. package/src/domains/eval/suites/run.ts +1 -7
  172. package/src/domains/memory/index.ts +22 -0
  173. package/src/domains/memory/operations.ts +58 -1
  174. package/src/domains/memory/promotion.ts +281 -0
  175. package/src/domains/memory/prompt-section.ts +25 -5
  176. package/src/domains/memory/proposal.ts +51 -7
  177. package/src/domains/memory/task-bank.ts +3 -2
  178. package/src/domains/memory/task-memory-handoff.ts +181 -24
  179. package/src/domains/memory/task-memory-policy.ts +3 -1
  180. package/src/domains/memory/types.ts +37 -0
  181. package/src/domains/memory/validate.ts +178 -0
  182. package/src/domains/middleware/memory-intervention.ts +35 -25
  183. package/src/domains/middleware/runtime.ts +6 -0
  184. package/src/domains/middleware/skills-reminder.ts +19 -4
  185. package/src/domains/middleware/stalled-turn.ts +43 -1
  186. package/src/domains/middleware/types.ts +10 -0
  187. package/src/domains/observability/contract.ts +6 -1
  188. package/src/domains/observability/cost.ts +20 -4
  189. package/src/domains/observability/extension.ts +2 -2
  190. package/src/domains/providers/index.ts +3 -0
  191. package/src/domains/providers/model-discovery.ts +9 -0
  192. package/src/domains/providers/runtime-resolution.ts +38 -1
  193. package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
  194. package/src/domains/providers/types/context-window-slots.ts +18 -0
  195. package/src/domains/providers/types/runtime-descriptor.ts +3 -1
  196. package/src/domains/safety/call-target.ts +211 -14
  197. package/src/domains/safety/decision-presentation.ts +268 -0
  198. package/src/domains/safety/redaction.ts +73 -0
  199. package/src/domains/session/context-ledger.ts +10 -1
  200. package/src/domains/session/decision-board.ts +4 -0
  201. package/src/domains/session/entries.ts +3 -0
  202. package/src/domains/session/history.ts +68 -19
  203. package/src/domains/session/usage.ts +24 -7
  204. package/src/engine/acp/event-mapper.ts +7 -0
  205. package/src/engine/acp/server.ts +29 -2
  206. package/src/engine/apis/lmstudio.ts +25 -4
  207. package/src/engine/apis/openai-completions.ts +147 -22
  208. package/src/engine/claude/sdk-runtime.ts +8 -2
  209. package/src/engine/claude/tool-safety.ts +13 -0
  210. package/src/engine/loop-guard.ts +27 -3
  211. package/src/engine/worker-events.ts +4 -3
  212. package/src/engine/worker-runtime.ts +59 -54
  213. package/src/entry/orchestrator.ts +18 -1
  214. package/src/interactive/chat-loop-messages.ts +22 -0
  215. package/src/interactive/chat-loop.ts +13 -0
  216. package/src/interactive/chat-renderer.ts +19 -3
  217. package/src/interactive/clio-editor.ts +44 -7
  218. package/src/interactive/context-overlay.ts +43 -5
  219. package/src/interactive/cost-overlay.ts +39 -8
  220. package/src/interactive/dispatch-board.ts +212 -35
  221. package/src/interactive/footer/widgets.ts +13 -0
  222. package/src/interactive/interactive-application.ts +6 -1
  223. package/src/interactive/interactive-input-runtime.ts +11 -1
  224. package/src/interactive/interactive-presentation.ts +11 -1
  225. package/src/interactive/memory-overlay.ts +89 -4
  226. package/src/interactive/overlay-ask-user-lifecycle.ts +1 -1
  227. package/src/interactive/overlay-frame.ts +5 -2
  228. package/src/interactive/overlay-general-openers.ts +40 -1
  229. package/src/interactive/overlay-key-routing.ts +41 -1
  230. package/src/interactive/overlay-lifecycle.ts +11 -4
  231. package/src/interactive/overlay-permission-lifecycle.ts +23 -8
  232. package/src/interactive/overlay-transitions.ts +11 -0
  233. package/src/interactive/overlays/ask-user.ts +74 -30
  234. package/src/interactive/overlays/decisions.ts +3 -1
  235. package/src/interactive/permission-hint.ts +35 -0
  236. package/src/interactive/permission-overlay.ts +95 -45
  237. package/src/interactive/renderers/tool-execution.ts +19 -49
  238. package/src/interactive/session-last-turn.ts +8 -1
  239. package/src/interactive/session-usage-reseed.ts +36 -10
  240. package/src/interactive/slash-commands.ts +2 -2
  241. package/src/interactive/status/summary.ts +5 -0
  242. package/src/interactive/status/types.ts +5 -0
  243. package/src/interactive/terminal-lease.ts +1 -0
  244. package/src/interactive/turn-context.ts +96 -23
  245. package/src/interactive/turn-middleware.ts +1 -0
  246. package/src/interactive/turn-runtime.ts +37 -8
  247. package/src/interactive/turn-state.ts +3 -0
  248. package/src/interactive/worker-progress.ts +440 -0
  249. package/src/interactive/worker-stream.ts +51 -110
  250. package/src/tools/agent-tools.ts +28 -3
  251. package/src/tools/ask-user.ts +21 -1
  252. package/src/tools/context/index.ts +2 -2
  253. package/src/tools/dispatch-arguments.ts +8 -0
  254. package/src/tools/dispatch-event-text.ts +19 -0
  255. package/src/tools/dispatch.ts +24 -1
  256. package/src/tools/monitor.ts +15 -0
  257. package/src/tools/registry.ts +15 -5
  258. package/src/tools/result-disposition.ts +156 -0
  259. package/src/tools/result-shaping.ts +59 -1
  260. package/src/tools/verify/authoring.ts +55 -54
  261. package/src/tools/worker-evidence.ts +19 -0
  262. package/src/worker/spec-contract.ts +43 -3
  263. package/dist/chunk-EFADSJET.js +0 -18
  264. package/dist/memory-4ALKDJ4Q.js +0 -246
  265. package/src/domains/eval/metrics/chaos-stream.ts +0 -93
@@ -0,0 +1,134 @@
1
+ /** What Clio directly observed about a provider response model id. */
2
+ export type ResponseModelIdObservation =
3
+ | { state: "reported"; reportedModelId: string }
4
+ | { state: "not-reported" }
5
+ | { state: "not-observed" }
6
+ | { state: "legacy-difference-only"; differingModelId: string | null };
7
+
8
+ export type MissingResponseModelIdObservation = "not-observed" | "legacy-difference-only";
9
+
10
+ /** Call counts grouped by the exact response model-id observation state. */
11
+ export interface ResponseModelIdObservationCounts {
12
+ reportedCalls: number;
13
+ notReportedCalls: number;
14
+ notObservedCalls: number;
15
+ legacyDifferenceOnlyCalls: number;
16
+ }
17
+
18
+ export function emptyResponseModelIdObservationCounts(): ResponseModelIdObservationCounts {
19
+ return { reportedCalls: 0, notReportedCalls: 0, notObservedCalls: 0, legacyDifferenceOnlyCalls: 0 };
20
+ }
21
+
22
+ export function addResponseModelIdObservationCount(
23
+ counts: ResponseModelIdObservationCounts,
24
+ observation: ResponseModelIdObservation,
25
+ calls = 1,
26
+ ): void {
27
+ switch (observation.state) {
28
+ case "reported":
29
+ counts.reportedCalls += calls;
30
+ break;
31
+ case "not-reported":
32
+ counts.notReportedCalls += calls;
33
+ break;
34
+ case "not-observed":
35
+ counts.notObservedCalls += calls;
36
+ break;
37
+ case "legacy-difference-only":
38
+ counts.legacyDifferenceOnlyCalls += calls;
39
+ break;
40
+ }
41
+ }
42
+
43
+ export function addResponseModelIdObservationCounts(
44
+ counts: ResponseModelIdObservationCounts,
45
+ additional: Readonly<ResponseModelIdObservationCounts>,
46
+ ): void {
47
+ counts.reportedCalls += additional.reportedCalls;
48
+ counts.notReportedCalls += additional.notReportedCalls;
49
+ counts.notObservedCalls += additional.notObservedCalls;
50
+ counts.legacyDifferenceOnlyCalls += additional.legacyDifferenceOnlyCalls;
51
+ }
52
+
53
+ export function responseModelIdObservationCountsLabel(counts: ResponseModelIdObservationCounts): string {
54
+ const parts: string[] = [];
55
+ if (counts.reportedCalls > 0) parts.push(`reported ${counts.reportedCalls}`);
56
+ if (counts.notReportedCalls > 0) parts.push(`not reported ${counts.notReportedCalls}`);
57
+ if (counts.notObservedCalls > 0) parts.push(`not observed ${counts.notObservedCalls}`);
58
+ if (counts.legacyDifferenceOnlyCalls > 0) parts.push(`legacy difference-only ${counts.legacyDifferenceOnlyCalls}`);
59
+ return parts.join(", ");
60
+ }
61
+
62
+ function nonEmptyString(value: unknown): string | null {
63
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
64
+ }
65
+
66
+ function parsedObservation(value: unknown): ResponseModelIdObservation | null {
67
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
68
+ const record = value as Record<string, unknown>;
69
+ switch (record.state) {
70
+ case "reported": {
71
+ const reportedModelId = nonEmptyString(record.reportedModelId);
72
+ return reportedModelId === null ? null : { state: "reported", reportedModelId };
73
+ }
74
+ case "not-reported":
75
+ return { state: "not-reported" };
76
+ case "not-observed":
77
+ return { state: "not-observed" };
78
+ case "legacy-difference-only":
79
+ return { state: "legacy-difference-only", differingModelId: nonEmptyString(record.differingModelId) };
80
+ default:
81
+ return null;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Read the current observation shape and the short-lived #193 shape. A caller
87
+ * chooses how to label a missing field because a live message is not historical,
88
+ * while a persisted row without either shape predates model-id presence capture.
89
+ */
90
+ export function responseModelIdObservationFromRecord(
91
+ record: Readonly<Record<string, unknown>>,
92
+ missing: MissingResponseModelIdObservation,
93
+ ): ResponseModelIdObservation {
94
+ const current = parsedObservation(record.responseModelIdObservation);
95
+ if (current !== null) return current;
96
+ if (Object.hasOwn(record, "servedModel")) {
97
+ const reportedModelId = nonEmptyString(record.servedModel);
98
+ return reportedModelId === null ? { state: "not-reported" } : { state: "reported", reportedModelId };
99
+ }
100
+ if (missing === "not-observed") return { state: "not-observed" };
101
+ return { state: "legacy-difference-only", differingModelId: nonEmptyString(record.responseModel) };
102
+ }
103
+
104
+ /** Human vocabulary shared by usage, footer, and receipt projections. */
105
+ export function responseModelIdObservationLabel(observation: ResponseModelIdObservation): string {
106
+ switch (observation.state) {
107
+ case "reported":
108
+ return "reported";
109
+ case "not-reported":
110
+ return "not reported";
111
+ case "not-observed":
112
+ return "not observed";
113
+ case "legacy-difference-only":
114
+ return "legacy difference-only";
115
+ }
116
+ }
117
+
118
+ /** Model id used for accounting without claiming more than the observation supports. */
119
+ export function attributedModelId(
120
+ observation: ResponseModelIdObservation,
121
+ requestedModelId: string,
122
+ differingResponseModelId: string | null,
123
+ ): string {
124
+ switch (observation.state) {
125
+ case "reported":
126
+ return observation.reportedModelId;
127
+ case "not-reported":
128
+ return "unknown";
129
+ case "not-observed":
130
+ return differingResponseModelId ?? requestedModelId;
131
+ case "legacy-difference-only":
132
+ return observation.differingModelId ?? requestedModelId;
133
+ }
134
+ }
@@ -0,0 +1,62 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { join } from "node:path";
4
+ import type { TomlTable } from "smol-toml";
5
+
6
+ type SmolTomlModule = typeof import("smol-toml");
7
+
8
+ const require = createRequire(import.meta.url);
9
+ let parse: SmolTomlModule["parse"] | undefined;
10
+
11
+ function parser(): SmolTomlModule["parse"] {
12
+ parse ??= (require("smol-toml") as SmolTomlModule).parse;
13
+ return parse;
14
+ }
15
+
16
+ /** Parse one TOML document, returning no structure when the input is malformed. */
17
+ export function parseTomlDocument(raw: string): TomlTable | null {
18
+ try {
19
+ return parser()(raw);
20
+ } catch {
21
+ return null;
22
+ }
23
+ }
24
+
25
+ function isTomlTable(value: unknown): value is TomlTable {
26
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
27
+ }
28
+
29
+ /** Resolve a structural table path from a parsed document. */
30
+ export function tomlTableAt(document: TomlTable, path: ReadonlyArray<string>): TomlTable | null {
31
+ let value: unknown = document;
32
+ for (const segment of path) {
33
+ if (!isTomlTable(value) || !Object.hasOwn(value, segment)) return null;
34
+ value = value[segment];
35
+ }
36
+ return isTomlTable(value) ? value : null;
37
+ }
38
+
39
+ export interface TomlFileReader {
40
+ read(relativePath: string): TomlTable | null;
41
+ }
42
+
43
+ /**
44
+ * Create an operation-scoped TOML reader. Each relative path is read and
45
+ * parsed at most once, including missing and malformed files.
46
+ */
47
+ export function createTomlFileReader(root: string): TomlFileReader {
48
+ const documents = new Map<string, TomlTable | null>();
49
+ return {
50
+ read(relativePath) {
51
+ if (documents.has(relativePath)) return documents.get(relativePath) ?? null;
52
+ let document: TomlTable | null = null;
53
+ try {
54
+ document = parseTomlDocument(readFileSync(join(root, relativePath), "utf8"));
55
+ } catch {
56
+ document = null;
57
+ }
58
+ documents.set(relativePath, document);
59
+ return document;
60
+ },
61
+ };
62
+ }
@@ -133,7 +133,6 @@ export const WORKSPACE_EXCLUDED_DIRS: ReadonlySet<string> = new Set([
133
133
  ".superpowers",
134
134
  ".codex",
135
135
  ".claude",
136
- ".clio-coder-benchmark",
137
136
  ]);
138
137
 
139
138
  function comparePaths(a: string, b: string): number {
@@ -11,7 +11,7 @@ category: plan
11
11
  capabilityClass: artifact-write
12
12
  latencyClass: deep
13
13
  projectContextTier: bounded
14
- budget: {toolCalls: 32, readReserve: 5, synthesis: true}
14
+ budget: {toolCalls: 32, readReserve: 5, synthesis: true, maximum: {toolCalls: 150, readReserve: 16}}
15
15
  resultContract: {kind: architect-plan, path: .clio-coder/artifacts/PLAN.md}
16
16
  tags: [architecture, boundaries, migration]
17
17
  ---
@@ -71,7 +71,9 @@ function fleetPromptPurpose(description: string): string {
71
71
  }
72
72
 
73
73
  function fleetPromptLine(spec: AgentSpec): string {
74
- const budget = spec.budget ? `${spec.budget.toolCalls} calls` : "default budget";
74
+ const budget = spec.budget.maximum
75
+ ? `${spec.budget.toolCalls}..${spec.budget.maximum.toolCalls} calls`
76
+ : `${spec.budget.toolCalls} calls`;
75
77
  const purpose = fleetPromptPurpose(spec.description);
76
78
  return `- ${spec.id} (${spec.capabilityClass}, ${budget})${purpose.length > 0 ? `: ${purpose}` : ""}`;
77
79
  }
@@ -160,9 +162,8 @@ export function renderAgentCatalogSectionsFromSpecs(input: ReadonlyArray<AgentSp
160
162
  function formatSpecLine(spec: AgentSpec, suffix: string): string {
161
163
  const tags = spec.tags.length > 0 ? `, tags=${spec.tags.join("/")}` : "";
162
164
  const skills = spec.skills.length > 0 ? `, skills=${spec.skills.join("/")}` : "";
163
- const budget = spec.budget
164
- ? `, budget=${spec.budget.toolCalls}/${spec.budget.readReserve}/${spec.budget.synthesis ? "synthesize" : "stop"}`
165
- : ", budget=operator-default";
165
+ const maximum = spec.budget.maximum ? `..${spec.budget.maximum.toolCalls}/${spec.budget.maximum.readReserve}` : "";
166
+ const budget = `, budget=${spec.budget.toolCalls}/${spec.budget.readReserve}${maximum}/${spec.budget.synthesis ? "synthesize" : "stop"}`;
166
167
  return `- ${spec.name} [${spec.id}] (${spec.audience}, ${spec.category}, ${spec.capabilityClass}, ${spec.latencyClass}, ${spec.source}${tags}${skills}${budget})${suffix}`;
167
168
  }
168
169
 
@@ -9,33 +9,33 @@ import type {
9
9
  AgentProjectContextTier,
10
10
  } from "./spec.js";
11
11
 
12
- /** Authored worker-loop phase policy carried by an agent recipe. */
13
- export interface AgentBudget {
12
+ /** One bounded worker-loop phase policy. */
13
+ export interface AgentBudgetPhase {
14
14
  /** Admitted tool-call boundary before the final-response phase. */
15
15
  toolCalls: number;
16
16
  /** Tail of the admitted boundary reserved for canonical `read` calls. */
17
17
  readReserve: number;
18
+ }
19
+
20
+ /** Authored worker-loop phase policy carried by an agent recipe. */
21
+ export interface AgentBudget extends AgentBudgetPhase {
18
22
  /** Whether reaching the boundary transitions to text-only synthesis. */
19
23
  synthesis: boolean;
24
+ /** Optional upper bound for invocation-level requests. Absence pins the default exactly. */
25
+ maximum?: AgentBudgetPhase;
20
26
  }
21
27
 
22
- const AGENT_BUDGET_KEYS = ["toolCalls", "readReserve", "synthesis"] as const;
28
+ const AGENT_BUDGET_KEYS = ["toolCalls", "readReserve", "synthesis", "maximum"] as const;
23
29
 
24
- /** Strict parser shared by the one recipe schema and direct contract fixtures. */
25
- export function parseAgentBudget(value: unknown, sourcePath: string): AgentBudget | undefined {
26
- if (value === undefined) return undefined;
27
- const prefix = `agent budget: ${sourcePath}: budget`;
30
+ function parseAgentBudgetPhase(value: unknown, prefix: string, relationTarget: string): AgentBudgetPhase {
28
31
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
29
32
  throw new Error(`${prefix} must be a non-null YAML object`);
30
33
  }
31
-
32
34
  const record = value as Record<string, unknown>;
33
35
  for (const key of Object.keys(record)) {
34
- if (!(AGENT_BUDGET_KEYS as ReadonlyArray<string>).includes(key)) {
35
- throw new Error(`${prefix}.${key} is unknown`);
36
- }
36
+ if (key !== "toolCalls" && key !== "readReserve") throw new Error(`${prefix}.${key} is unknown`);
37
37
  }
38
- for (const key of AGENT_BUDGET_KEYS) {
38
+ for (const key of ["toolCalls", "readReserve"] as const) {
39
39
  if (!Object.hasOwn(record, key)) throw new Error(`${prefix}.${key} is required`);
40
40
  }
41
41
 
@@ -50,12 +50,52 @@ export function parseAgentBudget(value: unknown, sourcePath: string): AgentBudge
50
50
  throw new Error(`${prefix}.readReserve must be a finite safe integer`);
51
51
  }
52
52
  if (readReserve < 0) throw new Error(`${prefix}.readReserve must be greater than or equal to 0`);
53
- if (readReserve >= toolCalls) throw new Error(`${prefix}.readReserve must be less than budget.toolCalls`);
53
+ if (readReserve >= toolCalls) throw new Error(`${prefix}.readReserve must be less than ${relationTarget}`);
54
+
55
+ return { toolCalls, readReserve };
56
+ }
57
+
58
+ /** Strict parser shared by the one recipe schema and direct contract fixtures. */
59
+ export function parseAgentBudget(value: unknown, sourcePath: string): AgentBudget | undefined {
60
+ if (value === undefined) return undefined;
61
+ const prefix = `agent budget: ${sourcePath}: budget`;
62
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
63
+ throw new Error(`${prefix} must be a non-null YAML object`);
64
+ }
65
+
66
+ const record = value as Record<string, unknown>;
67
+ for (const key of Object.keys(record)) {
68
+ if (!(AGENT_BUDGET_KEYS as ReadonlyArray<string>).includes(key)) {
69
+ throw new Error(`${prefix}.${key} is unknown`);
70
+ }
71
+ }
72
+ for (const key of ["toolCalls", "readReserve", "synthesis"] as const) {
73
+ if (!Object.hasOwn(record, key)) throw new Error(`${prefix}.${key} is required`);
74
+ }
75
+
76
+ const phase = parseAgentBudgetPhase(
77
+ { toolCalls: record.toolCalls, readReserve: record.readReserve },
78
+ prefix,
79
+ "budget.toolCalls",
80
+ );
54
81
 
55
82
  const synthesis = record.synthesis;
56
83
  if (typeof synthesis !== "boolean") throw new Error(`${prefix}.synthesis must be a boolean`);
57
84
 
58
- return { toolCalls, readReserve, synthesis };
85
+ const maximum =
86
+ record.maximum === undefined
87
+ ? undefined
88
+ : parseAgentBudgetPhase(record.maximum, `${prefix}.maximum`, "budget.maximum.toolCalls");
89
+ if (maximum !== undefined) {
90
+ if (maximum.toolCalls < phase.toolCalls) {
91
+ throw new Error(`${prefix}.maximum.toolCalls must be greater than or equal to budget.toolCalls`);
92
+ }
93
+ if (maximum.readReserve < phase.readReserve) {
94
+ throw new Error(`${prefix}.maximum.readReserve must be greater than or equal to budget.readReserve`);
95
+ }
96
+ }
97
+
98
+ return { ...phase, synthesis, ...(maximum === undefined ? {} : { maximum }) };
59
99
  }
60
100
 
61
101
  export interface AgentToolAnyOfRequirement {
@@ -1147,12 +1147,13 @@ export interface ResultContractRepairInput {
1147
1147
  attempt: number;
1148
1148
  /** `path:line` anchors from reads that succeeded in this run. */
1149
1149
  anchors: ReadonlyArray<string>;
1150
+ /** True only when admission preauthorized a larger tool-call revision phase. */
1151
+ toolsAvailable?: boolean;
1150
1152
  }
1151
1153
 
1152
1154
  /**
1153
- * One repair directive. It states the validator's reason, the exact shape, and
1154
- * that tool use is over, so the model's only remaining move is to emit the
1155
- * terminal document from evidence it already has.
1155
+ * One repair directive. It states the validator's reason and exact shape. Tool
1156
+ * use remains closed unless dispatch preauthorized a larger revision phase.
1156
1157
  */
1157
1158
  export function resultContractRepairMessage(input: ResultContractRepairInput): string {
1158
1159
  const last = input.attempt >= RESULT_CONTRACT_REPAIR_LIMIT;
@@ -1161,7 +1162,9 @@ export function resultContractRepairMessage(input: ResultContractRepairInput): s
1161
1162
  ? "FINAL RESULT REQUIRED IN THIS RESPONSE. Your previous response still did not satisfy this run's result contract."
1162
1163
  : "Your previous response did not satisfy this run's result contract.",
1163
1164
  `Validator reason: ${input.reason}`,
1164
- "Tool use is over. Answer from the evidence you already gathered.",
1165
+ input.toolsAvailable === true
1166
+ ? "You may use the admitted tools to repair this validator failure, then emit the required terminal result."
1167
+ : "Tool use is over. Answer from the evidence you already gathered.",
1165
1168
  `Emit exactly this shape and nothing else: ${resultContractShape(input.contract)}`,
1166
1169
  "Do not add prose, code fences, or commentary around it. Do not describe work you intend to do next.",
1167
1170
  ];
@@ -2,6 +2,7 @@ import { execFileSync } from "node:child_process";
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join, parse } from "node:path";
4
4
  import type { ContextActivityPayload } from "../../core/bus-events.js";
5
+ import { createTomlFileReader, type TomlFileReader, tomlTableAt } from "../../core/toml.js";
5
6
  import { classifyProjectPreload, type ProjectPreloadClass } from "../prompts/preload.js";
6
7
  import { detectProjectType, type ProjectType } from "../session/workspace/project-type.js";
7
8
  import {
@@ -15,7 +16,7 @@ import { type ClioMdSection, type ParsedClioMd, parseClioMd, serializeClioMd, tr
15
16
  import { buildCodewikiCandidate, coordinateCodewikiWrite } from "./codewiki/coordinator.js";
16
17
  import type { Codewiki } from "./codewiki/schema.js";
17
18
  import type { Fingerprint } from "./fingerprint.js";
18
- import { hasTomlTable, type ProjectMetadata, readProjectMetadata } from "./project-metadata.js";
19
+ import { type ProjectMetadata, readProjectMetadata } from "./project-metadata.js";
19
20
  import { renderPromptContext } from "./prompt-context.js";
20
21
  import type { SiblingContextFile } from "./sibling-files.js";
21
22
  import {
@@ -49,6 +50,8 @@ export interface BootstrapIo {
49
50
  export interface BootstrapGenerateInput {
50
51
  cwd: string;
51
52
  expectedProjectName?: string;
53
+ /** Operation-scoped parsed TOML documents shared by bootstrap consumers. */
54
+ tomlFiles?: TomlFileReader;
52
55
  projectType: ProjectType;
53
56
  siblingFiles: ReadonlyArray<SiblingContextFile>;
54
57
  adoption: AdoptionScanResult;
@@ -211,8 +214,8 @@ function projectNameFrom(metadata: ProjectMetadata, cwd: string): string {
211
214
  return titleFromPackageName(parse(cwd).base || "Project");
212
215
  }
213
216
 
214
- function projectName(cwd: string): string {
215
- return projectNameFrom(readProjectMetadata(cwd), cwd);
217
+ function projectName(cwd: string, tomlFiles: TomlFileReader): string {
218
+ return projectNameFrom(readProjectMetadata(cwd, tomlFiles), cwd);
216
219
  }
217
220
 
218
221
  function projectTypeLabel(projectType: ProjectType): string {
@@ -270,8 +273,9 @@ function resolveDefaultIdentity(
270
273
  cwd: string,
271
274
  projectType: ProjectType,
272
275
  files: ReadonlyArray<SiblingContextFile>,
276
+ tomlFiles: TomlFileReader,
273
277
  ): DeterministicIdentity {
274
- const metadata = readProjectMetadata(cwd);
278
+ const metadata = readProjectMetadata(cwd, tomlFiles);
275
279
  const name = projectNameFrom(metadata, cwd);
276
280
  const context = allContextText(files);
277
281
  if (/Clio owns the agent loop/i.test(context) && /pi-(?:ai|SDK)/i.test(context)) {
@@ -303,8 +307,8 @@ function resolveDefaultIdentity(
303
307
  * which tells a reader nothing the directory name did not. A model sentence is
304
308
  * strictly better than that, and only than that.
305
309
  */
306
- function stabilizedIdentity(input: BootstrapGenerateInput, modelIdentity: unknown): string {
307
- const deterministic = resolveDefaultIdentity(input.cwd, input.projectType, input.siblingFiles);
310
+ function stabilizedIdentity(input: BootstrapGenerateInput, modelIdentity: unknown, tomlFiles: TomlFileReader): string {
311
+ const deterministic = resolveDefaultIdentity(input.cwd, input.projectType, input.siblingFiles, tomlFiles);
308
312
  const model = typeof modelIdentity === "string" ? modelIdentity.trim() : "";
309
313
  if (deterministic.bare && model.length > 0) return model;
310
314
  return deterministic.text;
@@ -504,23 +508,21 @@ function cmakeVerificationLines(cwd: string): string[] {
504
508
  * other way. Tox wins when both are declared because it typically wraps the
505
509
  * pytest run.
506
510
  */
507
- function pythonVerificationLines(cwd: string): string[] {
508
- let pyproject = "";
509
- try {
510
- pyproject = readFileSync(join(cwd, "pyproject.toml"), "utf8");
511
- } catch {
512
- pyproject = "";
513
- }
514
- if (existsSync(join(cwd, "tox.ini")) || hasTomlTable(pyproject, "tool.tox")) {
511
+ function pythonVerificationLines(cwd: string, tomlFiles: TomlFileReader): string[] {
512
+ const pyproject = tomlFiles.read("pyproject.toml");
513
+ if (existsSync(join(cwd, "tox.ini")) || (pyproject !== null && tomlTableAt(pyproject, ["tool", "tox"]))) {
515
514
  return ["Run `tox` before handoff."];
516
515
  }
517
- if (existsSync(join(cwd, "pytest.ini")) || hasTomlTable(pyproject, "tool.pytest.ini_options")) {
516
+ if (
517
+ existsSync(join(cwd, "pytest.ini")) ||
518
+ (pyproject !== null && tomlTableAt(pyproject, ["tool", "pytest", "ini_options"]))
519
+ ) {
518
520
  return ["Run `pytest` before handoff."];
519
521
  }
520
522
  return [];
521
523
  }
522
524
 
523
- function verificationSection(cwd: string): ClioMdSection | null {
525
+ function verificationSection(cwd: string, tomlFiles: TomlFileReader): ClioMdSection | null {
524
526
  const scripts = packageScripts(cwd);
525
527
  const pm = packageManager(cwd);
526
528
  const hasScript = (name: string) => typeof scripts[name] === "string";
@@ -557,7 +559,8 @@ function verificationSection(cwd: string): ClioMdSection | null {
557
559
  // Cargo's commands are defined by the toolchain, not by each project, so
558
560
  // naming them for any Cargo workspace carries the same confidence as a
559
561
  // declared package script.
560
- if (existsSync(join(cwd, "Cargo.toml"))) {
562
+ const cargo = tomlFiles.read("Cargo.toml");
563
+ if (cargo !== null && (tomlTableAt(cargo, ["package"]) || tomlTableAt(cargo, ["workspace"]))) {
561
564
  lines.push("Run `cargo build` and `cargo test` before handoff.");
562
565
  }
563
566
  // Go's commands are defined by the toolchain, not by each project, so
@@ -566,7 +569,7 @@ function verificationSection(cwd: string): ClioMdSection | null {
566
569
  if (existsSync(join(cwd, "go.mod"))) {
567
570
  lines.push("Run `go build ./...` and `go test ./...` before handoff.");
568
571
  }
569
- lines.push(...pythonVerificationLines(cwd));
572
+ lines.push(...pythonVerificationLines(cwd, tomlFiles));
570
573
  if (lines.length === 0) return null;
571
574
  return { title: "Verification expectations", body: lines.join(" ") };
572
575
  }
@@ -717,6 +720,7 @@ function stabilizeGeneratedOutput(
717
720
  onModelSectionRetained?: () => void,
718
721
  groundModelOutput = false,
719
722
  ): BootstrapStructuredOutput {
723
+ const tomlFiles = input.tomlFiles ?? createTomlFileReader(input.cwd);
720
724
  const existing = input.existingClioMd;
721
725
  const conventions: string[] = [];
722
726
  for (const convention of existing?.conventions ?? []) pushUnique(conventions, convention);
@@ -727,7 +731,7 @@ function stabilizeGeneratedOutput(
727
731
  for (const invariant of existing?.invariants ?? []) pushUnique(invariants, invariant);
728
732
  for (const invariant of inferInvariants(input.siblingFiles)) pushUnique(invariants, invariant);
729
733
 
730
- const verification = verificationSection(input.cwd);
734
+ const verification = verificationSection(input.cwd, tomlFiles);
731
735
  const inferredSections = inferHeuristicSections(input);
732
736
  const existingSections = existing?.sections ?? [];
733
737
  const ordinarySections: ClioMdSection[] = [];
@@ -754,8 +758,8 @@ function stabilizeGeneratedOutput(
754
758
  }
755
759
  return {
756
760
  ...base,
757
- projectName: existing?.projectName ?? projectName(input.cwd),
758
- identity: existing?.identity ?? stabilizedIdentity(input, base.identity),
761
+ projectName: existing?.projectName ?? projectName(input.cwd, tomlFiles),
762
+ identity: existing?.identity ?? stabilizedIdentity(input, base.identity, tomlFiles),
759
763
  conventions: conventions.slice(0, 6),
760
764
  invariants: invariants.slice(0, 3),
761
765
  sections: [...retainedOrdinarySections, ...(verification ? [verification] : [])],
@@ -819,9 +823,11 @@ function inferHeuristicSections(input: BootstrapGenerateInput): ClioMdSection[]
819
823
  * unavailable or fails.
820
824
  */
821
825
  function heuristicBootstrapOutputSync(input: BootstrapGenerateInput): BootstrapStructuredOutput {
822
- return stabilizeGeneratedOutput(input, {
823
- projectName: projectName(input.cwd),
824
- identity: resolveDefaultIdentity(input.cwd, input.projectType, input.siblingFiles).text,
826
+ const tomlFiles = input.tomlFiles ?? createTomlFileReader(input.cwd);
827
+ const sharedInput = input.tomlFiles ? input : { ...input, tomlFiles };
828
+ return stabilizeGeneratedOutput(sharedInput, {
829
+ projectName: projectName(input.cwd, tomlFiles),
830
+ identity: resolveDefaultIdentity(input.cwd, input.projectType, input.siblingFiles, tomlFiles).text,
825
831
  conventions: inferConventions(input.cwd, input.siblingFiles, input.codewiki),
826
832
  invariants: inferInvariants(input.siblingFiles),
827
833
  sections: inferHeuristicSections(input),
@@ -1229,6 +1235,7 @@ function codewikiEntryPoints(codewiki: Codewiki, limit = 6): string[] {
1229
1235
 
1230
1236
  export async function runBootstrap(input: RunBootstrapInput = {}): Promise<RunBootstrapResult> {
1231
1237
  const cwd = input.cwd ?? process.cwd();
1238
+ const tomlFiles = createTomlFileReader(cwd);
1232
1239
  const projectType = detectProjectType(cwd);
1233
1240
  progress(input, {
1234
1241
  phase: "scan",
@@ -1327,7 +1334,8 @@ export async function runBootstrap(input: RunBootstrapInput = {}): Promise<RunBo
1327
1334
  });
1328
1335
  output = await (input.generate ?? heuristicBootstrapOutput)({
1329
1336
  cwd,
1330
- expectedProjectName: projectName(cwd),
1337
+ expectedProjectName: projectName(cwd, tomlFiles),
1338
+ tomlFiles,
1331
1339
  projectType,
1332
1340
  siblingFiles,
1333
1341
  adoption,
@@ -1342,7 +1350,8 @@ export async function runBootstrap(input: RunBootstrapInput = {}): Promise<RunBo
1342
1350
  output = stabilizeGeneratedOutput(
1343
1351
  {
1344
1352
  cwd,
1345
- expectedProjectName: projectName(cwd),
1353
+ expectedProjectName: projectName(cwd, tomlFiles),
1354
+ tomlFiles,
1346
1355
  projectType,
1347
1356
  siblingFiles,
1348
1357
  adoption,
@@ -1372,7 +1381,7 @@ export async function runBootstrap(input: RunBootstrapInput = {}): Promise<RunBo
1372
1381
  } else {
1373
1382
  output = existingParsed
1374
1383
  ? bootstrapOutputFromParsed(existingParsed)
1375
- : await heuristicBootstrapOutput({ cwd, projectType, siblingFiles, adoption, codewiki });
1384
+ : await heuristicBootstrapOutput({ cwd, tomlFiles, projectType, siblingFiles, adoption, codewiki });
1376
1385
  progress(input, {
1377
1386
  phase: "generate",
1378
1387
  status: "completed",