@nimiplatform/nimi-coding 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/CODE_OF_CONDUCT.md +28 -0
  3. package/CONTRIBUTING.md +45 -0
  4. package/README.md +371 -344
  5. package/README.zh-CN.md +307 -0
  6. package/SECURITY.md +26 -0
  7. package/adapters/oh-my-codex/README.md +8 -9
  8. package/cli/commands/audit-sweep.mjs +10 -10
  9. package/cli/commands/classify-spec-tree.mjs +5 -0
  10. package/cli/commands/closeout.mjs +3 -0
  11. package/cli/commands/generate-spec-derived-docs.mjs +20 -0
  12. package/cli/commands/generate-spec-migration-plan.mjs +30 -0
  13. package/cli/commands/start.mjs +5 -1
  14. package/cli/commands/surface-validator-command.mjs +49 -0
  15. package/cli/commands/sweep-design.mjs +295 -0
  16. package/cli/commands/sweep.mjs +22 -0
  17. package/cli/commands/sync.mjs +132 -0
  18. package/cli/commands/topic-formatters.mjs +8 -8
  19. package/cli/commands/validate-ai-governance.mjs +167 -46
  20. package/cli/commands/validate-domain-admission.mjs +5 -0
  21. package/cli/commands/validate-guidance-bodies.mjs +5 -0
  22. package/cli/commands/validate-placement.mjs +5 -0
  23. package/cli/commands/validate-projection-edges.mjs +5 -0
  24. package/cli/commands/validate-spec-audit.mjs +5 -1
  25. package/cli/commands/validate-table-family.mjs +5 -0
  26. package/cli/commands/validate-tracked-output-admission.mjs +5 -0
  27. package/cli/constants.mjs +5 -49
  28. package/cli/help.mjs +33 -11
  29. package/cli/index.mjs +20 -2
  30. package/cli/lib/audit-sweep-runtime/admissions.mjs +38 -29
  31. package/cli/lib/audit-sweep-runtime/audit-validity.mjs +8 -0
  32. package/cli/lib/audit-sweep-runtime/chunks.mjs +11 -11
  33. package/cli/lib/audit-sweep-runtime/closeout.mjs +8 -8
  34. package/cli/lib/audit-sweep-runtime/codex-auditor-evidence.mjs +3 -3
  35. package/cli/lib/audit-sweep-runtime/codex-auditor.mjs +10 -10
  36. package/cli/lib/audit-sweep-runtime/common.mjs +7 -7
  37. package/cli/lib/audit-sweep-runtime/format.mjs +3 -3
  38. package/cli/lib/audit-sweep-runtime/ingest.mjs +8 -8
  39. package/cli/lib/audit-sweep-runtime/inventory-spec-chunks.mjs +24 -27
  40. package/cli/lib/audit-sweep-runtime/inventory.mjs +58 -18
  41. package/cli/lib/audit-sweep-runtime/ledger.mjs +1 -1
  42. package/cli/lib/audit-sweep-runtime/p0p1-profile.mjs +2 -2
  43. package/cli/lib/audit-sweep-runtime/remediation.mjs +6 -6
  44. package/cli/lib/audit-sweep-runtime/rerun.mjs +6 -6
  45. package/cli/lib/audit-sweep-runtime/status.mjs +1 -1
  46. package/cli/lib/audit-sweep-runtime/validators.mjs +2 -2
  47. package/cli/lib/authority-convergence.mjs +397 -2
  48. package/cli/lib/blueprint-audit.mjs +5 -5
  49. package/cli/lib/closeout.mjs +126 -3
  50. package/cli/lib/contracts.mjs +21 -17
  51. package/cli/lib/handoff.mjs +29 -11
  52. package/cli/lib/high-risk-admission.mjs +60 -11
  53. package/cli/lib/high-risk-decision.mjs +31 -2
  54. package/cli/lib/high-risk-ingest.mjs +5 -1
  55. package/cli/lib/high-risk-review.mjs +5 -1
  56. package/cli/lib/internal/contracts-parse.mjs +195 -24
  57. package/cli/lib/internal/contracts-validators.mjs +3 -2
  58. package/cli/lib/internal/doctor-bootstrap-surface.mjs +82 -35
  59. package/cli/lib/internal/doctor-delegated-surface.mjs +1 -1
  60. package/cli/lib/internal/doctor-finalize.mjs +12 -8
  61. package/cli/lib/internal/doctor-inspectors.mjs +34 -1
  62. package/cli/lib/internal/governance/ai/ai-context-budget-core.mjs +74 -12
  63. package/cli/lib/internal/governance/ai/ai-structure-budget-core.mjs +24 -6
  64. package/cli/lib/internal/governance/ai/check-agents-freshness.mjs +18 -23
  65. package/cli/lib/internal/surface-taxonomy-validators.mjs +931 -0
  66. package/cli/lib/internal/validators-spec.mjs +229 -20
  67. package/cli/lib/sweep-design-runtime/common.mjs +246 -0
  68. package/cli/lib/sweep-design-runtime/engine.mjs +733 -0
  69. package/cli/lib/sweep-design-runtime/fix-topic.mjs +414 -0
  70. package/cli/lib/sweep-design-runtime/lifecycle.mjs +54 -0
  71. package/cli/lib/sweep-design-runtime/results.mjs +324 -0
  72. package/cli/lib/sweep-design.mjs +8 -0
  73. package/cli/lib/sync.mjs +143 -0
  74. package/cli/lib/topic-artifacts.mjs +186 -0
  75. package/cli/lib/topic-authority-coverage.mjs +73 -0
  76. package/cli/lib/topic-closeout.mjs +560 -0
  77. package/cli/lib/topic-common.mjs +404 -0
  78. package/cli/lib/topic-decisions.mjs +332 -0
  79. package/cli/lib/topic-draft-packets.mjs +126 -7
  80. package/cli/lib/topic-execution.mjs +515 -0
  81. package/cli/lib/topic-goal.mjs +112 -33
  82. package/cli/lib/topic-ledger.mjs +281 -0
  83. package/cli/lib/topic-lifecycle-artifacts.mjs +173 -0
  84. package/cli/lib/topic-root-validation.mjs +288 -0
  85. package/cli/lib/topic-runner-commands.mjs +174 -0
  86. package/cli/lib/topic-runner-deferral.mjs +532 -0
  87. package/cli/lib/topic-runner-stale-gates.mjs +114 -0
  88. package/cli/lib/topic-runner-validation.mjs +138 -0
  89. package/cli/lib/topic-runner.mjs +109 -154
  90. package/cli/lib/topic-scaffold.mjs +252 -0
  91. package/cli/lib/topic-waves.mjs +403 -0
  92. package/cli/lib/topic.mjs +81 -93
  93. package/cli/lib/value-helpers.mjs +6 -1
  94. package/cli/seeds/bootstrap.mjs +96 -20
  95. package/cli/seeds/seed-policy.yaml +67 -0
  96. package/config/bootstrap.yaml +1 -1
  97. package/config/skill-manifest.yaml +4 -2
  98. package/config/spec-generation-inputs.yaml +41 -19
  99. package/contracts/audit-remediation-map.schema.yaml +1 -0
  100. package/contracts/audit-sweep-result.yaml +4 -0
  101. package/contracts/domain-admission.schema.yaml +56 -0
  102. package/contracts/migration-inventory.schema.yaml +80 -0
  103. package/contracts/negative-fixtures.yaml +91 -0
  104. package/contracts/placement-contract.schema.yaml +163 -0
  105. package/contracts/projection-edge.schema.yaml +130 -0
  106. package/contracts/shared-enums.yaml +68 -0
  107. package/contracts/spec-generation-audit.schema.yaml +19 -4
  108. package/contracts/spec-generation-inputs.schema.yaml +130 -29
  109. package/contracts/spec-reconstruction-result.yaml +9 -5
  110. package/contracts/surface-taxonomy.schema.yaml +201 -0
  111. package/contracts/sweep-design-result.yaml +349 -0
  112. package/contracts/table-family.schema.yaml +121 -0
  113. package/contracts/topic-goal.schema.yaml +10 -1
  114. package/contracts/tracked-output-admission.schema.yaml +70 -0
  115. package/contracts/workflow-consumer.schema.yaml +112 -0
  116. package/methodology/audit-sweep-p0p1-recall.yaml +1 -1
  117. package/methodology/spec-reconstruction.yaml +53 -30
  118. package/package.json +19 -4
  119. package/spec/_meta/command-gating-matrix.yaml +33 -0
  120. package/spec/_meta/generate-drift-migration-checklist.yaml +44 -62
  121. package/spec/_meta/governance-routing-cutover-checklist.yaml +3 -3
  122. package/spec/_meta/phase2-impacted-surface-matrix.yaml +14 -14
  123. package/spec/_meta/spec-authority-cutover-readiness.yaml +3 -5
  124. package/spec/_meta/spec-tree-model.yaml +104 -36
  125. package/spec/bootstrap-state.yaml +36 -36
  126. package/spec/product-scope.yaml +13 -10
