@nimiplatform/nimi-coding 0.2.8 → 0.3.0

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 (205) hide show
  1. package/CHANGELOG.md +8 -92
  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/constants.mjs +9 -417
  8. package/cli/help.mjs +28 -145
  9. package/cli/index.mjs +5 -33
  10. package/cli/lib/blueprint-audit.mjs +5 -12
  11. package/cli/lib/bootstrap.mjs +0 -9
  12. package/cli/lib/contracts.mjs +0 -163
  13. package/cli/lib/doctor.mjs +155 -10
  14. package/cli/lib/entrypoints.mjs +63 -167
  15. package/cli/lib/internal/contracts-loaders.mjs +0 -85
  16. package/cli/lib/internal/contracts-parse.mjs +84 -520
  17. package/cli/lib/internal/governance/ai/check-agents-freshness.mjs +13 -8
  18. package/cli/lib/internal/surface-taxonomy-validators.mjs +316 -66
  19. package/cli/lib/internal/validators-shared.mjs +0 -16
  20. package/cli/lib/internal/validators-spec-helpers.mjs +7 -140
  21. package/cli/lib/internal/validators-spec.mjs +103 -342
  22. package/cli/lib/shared.mjs +1 -42
  23. package/cli/lib/validators.mjs +0 -48
  24. package/cli/seeds/seed-policy.yaml +0 -3
  25. package/config/bootstrap.yaml +4 -4
  26. package/config/spec-generation-inputs.yaml +6 -12
  27. package/contracts/domain-admission.schema.yaml +3 -3
  28. package/contracts/migration-inventory.schema.yaml +12 -75
  29. package/contracts/negative-fixtures.yaml +19 -99
  30. package/contracts/placement-contract.schema.yaml +49 -78
  31. package/contracts/projection-edge.schema.yaml +37 -121
  32. package/contracts/shared-enums.yaml +4 -8
  33. package/contracts/spec-generation-inputs.schema.yaml +15 -112
  34. package/contracts/spec-layout.schema.yaml +32 -0
  35. package/contracts/surface-taxonomy.schema.yaml +43 -94
  36. package/contracts/table-family.schema.yaml +1 -1
  37. package/contracts/tracked-output-admission.schema.yaml +13 -66
  38. package/methodology/core.yaml +33 -25
  39. package/methodology/four-closure-policy.yaml +18 -26
  40. package/methodology/role-separation-policy.yaml +21 -27
  41. package/methodology/spec-reconstruction.yaml +17 -34
  42. package/package.json +2 -4
  43. package/spec/_meta/spec-tree-model.yaml +5 -101
  44. package/spec/product-scope.yaml +25 -57
  45. package/adapters/README.md +0 -25
  46. package/adapters/claude/README.md +0 -89
  47. package/adapters/claude/profile.yaml +0 -70
  48. package/adapters/codex/README.md +0 -53
  49. package/adapters/codex/profile.yaml +0 -78
  50. package/adapters/oh-my-codex/README.md +0 -184
  51. package/adapters/oh-my-codex/profile.yaml +0 -46
  52. package/cli/commands/admit-high-risk-decision.mjs +0 -108
  53. package/cli/commands/audit-sweep.mjs +0 -364
  54. package/cli/commands/closeout.mjs +0 -186
  55. package/cli/commands/decide-high-risk-execution.mjs +0 -124
  56. package/cli/commands/handoff.mjs +0 -123
  57. package/cli/commands/ingest-high-risk-execution.mjs +0 -95
  58. package/cli/commands/review-high-risk-execution.mjs +0 -95
  59. package/cli/commands/sweep-design.mjs +0 -295
  60. package/cli/commands/sweep.mjs +0 -22
  61. package/cli/commands/topic-formatters.mjs +0 -382
  62. package/cli/commands/topic-goal.mjs +0 -33
  63. package/cli/commands/topic-options-shared.mjs +0 -27
  64. package/cli/commands/topic-options-workflow.mjs +0 -767
  65. package/cli/commands/topic-options.mjs +0 -626
  66. package/cli/commands/topic-runner.mjs +0 -169
  67. package/cli/commands/topic.mjs +0 -795
  68. package/cli/commands/validate-acceptance.mjs +0 -5
  69. package/cli/commands/validate-execution-packet.mjs +0 -5
  70. package/cli/commands/validate-orchestration-state.mjs +0 -5
  71. package/cli/commands/validate-prompt.mjs +0 -5
  72. package/cli/commands/validate-worker-output.mjs +0 -5
  73. package/cli/lib/adapter-profiles.mjs +0 -403
  74. package/cli/lib/audit-execution.mjs +0 -52
  75. package/cli/lib/audit-sweep-runtime/admissions.mjs +0 -508
  76. package/cli/lib/audit-sweep-runtime/audit-validity.mjs +0 -356
  77. package/cli/lib/audit-sweep-runtime/chunks.mjs +0 -697
  78. package/cli/lib/audit-sweep-runtime/claude-auditor.mjs +0 -658
  79. package/cli/lib/audit-sweep-runtime/closeout.mjs +0 -144
  80. package/cli/lib/audit-sweep-runtime/codex-auditor-evidence.mjs +0 -654
  81. package/cli/lib/audit-sweep-runtime/codex-auditor.mjs +0 -527
  82. package/cli/lib/audit-sweep-runtime/common.mjs +0 -341
  83. package/cli/lib/audit-sweep-runtime/coverage-quality.mjs +0 -172
  84. package/cli/lib/audit-sweep-runtime/evidence-assignment.mjs +0 -155
  85. package/cli/lib/audit-sweep-runtime/format.mjs +0 -57
  86. package/cli/lib/audit-sweep-runtime/ingest.mjs +0 -486
  87. package/cli/lib/audit-sweep-runtime/inventory-spec-chunks.mjs +0 -425
  88. package/cli/lib/audit-sweep-runtime/inventory.mjs +0 -798
  89. package/cli/lib/audit-sweep-runtime/ledger.mjs +0 -315
  90. package/cli/lib/audit-sweep-runtime/p0p1-profile.mjs +0 -101
  91. package/cli/lib/audit-sweep-runtime/remediation.mjs +0 -349
  92. package/cli/lib/audit-sweep-runtime/rerun.mjs +0 -129
  93. package/cli/lib/audit-sweep-runtime/risk-budget.mjs +0 -300
  94. package/cli/lib/audit-sweep-runtime/status.mjs +0 -62
  95. package/cli/lib/audit-sweep-runtime/validators-ledger.mjs +0 -215
  96. package/cli/lib/audit-sweep-runtime/validators.mjs +0 -797
  97. package/cli/lib/audit-sweep.mjs +0 -19
  98. package/cli/lib/authority-convergence.mjs +0 -720
  99. package/cli/lib/closeout.mjs +0 -732
  100. package/cli/lib/codex-sdk-runner.mjs +0 -76
  101. package/cli/lib/external-execution.mjs +0 -101
  102. package/cli/lib/handoff.mjs +0 -812
  103. package/cli/lib/high-risk-admission.mjs +0 -512
  104. package/cli/lib/high-risk-decision.mjs +0 -353
  105. package/cli/lib/high-risk-ingest.mjs +0 -321
  106. package/cli/lib/high-risk-review.mjs +0 -267
  107. package/cli/lib/internal/contracts-parse-high-risk.mjs +0 -131
  108. package/cli/lib/internal/contracts-validators.mjs +0 -399
  109. package/cli/lib/internal/doctor-bootstrap-surface.mjs +0 -406
  110. package/cli/lib/internal/doctor-delegated-surface.mjs +0 -256
  111. package/cli/lib/internal/doctor-finalize.mjs +0 -383
  112. package/cli/lib/internal/doctor-format.mjs +0 -286
  113. package/cli/lib/internal/doctor-inspectors.mjs +0 -327
  114. package/cli/lib/internal/doctor-state.mjs +0 -205
  115. package/cli/lib/internal/validators-artifacts.mjs +0 -515
  116. package/cli/lib/sweep-design-runtime/common.mjs +0 -246
  117. package/cli/lib/sweep-design-runtime/engine.mjs +0 -733
  118. package/cli/lib/sweep-design-runtime/fix-topic.mjs +0 -414
  119. package/cli/lib/sweep-design-runtime/lifecycle.mjs +0 -54
  120. package/cli/lib/sweep-design-runtime/results.mjs +0 -324
  121. package/cli/lib/sweep-design.mjs +0 -8
  122. package/cli/lib/topic-artifacts.mjs +0 -186
  123. package/cli/lib/topic-authority-coverage.mjs +0 -73
  124. package/cli/lib/topic-closeout.mjs +0 -560
  125. package/cli/lib/topic-common.mjs +0 -404
  126. package/cli/lib/topic-decisions.mjs +0 -332
  127. package/cli/lib/topic-draft-packets.mjs +0 -167
  128. package/cli/lib/topic-execution.mjs +0 -533
  129. package/cli/lib/topic-goal.mjs +0 -440
  130. package/cli/lib/topic-ledger.mjs +0 -281
  131. package/cli/lib/topic-lifecycle-artifacts.mjs +0 -173
  132. package/cli/lib/topic-root-validation.mjs +0 -288
  133. package/cli/lib/topic-runner-commands.mjs +0 -174
  134. package/cli/lib/topic-runner-deferral.mjs +0 -532
  135. package/cli/lib/topic-runner-stale-gates.mjs +0 -114
  136. package/cli/lib/topic-runner-validation.mjs +0 -138
  137. package/cli/lib/topic-runner.mjs +0 -727
  138. package/cli/lib/topic-scaffold.mjs +0 -252
  139. package/cli/lib/topic-waves.mjs +0 -403
  140. package/cli/lib/topic.mjs +0 -81
  141. package/config/audit-execution-artifacts.yaml +0 -20
  142. package/config/external-execution-artifacts.yaml +0 -16
  143. package/config/host-adapter.yaml +0 -30
  144. package/config/host-profile.yaml +0 -29
  145. package/config/installer-evidence.yaml +0 -31
  146. package/config/skill-installer.yaml +0 -23
  147. package/config/skill-manifest.yaml +0 -48
  148. package/config/skills.yaml +0 -30
  149. package/contracts/acceptance.schema.yaml +0 -16
  150. package/contracts/admission-checklist.schema.yaml +0 -15
  151. package/contracts/audit-chunk.schema.yaml +0 -123
  152. package/contracts/audit-closeout.schema.yaml +0 -51
  153. package/contracts/audit-finding.schema.yaml +0 -61
  154. package/contracts/audit-ledger.schema.yaml +0 -138
  155. package/contracts/audit-plan.schema.yaml +0 -138
  156. package/contracts/audit-remediation-map.schema.yaml +0 -52
  157. package/contracts/audit-rerun.schema.yaml +0 -31
  158. package/contracts/audit-sweep-result.yaml +0 -53
  159. package/contracts/authority-convergence-audit.schema.yaml +0 -19
  160. package/contracts/closeout.schema.yaml +0 -25
  161. package/contracts/decision-review.schema.yaml +0 -16
  162. package/contracts/doc-spec-audit-result.yaml +0 -19
  163. package/contracts/execution-packet.schema.yaml +0 -49
  164. package/contracts/external-host-compatibility.yaml +0 -22
  165. package/contracts/forbidden-shortcuts.catalog.yaml +0 -23
  166. package/contracts/high-risk-admission.schema.yaml +0 -23
  167. package/contracts/high-risk-execution-result.yaml +0 -20
  168. package/contracts/orchestration-state.schema.yaml +0 -41
  169. package/contracts/overflow-continuation.schema.yaml +0 -12
  170. package/contracts/packet.schema.yaml +0 -30
  171. package/contracts/pending-note.schema.yaml +0 -17
  172. package/contracts/prompt.schema.yaml +0 -12
  173. package/contracts/remediation.schema.yaml +0 -16
  174. package/contracts/result.schema.yaml +0 -24
  175. package/contracts/spec-reconstruction-result.yaml +0 -41
  176. package/contracts/sweep-design-result.yaml +0 -349
  177. package/contracts/topic-goal.schema.yaml +0 -87
  178. package/contracts/topic-run-ledger.schema.yaml +0 -72
  179. package/contracts/topic-step-decision.schema.yaml +0 -45
  180. package/contracts/topic.schema.yaml +0 -65
  181. package/contracts/true-close.schema.yaml +0 -15
  182. package/contracts/wave.schema.yaml +0 -29
  183. package/contracts/worker-output.schema.yaml +0 -15
  184. package/contracts/workflow-consumer.schema.yaml +0 -110
  185. package/methodology/audit-sweep-p0p1-recall.yaml +0 -45
  186. package/methodology/authority-convergence-policy.yaml +0 -42
  187. package/methodology/overflow-continuation-policy.yaml +0 -14
  188. package/methodology/skill-exchange-projection.yaml +0 -114
  189. package/methodology/skill-handoff.yaml +0 -34
  190. package/methodology/skill-installer-result.yaml +0 -27
  191. package/methodology/skill-installer-summary-projection.yaml +0 -181
  192. package/methodology/skill-runtime.yaml +0 -23
  193. package/methodology/spec-target-truth-profile.yaml +0 -46
  194. package/methodology/topic-lifecycle-report.yaml +0 -144
  195. package/methodology/topic-lifecycle.yaml +0 -37
  196. package/methodology/topic-naming-ontology.yaml +0 -21
  197. package/methodology/topic-ontology.yaml +0 -38
  198. package/methodology/topic-validation-policy.yaml +0 -9
  199. package/methodology/wave-dag-policy.yaml +0 -14
  200. package/spec/_meta/command-gating-matrix.yaml +0 -143
  201. package/spec/_meta/generate-drift-migration-checklist.yaml +0 -137
  202. package/spec/_meta/governance-routing-cutover-checklist.yaml +0 -35
  203. package/spec/_meta/phase2-impacted-surface-matrix.yaml +0 -44
  204. package/spec/_meta/spec-authority-cutover-readiness.yaml +0 -102
  205. 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 };