@peterxiaoyang/superspec 0.1.3 → 0.1.4

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/dist/src/cli.js CHANGED
@@ -8,13 +8,13 @@ export function main(argv = process.argv.slice(2)) {
8
8
  return argparseExit;
9
9
  args = parse_argv(argv);
10
10
  const [decision] = dispatch(args);
11
- printDecision(decision, { command: args.command });
11
+ printDecision(decision, { command: args.command, format: args.format });
12
12
  return decision.allowed ? 0 : 1;
13
13
  }
14
14
  catch (err) {
15
15
  const change = args?.change ?? "?";
16
16
  const errReason = err instanceof GuardError ? reason("guard_error", err.message) : reason("guard_internal_error", `${err.name}: ${err.message}`);
17
- printDecision(block(change, "guard_error", [errReason]), { command: args?.command });
17
+ printDecision(block(change, "guard_error", [errReason]), { command: args?.command, format: args?.format });
18
18
  return 2;
19
19
  }
20
20
  }
@@ -1,6 +1,8 @@
1
+ import { type DecisionOutputFormat } from "./util.ts";
1
2
  export type ParsedArgs = {
2
3
  command: string;
3
4
  change: string;
5
+ format?: DecisionOutputFormat;
4
6
  artifact?: string;
5
7
  gate?: string;
6
8
  task_id?: string;
@@ -1,4 +1,5 @@
1
1
  import { command_zh } from "./i18n.js";
2
+ import { GuardError, parseDecisionOutputFormat } from "./util.js";
2
3
  const SIMPLE_COMMANDS = [
3
4
  "status",
4
5
  "recompute",
@@ -35,7 +36,7 @@ function requiredBooleanFlags(command) {
35
36
  return command === "init" ? ["--create"] : [];
36
37
  }
37
38
  function optionalBooleanFlags(command) {
38
- return command === "recompute" ? ["--force-unlock", "--rebuild-corrupt"] : [];
39
+ return ["--user-facing", ...(command === "recompute" ? ["--force-unlock", "--rebuild-corrupt"] : [])];
39
40
  }
40
41
  function rootUsage() {
41
42
  return `usage: superspec_guard [-h]\n {${COMMAND_LIST}}\n ...\n`;
@@ -51,6 +52,7 @@ function commandUsage(command) {
51
52
  const usageFlags = [
52
53
  "[-h]",
53
54
  ...requiredValueFlags(command).map((flag) => `${flag} ${flag.slice(2).replace(/-/g, "_").toUpperCase()}`),
55
+ "[--format {json,agent,user}]",
54
56
  ...requiredBooleanFlags(command),
55
57
  ...optionalBooleanFlags(command),
56
58
  ];
@@ -66,6 +68,7 @@ function commandHelp(command) {
66
68
  for (const flag of requiredBooleanFlags(command)) {
67
69
  lines.push(` ${flag}\n`);
68
70
  }
71
+ lines.push(" --format {json,agent,user}\n");
69
72
  for (const flag of optionalBooleanFlags(command)) {
70
73
  lines.push(` ${flag}\n`);
71
74
  }
@@ -108,37 +111,51 @@ export function parse_argv(argv) {
108
111
  throw new Error("缺少命令");
109
112
  const command = argv[0];
110
113
  const args = argv.slice(1);
111
- const getValue = (flag) => {
112
- const idx = args.indexOf(flag);
113
- if (idx === -1)
114
- return undefined;
115
- return args[idx + 1];
114
+ const getValues = (flag) => {
115
+ const values = [];
116
+ for (let idx = 0; idx < args.length; idx += 1) {
117
+ if (args[idx] !== flag)
118
+ continue;
119
+ const value = args[idx + 1];
120
+ if (value === undefined || value.startsWith("--"))
121
+ throw new GuardError(`${flag} 缺少取值`);
122
+ values.push(value);
123
+ }
124
+ return values;
116
125
  };
126
+ const getValue = (flag) => getValues(flag)[0];
127
+ const formatValues = getValues("--format");
128
+ for (const value of formatValues)
129
+ parseDecisionOutputFormat(value);
130
+ const selectedFormat = hasFlag(args, "--user-facing")
131
+ ? "user"
132
+ : (formatValues.length > 0 ? formatValues[formatValues.length - 1] : "json");
133
+ const format = parseDecisionOutputFormat(selectedFormat);
117
134
  const change = getValue("--change");
118
135
  if (!change)
119
136
  throw new Error("缺少必填参数 --change");
120
137
  if (command === "init") {
121
138
  if (!hasFlag(args, "--create"))
122
139
  throw new Error("缺少必填参数 --create");
123
- return { command, change, create: true };
140
+ return { command, change, format, create: true };
124
141
  }
125
142
  if (command === "check-artifact") {
126
143
  const artifact = getValue("--artifact");
127
144
  if (!artifact)
128
145
  throw new Error("缺少必填参数 --artifact");
129
- return { command, change, artifact };
146
+ return { command, change, format, artifact };
130
147
  }
131
148
  if (command === "check-enter") {
132
149
  const gate = getValue("--gate");
133
150
  if (!gate)
134
151
  throw new Error("缺少必填参数 --gate");
135
- return { command, change, gate };
152
+ return { command, change, format, gate };
136
153
  }
137
154
  if (command === "check-task-reopen" || command === "check-task-edit" || command === "check-task-complete") {
138
155
  const taskId = getValue("--task-id");
139
156
  if (!taskId)
140
157
  throw new Error("缺少必填参数 --task-id");
141
- return { command, change, task_id: taskId };
158
+ return { command, change, format, task_id: taskId };
142
159
  }
143
160
  const simple = new Set(SIMPLE_COMMANDS);
144
161
  if (!simple.has(command))
@@ -146,6 +163,7 @@ export function parse_argv(argv) {
146
163
  return {
147
164
  command,
148
165
  change,
166
+ format,
149
167
  force_unlock: command === "recompute" && hasFlag(args, "--force-unlock"),
150
168
  rebuild_corrupt: command === "recompute" && hasFlag(args, "--rebuild-corrupt"),
151
169
  };
@@ -1,10 +1,11 @@
1
- import { runCommand, type OpenspecCliProbe } from "./core.ts";
1
+ import { runCommand, type DecisionOutputFormat, type OpenspecCliProbe } from "./core.ts";
2
2
  import { type InstallScope } from "./install_engine.ts";
3
3
  type InitArgs = {
4
4
  path: string;
5
5
  codexHome: string;
6
6
  mode: "install" | "update" | "uninstall";
7
7
  scope: InstallScope | null;
8
+ format: DecisionOutputFormat;
8
9
  dryRun: boolean;
9
10
  force: boolean;
10
11
  };
@@ -1,4 +1,4 @@
1
- import { block, commandExists, GuardError, printDecision, reason, runCommand, openspec_cli_probe, REQUIRED_OPENSPEC_MIN_VERSION, } from "./core.js";
1
+ import { block, commandExists, GuardError, printDecision, reason, runCommand, openspec_cli_probe, parseDecisionOutputFormat, REQUIRED_OPENSPEC_MIN_VERSION, } from "./core.js";
2
2
  import { forced_openspec_install_plan, project_init, recommended_openspec_install_plan } from "./project_init.js";
3
3
  import { install_workflow, uninstall_workflow, update_workflow } from "./install_engine.js";
4
4
  import { homedir } from "node:os";
@@ -6,21 +6,25 @@ import { resolve } from "node:path";
6
6
  import { createInterface } from "node:readline/promises";
7
7
  import { system_failure_zh } from "./i18n.js";
8
8
  function usage() {
9
- return "usage: superspec init [-h] [--scope {project,user}] [--path PATH] [--codex-home PATH] [--create] [--update] [--uninstall] [--dry-run] [--force]\n";
9
+ return "usage: superspec init [-h] [--scope {project,user}] [--path PATH] [--codex-home PATH] [--format {json,agent,user}] [--create] [--update] [--uninstall] [--dry-run] [--force]\n";
10
10
  }
11
11
  function help() {
12
- return `${usage()}\n可选参数:\n -h, --help 显示帮助并退出\n --scope {project,user} 安装到当前项目的 .codex 目录,或安装到用户级 Codex 目录(默认:project)\n --project 等价于 --scope project\n --user 等价于 --scope user\n --global 兼容别名,等价于 --user\n --path PATH --scope project 时使用的项目根目录(默认:当前目录)\n --codex-home PATH --scope user 时使用的 Codex 用户目录(默认:$CODEX_HOME 或 ~/.codex)\n --create 兼容参数;init 默认就会创建缺失内容\n --update 按 manifest 更新 SuperSpec 管理的文件;用户改动文件保留,新的版本写入 *.new\n --uninstall 按 manifest 卸载 SuperSpec 管理的文件;.superspec 数据与既有/用户改动文件会保留\n --dry-run 配合 --uninstall 时只预览将删除的文件,不实际修改\n --force 安装时覆盖已有且内容不同的文件,并保留 *.bak 备份\n`;
12
+ return `${usage()}\n可选参数:\n -h, --help 显示帮助并退出\n --scope {project,user} 安装到当前项目的 .codex 目录,或安装到用户级 Codex 目录(默认:project)\n --project 等价于 --scope project\n --user 等价于 --scope user\n --global 兼容别名,等价于 --user\n --path PATH --scope project 时使用的项目根目录(默认:当前目录)\n --codex-home PATH --scope user 时使用的 Codex 用户目录(默认:$CODEX_HOME 或 ~/.codex)\n --format {json,agent,user} 输出格式;json 用于诊断,agent/user 用于安全展示\n --user-facing 等价于 --format user\n --create 兼容参数;init 默认就会创建缺失内容\n --update 按 manifest 更新 SuperSpec 管理的文件;用户改动文件保留,新的版本写入 *.new\n --uninstall 按 manifest 卸载 SuperSpec 管理的文件;.superspec 数据与既有/用户改动文件会保留\n --dry-run 配合 --uninstall 时只预览将删除的文件,不实际修改\n --force 安装时覆盖已有且内容不同的文件,并保留 *.bak 备份\n`;
13
13
  }
14
14
  function parse_init_argv(argv) {
15
- const getValue = (flag) => {
16
- const idx = argv.indexOf(flag);
17
- if (idx === -1)
18
- return undefined;
19
- const value = argv[idx + 1];
20
- if (value === undefined || value.startsWith("--"))
21
- throw new GuardError(`${flag} 缺少取值`);
22
- return value;
15
+ const getValues = (flag) => {
16
+ const values = [];
17
+ for (let idx = 0; idx < argv.length; idx += 1) {
18
+ if (argv[idx] !== flag)
19
+ continue;
20
+ const value = argv[idx + 1];
21
+ if (value === undefined || value.startsWith("--"))
22
+ throw new GuardError(`${flag} 缺少取值`);
23
+ values.push(value);
24
+ }
25
+ return values;
23
26
  };
27
+ const getValue = (flag) => getValues(flag)[0];
24
28
  const update = argv.includes("--update");
25
29
  const uninstall = argv.includes("--uninstall");
26
30
  if (update && uninstall)
@@ -36,11 +40,19 @@ function parse_init_argv(argv) {
36
40
  scope = "user";
37
41
  if (argv.includes("--project"))
38
42
  scope = "project";
43
+ const formatValues = getValues("--format");
44
+ for (const value of formatValues)
45
+ parseDecisionOutputFormat(value);
46
+ const selectedFormat = argv.includes("--user-facing")
47
+ ? "user"
48
+ : (formatValues.length > 0 ? formatValues[formatValues.length - 1] : "json");
49
+ const format = parseDecisionOutputFormat(selectedFormat);
39
50
  return {
40
51
  path: resolve(getValue("--path") ?? process.cwd()),
41
52
  codexHome: resolve(getValue("--codex-home") ?? process.env.CODEX_HOME ?? joinHomeCodex()),
42
53
  mode: uninstall ? "uninstall" : update ? "update" : "install",
43
54
  scope,
55
+ format,
44
56
  dryRun: argv.includes("--dry-run"),
45
57
  force: argv.includes("--force"),
46
58
  };
@@ -149,7 +161,7 @@ function openspecPreflightBlocked(args, scope, installResult) {
149
161
  project_root: args.path,
150
162
  install_scope: scope,
151
163
  install_root: targetRoot,
152
- }, { command: "init" });
164
+ }, { command: "init", format: args.format });
153
165
  return 1;
154
166
  }
155
167
  function run_init(args, scope) {
@@ -176,16 +188,17 @@ function run_init(args, scope) {
176
188
  }
177
189
  summary.install_scope = scope;
178
190
  summary.install_root = targetRoot;
179
- printDecision(summary, { command: "init" });
191
+ printDecision(summary, { command: "init", format: args.format });
180
192
  return summary.allowed ? 0 : 1;
181
193
  }
182
194
  export function main_init(argv = process.argv.slice(2)) {
195
+ let args = null;
183
196
  try {
184
197
  if (argv.includes("-h") || argv.includes("--help")) {
185
198
  process.stdout.write(help());
186
199
  return 0;
187
200
  }
188
- const args = parse_init_argv(argv);
201
+ args = parse_init_argv(argv);
189
202
  const scope = args.scope ?? "project";
190
203
  const openspecInstall = maybe_install_missing_openspec({ cwd: args.path, scope, mode: args.mode });
191
204
  if (openspecInstall === "failed" || openspecInstall === "skipped")
@@ -195,17 +208,18 @@ export function main_init(argv = process.argv.slice(2)) {
195
208
  catch (err) {
196
209
  const change = "project";
197
210
  const errReason = err instanceof GuardError ? reason("guard_error", err.message) : reason("guard_internal_error", `${err.name}: ${err.message}`);
198
- printDecision(block(change, "guard_error", [errReason]), { command: "init" });
211
+ printDecision(block(change, "guard_error", [errReason]), { command: "init", format: args?.format });
199
212
  return 2;
200
213
  }
201
214
  }
202
215
  export async function main_init_async(argv = process.argv.slice(2)) {
216
+ let args = null;
203
217
  try {
204
218
  if (argv.includes("-h") || argv.includes("--help")) {
205
219
  process.stdout.write(help());
206
220
  return 0;
207
221
  }
208
- const args = parse_init_argv(argv);
222
+ args = parse_init_argv(argv);
209
223
  const scope = args.scope ?? (canPrompt() ? await promptInstallScope() : "project");
210
224
  const openspecInstall = maybe_install_missing_openspec({ cwd: args.path, scope, mode: args.mode });
211
225
  if (openspecInstall === "failed" || openspecInstall === "skipped")
@@ -215,7 +229,7 @@ export async function main_init_async(argv = process.argv.slice(2)) {
215
229
  catch (err) {
216
230
  const change = "project";
217
231
  const errReason = err instanceof GuardError ? reason("guard_error", err.message) : reason("guard_internal_error", `${err.name}: ${err.message}`);
218
- printDecision(block(change, "guard_error", [errReason]), { command: "init" });
232
+ printDecision(block(change, "guard_error", [errReason]), { command: "init", format: args?.format });
219
233
  return 2;
220
234
  }
221
235
  }
@@ -39,6 +39,8 @@ export type Decision = {
39
39
  trust_warnings_zh?: string[];
40
40
  workflow_terms_zh?: WorkflowTermHint[];
41
41
  };
42
+ export type DecisionOutputFormat = "json" | "agent" | "user";
43
+ export type AgentWorkflowAction = "continue" | "fix_artifacts" | "ask_user_confirmation" | "collect_review_evidence" | "collect_test_evidence" | "repair_evidence" | "rerun_check" | "inspect_diagnostics";
42
44
  export type TaskInfo = {
43
45
  task_id: string;
44
46
  checked: boolean;
@@ -82,6 +84,7 @@ export declare const GATE_ROUTE: Record<string, string>;
82
84
  export declare class GuardError extends Error {
83
85
  }
84
86
  export declare const runtime: JsonMap;
87
+ export declare function parseDecisionOutputFormat(raw: string): DecisionOutputFormat;
85
88
  export declare function reason(code: string, message: string, refs?: string[] | null): Reason;
86
89
  export declare function pinned_ref_key(item: JsonMap): string;
87
90
  export declare function trustWarnings(): string[];
@@ -99,8 +102,16 @@ export declare function block(change: string, gate: string, reasons: Reason[], o
99
102
  export declare function decorateDecision(decision: JsonMap, opts?: {
100
103
  command?: string;
101
104
  }): JsonMap;
105
+ export declare function workflowActionForReasonCodes(reasonCodes: string[], allowed?: boolean): AgentWorkflowAction;
106
+ export declare function renderAgentDecision(decision: JsonMap, opts?: {
107
+ command?: string;
108
+ }): JsonMap;
109
+ export declare function renderUserFacingDecision(decision: JsonMap, opts?: {
110
+ command?: string;
111
+ }): string;
102
112
  export declare function printDecision(decision: JsonMap, opts?: {
103
113
  command?: string;
114
+ format?: DecisionOutputFormat;
104
115
  }): void;
105
116
  export declare function runCommand(cmd: string, args: string[], opts?: {
106
117
  cwd?: string;
package/dist/src/util.js CHANGED
@@ -236,6 +236,11 @@ export const GATE_ROUTE = {
236
236
  export class GuardError extends Error {
237
237
  }
238
238
  export const runtime = {};
239
+ export function parseDecisionOutputFormat(raw) {
240
+ if (raw === "json" || raw === "agent" || raw === "user")
241
+ return raw;
242
+ throw new GuardError("--format 只允许 json、agent 或 user");
243
+ }
239
244
  export function reason(code, message, refs = null) {
240
245
  const zh = reason_zh(code);
241
246
  return { code, message, refs: refs ?? [], label_zh: zh.label_zh, hint_zh: zh.hint_zh };
@@ -370,8 +375,217 @@ function sanitizeDecisionForOutput(decision) {
370
375
  actions,
371
376
  };
372
377
  }
378
+ function userFacingLine(value) {
379
+ return String(value ?? "").replace(/\s+/gu, " ").trim();
380
+ }
381
+ const SAFE_TEXT_REPLACEMENTS = [
382
+ [/\bneeds_user_decision_pending\b/giu, "等待用户确认"],
383
+ [/\bneeds_user_decision\b/giu, "等待用户确认"],
384
+ [/\buser_review_decision\b/giu, "用户确认记录"],
385
+ [/\bmain_review_digest\b/giu, "审查问题记录"],
386
+ [/\breview_standing_authorization\b/giu, "长期授权记录"],
387
+ [/\bdecision_scope_key\b/giu, "确认范围"],
388
+ [/\bfinding_uid\b/giu, "问题标识"],
389
+ [/\bexport\s+function\s+[A-Za-z_$][\w$]*\s*\([^)]*\)/gu, "内部实现细节"],
390
+ [/\bfunction\s+[A-Za-z_$][\w$]*\s*\([^)]*\)/gu, "内部实现细节"],
391
+ [/\b[A-Za-z_$][\w$]*\s*\([^)]*\)/gu, "内部调用细节"],
392
+ [/裁决/gu, "确认"],
393
+ ];
394
+ function safeDisplayText(value) {
395
+ let out = userFacingLine(value);
396
+ for (const [pattern, replacement] of SAFE_TEXT_REPLACEMENTS)
397
+ out = out.replace(pattern, replacement);
398
+ return out;
399
+ }
400
+ function fallbackReasonForWorkflowAction(action) {
401
+ const fallbacks = {
402
+ continue: "当前检查已通过。",
403
+ ask_user_confirmation: "需要用户确认后才能继续。",
404
+ collect_review_evidence: "需要补齐审查或验证复核记录。",
405
+ collect_test_evidence: "需要补齐测试或校验证据。",
406
+ fix_artifacts: "需要修正方案、任务或证据结构。",
407
+ repair_evidence: "需要修复证据记录。",
408
+ rerun_check: "需要重新运行当前检查。",
409
+ inspect_diagnostics: "需要查看诊断输出后处理。",
410
+ };
411
+ return fallbacks[action];
412
+ }
413
+ function safeReasonText(_item, action = "inspect_diagnostics") {
414
+ return fallbackReasonForWorkflowAction(action);
415
+ }
416
+ const WORKFLOW_ACTION_BY_REASON = [
417
+ [new Set([
418
+ "needs_user_decision_pending",
419
+ "user_decision_unbound",
420
+ "missing_review_digest",
421
+ "missing_human_confirmation",
422
+ "apply_isolation_unconfirmed",
423
+ "scope_expansion_unconfirmed",
424
+ "finding_unresolved",
425
+ "round_budget_exhausted",
426
+ ]), "ask_user_confirmation"],
427
+ [new Set([
428
+ "missing_source_guidance",
429
+ "missing_verification_review",
430
+ "missing_final_verification_review",
431
+ "missing_roles",
432
+ "missing_native_subagent_evidence",
433
+ "missing_invariant_review",
434
+ "missing_test_contract_review",
435
+ "missing_architect_review",
436
+ "missing_critic_review",
437
+ "missing_test-engineer_review",
438
+ "missing_code-reviewer_review",
439
+ "missing_verifier_review",
440
+ "missing_main_adjudication",
441
+ "proposal_reviewed_failed",
442
+ "review_not_ready",
443
+ "missing_proposal_review",
444
+ ]), "collect_review_evidence"],
445
+ [new Set([
446
+ "missing_red_evidence",
447
+ "missing_green_evidence",
448
+ "missing_characterization",
449
+ "test_contract_not_honored",
450
+ "validate_failed",
451
+ "missing_final_tests",
452
+ "verify_failure_unconfirmed",
453
+ ]), "collect_test_evidence"],
454
+ [new Set([
455
+ "missing_discovery",
456
+ "missing_proposal",
457
+ "missing_design",
458
+ "missing_tasks",
459
+ "invalid_task_graph",
460
+ "invalid_business_invariants",
461
+ "review_finding_invalid",
462
+ "review_digest_invalid",
463
+ "user_decision_invalid",
464
+ "human_confirmation_invalid",
465
+ "standing_authorization_invalid",
466
+ "evidence_unknown_kind",
467
+ "evidence_missing_field",
468
+ "artifact_update_required",
469
+ "rereview_required",
470
+ ]), "fix_artifacts"],
471
+ [new Set([
472
+ "state_concurrent_update",
473
+ "state_fingerprint_stale",
474
+ ]), "rerun_check"],
475
+ [new Set([
476
+ "state_corrupt",
477
+ "openspec_cli_unavailable",
478
+ "openspec_native_surface_missing",
479
+ "dirty_worktree_unavailable",
480
+ "guard_error",
481
+ "guard_internal_error",
482
+ "unknown_gate",
483
+ "unknown_artifact",
484
+ "not_openspec_artifact",
485
+ ]), "inspect_diagnostics"],
486
+ ];
487
+ export function workflowActionForReasonCodes(reasonCodes, allowed = false) {
488
+ if (allowed)
489
+ return "continue";
490
+ const codes = new Set(reasonCodes);
491
+ for (const [matches, action] of WORKFLOW_ACTION_BY_REASON) {
492
+ for (const code of matches) {
493
+ if (codes.has(code))
494
+ return action;
495
+ }
496
+ }
497
+ return "inspect_diagnostics";
498
+ }
499
+ function summaryForWorkflowAction(action, allowed) {
500
+ if (allowed || action === "continue")
501
+ return "当前检查已通过,可以继续下一步。";
502
+ const summaries = {
503
+ ask_user_confirmation: "当前阶段需要用户确认一个范围或处理方式选择后才能继续。",
504
+ collect_review_evidence: "当前阶段缺少必要审查或验证复核记录,补齐后再继续。",
505
+ collect_test_evidence: "当前阶段缺少测试或校验证据,补齐后再继续。",
506
+ fix_artifacts: "当前阶段的方案、任务或证据结构需要修正后再继续。",
507
+ repair_evidence: "当前证据记录需要修复后再继续。",
508
+ rerun_check: "当前检查需要在输入稳定后重新运行。",
509
+ inspect_diagnostics: "当前检查需要查看诊断输出后处理。",
510
+ };
511
+ return summaries[action];
512
+ }
513
+ function nextStepsForWorkflowAction(action, allowed) {
514
+ if (allowed || action === "continue")
515
+ return ["继续执行下一步。"];
516
+ const steps = {
517
+ ask_user_confirmation: ["向用户展示待确认的问题与选项,记录选择后重新运行检查。"],
518
+ collect_review_evidence: ["补齐所需审查或验证复核记录,然后重新运行检查。"],
519
+ collect_test_evidence: ["补齐失败/通过测试或校验证据,然后重新运行检查。"],
520
+ fix_artifacts: ["修正相关方案、任务或证据结构,然后重新运行检查。"],
521
+ repair_evidence: ["修复证据记录中的结构或引用问题,然后重新运行检查。"],
522
+ rerun_check: ["等待输入稳定后重新运行当前检查。"],
523
+ inspect_diagnostics: ["使用诊断输出查看内部细节,再按对应问题处理。"],
524
+ };
525
+ return steps[action];
526
+ }
527
+ const DIAGNOSTIC_HINT = "需要排查内部细节时使用 --format json。";
528
+ export function renderAgentDecision(decision, opts = {}) {
529
+ const decorated = sanitizeDecisionForOutput(decorateDecision(decision, opts));
530
+ const reasons = Array.isArray(decorated.block_reasons) ? decorated.block_reasons : [];
531
+ const reasonCodes = reasons.map((item) => String(item.code ?? ""));
532
+ const allowed = Boolean(decorated.allowed);
533
+ const workflowAction = workflowActionForReasonCodes(reasonCodes, allowed);
534
+ const renderedReasons = reasons.map((item) => safeReasonText(item, workflowAction)).filter(Boolean);
535
+ return {
536
+ allowed,
537
+ status: allowed ? "allowed" : "blocked",
538
+ workflow_action: workflowAction,
539
+ stage_label_zh: safeDisplayText(decorated.gate_label_zh) || "当前阶段",
540
+ check_label_zh: safeDisplayText(decorated.command_label_zh) || "当前检查",
541
+ summary_zh: safeDisplayText(summaryForWorkflowAction(workflowAction, allowed)),
542
+ reasons_zh: renderedReasons.length > 0 ? renderedReasons : undefined,
543
+ next_steps_zh: nextStepsForWorkflowAction(workflowAction, allowed).map(safeDisplayText),
544
+ diagnostic_hint: DIAGNOSTIC_HINT,
545
+ };
546
+ }
547
+ export function renderUserFacingDecision(decision, opts = {}) {
548
+ const decorated = sanitizeDecisionForOutput(decorateDecision(decision, opts));
549
+ const agentView = renderAgentDecision(decorated, opts);
550
+ const gate = safeDisplayText(decorated.gate_label_zh) || "当前检查";
551
+ const command = safeDisplayText(decorated.command_label_zh);
552
+ const lines = [];
553
+ if (decorated.allowed) {
554
+ lines.push(`检查通过:${gate}。`);
555
+ }
556
+ else {
557
+ lines.push(`暂时不能继续:${gate}。`);
558
+ }
559
+ if (command && command !== gate) {
560
+ lines.push(`检查项:${command}。`);
561
+ }
562
+ const reasons = Array.isArray(decorated.block_reasons) ? decorated.block_reasons : [];
563
+ if (!decorated.allowed && reasons.length > 0) {
564
+ lines.push("原因:");
565
+ for (const item of reasons) {
566
+ const reasonText = safeReasonText(item, agentView.workflow_action);
567
+ lines.push(`- ${reasonText}`);
568
+ }
569
+ }
570
+ const nextActions = Array.isArray(agentView.next_steps_zh) ? agentView.next_steps_zh.map(safeDisplayText).filter(Boolean) : [];
571
+ if (nextActions.length > 0) {
572
+ lines.push("下一步:");
573
+ for (const action of nextActions)
574
+ lines.push(`- ${action}`);
575
+ }
576
+ lines.push(`诊断:${DIAGNOSTIC_HINT}`);
577
+ return `${lines.join("\n")}\n`;
578
+ }
373
579
  export function printDecision(decision, opts = {}) {
374
580
  const decorated = decorateDecision(decision, opts);
581
+ if (opts.format === "user") {
582
+ process.stdout.write(renderUserFacingDecision(decorated, opts));
583
+ return;
584
+ }
585
+ if (opts.format === "agent") {
586
+ process.stdout.write(`${JSON.stringify(renderAgentDecision(decorated, opts), null, 2)}\n`);
587
+ return;
588
+ }
375
589
  process.stdout.write(`${JSON.stringify(sanitizeDecisionForOutput(decorated), null, 2)}\n`);
376
590
  }
377
591
  export function runCommand(cmd, args, opts = {}) {
package/dist/superspec.js CHANGED
@@ -30,16 +30,16 @@ function help() {
30
30
  " version print SuperSpec CLI version",
31
31
  "",
32
32
  "examples:",
33
- " superspec init --scope project",
33
+ " superspec init --scope project --format agent",
34
34
  " superspec init --scope user",
35
- " superspec guard check-init --change <change>",
35
+ " superspec guard check-init --change <change> --format agent",
36
36
  " superspec doctor",
37
37
  "",
38
38
  ].join("\n");
39
39
  }
40
40
  function updateHelp() {
41
41
  return [
42
- "usage: superspec update [--scope {project,user}] [--path PATH] [--codex-home PATH] [--local-only]",
42
+ "usage: superspec update [--scope {project,user}] [--path PATH] [--codex-home PATH] [--format {json,agent,user}] [--local-only]",
43
43
  "",
44
44
  "updates the global SuperSpec CLI from npm, then updates manifest-managed SuperSpec surfaces.",
45
45
  "",
@@ -49,6 +49,7 @@ function updateHelp() {
49
49
  " --user, --global equivalent to --scope user",
50
50
  " --path PATH project root for project scope (default: current directory)",
51
51
  " --codex-home PATH Codex user home for user scope (default: $CODEX_HOME or ~/.codex)",
52
+ " --format {json,agent,user} output format; use agent for workflow consumption",
52
53
  " --local-only skip npm self-update and use the currently installed package",
53
54
  " -h, --help show this help",
54
55
  "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peterxiaoyang/superspec",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "SuperSpec workflow package: guard runtime, generic workflow templates, and Codex adapter payload.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,6 +16,8 @@ metadata:
16
16
  - 对话窗口里的解释、总结、提问和下一步说明必须使用中文;除命令、路径、字段名、代码标识符外,不要夹带英文说明词。
17
17
  - 对话窗口、AskUserQuestion 文案、进度更新和最终总结不得裸露内部证据种类、字段名或 reason code;用户确认记录、审查问题记录、审查轮次编号、问题唯一标识等都只用中文业务说法。原始协议名只允许写在证据 JSON、代码、测试、精确命令输出或用户明确要求的诊断片段中。
18
18
  - 本 skill 文档中的内部协议名只用于落盘证据或运行 guard;写给用户时必须先翻译成中文业务动作,例如“记录用户确认”“记录审查问题”“完成最终审查判断”。
19
+ - 用户可见文案不得使用“裁决”描述用户动作;统一说“确认”“范围取舍”“处理方式选择”或“用户确认记录”。
20
+ - 普通 workflow 命令使用 `--format agent` 读取 guard/init 输出;`--format json` 只用于诊断 evidence/schema/guard 内部,不得作为默认模型上下文或直接转述给用户。
19
21
  - 向用户转述 guard / review 输出时,不要直接贴英文 `message`、`next_allowed_actions` 或英文模板标题;应改写为中文,并仅在需要定位内部协议时保留英文 code/command 于反引号中。
20
22
 
21
23
  ## 命令执行 / Shell
@@ -53,7 +55,7 @@ metadata:
53
55
  2. 当 dirty worktree、untracked files 或 branch state 需要确认时,使用 AskUserQuestion 处理分支状态。
54
56
  3. 验证 apply readiness:
55
57
  ```text
56
- superspec guard check-apply-ready --change "<change>"
58
+ superspec guard check-apply-ready --change "<change>" --format agent
57
59
  ```
58
60
  4. 获取 native apply context 和 task list:
59
61
  ```text
@@ -65,21 +67,21 @@ metadata:
65
67
  - 若 `request_changes_route:"reopen_tasks"`,读取 `reopen_task_ids`,逐个判断当前 task 所处阶段:
66
68
  - 如果该 task 仍是 `[x]`,说明还处于首次回退前;先由主流程基于本轮 review 的结构化 output 写出该 task 的 `task_reopen` evidence 与配套 `status:"superseded"` evidence,形成完整 reopen package,再执行:
67
69
  ```text
68
- superspec guard check-task-reopen --change "<change>" --task-id "<task-id>"
70
+ superspec guard check-task-reopen --change "<change>" --task-id "<task-id>" --format agent
69
71
  ```
70
72
  只有该 guard `allow` 后,才允许把对应 task 从 `- [x]` 改为 `- [ ]`,并把它重新纳入本轮 apply。
71
73
  - 如果该 task 已经是 `[ ]`,且当前 `tasks.md` 已匹配授权后的 `after_tasks_sha256`,说明它已经处于合法 reopened apply;此时直接续跑 `check-task-edit -> RED/GREEN -> check-task-complete`,不要重复创建 `task_reopen`,也不要再次执行 pre-revert `check-task-reopen`。
72
74
  - 若 `request_changes_route:"change_update"`,停止 apply,回 propose / change update;不要试图通过 reopen 继续实现。
73
75
  6. 对每个 pending task(包括刚刚合法 reopen 的 task),在任何实现编辑前执行任务编辑前检查(`check-task-edit`):
74
76
  ```text
75
- superspec guard check-task-edit --change "<change>" --task-id "<task-id>"
77
+ superspec guard check-task-edit --change "<change>" --task-id "<task-id>" --format agent
76
78
  ```
77
79
  7. 在 runtime/business implementation edits 前产出 RED evidence,除非有允许的 `no_tdd_reason` 或处于现状锁定测试模式(`characterization mode`)。这里的 `characterization` 指“先把当前真实行为测出来并锁住,重构后保持一致”。RED/GREEN evidence 必须引用 task 的 `test_refs`,并在 task 声明 `invariant_refs` 时同步记录 `invariant_refs`。若 task 来自 reopen,本轮 successor GREEN / alternative verification / manual verification 必须携带同一 `reopen_id`。
78
80
  8. 按 native dynamic instruction 和 `contextFiles` 指引,实现最小 task scope。
79
81
  9. 产出 GREEN evidence,保留 `test_id`、`invariant_refs`、命令、输出摘要和 raw log ref;raw log ref 指向原始输出文件,evidence 中只写必要摘要,不复制完整日志。
80
82
  10. 勾选 task 前执行任务完成检查(`check-task-complete`):
81
83
  ```text
82
- superspec guard check-task-complete --change "<change>" --task-id "<task-id>"
84
+ superspec guard check-task-complete --change "<change>" --task-id "<task-id>" --format agent
83
85
  ```
84
86
  然后按 native apply semantics 将 task 从 `- [ ]` 改为 `- [x]`。
85
87
  11. 如果该 task 来自 reopen,在重新勾回 `[x]` 后写入 `kind:"task_reopen_resolved"` evidence,关闭本轮 reopen 授权;不要复用旧 reopen 生命周期。reopen 只授权:
@@ -16,6 +16,8 @@ metadata:
16
16
  - 对话窗口里的解释、确认、总结和下一步说明必须使用中文;除命令、路径、字段名、代码标识符外,不要夹带英文说明词。
17
17
  - 对话窗口、AskUserQuestion 文案、进度更新和最终总结不得裸露内部证据种类、字段名或 reason code;用户确认记录、审查问题记录、审查轮次编号、问题唯一标识等都只用中文业务说法。原始协议名只允许写在证据 JSON、代码、测试、精确命令输出或用户明确要求的诊断片段中。
18
18
  - 本 skill 文档中的内部协议名只用于落盘证据或运行 guard;写给用户时必须先翻译成中文业务动作,例如“记录用户确认”“记录审查问题”“完成最终审查判断”。
19
+ - 用户可见文案不得使用“裁决”描述用户动作;统一说“确认”“范围取舍”“处理方式选择”或“用户确认记录”。
20
+ - 普通 workflow 命令使用 `--format agent` 读取 guard/init 输出;`--format json` 只用于诊断 evidence/schema/guard 内部,不得作为默认模型上下文或直接转述给用户。
19
21
  - 向用户转述 guard / archive 输出时,不要直接贴英文 `message`、`next_allowed_actions` 或英文模板标题;应改写为中文,并仅在需要定位内部协议时保留英文 code/command 于反引号中。
20
22
 
21
23
  ## 命令执行 / Shell
@@ -44,7 +46,7 @@ metadata:
44
46
  1. 对 `archive_ready` 最终确认使用 AskUserQuestion,等待明确选择。记录 archive-scoped human-confirmation evidence。当前 v1 不询问也不使用 `--skip-specs`;若 change 不应同步 specs,应先回到 propose/change update 调整 OpenSpec 包,而不是在 archive 阶段跳过。
45
47
  2. 检查 archive readiness 并生成 preservation manifest:
46
48
  ```text
47
- superspec guard check-archive-ready --change "<change>"
49
+ superspec guard check-archive-ready --change "<change>" --format agent
48
50
  ```
49
51
  生成的 manifest 是 archive 前证据快照,必须能追踪 business-invariants、test-contract 和对应 invariant review evidence 的 sha256。
50
52
  3. 运行 native OpenSpec archive(移动 change、同步 delta->main specs、执行 validation):
@@ -53,7 +55,7 @@ metadata:
53
55
  ```
54
56
  4. 根据 manifest 验证 archived `.superspec/` preservation:
55
57
  ```text
56
- superspec guard check-archived --change "<change>"
58
+ superspec guard check-archived --change "<change>" --format agent
57
59
  ```
58
60
 
59
61
  遇到任何 guard `block` 就停止。
@@ -16,6 +16,8 @@ metadata:
16
16
  - 对话窗口里的解释、问题说明、总结、提问和下一步说明必须使用中文;除命令、路径、字段名、代码标识符外,不要夹带英文说明词。
17
17
  - 对话窗口、AskUserQuestion 文案、进度更新和最终总结不得裸露内部证据种类、字段名或 reason code;用户确认记录、审查问题记录、审查轮次编号、问题唯一标识等都只用中文业务说法。原始协议名只允许写在证据 JSON、代码、测试、精确命令输出或用户明确要求的诊断片段中。
18
18
  - 本 skill 文档中的内部协议名只用于落盘证据或运行 guard;写给用户时必须先翻译成中文业务动作,例如“记录用户确认”“记录审查问题”“完成最终审查判断”。
19
+ - 用户可见文案不得使用“裁决”描述用户动作;统一说“确认”“范围取舍”“处理方式选择”或“用户确认记录”。
20
+ - 普通 workflow 命令使用 `--format agent` 读取 guard/init 输出;`--format json` 只用于诊断 evidence/schema/guard 内部,不得作为默认模型上下文或直接转述给用户。
19
21
  - 向用户转述 guard / review 输出时,不要直接贴英文 `message`、`next_allowed_actions` 或英文模板标题;应改写为中文,并仅在需要定位内部协议时保留英文 code/command 于反引号中。
20
22
 
21
23
  ## 命令执行 / Shell
@@ -64,12 +66,12 @@ metadata:
64
66
 
65
67
  1. 确保项目级 SuperSpec surfaces 已存在:
66
68
  ```text
67
- superspec init --scope project
69
+ superspec init --scope project --format agent
68
70
  ```
69
71
  2. 创建或打开 native OpenSpec change root,然后确认 change-scoped guard readiness 并拉取 native context:
70
72
  ```text
71
73
  openspec new change "<change>" # 仅当该 change 不存在时执行
72
- superspec guard check-init --change "<change>"
74
+ superspec guard check-init --change "<change>" --format agent
73
75
  openspec list --json
74
76
  openspec status --change "<change>" --json # changeRoot / artifactPaths / actionContext for grounding
75
77
  ```
@@ -85,7 +87,7 @@ metadata:
85
87
  9. 对探索结论、范围边界和进入 propose 的授权使用 AskUserQuestion,并等待明确选择;记录探索阶段人工确认 evidence(JSON 中为 `gate:"explore_complete"`、`kind:"human_confirmation"`、`created_by:"user"`),`confirmed_refs` 固定记录用户确认过的探索记录。
86
88
  10. 运行进入阶段前检查(`check-enter`),验证 explore completion:
87
89
  ```text
88
- superspec guard check-enter --change "<change>" --gate explore_complete
90
+ superspec guard check-enter --change "<change>" --gate explore_complete --format agent
89
91
  ```
90
92
 
91
93
  遇到任何 guard `block` 就停止。用户确认相关阻塞原因包括:缺少审查问题记录(`missing_review_digest`)、等待用户确认(`needs_user_decision_pending`)、历史 finding 未处理完(`finding_unresolved`)、用户确认未绑定(`user_decision_unbound`)、缺少 finding 问题清单(`ledger_injection_missing`)、审查轮次已达上限(`round_budget_exhausted`)等。它们的唯一合法出路是回到确认循环或升级给用户,不允许绕过。
@@ -16,6 +16,8 @@ metadata:
16
16
  - 对话窗口里的解释、问题说明、总结、提问和下一步说明必须使用中文;除命令、路径、字段名、代码标识符外,不要夹带英文说明词。
17
17
  - 对话窗口、AskUserQuestion 文案、进度更新和最终总结不得裸露内部证据种类、字段名或 reason code;用户确认记录、审查问题记录、审查轮次编号、问题唯一标识等都只用中文业务说法。原始协议名只允许写在证据 JSON、代码、测试、精确命令输出或用户明确要求的诊断片段中。
18
18
  - 本 skill 文档中的内部协议名只用于落盘证据或运行 guard;写给用户时必须先翻译成中文业务动作,例如“记录用户确认”“记录审查问题”“完成最终审查判断”。
19
+ - 用户可见文案不得使用“裁决”描述用户动作;统一说“确认”“范围取舍”“处理方式选择”或“用户确认记录”。
20
+ - 普通 workflow 命令使用 `--format agent` 读取 guard/init 输出;`--format json` 只用于诊断 evidence/schema/guard 内部,不得作为默认模型上下文或直接转述给用户。
19
21
  - 向用户转述 guard / review 输出时,不要直接贴英文 `message`、`next_allowed_actions` 或英文模板标题;应改写为中文,并仅在需要定位内部协议时保留英文 code/command 于反引号中。
20
22
 
21
23
  ## 命令执行 / Shell
@@ -53,7 +55,7 @@ metadata:
53
55
 
54
56
  1. 运行前置门禁检查(`check-enter`),确认探索阶段已经完成;该 gate 必须包含用户对探索结论和进入 propose 的明确确认,若 guard block 则停止并回到 explore 补确认:
55
57
  ```text
56
- superspec guard check-enter --change "<change>" --gate explore_complete
58
+ superspec guard check-enter --change "<change>" --gate explore_complete --format agent
57
59
  ```
58
60
  2. 从 OpenSpec 获取方案文件生成顺序:
59
61
  ```text
@@ -72,28 +74,28 @@ metadata:
72
74
  - 主流程记录审查问题记录,给每个 finding 写处理结果:关键 findings(范围、非目标、验收标准、业务语义、设计边界)必须进入用户确认并停下来,用 AskUserQuestion 把原文和 A/B/C/D 选项展示给用户,拿到用户确认后才能继续;发现探索记录不完整时 route 用 `return_explore` 回 explore,不得自行补范围。
73
75
  - 修订 `proposal.md` 后必须重跑 `critic`(新一轮 round),直到 clean round + digest 通过,然后验证:
74
76
  ```text
75
- superspec guard check-enter --change "<change>" --gate propose.proposal_reviewed
77
+ superspec guard check-enter --change "<change>" --gate propose.proposal_reviewed --format agent
76
78
  ```
77
79
  guard 未通过前不要开始编写 `specs/**` 或 `design.md`(两者的入口门禁都是 `proposal_reviewed`)。
78
80
  5. 设计说明 `design.md` 编写后:获取 `architect`、`critic`、`test-engineer` 的 native-subagent review evidence(带 `review_round_id` `design_complete-r<N>` + `findings[]` + 全量 pinned target:`proposal.md` + `design.md` + `specs/**/*.md` + 探索记录)。主流程记录审查问题记录;关键问题必须停下来向用户说明并等待用户确认,按用户确认改 design/specs 后 supersede 旧轮并重审。对于设计选项选择和最终设计确认,使用 AskUserQuestion 并等待明确选择;记录 human-confirmation evidence,然后验证:
79
81
  ```text
80
- superspec guard check-enter --change "<change>" --gate propose.design_reviewed
82
+ superspec guard check-enter --change "<change>" --gate propose.design_reviewed --format agent
81
83
  ```
82
84
  6. 需求规格 `specs/**` 和设计说明 `design.md` 编写后、测试契约 `test-contract.md` 编写前:起草业务约束 `.superspec/artifacts/business-invariants.md`。每条 `INV-*` 必须有 statement、scope、source anchors、acceptance_refs、risk_refs、confidence、enforcement_level、test_refs_or_review_only_reason;记录 rejected candidates,防止把当前实现习惯误升格为业务真相。获取 `critic` + `test-engineer` review evidence(带 `review_round_id` `invariants_reviewed-r<N>` + `findings[]` + pinned target:business-invariants + design + specs glob)。主流程记录审查问题记录;关键业务语义问题必须进入用户确认,不得把实现习惯静默升格为 invariant 真相。然后验证:
83
85
  ```text
84
- superspec guard check-enter --change "<change>" --gate propose.invariants_reviewed
86
+ superspec guard check-enter --change "<change>" --gate propose.invariants_reviewed --format agent
85
87
  ```
86
88
  7. 业务约束 `business-invariants.md` 完成后、任务清单 `tasks.md` 编写前:起草测试契约 `.superspec/artifacts/test-contract.md`,覆盖 specs 中每个 `#### Scenario` 和命中本 change scope 的 hard `INV-*`,包含 TEST ids、关联 INV ids、预期 RED reasons、预期 GREEN criteria 和 commands。获取 `test-engineer` + `critic` review evidence(带 `review_round_id` `test_contract_drafted-r<N>` + `findings[]` + pinned target:test-contract + invariants + design + specs glob)。主流程记录审查问题记录;验收标准变更等关键问题必须用户确认。然后验证:
87
89
  ```text
88
- superspec guard check-enter --change "<change>" --gate propose.test_plan_drafted
90
+ superspec guard check-enter --change "<change>" --gate propose.test_plan_drafted --format agent
89
91
  ```
90
- 8. 通过 `openspec instructions tasks` 编写任务清单 `tasks.md` 时:为每个 task 补充 `requirement_refs`、`invariant_refs`(必须是 business-invariants `INV-*` ids 的子集)、`test_refs`(必须是 test-contract TEST ids 的子集)、`read_scope`、`write_scope`、dependencies、TDD metadata,以及需要时的 parallel group。若 reviewer 对 task 映射提出 round-tagged findings,走 `tasks_complete-r<N>` 确认循环(pinned target:tasks + test-contract + invariants + design + specs glob);验收标准问题 route 用 `return_test_contract_drafted`,映射问题用 `stay_same_gate_fix`。对于任务审查确认,使用 AskUserQuestion 并等待明确选择;按披露循环记录用户裁决和审查问题处理结果,然后验证:
92
+ 8. 通过 `openspec instructions tasks` 编写任务清单 `tasks.md` 时:为每个 task 补充 `requirement_refs`、`invariant_refs`(必须是 business-invariants `INV-*` ids 的子集)、`test_refs`(必须是 test-contract TEST ids 的子集)、`read_scope`、`write_scope`、dependencies、TDD metadata,以及需要时的 parallel group。若 reviewer 对 task 映射提出 round-tagged findings,走 `tasks_complete-r<N>` 确认循环(pinned target:tasks + test-contract + invariants + design + specs glob);验收标准问题 route 用 `return_test_contract_drafted`,映射问题用 `stay_same_gate_fix`。对于任务审查确认,使用 AskUserQuestion 并等待明确选择;按披露循环记录用户确认和审查问题处理结果,然后验证:
91
93
  ```text
92
- superspec guard check-enter --change "<change>" --gate propose.tasks_mapped
94
+ superspec guard check-enter --change "<change>" --gate propose.tasks_mapped --format agent
93
95
  ```
94
96
  9. 验证 apply readiness:
95
97
  ```text
96
- superspec guard check-apply-ready --change "<change>"
98
+ superspec guard check-apply-ready --change "<change>" --format agent
97
99
  ```
98
100
 
99
101
  遇到任何 guard `block` 就停止。
@@ -18,6 +18,8 @@ metadata:
18
18
  - 对话窗口里的解释、审查结论、验证结论、提问和下一步说明必须使用中文;除命令、路径、字段名、代码标识符外,不要夹带英文说明词。
19
19
  - 对话窗口、AskUserQuestion 文案、进度更新和最终总结不得裸露内部证据种类、字段名或 reason code;用户确认记录、审查问题记录、审查轮次编号、问题唯一标识等都只用中文业务说法。原始协议名只允许写在证据 JSON、代码、测试、精确命令输出或用户明确要求的诊断片段中。
20
20
  - 本 skill 文档中的内部协议名只用于落盘证据或运行 guard;写给用户时必须先翻译成中文业务动作,例如“记录用户确认”“记录审查问题”“完成最终审查判断”。
21
+ - 用户可见文案不得使用“裁决”描述用户动作;统一说“确认”“范围取舍”“处理方式选择”或“用户确认记录”。
22
+ - 普通 workflow 命令使用 `--format agent` 读取 guard/init 输出;`--format json` 只用于诊断 evidence/schema/guard 内部,不得作为默认模型上下文或直接转述给用户。
21
23
  - 向用户转述 guard / review / verification 输出时,不要直接贴英文 `message`、`next_allowed_actions`、`Summary`、`Justification`、`PASS/FAIL` 等模板词;应改写为中文,并仅在需要定位内部协议时保留英文 code/command 于反引号中。
22
24
 
23
25
  ## 命令执行 / Shell
@@ -55,7 +57,7 @@ metadata:
55
57
  Review 前必须确认这些 SuperSpec distribution files 存在;缺失、无效或当前 Codex surface 无法从它们启动 native subagents 时,review gate 必须 block:
56
58
 
57
59
  ```text
58
- superspec guard check-init --change "<change>"
60
+ superspec guard check-init --change "<change>" --format agent
59
61
  ```
60
62
 
61
63
  Required project-scope files: `.codex/agents/code-reviewer.toml`、`.codex/prompts/code-reviewer.md`、`.codex/agents/architect.toml`、`.codex/prompts/architect.md`、`.codex/agents/critic.toml`、`.codex/prompts/critic.md`、`.codex/agents/verifier.toml`、`.codex/prompts/verifier.md`。
@@ -64,7 +66,7 @@ Required project-scope files: `.codex/agents/code-reviewer.toml`、`.codex/promp
64
66
 
65
67
  1. 检查 review readiness:
66
68
  ```text
67
- superspec guard check-review-ready --change "<change>"
69
+ superspec guard check-review-ready --change "<change>" --format agent
68
70
  ```
69
71
  2. 从 guard decision、`git diff`、OpenSpec artifacts、tasks、business invariants、test contract、RED/GREEN 摘要和 live role output 摘要构建审查范围;不要默认打开完整 `.superspec/evidence/**/*.json`。
70
72
  3. 运行 repo-local review guidance:
@@ -105,7 +107,7 @@ Required project-scope files: `.codex/agents/code-reviewer.toml`、`.codex/promp
105
107
  - `request_changes` 只负责给出结构化回退方向,不直接修改 task checkbox。
106
108
  8. 仅在 allow path 检查 review completion:
107
109
  ```text
108
- superspec guard check-review-complete --change "<change>"
110
+ superspec guard check-review-complete --change "<change>" --format agent
109
111
  ```
110
112
  - 只有最终 allow path 才应执行并通过这一步。
111
113
  - 如果本轮 `main_adjudication.review_decision:"request_changes"`,则本轮 review 的正确出口是停止并回到对应路由;不要把 `request_changes` 轮次伪装成 `review_complete`。