@@ -0,0 +1,295 @@
1
+ import {
2
+ runAuditorPrompt,
3
+ runFinalize,
4
+ runFixTopic,
5
+ runIntake,
6
+ runLedgerValidate,
7
+ runPacketBuild,
8
+ runPacketBuildBatch,
9
+ runResultIngest,
10
+ runWavePlan,
11
+ } from "../lib/sweep-design.mjs";
12
+ import { localize } from "../lib/ui.mjs";
13
+
14
+ function readRequiredValue(args, index, optionName, commandName) {
15
+ const next = args[index + 1];
16
+ if (!next || next.startsWith("--")) {
17
+ return {
18
+ ok: false,
19
+ error: `${localize(
20
+ `nimicoding sweep design ${commandName} refused: ${optionName} requires a value.`,
21
+ `nimicoding sweep design ${commandName} 已拒绝:${optionName} 需要一个值。`,
22
+ )}\n`,
23
+ };
24
+ }
25
+ return { ok: true, value: next };
26
+ }
27
+
28
+ function unknownOption(commandName, arg) {
29
+ return {
30
+ ok: false,
31
+ error: `${localize(
32
+ `nimicoding sweep design ${commandName} refused: unknown option ${arg}.`,
33
+ `nimicoding sweep design ${commandName} 已拒绝:未知选项 ${arg}。`,
34
+ )}\n`,
35
+ };
36
+ }
37
+
38
+ function parseOptions(args, commandName, spec) {
39
+ const options = Object.fromEntries(Object.entries(spec).map(([name, config]) => [name, config.default ?? null]));
40
+ for (let index = 0; index < args.length; index += 1) {
41
+ const arg = args[index];
42
+ if (arg === "--json") {
43
+ options.json = true;
44
+ continue;
45
+ }
46
+ const entry = Object.entries(spec).find(([, config]) => config.flag === arg);
47
+ if (!entry) {
48
+ return unknownOption(commandName, arg);
49
+ }
50
+ const [name] = entry;
51
+ if (entry[1].type === "boolean") {
52
+ options[name] = true;
53
+ continue;
54
+ }
55
+ const value = readRequiredValue(args, index, arg, commandName);
56
+ if (!value.ok) return value;
57
+ options[name] = value.value;
58
+ index += 1;
59
+ }
60
+ const missing = Object.entries(spec)
61
+ .filter(([, config]) => config.required)
62
+ .filter(([name]) => !options[name])
63
+ .map(([, config]) => config.flag);
64
+ if (missing.length > 0) {
65
+ return {
66
+ ok: false,
67
+ error: `${localize(
68
+ `nimicoding sweep design ${commandName} refused: missing required options: ${missing.join(", ")}.`,
69
+ `nimicoding sweep design ${commandName} 已拒绝:缺少必填选项:${missing.join(", ")}。`,
70
+ )}\n`,
71
+ };
72
+ }
73
+ return { ok: true, options };
74
+ }
75
+
76
+ function parseSweepDesignOptions(args) {
77
+ const [phase] = args;
78
+ if (phase === "intake") {
79
+ return {
80
+ ok: true,
81
+ action: "intake",
82
+ parsed: parseOptions(args.slice(1), "intake", {
83
+ sweepId: { flag: "--sweep-id", required: true },
84
+ runId: { flag: "--run-id" },
85
+ verifiedAt: { flag: "--verified-at" },
86
+ json: { default: false },
87
+ }),
88
+ };
89
+ }
90
+ if (phase === "packet-build") {
91
+ return {
92
+ ok: true,
93
+ action: "packet-build",
94
+ parsed: parseOptions(args.slice(1), "packet-build", {
95
+ runId: { flag: "--run-id", required: true },
96
+ packetId: { flag: "--packet-id", required: true },
97
+ findingId: { flag: "--finding-id" },
98
+ findingIds: { flag: "--finding-ids" },
99
+ explicitQuestion: { flag: "--explicit-question" },
100
+ explicitQuestions: { flag: "--explicit-questions" },
101
+ priorDesignStateRefs: { flag: "--prior-design-state-refs" },
102
+ priorDesignStateMarker: { flag: "--prior-design-state-marker" },
103
+ currentClusterRefs: { flag: "--current-cluster-refs" },
104
+ currentWaveRefs: { flag: "--current-wave-refs" },
105
+ authorityOnly: { flag: "--authority-only", type: "boolean", default: false },
106
+ verifiedAt: { flag: "--verified-at" },
107
+ json: { default: false },
108
+ }),
109
+ };
110
+ }
111
+ if (phase === "packet-build-batch") {
112
+ return {
113
+ ok: true,
114
+ action: "packet-build-batch",
115
+ parsed: parseOptions(args.slice(1), "packet-build-batch", {
116
+ runId: { flag: "--run-id", required: true },
117
+ batchSize: { flag: "--batch-size", required: true },
118
+ findingIds: { flag: "--finding-ids" },
119
+ packetPrefix: { flag: "--packet-prefix" },
120
+ manifestId: { flag: "--manifest-id" },
121
+ explicitQuestion: { flag: "--explicit-question" },
122
+ explicitQuestions: { flag: "--explicit-questions" },
123
+ priorDesignStateRefs: { flag: "--prior-design-state-refs" },
124
+ priorDesignStateMarker: { flag: "--prior-design-state-marker" },
125
+ currentClusterRefs: { flag: "--current-cluster-refs" },
126
+ currentWaveRefs: { flag: "--current-wave-refs" },
127
+ authorityOnly: { flag: "--authority-only", type: "boolean", default: false },
128
+ verifiedAt: { flag: "--verified-at" },
129
+ json: { default: false },
130
+ }),
131
+ };
132
+ }
133
+ if (phase === "result-ingest") {
134
+ return {
135
+ ok: true,
136
+ action: "result-ingest",
137
+ parsed: parseOptions(args.slice(1), "result-ingest", {
138
+ runId: { flag: "--run-id", required: true },
139
+ from: { flag: "--from", required: true },
140
+ mode: { flag: "--mode", default: "focused" },
141
+ allowSyntheticTrial: { flag: "--allow-synthetic-trial", type: "boolean", default: false },
142
+ verifiedAt: { flag: "--verified-at" },
143
+ json: { default: false },
144
+ }),
145
+ };
146
+ }
147
+ if (phase === "auditor-prompt") {
148
+ return {
149
+ ok: true,
150
+ action: "auditor-prompt",
151
+ parsed: parseOptions(args.slice(1), "auditor-prompt", {
152
+ runId: { flag: "--run-id", required: true },
153
+ packetId: { flag: "--packet-id", required: true },
154
+ verifiedAt: { flag: "--verified-at" },
155
+ json: { default: false },
156
+ }),
157
+ };
158
+ }
159
+ if (phase === "finalize") {
160
+ return {
161
+ ok: true,
162
+ action: "finalize",
163
+ parsed: parseOptions(args.slice(1), "finalize", {
164
+ runId: { flag: "--run-id", required: true },
165
+ allowSyntheticCloseout: { flag: "--allow-synthetic-closeout", type: "boolean", default: false },
166
+ verifiedAt: { flag: "--verified-at" },
167
+ json: { default: false },
168
+ }),
169
+ };
170
+ }
171
+ if (phase === "ledger-validate") {
172
+ return {
173
+ ok: true,
174
+ action: "ledger-validate",
175
+ parsed: parseOptions(args.slice(1), "ledger-validate", {
176
+ runId: { flag: "--run-id", required: true },
177
+ verifiedAt: { flag: "--verified-at" },
178
+ json: { default: false },
179
+ }),
180
+ };
181
+ }
182
+ if (phase === "wave-plan") {
183
+ return {
184
+ ok: true,
185
+ action: "wave-plan",
186
+ parsed: parseOptions(args.slice(1), "wave-plan", {
187
+ runId: { flag: "--run-id", required: true },
188
+ topicId: { flag: "--topic-id", required: true },
189
+ allowSyntheticTrial: { flag: "--allow-synthetic-trial", type: "boolean", default: false },
190
+ verifiedAt: { flag: "--verified-at" },
191
+ json: { default: false },
192
+ }),
193
+ };
194
+ }
195
+ if (phase === "fix-topic") {
196
+ return {
197
+ ok: true,
198
+ action: "fix-topic",
199
+ parsed: parseOptions(args.slice(1), "fix-topic", {
200
+ runId: { flag: "--run-id", required: true },
201
+ slug: { flag: "--slug" },
202
+ title: { flag: "--title" },
203
+ admitFirstWave: { flag: "--admit-first-wave", type: "boolean", default: false },
204
+ admitWaveId: { flag: "--admit-wave-id" },
205
+ verifiedAt: { flag: "--verified-at" },
206
+ json: { default: false },
207
+ }),
208
+ };
209
+ }
210
+ return {
211
+ ok: false,
212
+ error: `${localize(
213
+ "nimicoding sweep design refused: expected intake, packet-build, packet-build-batch, auditor-prompt, result-ingest, ledger-validate, finalize, wave-plan, or fix-topic.",
214
+ "nimicoding sweep design 已拒绝:需要 intake、packet-build、packet-build-batch、auditor-prompt、result-ingest、ledger-validate、finalize、wave-plan 或 fix-topic。",
215
+ )}\n`,
216
+ };
217
+ }
218
+
219
+ function emitResult(result, json) {
220
+ if (result.inputError) {
221
+ process.stderr.write(result.error);
222
+ return result.exitCode ?? 2;
223
+ }
224
+ if (!result.ok) {
225
+ process.stderr.write(result.error ?? "nimicoding sweep design failed.\n");
226
+ return result.exitCode ?? 1;
227
+ }
228
+ if (json) {
229
+ process.stdout.write(`${JSON.stringify({ command: "sweep.design", ...result }, null, 2)}\n`);
230
+ } else {
231
+ const lines = ["sweep design result"];
232
+ for (const [label, value] of [
233
+ ["run", result.runId],
234
+ ["inventory", result.inventoryRef],
235
+ ["ledger", result.ledgerRef],
236
+ ["packet", result.packetRef],
237
+ ["auditor prompt", result.promptRef],
238
+ ["auditor result", result.resultRef],
239
+ ["decision queue", result.decisionQueueRef],
240
+ ["final state report", result.finalStateReportRef],
241
+ ["wave plan", result.wavePlanRef],
242
+ ["topic", result.topicRef],
243
+ ["sweep fix source", result.sourceRef],
244
+ ["wave catalog", result.waveCatalogRef],
245
+ ]) {
246
+ if (value !== undefined && value !== null) {
247
+ lines.push(`${label}: ${value}`);
248
+ }
249
+ }
250
+ for (const [label, value] of [
251
+ ["findings", result.findingCount],
252
+ ["finding outcomes", result.findingOutcomeCount],
253
+ ["revision entries", result.revisionEntryCount],
254
+ ["total findings", result.totalFindingCount],
255
+ ["final findings", result.finalFindingCount],
256
+ ["transient findings", result.transientFindingCount],
257
+ ["waves", result.waveCount],
258
+ ["admitted wave", result.admittedWaveId],
259
+ ["stop class", result.stopClass],
260
+ ["stop reason", result.stopReason],
261
+ ]) {
262
+ if (value !== undefined && value !== null) {
263
+ lines.push(`${label}: ${value}`);
264
+ }
265
+ }
266
+ process.stdout.write(`${lines.join("\n")}\n`);
267
+ }
268
+ return result.exitCode ?? 0;
269
+ }
270
+
271
+ export async function runSweepDesign(args) {
272
+ const parsedAction = parseSweepDesignOptions(args);
273
+ if (!parsedAction.ok) {
274
+ process.stderr.write(parsedAction.error);
275
+ return 2;
276
+ }
277
+ if (!parsedAction.parsed.ok) {
278
+ process.stderr.write(parsedAction.parsed.error);
279
+ return 2;
280
+ }
281
+ const options = parsedAction.parsed.options;
282
+ const projectRoot = process.cwd();
283
+ const actions = {
284
+ intake: runIntake,
285
+ "packet-build": runPacketBuild,
286
+ "packet-build-batch": runPacketBuildBatch,
287
+ "auditor-prompt": runAuditorPrompt,
288
+ "result-ingest": runResultIngest,
289
+ "ledger-validate": runLedgerValidate,
290
+ finalize: runFinalize,
291
+ "wave-plan": runWavePlan,
292
+ "fix-topic": runFixTopic,
293
+ };
294
+ return emitResult(await actions[parsedAction.action](projectRoot, options), options.json);
295
+ }
@@ -0,0 +1,22 @@
1
+ import { runAuditSweep } from "./audit-sweep.mjs";
2
+ import { runSweepDesign } from "./sweep-design.mjs";
3
+ import { localize } from "../lib/ui.mjs";
4
+
5
+ export async function runSweep(args) {
6
+ const [command] = args;
7
+ const rest = args.slice(1);
8
+
9
+ if (command === "audit") {
10
+ return runAuditSweep(rest);
11
+ }
12
+
13
+ if (command === "design") {
14
+ return runSweepDesign(rest);
15
+ }
16
+
17
+ process.stderr.write(localize(
18
+ "nimicoding sweep refused: expected `audit` or `design`.\n",
19
+ "nimicoding sweep 已拒绝:需要使用 `audit` 或 `design`。\n",
20
+ ));
21
+ return 2;
22
+ }
@@ -0,0 +1,132 @@
1
+ import { runSeedSync, SYNC_MODE, SYNC_RESULT_STATUS } from "../lib/sync.mjs";
2
+ import { localize } from "../lib/ui.mjs";
3
+
4
+ function parseSyncOptions(args) {
5
+ const options = {
6
+ mode: SYNC_MODE.DRY_RUN,
7
+ json: false,
8
+ };
9
+ let modeSet = false;
10
+
11
+ for (const arg of args) {
12
+ if (arg === "--apply") {
13
+ if (modeSet) {
14
+ return {
15
+ ok: false,
16
+ error: localize(
17
+ "nimicoding sync refused: --apply, --check, and --dry-run are mutually exclusive.\n",
18
+ "nimicoding sync 拒绝执行:--apply、--check、--dry-run 互斥。\n",
19
+ ),
20
+ };
21
+ }
22
+ options.mode = SYNC_MODE.APPLY;
23
+ modeSet = true;
24
+ continue;
25
+ }
26
+ if (arg === "--check") {
27
+ if (modeSet) {
28
+ return {
29
+ ok: false,
30
+ error: localize(
31
+ "nimicoding sync refused: --apply, --check, and --dry-run are mutually exclusive.\n",
32
+ "nimicoding sync 拒绝执行:--apply、--check、--dry-run 互斥。\n",
33
+ ),
34
+ };
35
+ }
36
+ options.mode = SYNC_MODE.CHECK;
37
+ modeSet = true;
38
+ continue;
39
+ }
40
+ if (arg === "--dry-run") {
41
+ if (modeSet) {
42
+ return {
43
+ ok: false,
44
+ error: localize(
45
+ "nimicoding sync refused: --apply, --check, and --dry-run are mutually exclusive.\n",
46
+ "nimicoding sync 拒绝执行:--apply、--check、--dry-run 互斥。\n",
47
+ ),
48
+ };
49
+ }
50
+ options.mode = SYNC_MODE.DRY_RUN;
51
+ modeSet = true;
52
+ continue;
53
+ }
54
+ if (arg === "--json") {
55
+ options.json = true;
56
+ continue;
57
+ }
58
+ return {
59
+ ok: false,
60
+ error: localize(
61
+ `nimicoding sync refused: unknown option ${arg}.\n`,
62
+ `nimicoding sync 拒绝执行:未知选项 ${arg}。\n`,
63
+ ),
64
+ };
65
+ }
66
+
67
+ return { ok: true, options };
68
+ }
69
+
70
+ function formatHumanReport(result) {
71
+ const lines = [];
72
+ lines.push(localize(
73
+ `nimicoding sync (${result.mode})`,
74
+ `nimicoding sync (${result.mode})`,
75
+ ));
76
+ lines.push("");
77
+ lines.push(localize("Summary:", "概览:"));
78
+ lines.push(` total: ${result.summary.total}`);
79
+ lines.push(` in_sync: ${result.summary.in_sync}`);
80
+ if (result.mode === SYNC_MODE.APPLY) {
81
+ lines.push(` created: ${result.summary.created}`);
82
+ lines.push(` updated: ${result.summary.updated}`);
83
+ } else {
84
+ lines.push(` would_create: ${result.summary.would_create}`);
85
+ lines.push(` would_update: ${result.summary.would_update}`);
86
+ }
87
+ lines.push(` drifted_preserved (host-owned seed): ${result.summary.drifted_preserved}`);
88
+ if (result.mode === SYNC_MODE.CHECK) {
89
+ lines.push(` missing_package_canonical: ${result.summary.missing_package_canonical}`);
90
+ lines.push(` missing_host_state_seed: ${result.summary.missing_host_state_seed}`);
91
+ lines.push(` drifted_package_canonical: ${result.summary.drifted_package_canonical}`);
92
+ }
93
+
94
+ const noteworthy = result.results.filter((entry) => entry.status !== SYNC_RESULT_STATUS.IN_SYNC);
95
+ if (noteworthy.length > 0) {
96
+ lines.push("");
97
+ lines.push(localize("Per-file status:", "逐文件状态:"));
98
+ for (const entry of noteworthy) {
99
+ lines.push(` [${entry.status}] (${entry.ownership}) ${entry.outputRelativePath}`);
100
+ }
101
+ }
102
+
103
+ if (result.mode === SYNC_MODE.CHECK && !result.ok) {
104
+ lines.push("");
105
+ lines.push(localize(
106
+ "FAIL: package_canonical drift or missing seed detected; run `nimicoding sync --apply` to refresh.",
107
+ "FAIL:检测到 package_canonical drift 或缺失 seed;执行 `nimicoding sync --apply` 以刷新。",
108
+ ));
109
+ }
110
+
111
+ return `${lines.join("\n")}\n`;
112
+ }
113
+
114
+ export async function runSync(args) {
115
+ const parsed = parseSyncOptions(args);
116
+ if (!parsed.ok) {
117
+ process.stderr.write(parsed.error);
118
+ return 2;
119
+ }
120
+
121
+ const result = await runSeedSync(process.cwd(), parsed.options.mode);
122
+
123
+ if (parsed.options.json) {
124
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
125
+ } else {
126
+ process.stdout.write(formatHumanReport(result));
127
+ }
128
+
129
+ return result.ok ? 0 : 1;
130
+ }
131
+
132
+ export { parseSyncOptions };
@@ -62,16 +62,16 @@ export function formatTopicStatus(report) {
62
62
  ...Object.entries(report.featureFlags).map(([key, value]) => `- ${key}: ${value ? "true" : "false"}`),
63
63
  );
