@nimiplatform/nimi-coding 0.2.8 → 0.3.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 (206) hide show
  1. package/CHANGELOG.md +10 -90
  2. package/CONTRIBUTING.md +5 -34
  3. package/README.md +47 -350
  4. package/README.zh-CN.md +33 -287
  5. package/cli/commands/start.mjs +48 -688
  6. package/cli/commands/validate-spec-audit.mjs +1 -5
  7. package/cli/commands/validate-spec-governance.mjs +20 -0
  8. package/cli/constants.mjs +9 -417
  9. package/cli/help.mjs +28 -145
  10. package/cli/index.mjs +5 -33
  11. package/cli/lib/blueprint-audit.mjs +5 -12
  12. package/cli/lib/bootstrap.mjs +0 -9
  13. package/cli/lib/contracts.mjs +0 -163
  14. package/cli/lib/doctor.mjs +155 -10
  15. package/cli/lib/entrypoints.mjs +63 -167
  16. package/cli/lib/internal/contracts-loaders.mjs +0 -85
  17. package/cli/lib/internal/contracts-parse.mjs +84 -520
  18. package/cli/lib/internal/governance/ai/check-agents-freshness.mjs +13 -8
  19. package/cli/lib/internal/surface-taxonomy-validators.mjs +316 -66
  20. package/cli/lib/internal/validators-shared.mjs +0 -16
  21. package/cli/lib/internal/validators-spec-helpers.mjs +7 -140
  22. package/cli/lib/internal/validators-spec.mjs +103 -342
  23. package/cli/lib/shared.mjs +1 -42
  24. package/cli/lib/validators.mjs +0 -48
  25. package/cli/seeds/seed-policy.yaml +0 -3
  26. package/config/bootstrap.yaml +4 -4
  27. package/config/spec-generation-inputs.yaml +6 -12
  28. package/contracts/domain-admission.schema.yaml +3 -3
  29. package/contracts/migration-inventory.schema.yaml +12 -75
  30. package/contracts/negative-fixtures.yaml +19 -99
  31. package/contracts/placement-contract.schema.yaml +49 -78
  32. package/contracts/projection-edge.schema.yaml +37 -121
  33. package/contracts/shared-enums.yaml +4 -8
  34. package/contracts/spec-generation-inputs.schema.yaml +15 -112
  35. package/contracts/spec-layout.schema.yaml +32 -0
  36. package/contracts/surface-taxonomy.schema.yaml +43 -94
  37. package/contracts/table-family.schema.yaml +1 -1
  38. package/contracts/tracked-output-admission.schema.yaml +13 -66
  39. package/methodology/core.yaml +33 -25
  40. package/methodology/four-closure-policy.yaml +18 -26
  41. package/methodology/role-separation-policy.yaml +21 -27
  42. package/methodology/spec-reconstruction.yaml +17 -34
  43. package/package.json +2 -4
  44. package/spec/_meta/spec-tree-model.yaml +5 -101
  45. package/spec/product-scope.yaml +25 -57
  46. package/adapters/README.md +0 -25
  47. package/adapters/claude/README.md +0 -89
  48. package/adapters/claude/profile.yaml +0 -70
  49. package/adapters/codex/README.md +0 -53
  50. package/adapters/codex/profile.yaml +0 -78
  51. package/adapters/oh-my-codex/README.md +0 -184
  52. package/adapters/oh-my-codex/profile.yaml +0 -46
  53. package/cli/commands/admit-high-risk-decision.mjs +0 -108
  54. package/cli/commands/audit-sweep.mjs +0 -364
  55. package/cli/commands/closeout.mjs +0 -186
  56. package/cli/commands/decide-high-risk-execution.mjs +0 -124
  57. package/cli/commands/handoff.mjs +0 -123
  58. package/cli/commands/ingest-high-risk-execution.mjs +0 -95
  59. package/cli/commands/review-high-risk-execution.mjs +0 -95
  60. package/cli/commands/sweep-design.mjs +0 -295
  61. package/cli/commands/sweep.mjs +0 -22
  62. package/cli/commands/topic-formatters.mjs +0 -382
  63. package/cli/commands/topic-goal.mjs +0 -33
  64. package/cli/commands/topic-options-shared.mjs +0 -27
  65. package/cli/commands/topic-options-workflow.mjs +0 -767
  66. package/cli/commands/topic-options.mjs +0 -626
  67. package/cli/commands/topic-runner.mjs +0 -169
  68. package/cli/commands/topic.mjs +0 -795
  69. package/cli/commands/validate-acceptance.mjs +0 -5
  70. package/cli/commands/validate-execution-packet.mjs +0 -5
  71. package/cli/commands/validate-orchestration-state.mjs +0 -5
  72. package/cli/commands/validate-prompt.mjs +0 -5
  73. package/cli/commands/validate-worker-output.mjs +0 -5
  74. package/cli/lib/adapter-profiles.mjs +0 -403
  75. package/cli/lib/audit-execution.mjs +0 -52
  76. package/cli/lib/audit-sweep-runtime/admissions.mjs +0 -508
  77. package/cli/lib/audit-sweep-runtime/audit-validity.mjs +0 -356
  78. package/cli/lib/audit-sweep-runtime/chunks.mjs +0 -697
  79. package/cli/lib/audit-sweep-runtime/claude-auditor.mjs +0 -658
  80. package/cli/lib/audit-sweep-runtime/closeout.mjs +0 -144
  81. package/cli/lib/audit-sweep-runtime/codex-auditor-evidence.mjs +0 -654
  82. package/cli/lib/audit-sweep-runtime/codex-auditor.mjs +0 -527
  83. package/cli/lib/audit-sweep-runtime/common.mjs +0 -341
  84. package/cli/lib/audit-sweep-runtime/coverage-quality.mjs +0 -172
  85. package/cli/lib/audit-sweep-runtime/evidence-assignment.mjs +0 -155
  86. package/cli/lib/audit-sweep-runtime/format.mjs +0 -57
  87. package/cli/lib/audit-sweep-runtime/ingest.mjs +0 -486
  88. package/cli/lib/audit-sweep-runtime/inventory-spec-chunks.mjs +0 -425
  89. package/cli/lib/audit-sweep-runtime/inventory.mjs +0 -798
  90. package/cli/lib/audit-sweep-runtime/ledger.mjs +0 -315
  91. package/cli/lib/audit-sweep-runtime/p0p1-profile.mjs +0 -101
  92. package/cli/lib/audit-sweep-runtime/remediation.mjs +0 -349
  93. package/cli/lib/audit-sweep-runtime/rerun.mjs +0 -129
  94. package/cli/lib/audit-sweep-runtime/risk-budget.mjs +0 -300
  95. package/cli/lib/audit-sweep-runtime/status.mjs +0 -62
  96. package/cli/lib/audit-sweep-runtime/validators-ledger.mjs +0 -215
  97. package/cli/lib/audit-sweep-runtime/validators.mjs +0 -797
  98. package/cli/lib/audit-sweep.mjs +0 -19
  99. package/cli/lib/authority-convergence.mjs +0 -720
  100. package/cli/lib/closeout.mjs +0 -732
  101. package/cli/lib/codex-sdk-runner.mjs +0 -76
  102. package/cli/lib/external-execution.mjs +0 -101
  103. package/cli/lib/handoff.mjs +0 -812
  104. package/cli/lib/high-risk-admission.mjs +0 -512
  105. package/cli/lib/high-risk-decision.mjs +0 -353
  106. package/cli/lib/high-risk-ingest.mjs +0 -321
  107. package/cli/lib/high-risk-review.mjs +0 -267
  108. package/cli/lib/internal/contracts-parse-high-risk.mjs +0 -131
  109. package/cli/lib/internal/contracts-validators.mjs +0 -399
  110. package/cli/lib/internal/doctor-bootstrap-surface.mjs +0 -406
  111. package/cli/lib/internal/doctor-delegated-surface.mjs +0 -256
  112. package/cli/lib/internal/doctor-finalize.mjs +0 -383
  113. package/cli/lib/internal/doctor-format.mjs +0 -286
  114. package/cli/lib/internal/doctor-inspectors.mjs +0 -327
  115. package/cli/lib/internal/doctor-state.mjs +0 -205
  116. package/cli/lib/internal/validators-artifacts.mjs +0 -515
  117. package/cli/lib/sweep-design-runtime/common.mjs +0 -246
  118. package/cli/lib/sweep-design-runtime/engine.mjs +0 -733
  119. package/cli/lib/sweep-design-runtime/fix-topic.mjs +0 -414
  120. package/cli/lib/sweep-design-runtime/lifecycle.mjs +0 -54
  121. package/cli/lib/sweep-design-runtime/results.mjs +0 -324
  122. package/cli/lib/sweep-design.mjs +0 -8
  123. package/cli/lib/topic-artifacts.mjs +0 -186
  124. package/cli/lib/topic-authority-coverage.mjs +0 -73
  125. package/cli/lib/topic-closeout.mjs +0 -560
  126. package/cli/lib/topic-common.mjs +0 -404
  127. package/cli/lib/topic-decisions.mjs +0 -332
  128. package/cli/lib/topic-draft-packets.mjs +0 -167
  129. package/cli/lib/topic-execution.mjs +0 -533
  130. package/cli/lib/topic-goal.mjs +0 -440
  131. package/cli/lib/topic-ledger.mjs +0 -281
  132. package/cli/lib/topic-lifecycle-artifacts.mjs +0 -173
  133. package/cli/lib/topic-root-validation.mjs +0 -288
  134. package/cli/lib/topic-runner-commands.mjs +0 -174
  135. package/cli/lib/topic-runner-deferral.mjs +0 -532
  136. package/cli/lib/topic-runner-stale-gates.mjs +0 -114
  137. package/cli/lib/topic-runner-validation.mjs +0 -138
  138. package/cli/lib/topic-runner.mjs +0 -727
  139. package/cli/lib/topic-scaffold.mjs +0 -252
  140. package/cli/lib/topic-waves.mjs +0 -403
  141. package/cli/lib/topic.mjs +0 -81
  142. package/config/audit-execution-artifacts.yaml +0 -20
  143. package/config/external-execution-artifacts.yaml +0 -16
  144. package/config/host-adapter.yaml +0 -30
  145. package/config/host-profile.yaml +0 -29
  146. package/config/installer-evidence.yaml +0 -31
  147. package/config/skill-installer.yaml +0 -23
  148. package/config/skill-manifest.yaml +0 -48
  149. package/config/skills.yaml +0 -30
  150. package/contracts/acceptance.schema.yaml +0 -16
  151. package/contracts/admission-checklist.schema.yaml +0 -15
  152. package/contracts/audit-chunk.schema.yaml +0 -123
  153. package/contracts/audit-closeout.schema.yaml +0 -51
  154. package/contracts/audit-finding.schema.yaml +0 -61
  155. package/contracts/audit-ledger.schema.yaml +0 -138
  156. package/contracts/audit-plan.schema.yaml +0 -138
  157. package/contracts/audit-remediation-map.schema.yaml +0 -52
  158. package/contracts/audit-rerun.schema.yaml +0 -31
  159. package/contracts/audit-sweep-result.yaml +0 -53
  160. package/contracts/authority-convergence-audit.schema.yaml +0 -19
  161. package/contracts/closeout.schema.yaml +0 -25
  162. package/contracts/decision-review.schema.yaml +0 -16
  163. package/contracts/doc-spec-audit-result.yaml +0 -19
  164. package/contracts/execution-packet.schema.yaml +0 -49
  165. package/contracts/external-host-compatibility.yaml +0 -22
  166. package/contracts/forbidden-shortcuts.catalog.yaml +0 -23
  167. package/contracts/high-risk-admission.schema.yaml +0 -23
  168. package/contracts/high-risk-execution-result.yaml +0 -20
  169. package/contracts/orchestration-state.schema.yaml +0 -41
  170. package/contracts/overflow-continuation.schema.yaml +0 -12
  171. package/contracts/packet.schema.yaml +0 -30
  172. package/contracts/pending-note.schema.yaml +0 -17
  173. package/contracts/prompt.schema.yaml +0 -12
  174. package/contracts/remediation.schema.yaml +0 -16
  175. package/contracts/result.schema.yaml +0 -24
  176. package/contracts/spec-reconstruction-result.yaml +0 -41
  177. package/contracts/sweep-design-result.yaml +0 -349
  178. package/contracts/topic-goal.schema.yaml +0 -87
  179. package/contracts/topic-run-ledger.schema.yaml +0 -72
  180. package/contracts/topic-step-decision.schema.yaml +0 -45
  181. package/contracts/topic.schema.yaml +0 -65
  182. package/contracts/true-close.schema.yaml +0 -15
  183. package/contracts/wave.schema.yaml +0 -29
  184. package/contracts/worker-output.schema.yaml +0 -15
  185. package/contracts/workflow-consumer.schema.yaml +0 -110
  186. package/methodology/audit-sweep-p0p1-recall.yaml +0 -45
  187. package/methodology/authority-convergence-policy.yaml +0 -42
  188. package/methodology/overflow-continuation-policy.yaml +0 -14
  189. package/methodology/skill-exchange-projection.yaml +0 -114
  190. package/methodology/skill-handoff.yaml +0 -34
  191. package/methodology/skill-installer-result.yaml +0 -27
  192. package/methodology/skill-installer-summary-projection.yaml +0 -181
  193. package/methodology/skill-runtime.yaml +0 -23
  194. package/methodology/spec-target-truth-profile.yaml +0 -46
  195. package/methodology/topic-lifecycle-report.yaml +0 -144
  196. package/methodology/topic-lifecycle.yaml +0 -37
  197. package/methodology/topic-naming-ontology.yaml +0 -21
  198. package/methodology/topic-ontology.yaml +0 -38
  199. package/methodology/topic-validation-policy.yaml +0 -9
  200. package/methodology/wave-dag-policy.yaml +0 -14
  201. package/spec/_meta/command-gating-matrix.yaml +0 -143
  202. package/spec/_meta/generate-drift-migration-checklist.yaml +0 -137
  203. package/spec/_meta/governance-routing-cutover-checklist.yaml +0 -35
  204. package/spec/_meta/phase2-impacted-surface-matrix.yaml +0 -44
  205. package/spec/_meta/spec-authority-cutover-readiness.yaml +0 -102
  206. package/spec/bootstrap-state.yaml +0 -99
