@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.
- package/CHANGELOG.md +10 -90
- package/CONTRIBUTING.md +5 -34
- package/README.md +47 -350
- package/README.zh-CN.md +33 -287
- package/cli/commands/start.mjs +48 -688
- package/cli/commands/validate-spec-audit.mjs +1 -5
- package/cli/commands/validate-spec-governance.mjs +20 -0
- package/cli/constants.mjs +9 -417
- package/cli/help.mjs +28 -145
- package/cli/index.mjs +5 -33
- package/cli/lib/blueprint-audit.mjs +5 -12
- package/cli/lib/bootstrap.mjs +0 -9
- package/cli/lib/contracts.mjs +0 -163
- package/cli/lib/doctor.mjs +155 -10
- package/cli/lib/entrypoints.mjs +63 -167
- package/cli/lib/internal/contracts-loaders.mjs +0 -85
- package/cli/lib/internal/contracts-parse.mjs +84 -520
- package/cli/lib/internal/governance/ai/check-agents-freshness.mjs +13 -8
- package/cli/lib/internal/surface-taxonomy-validators.mjs +316 -66
- package/cli/lib/internal/validators-shared.mjs +0 -16
- package/cli/lib/internal/validators-spec-helpers.mjs +7 -140
- package/cli/lib/internal/validators-spec.mjs +103 -342
- package/cli/lib/shared.mjs +1 -42
- package/cli/lib/validators.mjs +0 -48
- package/cli/seeds/seed-policy.yaml +0 -3
- package/config/bootstrap.yaml +4 -4
- package/config/spec-generation-inputs.yaml +6 -12
- package/contracts/domain-admission.schema.yaml +3 -3
- package/contracts/migration-inventory.schema.yaml +12 -75
- package/contracts/negative-fixtures.yaml +19 -99
- package/contracts/placement-contract.schema.yaml +49 -78
- package/contracts/projection-edge.schema.yaml +37 -121
- package/contracts/shared-enums.yaml +4 -8
- package/contracts/spec-generation-inputs.schema.yaml +15 -112
- package/contracts/spec-layout.schema.yaml +32 -0
- package/contracts/surface-taxonomy.schema.yaml +43 -94
- package/contracts/table-family.schema.yaml +1 -1
- package/contracts/tracked-output-admission.schema.yaml +13 -66
- package/methodology/core.yaml +33 -25
- package/methodology/four-closure-policy.yaml +18 -26
- package/methodology/role-separation-policy.yaml +21 -27
- package/methodology/spec-reconstruction.yaml +17 -34
- package/package.json +2 -4
- package/spec/_meta/spec-tree-model.yaml +5 -101
- package/spec/product-scope.yaml +25 -57
- package/adapters/README.md +0 -25
- package/adapters/claude/README.md +0 -89
- package/adapters/claude/profile.yaml +0 -70
- package/adapters/codex/README.md +0 -53
- package/adapters/codex/profile.yaml +0 -78
- package/adapters/oh-my-codex/README.md +0 -184
- package/adapters/oh-my-codex/profile.yaml +0 -46
- package/cli/commands/admit-high-risk-decision.mjs +0 -108
- package/cli/commands/audit-sweep.mjs +0 -364
- package/cli/commands/closeout.mjs +0 -186
- package/cli/commands/decide-high-risk-execution.mjs +0 -124
- package/cli/commands/handoff.mjs +0 -123
- package/cli/commands/ingest-high-risk-execution.mjs +0 -95
- package/cli/commands/review-high-risk-execution.mjs +0 -95
- package/cli/commands/sweep-design.mjs +0 -295
- package/cli/commands/sweep.mjs +0 -22
- package/cli/commands/topic-formatters.mjs +0 -382
- package/cli/commands/topic-goal.mjs +0 -33
- package/cli/commands/topic-options-shared.mjs +0 -27
- package/cli/commands/topic-options-workflow.mjs +0 -767
- package/cli/commands/topic-options.mjs +0 -626
- package/cli/commands/topic-runner.mjs +0 -169
- package/cli/commands/topic.mjs +0 -795
- package/cli/commands/validate-acceptance.mjs +0 -5
- package/cli/commands/validate-execution-packet.mjs +0 -5
- package/cli/commands/validate-orchestration-state.mjs +0 -5
- package/cli/commands/validate-prompt.mjs +0 -5
- package/cli/commands/validate-worker-output.mjs +0 -5
- package/cli/lib/adapter-profiles.mjs +0 -403
- package/cli/lib/audit-execution.mjs +0 -52
- package/cli/lib/audit-sweep-runtime/admissions.mjs +0 -508
- package/cli/lib/audit-sweep-runtime/audit-validity.mjs +0 -356
- package/cli/lib/audit-sweep-runtime/chunks.mjs +0 -697
- package/cli/lib/audit-sweep-runtime/claude-auditor.mjs +0 -658
- package/cli/lib/audit-sweep-runtime/closeout.mjs +0 -144
- package/cli/lib/audit-sweep-runtime/codex-auditor-evidence.mjs +0 -654
- package/cli/lib/audit-sweep-runtime/codex-auditor.mjs +0 -527
- package/cli/lib/audit-sweep-runtime/common.mjs +0 -341
- package/cli/lib/audit-sweep-runtime/coverage-quality.mjs +0 -172
- package/cli/lib/audit-sweep-runtime/evidence-assignment.mjs +0 -155
- package/cli/lib/audit-sweep-runtime/format.mjs +0 -57
- package/cli/lib/audit-sweep-runtime/ingest.mjs +0 -486
- package/cli/lib/audit-sweep-runtime/inventory-spec-chunks.mjs +0 -425
- package/cli/lib/audit-sweep-runtime/inventory.mjs +0 -798
- package/cli/lib/audit-sweep-runtime/ledger.mjs +0 -315
- package/cli/lib/audit-sweep-runtime/p0p1-profile.mjs +0 -101
- package/cli/lib/audit-sweep-runtime/remediation.mjs +0 -349
- package/cli/lib/audit-sweep-runtime/rerun.mjs +0 -129
- package/cli/lib/audit-sweep-runtime/risk-budget.mjs +0 -300
- package/cli/lib/audit-sweep-runtime/status.mjs +0 -62
- package/cli/lib/audit-sweep-runtime/validators-ledger.mjs +0 -215
- package/cli/lib/audit-sweep-runtime/validators.mjs +0 -797
- package/cli/lib/audit-sweep.mjs +0 -19
- package/cli/lib/authority-convergence.mjs +0 -720
- package/cli/lib/closeout.mjs +0 -732
- package/cli/lib/codex-sdk-runner.mjs +0 -76
- package/cli/lib/external-execution.mjs +0 -101
- package/cli/lib/handoff.mjs +0 -812
- package/cli/lib/high-risk-admission.mjs +0 -512
- package/cli/lib/high-risk-decision.mjs +0 -353
- package/cli/lib/high-risk-ingest.mjs +0 -321
- package/cli/lib/high-risk-review.mjs +0 -267
- package/cli/lib/internal/contracts-parse-high-risk.mjs +0 -131
- package/cli/lib/internal/contracts-validators.mjs +0 -399
- package/cli/lib/internal/doctor-bootstrap-surface.mjs +0 -406
- package/cli/lib/internal/doctor-delegated-surface.mjs +0 -256
- package/cli/lib/internal/doctor-finalize.mjs +0 -383
- package/cli/lib/internal/doctor-format.mjs +0 -286
- package/cli/lib/internal/doctor-inspectors.mjs +0 -327
- package/cli/lib/internal/doctor-state.mjs +0 -205
- package/cli/lib/internal/validators-artifacts.mjs +0 -515
- package/cli/lib/sweep-design-runtime/common.mjs +0 -246
- package/cli/lib/sweep-design-runtime/engine.mjs +0 -733
- package/cli/lib/sweep-design-runtime/fix-topic.mjs +0 -414
- package/cli/lib/sweep-design-runtime/lifecycle.mjs +0 -54
- package/cli/lib/sweep-design-runtime/results.mjs +0 -324
- package/cli/lib/sweep-design.mjs +0 -8
- package/cli/lib/topic-artifacts.mjs +0 -186
- package/cli/lib/topic-authority-coverage.mjs +0 -73
- package/cli/lib/topic-closeout.mjs +0 -560
- package/cli/lib/topic-common.mjs +0 -404
- package/cli/lib/topic-decisions.mjs +0 -332
- package/cli/lib/topic-draft-packets.mjs +0 -167
- package/cli/lib/topic-execution.mjs +0 -533
- package/cli/lib/topic-goal.mjs +0 -440
- package/cli/lib/topic-ledger.mjs +0 -281
- package/cli/lib/topic-lifecycle-artifacts.mjs +0 -173
- package/cli/lib/topic-root-validation.mjs +0 -288
- package/cli/lib/topic-runner-commands.mjs +0 -174
- package/cli/lib/topic-runner-deferral.mjs +0 -532
- package/cli/lib/topic-runner-stale-gates.mjs +0 -114
- package/cli/lib/topic-runner-validation.mjs +0 -138
- package/cli/lib/topic-runner.mjs +0 -727
- package/cli/lib/topic-scaffold.mjs +0 -252
- package/cli/lib/topic-waves.mjs +0 -403
- package/cli/lib/topic.mjs +0 -81
- package/config/audit-execution-artifacts.yaml +0 -20
- package/config/external-execution-artifacts.yaml +0 -16
- package/config/host-adapter.yaml +0 -30
- package/config/host-profile.yaml +0 -29
- package/config/installer-evidence.yaml +0 -31
- package/config/skill-installer.yaml +0 -23
- package/config/skill-manifest.yaml +0 -48
- package/config/skills.yaml +0 -30
- package/contracts/acceptance.schema.yaml +0 -16
- package/contracts/admission-checklist.schema.yaml +0 -15
- package/contracts/audit-chunk.schema.yaml +0 -123
- package/contracts/audit-closeout.schema.yaml +0 -51
- package/contracts/audit-finding.schema.yaml +0 -61
- package/contracts/audit-ledger.schema.yaml +0 -138
- package/contracts/audit-plan.schema.yaml +0 -138
- package/contracts/audit-remediation-map.schema.yaml +0 -52
- package/contracts/audit-rerun.schema.yaml +0 -31
- package/contracts/audit-sweep-result.yaml +0 -53
- package/contracts/authority-convergence-audit.schema.yaml +0 -19
- package/contracts/closeout.schema.yaml +0 -25
- package/contracts/decision-review.schema.yaml +0 -16
- package/contracts/doc-spec-audit-result.yaml +0 -19
- package/contracts/execution-packet.schema.yaml +0 -49
- package/contracts/external-host-compatibility.yaml +0 -22
- package/contracts/forbidden-shortcuts.catalog.yaml +0 -23
- package/contracts/high-risk-admission.schema.yaml +0 -23
- package/contracts/high-risk-execution-result.yaml +0 -20
- package/contracts/orchestration-state.schema.yaml +0 -41
- package/contracts/overflow-continuation.schema.yaml +0 -12
- package/contracts/packet.schema.yaml +0 -30
- package/contracts/pending-note.schema.yaml +0 -17
- package/contracts/prompt.schema.yaml +0 -12
- package/contracts/remediation.schema.yaml +0 -16
- package/contracts/result.schema.yaml +0 -24
- package/contracts/spec-reconstruction-result.yaml +0 -41
- package/contracts/sweep-design-result.yaml +0 -349
- package/contracts/topic-goal.schema.yaml +0 -87
- package/contracts/topic-run-ledger.schema.yaml +0 -72
- package/contracts/topic-step-decision.schema.yaml +0 -45
- package/contracts/topic.schema.yaml +0 -65
- package/contracts/true-close.schema.yaml +0 -15
- package/contracts/wave.schema.yaml +0 -29
- package/contracts/worker-output.schema.yaml +0 -15
- package/contracts/workflow-consumer.schema.yaml +0 -110
- package/methodology/audit-sweep-p0p1-recall.yaml +0 -45
- package/methodology/authority-convergence-policy.yaml +0 -42
- package/methodology/overflow-continuation-policy.yaml +0 -14
- package/methodology/skill-exchange-projection.yaml +0 -114
- package/methodology/skill-handoff.yaml +0 -34
- package/methodology/skill-installer-result.yaml +0 -27
- package/methodology/skill-installer-summary-projection.yaml +0 -181
- package/methodology/skill-runtime.yaml +0 -23
- package/methodology/spec-target-truth-profile.yaml +0 -46
- package/methodology/topic-lifecycle-report.yaml +0 -144
- package/methodology/topic-lifecycle.yaml +0 -37
- package/methodology/topic-naming-ontology.yaml +0 -21
- package/methodology/topic-ontology.yaml +0 -38
- package/methodology/topic-validation-policy.yaml +0 -9
- package/methodology/wave-dag-policy.yaml +0 -14
- package/spec/_meta/command-gating-matrix.yaml +0 -143
- package/spec/_meta/generate-drift-migration-checklist.yaml +0 -137
- package/spec/_meta/governance-routing-cutover-checklist.yaml +0 -35
- package/spec/_meta/phase2-impacted-surface-matrix.yaml +0 -44
- package/spec/_meta/spec-authority-cutover-readiness.yaml +0 -102
- package/spec/bootstrap-state.yaml +0 -99
package/cli/commands/start.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
"
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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
|
-
|
|
690
|
-
|
|
691
|
-
|
|
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
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
?
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
)
|
|
700
|
-
:
|
|
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
|
-
|
|
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
|
-
|
|
709
|
-
|
|
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 };
|