64
64
  }
65
- if (Array.isArray(report.legacyObservedWaves) && report.legacyObservedWaves.length > 0) {
65
+ if (Array.isArray(report.observedWaves) && report.observedWaves.length > 0) {
66
66
  lines.push(
67
67
  "",
68
68
  styleLabel(localize("Observed Waves", "Observed Waves")),
69
- ...report.legacyObservedWaves.slice(0, 8).map((entry) => (
69
+ ...report.observedWaves.slice(0, 8).map((entry) => (
70
70
  `- ${entry.wave_id}: ${entry.observed_lineage} packets=${entry.packets} results=${entry.results} closeouts=${entry.closeouts} exec_packs=${entry.exec_packs}`
71
71
  )),
72
72
  );
73
- if (report.legacyObservedWaves.length > 8) {
74
- lines.push(styleMuted(`- ... ${report.legacyObservedWaves.length - 8} more wave observations`));
73
+ if (report.observedWaves.length > 8) {
74
+ lines.push(styleMuted(`- ... ${report.observedWaves.length - 8} more wave observations`));
75
75
  }
76
76
  }
77
77
  if (report.warnings.length > 0) {
@@ -119,16 +119,16 @@ export function formatTopicValidate(report) {
119
119
  ...Object.entries(report.featureFlags).map(([key, value]) => `- ${key}: ${value ? "true" : "false"}`),
120
120
  );
121
121
  }
122
- if (Array.isArray(report.legacyObservedWaves) && report.legacyObservedWaves.length > 0) {
122
+ if (Array.isArray(report.observedWaves) && report.observedWaves.length > 0) {
123
123
  lines.push(
124
124
  "",
125
125
  styleLabel(localize("Observed Waves", "Observed Waves")),
126
- ...report.legacyObservedWaves.slice(0, 8).map((entry) => (
126
+ ...report.observedWaves.slice(0, 8).map((entry) => (
127
127
  `- ${entry.wave_id}: ${entry.observed_lineage} packets=${entry.packets} results=${entry.results} closeouts=${entry.closeouts} exec_packs=${entry.exec_packs}`
128
128
  )),
129
129
  );
130
- if (report.legacyObservedWaves.length > 8) {
131
- lines.push(styleMuted(`- ... ${report.legacyObservedWaves.length - 8} more wave observations`));
130
+ if (report.observedWaves.length > 8) {
131
+ lines.push(styleMuted(`- ... ${report.observedWaves.length - 8} more wave observations`));
132
132
  }
133
133
  }
134
134
  if (report.warnings.length > 0) {