@@ -1,501 +1,31 @@
1
- import { emitKeypressEvents } from "node:readline";
2
- import process from "node:process";
3
1
  import path from "node:path";
4
2
 
5
3
  import {
6
- buildHandoffPayload,
7
- formatDoctorResult,
8
- formatStartPastePrompt,
9
- getStartHostOption,
10
4
  inspectBootstrapCompatibility,
11
- inspectDoctorState,
12
5
  integrateEntrypoints,
13
6
  pathExists,
14
7
  previewBootstrapWrites,
15
8
  previewEntrypointIntegration,
16
- resolveStartHostChoice,
17
- START_HOST_OPTIONS,
18
- writeHandoffJsonArtifact,
19
9
  writeMissingBootstrapFiles,
20
10
  } from "../lib/shared.mjs";
21
- import {
22
- configureCliUi,
23
- getCliColorEnabled,
24
- getCliLocale,
25
- isCliLocalePinned,
26
- localize,
27
- styleCommand,
28
- styleHeading,
29
- styleLabel,
30
- styleMuted,
31
- } from "../lib/ui.mjs";
11
+ import { localize } from "../lib/ui.mjs";
32
12
 
33
13
  function parseStartOptions(args) {
34
- const options = {
35
- yes: false,
36
- host: null,
37
- };
38
-
39
- for (let index = 0; index < args.length; index += 1) {
40
- const arg = args[index];
41
-
42
- if (arg === "--yes") {
43
- options.yes = true;
44
- continue;
45
- }
46
-
47
- if (arg === "--host") {
48
- const next = args[index + 1];
49
- if (!next || next.startsWith("--")) {
50
- return {
51
- ok: false,
52
- error: `${localize(
53
- "nimicoding start refused: --host requires one of `generic`, `codex`, `claude`, or `oh-my-codex`.",
54
- "nimicoding start 已拒绝:--host 需要 `generic`、`codex`、`claude` 或 `oh-my-codex` 之一。",
55
- )}\n`,
56
- };
57
- }
58
- if (!getStartHostOption(next)) {
59
- return {
60
- ok: false,
61
- error: `${localize(
62
- `nimicoding start refused: unsupported --host value ${next}.`,
63
- `nimicoding start 已拒绝:不支持的 --host 值 ${next}。`,
64
- )}\n`,
65
- };
66
- }
67
- options.host = next;
68
- index += 1;
69
- continue;
70
- }
71
-
72
- return {
73
- ok: false,
74
- error: `${localize(
75
- `nimicoding start refused: unknown option ${arg}.`,
76
- `nimicoding start 已拒绝:未知选项 ${arg}。`,
77
- )}\n`,
78
- };
79
- }
80
-
81
- return { ok: true, options };
82
- }
83
-
84
- function canPromptInteractively() {
85
- return Boolean(process.stdin.isTTY && process.stdout.isTTY);
86
- }
87
-
88
- function buildStartBanner() {
89
- const lines = [
90
- " _ _ ___ __ __ ___ ____ ___ ____ ___ _ _ ____ ",
91
- "| \\ | |_ _| \\/ |_ _| / ___/ _ \\| _ \\|_ _| \\ | |/ ___|",
92
- "| \\| || || |\\/| || | | | | | | | | | || || \\| | | _ ",
93
- "| |\\ || || | | || | | |__| |_| | |_| || || |\\ | |_| |",
94
- "|_| \\_|___|_| |_|___| \\____\\___/|____/|___|_| \\_|\\____|",
95
- ];
96
-
97
- return `${lines.map((line) => styleHeading(line)).join("\n")}
98
- ${styleMuted(localize(
99
- "Bootstrap, project guidance, and next-step AI task preparation",
100
- "Bootstrap、项目指导与下一步 AI 任务准备",
101
- ))}`;
102
- }
103
-
104
- function formatSectionTitle(index, title) {
105
- return styleLabel(`${index}. ${title}`);
106
- }
107
-
108
- function formatSectionDivider() {
109
- return styleMuted("────────────────────────────────────────");
110
- }
111
-
112
- async function maybeSelectLanguage(options) {
113
- if (options.yes || !canPromptInteractively() || isCliLocalePinned()) {
114
- return;
115
- }
116
-
117
- const currentLocale = getCliLocale();
118
- const choice = await chooseWithArrowKeys(
119
- "Language / 语言",
120
- [
121
- {
122
- value: "en",
123
- label: "English",
124
- description: "Use English for interactive output",
125
- },
126
- {
127
- value: "zh",
128
- label: "中文",
129
- description: "使用中文进行交互输出",
130
- },
131
- ],
132
- currentLocale === "zh" ? 1 : 0,
133
- );
134
-
135
- configureCliUi({
136
- locale: choice.value,
137
- colorEnabled: getCliColorEnabled(),
138
- });
139
- }
140
-
141
- async function chooseWithArrowKeys(title, options, initialIndex = 0) {
142
- const input = process.stdin;
143
- const output = process.stdout;
144
- const previousRawMode = input.isRaw;
145
- let selectedIndex = initialIndex;
146
- let renderedLines = 0;
147
-
148
- emitKeypressEvents(input);
149
- if (input.setRawMode) {
150
- input.setRawMode(true);
151
- }
152
-
153
- function render() {
154
- const lines = [
155
- title,
156
- ...options.map((option, index) => {
157
- const prefix = index === selectedIndex ? styleCommand("›") : " ";
158
- return `${prefix} ${option.label}${option.description ? styleMuted(` - ${option.description}`) : ""}`;
159
- }),
160
- styleMuted(localize("Use ↑/↓ and Enter to choose.", "使用 ↑/↓ 和 Enter 进行选择。")),
161
- ];
162
-
163
- if (renderedLines > 0) {
164
- output.write(`\u001B[${renderedLines}A\u001B[0J`);
165
- }
166
-
167
- output.write(`${lines.join("\n")}\n`);
168
- renderedLines = lines.length;
169
- }
170
-
171
- return new Promise((resolve, reject) => {
172
- function cleanup() {
173
- input.off("keypress", onKeypress);
174
- if (input.setRawMode) {
175
- input.setRawMode(Boolean(previousRawMode));
176
- }
177
- }
178
-
179
- function finish(value) {
180
- output.write(`\u001B[${renderedLines}A\u001B[0J`);
181
- cleanup();
182
- resolve(value);
183
- }
184
-
185
- function fail(error) {
186
- output.write(`\u001B[${renderedLines}A\u001B[0J`);
187
- cleanup();
188
- reject(error);
189
- }
190
-
191
- function onKeypress(_, key) {
192
- if (!key) {
193
- return;
194
- }
195
-
196
- if (key.name === "up") {
197
- selectedIndex = (selectedIndex - 1 + options.length) % options.length;
198
- render();
199
- return;
200
- }
201
-
202
- if (key.name === "down") {
203
- selectedIndex = (selectedIndex + 1) % options.length;
204
- render();
205
- return;
206
- }
207
-
208
- if (key.name === "return") {
209
- finish(options[selectedIndex]);
210
- return;
211
- }
212
-
213
- if (key.ctrl && key.name === "c") {
214
- fail(new Error("SIGINT"));
215
- }
216
- }
217
-
218
- render();
219
- input.on("keypress", onKeypress);
220
- });
221
- }
222
-
223
- function buildBootstrapStageLines(preview) {
224
- const lines = [
225
- styleLabel(localize("Scope", "范围")),
226
- styleMuted(localize(
227
- " - prepare the local nimicoding bootstrap surface under `.nimi/**`",
228
- " - 准备 `.nimi/**` 下的本地 nimicoding bootstrap 面",
229
- )),
230
- styleLabel(localize("Writes", "写入内容")),
231
- ];
232
-
233
- if (!preview.hasWork) {
234
- lines.push(styleMuted(localize(" - no bootstrap writes are needed in this run", " - 本次运行不需要写入 bootstrap 内容")));
235
- lines.push(styleLabel(localize("Keeps", "保留内容")));
236
- lines.push(styleMuted(localize(" - existing project rules remain unchanged", " - 现有项目规则保持不变")));
237
- return lines;
238
- }
239
-
240
- if (preview.missingFiles.length > 0) {
241
- lines.push(styleMuted(localize(
242
- " - create the missing `.nimi/**` bootstrap files:",
243
- " - 创建缺失的 `.nimi/**` bootstrap 文件:",
244
- )));
245
- for (const filePath of preview.missingFiles) {
246
- lines.push(` - ${filePath}`);
247
- }
248
- }
249
-
250
- if (preview.missingDirs.length > 0) {
251
- lines.push(styleMuted(localize(
252
- " - create the missing local work directories:",
253
- " - 创建缺失的本地工作目录:",
254
- )));
255
- for (const dirPath of preview.missingDirs) {
256
- lines.push(` - ${dirPath}/`);
257
- }
258
- }
259
-
260
- if (preview.missingGitignoreEntries.length > 0) {
261
- lines.push(styleMuted(localize(
262
- " - update `.gitignore` so local nimicoding state and topic workspaces remain untracked by default",
263
- " - 更新 `.gitignore`,默认避免本地 nimicoding 状态和 topic 工作区被跟踪",
264
- )));
265
- }
266
-
267
- lines.push(styleLabel(localize("Keeps", "保留内容")));
268
- lines.push(styleMuted(localize(
269
- " - existing `.nimi/**` files are not overwritten",
270
- " - 现有 `.nimi/**` 文件不会被覆盖",
271
- )));
272
-
273
- return lines;
274
- }
275
-
276
- function buildEntrypointStageLines(updates) {
277
- const lines = [
278
- styleLabel(localize("Scope", "范围")),
279
- styleMuted(localize(
280
- " - update the managed guidance blocks used by external coding hosts",
281
- " - 更新供外部编码宿主使用的托管指导区块",
282
- )),
283
- styleLabel(localize("Writes", "写入内容")),
284
- ];
285
-
286
- if (updates.length === 0) {
287
- lines.push(styleMuted(localize(" - no entry file updates are needed in this run", " - 本次运行不需要更新入口文件")));
288
- lines.push(styleLabel(localize("Keeps", "保留内容")));
289
- lines.push(styleMuted(localize(
290
- " - project-specific content outside the managed block remains unchanged",
291
- " - 托管区块之外的项目内容保持不变",
292
- )));
293
- return lines;
14
+ if (args.length === 0) {
15
+ return { ok: true, options: { yes: false } };
294
16
  }
295
-
296
- lines.push(styleMuted(localize(
297
- " - update the managed guidance blocks in:",
298
- " - 更新以下文件中的托管指导区块:",
299
- )));
300
- for (const filePath of updates) {
301
- lines.push(` - ${filePath}`);
17
+ if (args.length === 1 && args[0] === "--yes") {
18
+ return { ok: true, options: { yes: true } };
302
19
  }
303
- lines.push(styleLabel(localize("Keeps", "保留内容")));
304
- lines.push(styleMuted(localize(
305
- " - only the nimicoding-managed block is updated",
306
- " - 只有 nimicoding 托管区块会被更新",
307
- )));
308
- return lines;
309
- }
310
-
311
- function buildTaskStageLines(mode) {
312
- const lines = [
313
- styleLabel(localize("Scope", "范围")),
314
- styleMuted(localize(
315
- mode === "reconstruction"
316
- ? " - prepare the reconstruction task package for an external AI host"
317
- : " - prepare the doc/spec audit task package for an external AI host",
318
- mode === "reconstruction"
319
- ? " - 为外部 AI Host 准备 reconstruction 任务包"
320
- : " - 为外部 AI Host 准备 doc/spec audit 任务包",
321
- )),
322
- styleLabel(localize("Writes", "写入内容")),
323
- styleMuted(localize(
324
- " - one authoritative JSON contract for the task",
325
- " - 一个用于该任务的权威 JSON 契约",
326
- )),
327
- styleMuted(localize(
328
- " - one short prompt that can be pasted directly into the selected host",
329
- " - 一段可直接粘贴到所选 Host 的短 prompt",
330
- )),
331
- styleLabel(localize("Keeps", "保留内容")),
332
- styleMuted(localize(
333
- " - the JSON file remains the source of truth",
334
- " - JSON 文件仍然是权威真相源",
335
- )),
336
- styleMuted(localize(
337
- " - the short prompt remains a compact instruction layer only",
338
- " - 短 prompt 仅作为紧凑指令层使用",
339
- )),
340
- ];
341
-
342
- return lines;
343
- }
344
-
345
- function canonicalTreeReady(doctorResult) {
346
- const v2Ready = doctorResult.specGenerationInputs?.mode === "class_filtered"
347
- && doctorResult.canonicalTree?.requiredFilesValid === true
348
- && doctorResult.specGenerationAudit?.ok === true;
349
- const legacyReady = doctorResult.lifecycleState?.treeState === "canonical_tree_ready"
350
- && doctorResult.canonicalTree?.requiredFilesValid === true;
351
- return v2Ready || legacyReady;
352
- }
353
-
354
- function determineWizardStage(doctorResult) {
355
- if (!canonicalTreeReady(doctorResult)) {
356
- return {
357
- title: localize("Step 3. Rebuild project rules", "第 3 步:重建项目规则"),
358
- detail: localize(
359
- "Bootstrap is ready. The next action is to hand the reconstruction task package to an external AI host.",
360
- "bootstrap 已就绪。下一步是将 reconstruction 任务包交给外部 AI Host。",
361
- ),
362
- };
363
- }
364
-
365
- if (!doctorResult.auditArtifact.present) {
366
- return {
367
- title: localize("Step 4. Run a doc/spec audit", "第 4 步:执行 doc/spec 审计"),
368
- detail: localize(
369
- "Project rules are present. The next action is to hand the audit package to an external AI host and review the result locally.",
370
- "项目规则已存在。下一步是将 audit 任务包交给外部 AI Host,并在本地审阅结果。",
371
- ),
372
- };
373
- }
374
-
375
20
  return {
376
- title: localize("Ready", "已就绪"),
377
- detail: localize(
378
- "Bootstrap and project rules are aligned. The project is ready for the next approved task.",
379
- "bootstrap 与项目规则已对齐。当前可以继续下一项已批准任务。",
21
+ ok: false,
22
+ error: localize(
23
+ `nimicoding start refused: unknown options ${args.join(" ")}.\n`,
24
+ `nimicoding start 已拒绝:未知选项 ${args.join(" ")}。\n`,
380
25
  ),
381
26
  };
382
27
  }
383
28
 
384
- function isReconstructionContinuable(doctorResult) {
385
- if (canonicalTreeReady(doctorResult)) {
386
- return false;
387
- }
388
-
389
- const blockingChecks = doctorResult.checks.filter((check) => check.severity === "error");
390
- if (blockingChecks.length === 0) {
391
- return true;
392
- }
393
-
394
- const tolerated = new Set([
395
- "canonical_tree_progress",
396
- "bootstrap_lifecycle_alignment",
397
- "doc_spec_audit_state_alignment",
398
- ]);
399
-
400
- return blockingChecks.every((check) => tolerated.has(check.id));
401
- }
402
-
403
- function printStage(title, bodyLines) {
404
- process.stdout.write(`${styleHeading(title)}\n${formatSectionDivider()}\n${bodyLines.join("\n")}\n\n`);
405
- }
406
-
407
- async function maybeApproveStage(options, prompt, defaultAnswer = true) {
408
- if (options.yes || !canPromptInteractively()) {
409
- return true;
410
- }
411
-
412
- const choice = await chooseWithArrowKeys(prompt, [
413
- {
414
- value: true,
415
- label: localize("Apply stage", "执行此阶段"),
416
- description: localize("continue and write the planned changes", "继续并写入计划中的变更"),
417
- },
418
- {
419
- value: false,
420
- label: localize("Skip for now", "暂时跳过"),
421
- description: localize("leave this stage unchanged in this run", "本次运行中不处理这一阶段"),
422
- },
423
- ], defaultAnswer ? 0 : 1);
424
-
425
- return choice.value;
426
- }
427
-
428
- async function chooseExternalHost(options, payload) {
429
- const resolvedHost = resolveStartHostChoice(options.host, payload);
430
- if (options.yes || !canPromptInteractively()) {
431
- return resolvedHost;
432
- }
433
-
434
- const choice = await chooseWithArrowKeys(
435
- localize(
436
- "Select the external host that will receive the short prompt:",
437
- "选择将接收这段短 prompt 的外部 Host:",
438
- ),
439
- START_HOST_OPTIONS.map((option) => ({
440
- value: option.id,
441
- label: localize(option.label, option.zhLabel),
442
- description: localize(option.description, option.zhDescription),
443
- })),
444
- Math.max(START_HOST_OPTIONS.findIndex((option) => option.id === resolvedHost), 0),
445
- );
446
-
447
- return choice.value;
448
- }
449
-
450
- async function maybePrepareHandoff(projectRoot, options, skillId, title, detail) {
451
- const payload = await buildHandoffPayload(projectRoot, skillId);
452
- if (!payload.ok) {
453
- return {
454
- ok: false,
455
- payload,
456
- refs: null,
457
- };
458
- }
459
-
460
- const approved = await maybeApproveStage(
461
- options,
462
- localize(
463
- `Apply this stage and prepare the next AI task package for ${title}?`,
464
- `执行这一阶段并为 ${title} 准备下一步 AI 任务包吗?`,
465
- ),
466
- true,
467
- );
468
-
469
- if (!approved) {
470
- return {
471
- ok: true,
472
- skipped: true,
473
- payload,
474
- refs: null,
475
- detail,
476
- hostId: null,
477
- pastePrompt: null,
478
- };
479
- }
480
-
481
- const hostId = await chooseExternalHost(options, payload);
482
- const refs = await writeHandoffJsonArtifact(projectRoot, payload);
483
- const pastePrompt = formatStartPastePrompt(payload, {
484
- hostId,
485
- jsonRef: refs.jsonRef,
486
- });
487
-
488
- return {
489
- ok: true,
490
- skipped: false,
491
- payload,
492
- refs,
493
- detail,
494
- hostId,
495
- pastePrompt,
496
- };
497
- }
498
-
499
29
  export async function runStart(args) {
500
30
  const parsed = parseStartOptions(args);
501
31
  if (!parsed.ok) {
@@ -503,219 +33,49 @@ export async function runStart(args) {
503
33
  return 2;
504
34
  }
505
35
 
506
- try {
507
- await maybeSelectLanguage(parsed.options);
508
-
509
- const projectRoot = process.cwd();
510
- const nimiRoot = path.join(projectRoot, ".nimi");
511
- const nimiInfo = await pathExists(nimiRoot);
512
-
513
- if (nimiInfo && !nimiInfo.isDirectory()) {
514
- process.stderr.write(`${localize(
515
- `nimicoding start refused: ${nimiRoot} exists and is not a directory.`,
516
- `nimicoding start 已拒绝:${nimiRoot} 已存在且不是目录。`,
517
- )}\n`);
518
- return 2;
519
- }
520
-
521
- const compatibility = await inspectBootstrapCompatibility(projectRoot);
522
- if (compatibility.status === "unsupported") {
523
- process.stderr.write(`${localize(
524
- "nimicoding start refused: bootstrap.yaml declares an unsupported bootstrap contract id or version.",
525
- "nimicoding start 已拒绝:bootstrap.yaml 声明了不受支持的 bootstrap contract id 或 version。",
526
- )}\n`);
527
- return 1;
528
- }
529
-
530
- const bootstrapPreview = await previewBootstrapWrites(projectRoot);
531
- const entrypointPreview = await previewEntrypointIntegration(projectRoot);
532
-
533
- if (canPromptInteractively() && !parsed.options.yes) {
534
- process.stdout.write(`${buildStartBanner()}
535
-
536
- ${styleHeading(localize(`nimicoding start wizard for ${projectRoot}`, `nimicoding start 向导:${projectRoot}`))}
537
-
538
- ${styleMuted(localize(
539
- "Project inspection complete. Start runs in ordered stages. Each stage is previewed first, then confirmed, then applied.",
540
- "项目检查已完成。start 会按顺序执行各阶段。每个阶段都会先预览、再确认、再执行。",
541
- ))}
542
-
543
- `);
544
- }
545
-
546
- const applied = {
547
- bootstrap: null,
548
- entrypoints: null,
549
- handoff: null,
550
- };
551
-
552
- if (bootstrapPreview.hasWork) {
553
- if (canPromptInteractively() && !parsed.options.yes) {
554
- printStage(
555
- localize("Step 1. Set up project files", "第 1 步:准备项目文件"),
556
- buildBootstrapStageLines(bootstrapPreview),
557
- );
558
- }
559
-
560
- const approved = await maybeApproveStage(
561
- parsed.options,
562
- localize(
563
- "Apply stage 1 and create the missing local .nimi bootstrap files?",
564
- "执行第 1 阶段并创建缺失的本地 .nimi bootstrap 文件吗?",
565
- ),
566
- true,
567
- );
568
-
569
- if (approved) {
570
- applied.bootstrap = await writeMissingBootstrapFiles(projectRoot);
571
- }
572
- }
573
-
574
- if (entrypointPreview.length > 0) {
575
- if (canPromptInteractively() && !parsed.options.yes) {
576
- printStage(
577
- localize("Step 2. Connect AI entry files", "第 2 步:接入 AI 入口文件"),
578
- buildEntrypointStageLines(entrypointPreview),
579
- );
580
- }
581
-
582
- const approved = await maybeApproveStage(
583
- parsed.options,
584
- localize(
585
- "Apply stage 2 and update the managed guidance blocks in AGENTS.md / CLAUDE.md?",
586
- "执行第 2 阶段并更新 AGENTS.md / CLAUDE.md 中的托管指导区块吗?",
587
- ),
588
- true,
589
- );
590
-
591
- if (approved) {
592
- applied.entrypoints = await integrateEntrypoints(projectRoot);
593
- }
594
- }
595
-
596
- const doctorResult = await inspectDoctorState(projectRoot);
597
-
598
- if (!doctorResult.ok && !isReconstructionContinuable(doctorResult)) {
599
- process.stdout.write(`${styleHeading(localize("nimicoding start paused", "nimicoding start 已暂停"))}
600
-
601
- ${styleMuted(localize(
602
- "Blocking diagnostic errors are present. Review the doctor output below before continuing.",
603
- "当前存在阻塞性的诊断错误。继续前请先查看下面的 doctor 输出。",
604
- ))}
605
-
606
- ${formatDoctorResult(doctorResult)}`);
607
- return 1;
608
- }
609
-
610
- if (!canonicalTreeReady(doctorResult)) {
611
- if (canPromptInteractively() && !parsed.options.yes) {
612
- printStage(
613
- localize("Step 3. Prepare the next AI task", "第 3 步:准备下一项 AI 任务"),
614
- buildTaskStageLines("reconstruction"),
615
- );
616
- }
617
-
618
- applied.handoff = await maybePrepareHandoff(
619
- projectRoot,
620
- parsed.options,
621
- "spec_reconstruction",
622
- localize("spec reconstruction", "spec reconstruction"),
623
- localize(
624
- "Prepare an AI task package for spec reconstruction.",
625
- "为 spec reconstruction 准备 AI 任务包。",
626
- ),
627
- );
628
- } else if (!doctorResult.auditArtifact.present) {
629
- if (canPromptInteractively() && !parsed.options.yes) {
630
- printStage(
631
- localize("Step 3. Prepare the next AI task", "第 3 步:准备下一项 AI 任务"),
632
- buildTaskStageLines("audit"),
633
- );
634
- }
635
-
636
- applied.handoff = await maybePrepareHandoff(
637
- projectRoot,
638
- parsed.options,
639
- "doc_spec_audit",
640
- localize("doc spec audit", "doc spec audit"),
641
- localize(
642
- "Prepare an AI task package for doc/spec audit.",
643
- "为 doc/spec audit 准备 AI 任务包。",
644
- ),
645
- );
646
- }
647
-
648
- const continuationLines = [];
649
- if (applied.handoff?.refs) {
650
- continuationLines.push(` - ${localize("AI task package", "AI 任务包")}: ${applied.handoff.refs.jsonRef}`);
651
- continuationLines.push(` - ${localize("selected host", "已选择的 Host")}: ${localize(getStartHostOption(applied.handoff.hostId)?.label ?? "Generic external host", getStartHostOption(applied.handoff.hostId)?.zhLabel ?? "通用外部 Host")}`);
652
- } else if (applied.handoff?.skipped) {
653
- continuationLines.push(` - ${localize("AI task package preparation was skipped for now", "本次已跳过 AI 任务包准备")}`);
654
- } else if (canonicalTreeReady(doctorResult) && doctorResult.auditArtifact.present) {
655
- continuationLines.push(` - ${localize("no AI task package is needed right now", "当前不需要准备 AI 任务包")}`);
656
- }
657
-
658
- const completedLines = [
659
- ...(applied.bootstrap
660
- ? [
661
- ...((applied.bootstrap.createdFiles ?? []).map((filePath) => ` - ${localize("created", "已创建")}: ${filePath}`)),
662
- ...((applied.bootstrap.createdDirs ?? []).map((dirPath) => ` - ${localize("created", "已创建")}: ${dirPath}/`)),
663
- ...((applied.bootstrap.gitignoreUpdated ?? false) ? [` - ${localize("updated", "已更新")}: .gitignore`] : []),
664
- ]
665
- : []),
666
- ...((applied.entrypoints ?? []).map((filePath) => ` - ${localize("updated", "已更新")}: ${filePath}`)),
667
- ];
668
-
669
- const wizardStage = determineWizardStage(doctorResult);
670
- const pastePromptSection = applied.handoff?.pastePrompt
671
- ? `${formatSectionTitle(4, localize("Paste Prompt", "可粘贴 Prompt"))}
672
- ${formatSectionDivider()}
673
- ${applied.handoff.pastePrompt.split("\n").filter(Boolean).map((line) => ` ${line}`).join("\n")}
674
-
675
- `
676
- : "";
677
-
678
- process.stdout.write(`${styleHeading(localize(`nimicoding start wizard: ${projectRoot}`, `nimicoding start 向导:${projectRoot}`))}
679
-
680
- ${formatSectionTitle(1, localize("Current Step", "当前步骤"))}
681
- ${formatSectionDivider()}
682
- - ${wizardStage.title}
683
- - ${wizardStage.detail}
684
-
685
- ${formatSectionTitle(2, localize("Changes Applied", "已执行变更"))}
686
- ${formatSectionDivider()}
687
- ${completedLines.length > 0 ? completedLines.join("\n") : localize(" - no new changes were needed in this run", " - 本次运行不需要新增任何变更")}
36
+ const projectRoot = process.cwd();
37
+ const nimiRoot = path.join(projectRoot, ".nimi");
38
+ const nimiInfo = await pathExists(nimiRoot);
39
+ if (nimiInfo && !nimiInfo.isDirectory()) {
40
+ process.stderr.write(localize(
41
+ `nimicoding start refused: ${nimiRoot} exists and is not a directory.\n`,
42
+ `nimicoding start 已拒绝:${nimiRoot} 已存在且不是目录。\n`,
43
+ ));
44
+ return 2;
45
+ }
688
46
 
689
- ${formatSectionTitle(3, localize("Outputs Ready", "已准备输出"))}
690
- ${formatSectionDivider()}
691
- ${continuationLines.length > 0 ? continuationLines.join("\n") : localize(" - no additional staged outputs", " - 没有额外阶段输出")}
47
+ const compatibility = await inspectBootstrapCompatibility(projectRoot);
48
+ if (compatibility.status === "unsupported") {
49
+ process.stderr.write(localize(
50
+ "nimicoding start refused: bootstrap.yaml uses an unsupported contract. Run the hard-cut migration before retrying.\n",
51
+ "nimicoding start 已拒绝:bootstrap.yaml 使用了不受支持的 contract。请先完成硬切迁移。\n",
52
+ ));
53
+ return 1;
54
+ }
692
55
 
693
- ${pastePromptSection}${formatSectionTitle(applied.handoff?.pastePrompt ? 5 : 4, localize("Next Action", "下一步操作"))}
694
- ${formatSectionDivider()}
695
- - ${applied.handoff?.refs
696
- ? localize(
697
- `Paste the short prompt above into the selected external AI host. Keep ${applied.handoff.refs.jsonRef} as the authoritative machine contract.`,
698
- `把上面的短 prompt 粘贴给所选外部 AI Host,并把 ${applied.handoff.refs.jsonRef} 作为权威机器契约保留。`,
699
- )
700
- : localize(
701
- "Continue with the next admitted project task. If a full diagnostic snapshot is needed, run `nimicoding doctor`.",
702
- "继续执行项目中的下一项已准入任务。如果需要完整诊断快照,请运行 `nimicoding doctor`。",
703
- )}
56
+ const bootstrapPreview = await previewBootstrapWrites(projectRoot);
57
+ const entrypointPreview = await previewEntrypointIntegration(projectRoot);
58
+ const bootstrap = bootstrapPreview.hasWork
59
+ ? await writeMissingBootstrapFiles(projectRoot)
60
+ : { createdFiles: [], createdDirs: [], gitignoreUpdated: false };
61
+ const entrypoints = entrypointPreview.length > 0
62
+ ? await integrateEntrypoints(projectRoot)
63
+ : [];
704
64
 
705
- ${styleMuted(localize("Need more detail? Run `nimicoding doctor`.", "如果你需要更多细节,请运行 `nimicoding doctor`。"))}
706
- `);
65
+ const output = {
66
+ ok: true,
67
+ projectRoot,
68
+ bootstrap,
69
+ entrypoints,
70
+ next: [
71
+ "Build or update canonical product authority under .nimi/spec.",
72
+ "Run nimicoding validate-spec-tree.",
73
+ "Run nimicoding validate-spec-audit when generation audit evidence exists.",
74
+ ],
75
+ };
707
76
 
708
- return 0;
709
- } catch (error) {
710
- if (error instanceof Error && error.message === "SIGINT") {
711
- process.stderr.write(`${localize(
712
- "nimicoding start canceled.\n",
713
- "nimicoding start 已取消。\n",
714
- )}`);
715
- return 130;
716
- }
717
- throw error;
718
- }
77
+ process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
78
+ return 0;
719
79
  }
720
80
 
721
81
  export { parseStartOptions };