@nimiplatform/nimi-coding 0.1.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.
- package/LICENSE +21 -0
- package/README.md +348 -0
- package/adapters/README.md +25 -0
- package/adapters/claude/README.md +89 -0
- package/adapters/claude/profile.yaml +70 -0
- package/adapters/codex/README.md +53 -0
- package/adapters/codex/profile.yaml +78 -0
- package/adapters/oh-my-codex/README.md +185 -0
- package/adapters/oh-my-codex/profile.yaml +46 -0
- package/bin/nimicoding.mjs +6 -0
- package/cli/commands/admit-high-risk-decision.mjs +108 -0
- package/cli/commands/audit-sweep.mjs +341 -0
- package/cli/commands/blueprint-audit.mjs +91 -0
- package/cli/commands/clear.mjs +168 -0
- package/cli/commands/closeout.mjs +183 -0
- package/cli/commands/decide-high-risk-execution.mjs +124 -0
- package/cli/commands/doctor.mjs +53 -0
- package/cli/commands/generate-spec-derived-docs.mjs +131 -0
- package/cli/commands/handoff.mjs +123 -0
- package/cli/commands/ingest-high-risk-execution.mjs +95 -0
- package/cli/commands/review-high-risk-execution.mjs +95 -0
- package/cli/commands/start.mjs +717 -0
- package/cli/commands/topic-formatters.mjs +382 -0
- package/cli/commands/topic-goal.mjs +33 -0
- package/cli/commands/topic-options-shared.mjs +27 -0
- package/cli/commands/topic-options-workflow.mjs +767 -0
- package/cli/commands/topic-options.mjs +626 -0
- package/cli/commands/topic-runner.mjs +169 -0
- package/cli/commands/topic.mjs +795 -0
- package/cli/commands/validate-acceptance.mjs +5 -0
- package/cli/commands/validate-ai-governance.mjs +214 -0
- package/cli/commands/validate-execution-packet.mjs +5 -0
- package/cli/commands/validate-orchestration-state.mjs +5 -0
- package/cli/commands/validate-prompt.mjs +5 -0
- package/cli/commands/validate-spec-audit.mjs +27 -0
- package/cli/commands/validate-spec-governance.mjs +124 -0
- package/cli/commands/validate-spec-tree.mjs +27 -0
- package/cli/commands/validate-worker-output.mjs +5 -0
- package/cli/constants.mjs +489 -0
- package/cli/help.mjs +134 -0
- package/cli/index.mjs +103 -0
- package/cli/lib/adapter-profiles.mjs +403 -0
- package/cli/lib/audit-execution.mjs +52 -0
- package/cli/lib/audit-sweep-runtime/admissions.mjs +381 -0
- package/cli/lib/audit-sweep-runtime/audit-validity.mjs +333 -0
- package/cli/lib/audit-sweep-runtime/chunks.mjs +697 -0
- package/cli/lib/audit-sweep-runtime/closeout.mjs +144 -0
- package/cli/lib/audit-sweep-runtime/codex-auditor-evidence.mjs +639 -0
- package/cli/lib/audit-sweep-runtime/codex-auditor.mjs +515 -0
- package/cli/lib/audit-sweep-runtime/common.mjs +329 -0
- package/cli/lib/audit-sweep-runtime/coverage-quality.mjs +172 -0
- package/cli/lib/audit-sweep-runtime/evidence-assignment.mjs +152 -0
- package/cli/lib/audit-sweep-runtime/format.mjs +57 -0
- package/cli/lib/audit-sweep-runtime/ingest.mjs +486 -0
- package/cli/lib/audit-sweep-runtime/inventory-spec-chunks.mjs +198 -0
- package/cli/lib/audit-sweep-runtime/inventory.mjs +728 -0
- package/cli/lib/audit-sweep-runtime/ledger.mjs +315 -0
- package/cli/lib/audit-sweep-runtime/p0p1-profile.mjs +101 -0
- package/cli/lib/audit-sweep-runtime/remediation.mjs +349 -0
- package/cli/lib/audit-sweep-runtime/rerun.mjs +129 -0
- package/cli/lib/audit-sweep-runtime/risk-budget.mjs +300 -0
- package/cli/lib/audit-sweep-runtime/status.mjs +62 -0
- package/cli/lib/audit-sweep-runtime/validators-ledger.mjs +215 -0
- package/cli/lib/audit-sweep-runtime/validators.mjs +758 -0
- package/cli/lib/audit-sweep.mjs +18 -0
- package/cli/lib/authority-convergence.mjs +309 -0
- package/cli/lib/blueprint-audit.mjs +370 -0
- package/cli/lib/bootstrap.mjs +228 -0
- package/cli/lib/closeout.mjs +623 -0
- package/cli/lib/codex-sdk-runner.mjs +76 -0
- package/cli/lib/contracts.mjs +180 -0
- package/cli/lib/doctor.mjs +18 -0
- package/cli/lib/entrypoints.mjs +274 -0
- package/cli/lib/external-execution.mjs +101 -0
- package/cli/lib/fs-helpers.mjs +33 -0
- package/cli/lib/handoff.mjs +785 -0
- package/cli/lib/high-risk-admission.mjs +442 -0
- package/cli/lib/high-risk-decision.mjs +324 -0
- package/cli/lib/high-risk-ingest.mjs +317 -0
- package/cli/lib/high-risk-review.mjs +263 -0
- package/cli/lib/internal/contracts-loaders.mjs +132 -0
- package/cli/lib/internal/contracts-parse-high-risk.mjs +131 -0
- package/cli/lib/internal/contracts-parse.mjs +457 -0
- package/cli/lib/internal/contracts-validators.mjs +398 -0
- package/cli/lib/internal/doctor-bootstrap-surface.mjs +359 -0
- package/cli/lib/internal/doctor-delegated-surface.mjs +256 -0
- package/cli/lib/internal/doctor-finalize.mjs +385 -0
- package/cli/lib/internal/doctor-format.mjs +286 -0
- package/cli/lib/internal/doctor-inspectors.mjs +294 -0
- package/cli/lib/internal/doctor-state.mjs +205 -0
- package/cli/lib/internal/governance/ai/ai-context-budget-core.mjs +315 -0
- package/cli/lib/internal/governance/ai/ai-structure-budget-core.mjs +358 -0
- package/cli/lib/internal/governance/ai/check-agents-freshness.mjs +155 -0
- package/cli/lib/internal/governance/ai/check-high-risk-doc-metadata-core.mjs +173 -0
- package/cli/lib/internal/governance/config.mjs +150 -0
- package/cli/lib/internal/governance/runner.mjs +35 -0
- package/cli/lib/internal/governance/shared/read-yaml-with-fragments.mjs +49 -0
- package/cli/lib/internal/validators-artifacts.mjs +515 -0
- package/cli/lib/internal/validators-shared.mjs +28 -0
- package/cli/lib/internal/validators-spec-helpers.mjs +186 -0
- package/cli/lib/internal/validators-spec.mjs +410 -0
- package/cli/lib/shared.mjs +83 -0
- package/cli/lib/topic-draft-packets.mjs +48 -0
- package/cli/lib/topic-goal.mjs +361 -0
- package/cli/lib/topic-runner.mjs +772 -0
- package/cli/lib/topic.mjs +93 -0
- package/cli/lib/ui.mjs +178 -0
- package/cli/lib/validators.mjs +78 -0
- package/cli/lib/value-helpers.mjs +24 -0
- package/cli/lib/yaml-helpers.mjs +133 -0
- package/cli/nimicoding.mjs +1 -0
- package/cli/seeds/bootstrap.mjs +47 -0
- package/config/audit-execution-artifacts.yaml +20 -0
- package/config/bootstrap.yaml +6 -0
- package/config/external-execution-artifacts.yaml +16 -0
- package/config/host-adapter.yaml +30 -0
- package/config/host-profile.yaml +29 -0
- package/config/installer-evidence.yaml +31 -0
- package/config/skill-installer.yaml +23 -0
- package/config/skill-manifest.yaml +46 -0
- package/config/skills.yaml +30 -0
- package/config/spec-generation-inputs.yaml +25 -0
- package/contracts/acceptance.schema.yaml +16 -0
- package/contracts/admission-checklist.schema.yaml +15 -0
- package/contracts/audit-chunk.schema.yaml +110 -0
- package/contracts/audit-closeout.schema.yaml +51 -0
- package/contracts/audit-finding.schema.yaml +61 -0
- package/contracts/audit-ledger.schema.yaml +138 -0
- package/contracts/audit-plan.schema.yaml +123 -0
- package/contracts/audit-remediation-map.schema.yaml +51 -0
- package/contracts/audit-rerun.schema.yaml +31 -0
- package/contracts/audit-sweep-result.yaml +49 -0
- package/contracts/authority-convergence-audit.schema.yaml +19 -0
- package/contracts/closeout.schema.yaml +25 -0
- package/contracts/decision-review.schema.yaml +16 -0
- package/contracts/doc-spec-audit-result.yaml +19 -0
- package/contracts/execution-packet.schema.yaml +49 -0
- package/contracts/external-host-compatibility.yaml +22 -0
- package/contracts/forbidden-shortcuts.catalog.yaml +23 -0
- package/contracts/high-risk-admission.schema.yaml +23 -0
- package/contracts/high-risk-execution-result.yaml +20 -0
- package/contracts/orchestration-state.schema.yaml +41 -0
- package/contracts/overflow-continuation.schema.yaml +12 -0
- package/contracts/packet.schema.yaml +30 -0
- package/contracts/pending-note.schema.yaml +17 -0
- package/contracts/prompt.schema.yaml +12 -0
- package/contracts/remediation.schema.yaml +16 -0
- package/contracts/result.schema.yaml +24 -0
- package/contracts/spec-generation-audit.schema.yaml +31 -0
- package/contracts/spec-generation-inputs.schema.yaml +39 -0
- package/contracts/spec-reconstruction-result.yaml +37 -0
- package/contracts/topic-goal.schema.yaml +78 -0
- package/contracts/topic-run-ledger.schema.yaml +72 -0
- package/contracts/topic-step-decision.schema.yaml +45 -0
- package/contracts/topic.schema.yaml +65 -0
- package/contracts/true-close.schema.yaml +15 -0
- package/contracts/wave.schema.yaml +29 -0
- package/contracts/worker-output.schema.yaml +15 -0
- package/methodology/audit-sweep-p0p1-recall.yaml +45 -0
- package/methodology/authority-convergence-policy.yaml +42 -0
- package/methodology/core.yaml +25 -0
- package/methodology/four-closure-policy.yaml +28 -0
- package/methodology/overflow-continuation-policy.yaml +14 -0
- package/methodology/role-separation-policy.yaml +28 -0
- package/methodology/skill-exchange-projection.yaml +114 -0
- package/methodology/skill-handoff.yaml +34 -0
- package/methodology/skill-installer-result.yaml +27 -0
- package/methodology/skill-installer-summary-projection.yaml +181 -0
- package/methodology/skill-runtime.yaml +23 -0
- package/methodology/spec-reconstruction.yaml +63 -0
- package/methodology/spec-target-truth-profile.yaml +53 -0
- package/methodology/topic-lifecycle-report.yaml +144 -0
- package/methodology/topic-lifecycle.yaml +37 -0
- package/methodology/topic-naming-ontology.yaml +21 -0
- package/methodology/topic-ontology.yaml +38 -0
- package/methodology/topic-validation-policy.yaml +9 -0
- package/methodology/wave-dag-policy.yaml +14 -0
- package/package.json +50 -0
- package/spec/_meta/command-gating-matrix.yaml +110 -0
- package/spec/_meta/generate-drift-migration-checklist.yaml +155 -0
- package/spec/_meta/governance-routing-cutover-checklist.yaml +35 -0
- package/spec/_meta/phase2-impacted-surface-matrix.yaml +44 -0
- package/spec/_meta/spec-authority-cutover-readiness.yaml +104 -0
- package/spec/_meta/spec-tree-model.yaml +72 -0
- package/spec/bootstrap-state.yaml +99 -0
- package/spec/product-scope.yaml +56 -0
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
import { emitKeypressEvents } from "node:readline";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
buildHandoffPayload,
|
|
7
|
+
formatDoctorResult,
|
|
8
|
+
formatStartPastePrompt,
|
|
9
|
+
getStartHostOption,
|
|
10
|
+
inspectBootstrapCompatibility,
|
|
11
|
+
inspectDoctorState,
|
|
12
|
+
integrateEntrypoints,
|
|
13
|
+
pathExists,
|
|
14
|
+
previewBootstrapWrites,
|
|
15
|
+
previewEntrypointIntegration,
|
|
16
|
+
resolveStartHostChoice,
|
|
17
|
+
START_HOST_OPTIONS,
|
|
18
|
+
writeHandoffJsonArtifact,
|
|
19
|
+
writeMissingBootstrapFiles,
|
|
20
|
+
} 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";
|
|
32
|
+
|
|
33
|
+
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;
|
|
294
|
+
}
|
|
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}`);
|
|
302
|
+
}
|
|
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
|
+
return doctorResult.lifecycleState?.treeState === "canonical_tree_ready"
|
|
347
|
+
&& doctorResult.canonicalTree?.requiredFilesValid === true;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function determineWizardStage(doctorResult) {
|
|
351
|
+
if (!canonicalTreeReady(doctorResult)) {
|
|
352
|
+
return {
|
|
353
|
+
title: localize("Step 3. Rebuild project rules", "第 3 步:重建项目规则"),
|
|
354
|
+
detail: localize(
|
|
355
|
+
"Bootstrap is ready. The next action is to hand the reconstruction task package to an external AI host.",
|
|
356
|
+
"bootstrap 已就绪。下一步是将 reconstruction 任务包交给外部 AI Host。",
|
|
357
|
+
),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (!doctorResult.auditArtifact.present) {
|
|
362
|
+
return {
|
|
363
|
+
title: localize("Step 4. Run a doc/spec audit", "第 4 步:执行 doc/spec 审计"),
|
|
364
|
+
detail: localize(
|
|
365
|
+
"Project rules are present. The next action is to hand the audit package to an external AI host and review the result locally.",
|
|
366
|
+
"项目规则已存在。下一步是将 audit 任务包交给外部 AI Host,并在本地审阅结果。",
|
|
367
|
+
),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return {
|
|
372
|
+
title: localize("Ready", "已就绪"),
|
|
373
|
+
detail: localize(
|
|
374
|
+
"Bootstrap and project rules are aligned. The project is ready for the next approved task.",
|
|
375
|
+
"bootstrap 与项目规则已对齐。当前可以继续下一项已批准任务。",
|
|
376
|
+
),
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function isReconstructionContinuable(doctorResult) {
|
|
381
|
+
if (canonicalTreeReady(doctorResult)) {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const blockingChecks = doctorResult.checks.filter((check) => check.severity === "error");
|
|
386
|
+
if (blockingChecks.length === 0) {
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const tolerated = new Set([
|
|
391
|
+
"canonical_tree_progress",
|
|
392
|
+
"bootstrap_lifecycle_alignment",
|
|
393
|
+
"doc_spec_audit_state_alignment",
|
|
394
|
+
]);
|
|
395
|
+
|
|
396
|
+
return blockingChecks.every((check) => tolerated.has(check.id));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function printStage(title, bodyLines) {
|
|
400
|
+
process.stdout.write(`${styleHeading(title)}\n${formatSectionDivider()}\n${bodyLines.join("\n")}\n\n`);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async function maybeApproveStage(options, prompt, defaultAnswer = true) {
|
|
404
|
+
if (options.yes || !canPromptInteractively()) {
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const choice = await chooseWithArrowKeys(prompt, [
|
|
409
|
+
{
|
|
410
|
+
value: true,
|
|
411
|
+
label: localize("Apply stage", "执行此阶段"),
|
|
412
|
+
description: localize("continue and write the planned changes", "继续并写入计划中的变更"),
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
value: false,
|
|
416
|
+
label: localize("Skip for now", "暂时跳过"),
|
|
417
|
+
description: localize("leave this stage unchanged in this run", "本次运行中不处理这一阶段"),
|
|
418
|
+
},
|
|
419
|
+
], defaultAnswer ? 0 : 1);
|
|
420
|
+
|
|
421
|
+
return choice.value;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async function chooseExternalHost(options, payload) {
|
|
425
|
+
const resolvedHost = resolveStartHostChoice(options.host, payload);
|
|
426
|
+
if (options.yes || !canPromptInteractively()) {
|
|
427
|
+
return resolvedHost;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const choice = await chooseWithArrowKeys(
|
|
431
|
+
localize(
|
|
432
|
+
"Select the external host that will receive the short prompt:",
|
|
433
|
+
"选择将接收这段短 prompt 的外部 Host:",
|
|
434
|
+
),
|
|
435
|
+
START_HOST_OPTIONS.map((option) => ({
|
|
436
|
+
value: option.id,
|
|
437
|
+
label: localize(option.label, option.zhLabel),
|
|
438
|
+
description: localize(option.description, option.zhDescription),
|
|
439
|
+
})),
|
|
440
|
+
Math.max(START_HOST_OPTIONS.findIndex((option) => option.id === resolvedHost), 0),
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
return choice.value;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async function maybePrepareHandoff(projectRoot, options, skillId, title, detail) {
|
|
447
|
+
const payload = await buildHandoffPayload(projectRoot, skillId);
|
|
448
|
+
if (!payload.ok) {
|
|
449
|
+
return {
|
|
450
|
+
ok: false,
|
|
451
|
+
payload,
|
|
452
|
+
refs: null,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const approved = await maybeApproveStage(
|
|
457
|
+
options,
|
|
458
|
+
localize(
|
|
459
|
+
`Apply this stage and prepare the next AI task package for ${title}?`,
|
|
460
|
+
`执行这一阶段并为 ${title} 准备下一步 AI 任务包吗?`,
|
|
461
|
+
),
|
|
462
|
+
true,
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
if (!approved) {
|
|
466
|
+
return {
|
|
467
|
+
ok: true,
|
|
468
|
+
skipped: true,
|
|
469
|
+
payload,
|
|
470
|
+
refs: null,
|
|
471
|
+
detail,
|
|
472
|
+
hostId: null,
|
|
473
|
+
pastePrompt: null,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const hostId = await chooseExternalHost(options, payload);
|
|
478
|
+
const refs = await writeHandoffJsonArtifact(projectRoot, payload);
|
|
479
|
+
const pastePrompt = formatStartPastePrompt(payload, {
|
|
480
|
+
hostId,
|
|
481
|
+
jsonRef: refs.jsonRef,
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
return {
|
|
485
|
+
ok: true,
|
|
486
|
+
skipped: false,
|
|
487
|
+
payload,
|
|
488
|
+
refs,
|
|
489
|
+
detail,
|
|
490
|
+
hostId,
|
|
491
|
+
pastePrompt,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export async function runStart(args) {
|
|
496
|
+
const parsed = parseStartOptions(args);
|
|
497
|
+
if (!parsed.ok) {
|
|
498
|
+
process.stderr.write(parsed.error);
|
|
499
|
+
return 2;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
try {
|
|
503
|
+
await maybeSelectLanguage(parsed.options);
|
|
504
|
+
|
|
505
|
+
const projectRoot = process.cwd();
|
|
506
|
+
const nimiRoot = path.join(projectRoot, ".nimi");
|
|
507
|
+
const nimiInfo = await pathExists(nimiRoot);
|
|
508
|
+
|
|
509
|
+
if (nimiInfo && !nimiInfo.isDirectory()) {
|
|
510
|
+
process.stderr.write(`${localize(
|
|
511
|
+
`nimicoding start refused: ${nimiRoot} exists and is not a directory.`,
|
|
512
|
+
`nimicoding start 已拒绝:${nimiRoot} 已存在且不是目录。`,
|
|
513
|
+
)}\n`);
|
|
514
|
+
return 2;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const compatibility = await inspectBootstrapCompatibility(projectRoot);
|
|
518
|
+
if (compatibility.status === "unsupported") {
|
|
519
|
+
process.stderr.write(`${localize(
|
|
520
|
+
"nimicoding start refused: bootstrap.yaml declares an unsupported bootstrap contract id or version.",
|
|
521
|
+
"nimicoding start 已拒绝:bootstrap.yaml 声明了不受支持的 bootstrap contract id 或 version。",
|
|
522
|
+
)}\n`);
|
|
523
|
+
return 1;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const bootstrapPreview = await previewBootstrapWrites(projectRoot);
|
|
527
|
+
const entrypointPreview = await previewEntrypointIntegration(projectRoot);
|
|
528
|
+
|
|
529
|
+
if (canPromptInteractively() && !parsed.options.yes) {
|
|
530
|
+
process.stdout.write(`${buildStartBanner()}
|
|
531
|
+
|
|
532
|
+
${styleHeading(localize(`nimicoding start wizard for ${projectRoot}`, `nimicoding start 向导:${projectRoot}`))}
|
|
533
|
+
|
|
534
|
+
${styleMuted(localize(
|
|
535
|
+
"Project inspection complete. Start runs in ordered stages. Each stage is previewed first, then confirmed, then applied.",
|
|
536
|
+
"项目检查已完成。start 会按顺序执行各阶段。每个阶段都会先预览、再确认、再执行。",
|
|
537
|
+
))}
|
|
538
|
+
|
|
539
|
+
`);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const applied = {
|
|
543
|
+
bootstrap: null,
|
|
544
|
+
entrypoints: null,
|
|
545
|
+
handoff: null,
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
if (bootstrapPreview.hasWork) {
|
|
549
|
+
if (canPromptInteractively() && !parsed.options.yes) {
|
|
550
|
+
printStage(
|
|
551
|
+
localize("Step 1. Set up project files", "第 1 步:准备项目文件"),
|
|
552
|
+
buildBootstrapStageLines(bootstrapPreview),
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const approved = await maybeApproveStage(
|
|
557
|
+
parsed.options,
|
|
558
|
+
localize(
|
|
559
|
+
"Apply stage 1 and create the missing local .nimi bootstrap files?",
|
|
560
|
+
"执行第 1 阶段并创建缺失的本地 .nimi bootstrap 文件吗?",
|
|
561
|
+
),
|
|
562
|
+
true,
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
if (approved) {
|
|
566
|
+
applied.bootstrap = await writeMissingBootstrapFiles(projectRoot);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (entrypointPreview.length > 0) {
|
|
571
|
+
if (canPromptInteractively() && !parsed.options.yes) {
|
|
572
|
+
printStage(
|
|
573
|
+
localize("Step 2. Connect AI entry files", "第 2 步:接入 AI 入口文件"),
|
|
574
|
+
buildEntrypointStageLines(entrypointPreview),
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const approved = await maybeApproveStage(
|
|
579
|
+
parsed.options,
|
|
580
|
+
localize(
|
|
581
|
+
"Apply stage 2 and update the managed guidance blocks in AGENTS.md / CLAUDE.md?",
|
|
582
|
+
"执行第 2 阶段并更新 AGENTS.md / CLAUDE.md 中的托管指导区块吗?",
|
|
583
|
+
),
|
|
584
|
+
true,
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
if (approved) {
|
|
588
|
+
applied.entrypoints = await integrateEntrypoints(projectRoot);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const doctorResult = await inspectDoctorState(projectRoot);
|
|
593
|
+
|
|
594
|
+
if (!doctorResult.ok && !isReconstructionContinuable(doctorResult)) {
|
|
595
|
+
process.stdout.write(`${styleHeading(localize("nimicoding start paused", "nimicoding start 已暂停"))}
|
|
596
|
+
|
|
597
|
+
${styleMuted(localize(
|
|
598
|
+
"Blocking diagnostic errors are present. Review the doctor output below before continuing.",
|
|
599
|
+
"当前存在阻塞性的诊断错误。继续前请先查看下面的 doctor 输出。",
|
|
600
|
+
))}
|
|
601
|
+
|
|
602
|
+
${formatDoctorResult(doctorResult)}`);
|
|
603
|
+
return 1;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (!canonicalTreeReady(doctorResult)) {
|
|
607
|
+
if (canPromptInteractively() && !parsed.options.yes) {
|
|
608
|
+
printStage(
|
|
609
|
+
localize("Step 3. Prepare the next AI task", "第 3 步:准备下一项 AI 任务"),
|
|
610
|
+
buildTaskStageLines("reconstruction"),
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
applied.handoff = await maybePrepareHandoff(
|
|
615
|
+
projectRoot,
|
|
616
|
+
parsed.options,
|
|
617
|
+
"spec_reconstruction",
|
|
618
|
+
localize("spec reconstruction", "spec reconstruction"),
|
|
619
|
+
localize(
|
|
620
|
+
"Prepare an AI task package for spec reconstruction.",
|
|
621
|
+
"为 spec reconstruction 准备 AI 任务包。",
|
|
622
|
+
),
|
|
623
|
+
);
|
|
624
|
+
} else if (!doctorResult.auditArtifact.present) {
|
|
625
|
+
if (canPromptInteractively() && !parsed.options.yes) {
|
|
626
|
+
printStage(
|
|
627
|
+
localize("Step 3. Prepare the next AI task", "第 3 步:准备下一项 AI 任务"),
|
|
628
|
+
buildTaskStageLines("audit"),
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
applied.handoff = await maybePrepareHandoff(
|
|
633
|
+
projectRoot,
|
|
634
|
+
parsed.options,
|
|
635
|
+
"doc_spec_audit",
|
|
636
|
+
localize("doc spec audit", "doc spec audit"),
|
|
637
|
+
localize(
|
|
638
|
+
"Prepare an AI task package for doc/spec audit.",
|
|
639
|
+
"为 doc/spec audit 准备 AI 任务包。",
|
|
640
|
+
),
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const continuationLines = [];
|
|
645
|
+
if (applied.handoff?.refs) {
|
|
646
|
+
continuationLines.push(` - ${localize("AI task package", "AI 任务包")}: ${applied.handoff.refs.jsonRef}`);
|
|
647
|
+
continuationLines.push(` - ${localize("selected host", "已选择的 Host")}: ${localize(getStartHostOption(applied.handoff.hostId)?.label ?? "Generic external host", getStartHostOption(applied.handoff.hostId)?.zhLabel ?? "通用外部 Host")}`);
|
|
648
|
+
} else if (applied.handoff?.skipped) {
|
|
649
|
+
continuationLines.push(` - ${localize("AI task package preparation was skipped for now", "本次已跳过 AI 任务包准备")}`);
|
|
650
|
+
} else if (canonicalTreeReady(doctorResult) && doctorResult.auditArtifact.present) {
|
|
651
|
+
continuationLines.push(` - ${localize("no AI task package is needed right now", "当前不需要准备 AI 任务包")}`);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const completedLines = [
|
|
655
|
+
...(applied.bootstrap
|
|
656
|
+
? [
|
|
657
|
+
...((applied.bootstrap.createdFiles ?? []).map((filePath) => ` - ${localize("created", "已创建")}: ${filePath}`)),
|
|
658
|
+
...((applied.bootstrap.createdDirs ?? []).map((dirPath) => ` - ${localize("created", "已创建")}: ${dirPath}/`)),
|
|
659
|
+
...((applied.bootstrap.gitignoreUpdated ?? false) ? [` - ${localize("updated", "已更新")}: .gitignore`] : []),
|
|
660
|
+
]
|
|
661
|
+
: []),
|
|
662
|
+
...((applied.entrypoints ?? []).map((filePath) => ` - ${localize("updated", "已更新")}: ${filePath}`)),
|
|
663
|
+
];
|
|
664
|
+
|
|
665
|
+
const wizardStage = determineWizardStage(doctorResult);
|
|
666
|
+
const pastePromptSection = applied.handoff?.pastePrompt
|
|
667
|
+
? `${formatSectionTitle(4, localize("Paste Prompt", "可粘贴 Prompt"))}
|
|
668
|
+
${formatSectionDivider()}
|
|
669
|
+
${applied.handoff.pastePrompt.split("\n").filter(Boolean).map((line) => ` ${line}`).join("\n")}
|
|
670
|
+
|
|
671
|
+
`
|
|
672
|
+
: "";
|
|
673
|
+
|
|
674
|
+
process.stdout.write(`${styleHeading(localize(`nimicoding start wizard: ${projectRoot}`, `nimicoding start 向导:${projectRoot}`))}
|
|
675
|
+
|
|
676
|
+
${formatSectionTitle(1, localize("Current Step", "当前步骤"))}
|
|
677
|
+
${formatSectionDivider()}
|
|
678
|
+
- ${wizardStage.title}
|
|
679
|
+
- ${wizardStage.detail}
|
|
680
|
+
|
|
681
|
+
${formatSectionTitle(2, localize("Changes Applied", "已执行变更"))}
|
|
682
|
+
${formatSectionDivider()}
|
|
683
|
+
${completedLines.length > 0 ? completedLines.join("\n") : localize(" - no new changes were needed in this run", " - 本次运行不需要新增任何变更")}
|
|
684
|
+
|
|
685
|
+
${formatSectionTitle(3, localize("Outputs Ready", "已准备输出"))}
|
|
686
|
+
${formatSectionDivider()}
|
|
687
|
+
${continuationLines.length > 0 ? continuationLines.join("\n") : localize(" - no additional staged outputs", " - 没有额外阶段输出")}
|
|
688
|
+
|
|
689
|
+
${pastePromptSection}${formatSectionTitle(applied.handoff?.pastePrompt ? 5 : 4, localize("Next Action", "下一步操作"))}
|
|
690
|
+
${formatSectionDivider()}
|
|
691
|
+
- ${applied.handoff?.refs
|
|
692
|
+
? localize(
|
|
693
|
+
`Paste the short prompt above into the selected external AI host. Keep ${applied.handoff.refs.jsonRef} as the authoritative machine contract.`,
|
|
694
|
+
`把上面的短 prompt 粘贴给所选外部 AI Host,并把 ${applied.handoff.refs.jsonRef} 作为权威机器契约保留。`,
|
|
695
|
+
)
|
|
696
|
+
: localize(
|
|
697
|
+
"Continue with the next admitted project task. If a full diagnostic snapshot is needed, run `nimicoding doctor`.",
|
|
698
|
+
"继续执行项目中的下一项已准入任务。如果需要完整诊断快照,请运行 `nimicoding doctor`。",
|
|
699
|
+
)}
|
|
700
|
+
|
|
701
|
+
${styleMuted(localize("Need more detail? Run `nimicoding doctor`.", "如果你需要更多细节,请运行 `nimicoding doctor`。"))}
|
|
702
|
+
`);
|
|
703
|
+
|
|
704
|
+
return 0;
|
|
705
|
+
} catch (error) {
|
|
706
|
+
if (error instanceof Error && error.message === "SIGINT") {
|
|
707
|
+
process.stderr.write(`${localize(
|
|
708
|
+
"nimicoding start canceled.\n",
|
|
709
|
+
"nimicoding start 已取消。\n",
|
|
710
|
+
)}`);
|
|
711
|
+
return 130;
|
|
712
|
+
}
|
|
713
|
+
throw error;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
export { parseStartOptions };
|