@peterxiaoyang/superspec 0.1.3 → 0.1.5

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 (34) hide show
  1. package/adapters/codex/agents/architect.toml +4 -148
  2. package/adapters/codex/agents/code-reviewer.toml +4 -166
  3. package/adapters/codex/agents/critic.toml +5 -106
  4. package/adapters/codex/agents/test-engineer.toml +4 -154
  5. package/adapters/codex/agents/verifier.toml +4 -110
  6. package/dist/src/cli.js +15 -2
  7. package/dist/src/cli_args.d.ts +6 -0
  8. package/dist/src/cli_args.js +143 -16
  9. package/dist/src/gates.d.ts +1 -1
  10. package/dist/src/gates.js +3 -19
  11. package/dist/src/i18n.js +4 -3
  12. package/dist/src/init_cli.d.ts +2 -1
  13. package/dist/src/init_cli.js +31 -17
  14. package/dist/src/packet_measure.d.ts +43 -0
  15. package/dist/src/packet_measure.js +395 -0
  16. package/dist/src/packet_render.d.ts +4 -0
  17. package/dist/src/packet_render.js +652 -0
  18. package/dist/src/packet_schema.d.ts +55 -0
  19. package/dist/src/packet_schema.js +1 -0
  20. package/dist/src/project_init.js +7 -49
  21. package/dist/src/util.d.ts +21 -2
  22. package/dist/src/util.js +238 -6
  23. package/dist/superspec.js +4 -3
  24. package/package.json +2 -2
  25. package/templates/workflow/prompts/architect.md +16 -109
  26. package/templates/workflow/prompts/code-reviewer.md +17 -137
  27. package/templates/workflow/prompts/critic.md +18 -75
  28. package/templates/workflow/prompts/test-engineer.md +16 -126
  29. package/templates/workflow/prompts/verifier.md +17 -80
  30. package/templates/workflow/skills/superspec-apply/SKILL.md +65 -77
  31. package/templates/workflow/skills/superspec-archive/SKILL.md +42 -36
  32. package/templates/workflow/skills/superspec-explore/SKILL.md +64 -76
  33. package/templates/workflow/skills/superspec-propose/SKILL.md +65 -84
  34. package/templates/workflow/skills/superspec-review/SKILL.md +77 -232
@@ -3,117 +3,11 @@ name = "verifier"
3
3
  description = "Completion evidence, claim validation, test adequacy"
4
4
  model_reasoning_effort = "xhigh"
5
5
  developer_instructions = """
6
- <identity>
7
- You are Verifier. Prove or disprove completion with direct evidence.
8
- </identity>
6
+ Role: Verifier. Prove or disprove completion claims with reproducible evidence; missing evidence is not a pass.
9
7
 
10
- <goal>
11
- Turn claims into reproducible proof or proof gaps by checking code, diffs, commands, diagnostics, tests, artifacts, and acceptance criteria. Missing evidence is a gap, not a pass, and the main thread remains responsible for final adjudication.
12
- </goal>
8
+ Prompt binding: load `.codex/prompts/verifier.md` first, then read the provided `review-packet` or `prompt_ref`. The packet's refs, output kind, contract fields, review scope, and stop conditions override static prompt memory.
13
9
 
14
- <constraints>
15
- <scope_guard>
16
- - Verify claims against observable evidence; do not trust implementation summaries.
17
- - Distinguish failed behavior from unavailable or missing proof.
18
- - Prefer fresh command output when available.
19
- </scope_guard>
10
+ Boundary: read-only. Check commands, test output, diff, artifacts, evidence refs, and acceptance criteria without editing files.
20
11
 
21
- <ask_gate>
22
- <!-- OMX:GUIDANCE:VERIFIER:CONSTRAINTS:START -->
23
- - Default reports to outcome-first, evidence-dense verdicts: name the claim, success criteria, validation evidence, gaps, and stop condition before adding process detail.
24
- - Keep collaboration style direct and concise; do not expand verification scope beyond what materially proves or disproves the claim.
25
- - For multi-step verification, start with a concise preamble that names the first check; keep intermediate updates brief and evidence-based.
26
- - AUTO-CONTINUE for clear, already-requested, low-risk, reversible, local inspect-test-verify work; keep inspecting, testing, and verifying without permission handoff.
27
- - ASK only for destructive, irreversible, credential-gated, external-production, or materially scope-changing actions, or when missing authority blocks progress.
28
- - On AUTO-CONTINUE branches, do not use permission-handoff phrasing; state the next verification action or evidence-backed verdict.
29
- - Use absolute language only for true invariants: safety, security, side-effect boundaries, required output fields, workflow state transitions, and product contracts.
30
- - Keep gathering evidence until the verdict is grounded or blocked by a missing acceptance target or unavailable proof source.
31
- - If correctness depends on additional tests, diagnostics, or inspection, keep using those tools until the verdict is grounded; stop once enough evidence proves the core claim.
32
- - More verification effort does not mean unrelated tool churn; gather the proof that matters, not every possible artifact.
33
- <!-- OMX:GUIDANCE:VERIFIER:CONSTRAINTS:END -->
34
- - Ask only when the acceptance target is materially unclear and cannot be derived from repo or task history.
35
- </ask_gate>
36
- </constraints>
37
-
38
- <execution_loop>
39
- 1. State what must be proven.
40
- 2. Inspect relevant files, diffs, outputs, and artifacts.
41
- 3. Run or review the commands that directly prove the claim.
42
- 4. Report proof status, evidence, gaps, risks, and any blocked proof source.
43
- </execution_loop>
44
-
45
- <success_criteria>
46
- - Acceptance criteria are checked directly.
47
- - Evidence is concrete and reproducible.
48
- - Missing proof is called out explicitly.
49
- - The verdict is grounded and actionable.
50
- </success_criteria>
51
-
52
- <verification_loop>
53
- <!-- OMX:GUIDANCE:VERIFIER:INVESTIGATION:START -->
54
- 5) If a newer user instruction only changes the current verification target or report shape, apply that override locally without discarding earlier non-conflicting acceptance criteria; preserve traceability from each claim to evidence, validation command, or explicit proof gap.
55
- <!-- OMX:GUIDANCE:VERIFIER:INVESTIGATION:END -->
56
- Keep gathering the required evidence until the verdict is grounded or the proof source is unavailable.
57
- </verification_loop>
58
-
59
- <tools>
60
- Use Read/Grep/Glob for evidence, diagnostics/test/build commands for behavior, and diff/history inspection when scope depends on recent changes.
61
- </tools>
62
-
63
- <style>
64
- <output_contract>
65
- ## Verdict
66
- - PASS / FAIL / PARTIAL
67
-
68
- ## Evidence
69
- - `command or artifact` — result
70
-
71
- ## Gaps
72
- - Missing or inconclusive proof
73
-
74
- ## Risks
75
- - Remaining uncertainty or follow-up needed
76
- </output_contract>
77
-
78
- <scenario_handling>
79
- - If the user says `continue`, keep gathering the required evidence instead of restating a partial verdict.
80
- - If the user says `merge if CI green`, check relevant statuses, confirm they are green, and report the gate outcome.
81
- </scenario_handling>
82
-
83
- <stop_rules>
84
- Stop only when the verdict is evidence-backed or the needed proof source/authority is unavailable.
85
- </stop_rules>
86
- </style>
87
-
88
- <posture_overlay>
89
-
90
- You are operating in the frontier-orchestrator posture.
91
- - Prioritize intent classification before implementation.
92
- - Default to delegation and orchestration when specialists exist.
93
- - Treat the first decision as a routing problem: research vs planning vs implementation vs verification.
94
- - Challenge flawed user assumptions concisely before execution when the design is likely to cause avoidable problems.
95
- - Preserve explicit executor handoff boundaries: do not absorb deep implementation work when a specialized executor is more appropriate.
96
-
97
- </posture_overlay>
98
-
99
- <model_class_guidance>
100
-
101
- This role is tuned for standard-capability models.
102
- - Balance autonomy with clear boundaries.
103
- - Prefer explicit verification and narrow scope control over speculative reasoning.
104
-
105
- </model_class_guidance>
106
-
107
- <native_subagent_leaf_guard>
108
-
109
- Leaf native subagent: do not call Task, spawn_agent, or native child agents.
110
- Use local tools; report missing specialist coverage to the leader.
111
-
112
- </native_subagent_leaf_guard>
113
-
114
- ## OMX Agent Metadata
115
- - role: verifier
116
- - posture: frontier-orchestrator
117
- - model_class: standard
118
- - routing_role: leader
12
+ Output: concise Simplified Chinese. State pass, fail, partial, or evidence gap first; list evidence, gaps, residual risk, and stop conditions.
119
13
  """
package/dist/src/cli.js CHANGED
@@ -1,20 +1,33 @@
1
1
  import { block, dispatch, GuardError, printDecision, reason } from "./core.js";
2
2
  import { emitArgparsePreamble, parse_argv } from "./cli_args.js";
3
+ import { dispatch_packet, is_packet_command } from "./packet_render.js";
4
+ import { printPacket, printPacketError } from "./util.js";
3
5
  export function main(argv = process.argv.slice(2)) {
4
6
  let args = null;
7
+ const rawCommand = argv[0] ?? "";
5
8
  try {
6
9
  const argparseExit = emitArgparsePreamble(argv);
7
10
  if (argparseExit !== null)
8
11
  return argparseExit;
9
12
  args = parse_argv(argv);
13
+ if (is_packet_command(args.command)) {
14
+ const result = dispatch_packet(args);
15
+ printPacket(result.payload, { format: result.output_format });
16
+ return 0;
17
+ }
10
18
  const [decision] = dispatch(args);
11
- printDecision(decision, { command: args.command });
19
+ printDecision(decision, { command: args.command, format: args.format });
12
20
  return decision.allowed ? 0 : 1;
13
21
  }
14
22
  catch (err) {
23
+ if ((args && is_packet_command(args.command)) || is_packet_command(rawCommand)) {
24
+ const errCode = err instanceof GuardError ? "guard_error" : "guard_internal_error";
25
+ printPacketError(errCode, `${err.message}`);
26
+ return 2;
27
+ }
15
28
  const change = args?.change ?? "?";
16
29
  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 });
30
+ printDecision(block(change, "guard_error", [errReason]), { command: args?.command, format: args?.format });
18
31
  return 2;
19
32
  }
20
33
  }
@@ -1,9 +1,15 @@
1
+ import { type DecisionOutputFormat, type PacketOutputFormat } from "./util.ts";
1
2
  export type ParsedArgs = {
2
3
  command: string;
3
4
  change: string;
5
+ format?: DecisionOutputFormat;
6
+ packet_format?: PacketOutputFormat;
4
7
  artifact?: string;
5
8
  gate?: string;
6
9
  task_id?: string;
10
+ role?: string;
11
+ evidence_kind?: string;
12
+ round?: number;
7
13
  create?: boolean;
8
14
  force_unlock?: boolean;
9
15
  rebuild_corrupt?: boolean;
@@ -1,4 +1,6 @@
1
1
  import { command_zh } from "./i18n.js";
2
+ import { GuardError, parseDecisionOutputFormat, parsePacketOutputFormat } from "./util.js";
3
+ import { normalize_gate } from "./openspec.js";
2
4
  const SIMPLE_COMMANDS = [
3
5
  "status",
4
6
  "recompute",
@@ -18,9 +20,15 @@ const COMMANDS = [
18
20
  "check-task-reopen",
19
21
  "check-task-edit",
20
22
  "check-task-complete",
23
+ "workflow-packet",
24
+ "review-packet",
25
+ "ledger-render",
21
26
  ];
22
27
  const COMMAND_LIST = COMMANDS.join(",");
23
28
  const COMMAND_CHOICES = COMMANDS.map((item) => `'${item}'`).join(", ");
29
+ function isPacketCommand(command) {
30
+ return command === "workflow-packet" || command === "review-packet" || command === "ledger-render";
31
+ }
24
32
  function requiredValueFlags(command) {
25
33
  const flags = ["--change"];
26
34
  if (command === "check-artifact")
@@ -29,13 +37,42 @@ function requiredValueFlags(command) {
29
37
  flags.push("--gate");
30
38
  if (command === "check-task-reopen" || command === "check-task-edit" || command === "check-task-complete")
31
39
  flags.push("--task-id");
40
+ if (command === "workflow-packet")
41
+ flags.push("--gate");
42
+ if (command === "review-packet")
43
+ flags.push("--gate", "--role", "--round");
44
+ if (command === "ledger-render")
45
+ flags.push("--gate");
32
46
  return flags;
33
47
  }
34
48
  function requiredBooleanFlags(command) {
35
49
  return command === "init" ? ["--create"] : [];
36
50
  }
37
51
  function optionalBooleanFlags(command) {
38
- return command === "recompute" ? ["--force-unlock", "--rebuild-corrupt"] : [];
52
+ if (isPacketCommand(command))
53
+ return [];
54
+ return ["--user-facing", ...(command === "recompute" ? ["--force-unlock", "--rebuild-corrupt"] : [])];
55
+ }
56
+ function optionalValueFlags(command) {
57
+ if (command === "workflow-packet")
58
+ return ["--task-id"];
59
+ if (command === "review-packet")
60
+ return ["--kind"];
61
+ if (command === "ledger-render")
62
+ return ["--round"];
63
+ return [];
64
+ }
65
+ function formatUsage(command) {
66
+ if (command === "workflow-packet")
67
+ return "--format {agent}";
68
+ if (command === "review-packet")
69
+ return "--format {agent,prompt}";
70
+ if (command === "ledger-render")
71
+ return null;
72
+ return "[--format {json,agent,user}]";
73
+ }
74
+ function requiresFormat(command) {
75
+ return command === "workflow-packet" || command === "review-packet";
39
76
  }
40
77
  function rootUsage() {
41
78
  return `usage: superspec_guard [-h]\n {${COMMAND_LIST}}\n ...\n`;
@@ -51,6 +88,8 @@ function commandUsage(command) {
51
88
  const usageFlags = [
52
89
  "[-h]",
53
90
  ...requiredValueFlags(command).map((flag) => `${flag} ${flag.slice(2).replace(/-/g, "_").toUpperCase()}`),
91
+ ...(formatUsage(command) ? [formatUsage(command)] : []),
92
+ ...optionalValueFlags(command).map((flag) => `[${flag} ${flag.slice(2).replace(/-/g, "_").toUpperCase()}]`),
54
93
  ...requiredBooleanFlags(command),
55
94
  ...optionalBooleanFlags(command),
56
95
  ];
@@ -66,6 +105,13 @@ function commandHelp(command) {
66
105
  for (const flag of requiredBooleanFlags(command)) {
67
106
  lines.push(` ${flag}\n`);
68
107
  }
108
+ const formatToken = formatUsage(command);
109
+ if (formatToken)
110
+ lines.push(` ${formatToken}\n`);
111
+ for (const flag of optionalValueFlags(command)) {
112
+ const metavariable = flag.slice(2).replace(/-/g, "_").toUpperCase();
113
+ lines.push(` ${flag} ${metavariable}\n`);
114
+ }
69
115
  for (const flag of optionalBooleanFlags(command)) {
70
116
  lines.push(` ${flag}\n`);
71
117
  }
@@ -75,7 +121,10 @@ function hasFlag(argv, flag) {
75
121
  return argv.includes(flag);
76
122
  }
77
123
  function missingRequiredFlags(command, args) {
78
- return [...requiredValueFlags(command), ...requiredBooleanFlags(command)].filter((flag) => !hasFlag(args, flag));
124
+ const missing = [...requiredValueFlags(command), ...requiredBooleanFlags(command)].filter((flag) => !hasFlag(args, flag));
125
+ if (requiresFormat(command) && !hasFlag(args, "--format"))
126
+ missing.push("--format");
127
+ return missing;
79
128
  }
80
129
  export function emitArgparsePreamble(argv) {
81
130
  if (argv.length === 0) {
@@ -96,10 +145,12 @@ export function emitArgparsePreamble(argv) {
96
145
  process.stdout.write(commandHelp(command));
97
146
  return 0;
98
147
  }
99
- const missing = missingRequiredFlags(command, args);
100
- if (missing.length > 0) {
101
- process.stderr.write(`${commandUsage(command)}superspec_guard ${command}:错误:缺少必填参数:${missing.join(", ")}\n`);
102
- return 2;
148
+ if (!isPacketCommand(command)) {
149
+ const missing = missingRequiredFlags(command, args);
150
+ if (missing.length > 0) {
151
+ process.stderr.write(`${commandUsage(command)}superspec_guard ${command}:错误:缺少必填参数:${missing.join(", ")}\n`);
152
+ return 2;
153
+ }
103
154
  }
104
155
  return null;
105
156
  }
@@ -108,37 +159,112 @@ export function parse_argv(argv) {
108
159
  throw new Error("缺少命令");
109
160
  const command = argv[0];
110
161
  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];
162
+ const getValues = (flag) => {
163
+ const values = [];
164
+ for (let idx = 0; idx < args.length; idx += 1) {
165
+ if (args[idx] !== flag)
166
+ continue;
167
+ const value = args[idx + 1];
168
+ if (value === undefined || value.startsWith("--"))
169
+ throw new GuardError(`${flag} 缺少取值`);
170
+ values.push(value);
171
+ }
172
+ return values;
116
173
  };
174
+ const getValue = (flag) => getValues(flag)[0];
175
+ const formatValues = getValues("--format");
176
+ const isPacketOutputCommand = command === "workflow-packet" || command === "review-packet";
177
+ const isPacketCommand = isPacketOutputCommand || command === "ledger-render";
178
+ const selectedPacketFormat = formatValues.length > 0 ? formatValues[formatValues.length - 1] : undefined;
179
+ if (isPacketOutputCommand) {
180
+ if (!selectedPacketFormat)
181
+ throw new GuardError("缺少必填参数 --format");
182
+ for (const value of formatValues)
183
+ parsePacketOutputFormat(value, { allowPrompt: command === "review-packet" });
184
+ }
185
+ else {
186
+ for (const value of formatValues)
187
+ parseDecisionOutputFormat(value);
188
+ }
189
+ const selectedFormat = hasFlag(args, "--user-facing")
190
+ ? "user"
191
+ : (formatValues.length > 0 ? formatValues[formatValues.length - 1] : "json");
192
+ const format = isPacketCommand ? undefined : parseDecisionOutputFormat(selectedFormat);
117
193
  const change = getValue("--change");
118
194
  if (!change)
119
- throw new Error("缺少必填参数 --change");
195
+ throw new (isPacketCommand ? GuardError : Error)("缺少必填参数 --change");
120
196
  if (command === "init") {
121
197
  if (!hasFlag(args, "--create"))
122
198
  throw new Error("缺少必填参数 --create");
123
- return { command, change, create: true };
199
+ return { command, change, format, create: true };
124
200
  }
125
201
  if (command === "check-artifact") {
126
202
  const artifact = getValue("--artifact");
127
203
  if (!artifact)
128
204
  throw new Error("缺少必填参数 --artifact");
129
- return { command, change, artifact };
205
+ return { command, change, format, artifact };
130
206
  }
131
207
  if (command === "check-enter") {
132
208
  const gate = getValue("--gate");
133
209
  if (!gate)
134
210
  throw new Error("缺少必填参数 --gate");
135
- return { command, change, gate };
211
+ return { command, change, format, gate };
212
+ }
213
+ if (command === "workflow-packet") {
214
+ const gate = getValue("--gate");
215
+ if (!gate)
216
+ throw new GuardError("缺少必填参数 --gate");
217
+ const normalizedGate = normalize_gate(gate);
218
+ const taskId = getValue("--task-id");
219
+ if ((normalizedGate === "task_edit" || normalizedGate === "task_complete" || normalizedGate === "task_reopen") && !taskId) {
220
+ throw new GuardError("workflow-packet 缺少必填参数 --task-id");
221
+ }
222
+ return { command, change, gate, task_id: taskId, packet_format: parsePacketOutputFormat(selectedPacketFormat, { allowPrompt: false }) };
223
+ }
224
+ if (command === "review-packet") {
225
+ const gate = getValue("--gate");
226
+ const role = getValue("--role");
227
+ const roundValue = getValue("--round");
228
+ const evidenceKind = getValue("--kind");
229
+ if (!gate)
230
+ throw new GuardError("缺少必填参数 --gate");
231
+ if (!role)
232
+ throw new GuardError("缺少必填参数 --role");
233
+ if (!roundValue)
234
+ throw new GuardError("缺少必填参数 --round");
235
+ if (evidenceKind !== undefined && evidenceKind !== "source_guidance" && evidenceKind !== "verification_review") {
236
+ throw new GuardError("--kind 只允许 source_guidance 或 verification_review");
237
+ }
238
+ const round = Number.parseInt(roundValue, 10);
239
+ if (!Number.isInteger(round) || round < 1)
240
+ throw new GuardError("--round 必须是大于等于 1 的整数");
241
+ return {
242
+ command,
243
+ change,
244
+ gate,
245
+ role,
246
+ evidence_kind: evidenceKind,
247
+ round,
248
+ packet_format: parsePacketOutputFormat(selectedPacketFormat, { allowPrompt: true }),
249
+ };
250
+ }
251
+ if (command === "ledger-render") {
252
+ const gate = getValue("--gate");
253
+ const roundValue = getValue("--round");
254
+ if (!gate)
255
+ throw new GuardError("缺少必填参数 --gate");
256
+ if (roundValue === undefined)
257
+ return { command, change, gate };
258
+ const round = Number.parseInt(roundValue, 10);
259
+ if (!Number.isInteger(round) || round < 1)
260
+ throw new GuardError("--round 必须是大于等于 1 的整数");
261
+ return { command, change, gate, round };
136
262
  }
137
263
  if (command === "check-task-reopen" || command === "check-task-edit" || command === "check-task-complete") {
138
264
  const taskId = getValue("--task-id");
139
265
  if (!taskId)
140
266
  throw new Error("缺少必填参数 --task-id");
141
- return { command, change, task_id: taskId };
267
+ return { command, change, format, task_id: taskId };
142
268
  }
143
269
  const simple = new Set(SIMPLE_COMMANDS);
144
270
  if (!simple.has(command))
@@ -146,6 +272,7 @@ export function parse_argv(argv) {
146
272
  return {
147
273
  command,
148
274
  change,
275
+ format,
149
276
  force_unlock: command === "recompute" && hasFlag(args, "--force-unlock"),
150
277
  rebuild_corrupt: command === "recompute" && hasFlag(args, "--rebuild-corrupt"),
151
278
  };
@@ -1,5 +1,5 @@
1
1
  import type { Decision, JsonMap, Reason } from "./util.ts";
2
- export declare function openspec_init_reasons(repoRoot: string): Reason[];
2
+ export declare function openspec_init_reasons(_repoRoot: string): Reason[];
3
3
  export declare function superspec_workflow_skill_reasons(repoRoot: string): Reason[];
4
4
  export declare function superspec_agent_reasons(repoRoot: string): Reason[];
5
5
  export declare function openspec_cli_capability_reasons(): Reason[];
package/dist/src/gates.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync, statSync } from "node:fs";
2
2
  import { join, relative } from "node:path";
3
- import { ARTIFACT_ENTER_GATE, MAIN_ADJUDICATION_DECISIONS, REQUEST_CHANGES_ROUTES, NO_TDD_REASONS, OPENSPEC_ARTIFACTS, FINAL_VERIFICATION_ROLES, REQUIRED_SUPERSPEC_AGENT_ROLES, REQUIRED_SUPERSPEC_WORKFLOW_SKILLS, REQUIRED_OPENSPEC_CODEX_SKILLS, REVIEW_GUIDANCE_ROLES, REVIEW_EVIDENCE_REQUIRED_FIELDS, TDD_MODES, VERIFY_EVIDENCE_REQUIRED_FIELDS, allow, block, isObject, reason, renderList, repr, pinned_ref_key, safe_within, sha256_text, runtime, toPosix, } from "./util.js";
3
+ import { ARTIFACT_ENTER_GATE, MAIN_ADJUDICATION_DECISIONS, REQUEST_CHANGES_ROUTES, NO_TDD_REASONS, OPENSPEC_ARTIFACTS, FINAL_VERIFICATION_ROLES, REQUIRED_SUPERSPEC_AGENT_ROLES, REQUIRED_SUPERSPEC_WORKFLOW_SKILLS, REVIEW_GUIDANCE_ROLES, REVIEW_EVIDENCE_REQUIRED_FIELDS, TDD_MODES, VERIFY_EVIDENCE_REQUIRED_FIELDS, allow, block, isObject, reason, renderList, repr, pinned_ref_key, safe_within, sha256_text, runtime, toPosix, } from "./util.js";
4
4
  import { all_done, artifact_status_map, get_repo_root, is_done, normalize_gate, openspec_cli_probe } from "./openspec.js";
5
5
  import { read_agent_toml_name, read_skill_frontmatter_name, sidecar_business_invariants_path, sidecar_discovery_path, sidecar_test_contract_path } from "./paths.js";
6
6
  import { business_invariant_ids, business_invariant_validation_reasons, automated_hard_business_invariant_ids, evidence_invariant_refs, evidence_invariant_ref_reasons, evidence_test_contract_invariant_reasons, human_confirmation_business_invariant_ids, invariant_matrix_coverage_reasons, post_implementation_business_invariant_ids, red_green_invariant_ids, test_contract_invariant_ids, } from "./invariants.js";
@@ -330,24 +330,8 @@ function default_gate_next_actions(gate) {
330
330
  return [];
331
331
  }
332
332
  }
333
- export function openspec_init_reasons(repoRoot) {
334
- const reasons = [];
335
- reasons.push(...runtime.openspec_cli_capability_reasons());
336
- const skillsRoot = join(repoRoot, ".codex", "skills");
337
- const missing = REQUIRED_OPENSPEC_CODEX_SKILLS.filter((name) => !existsSync(join(skillsRoot, name, "SKILL.md")));
338
- if (missing.length > 0) {
339
- reasons.push(reason("openspec_init_missing", "OpenSpec native Codex skills are missing; run `openspec init --tools codex .` or `openspec update --force .` from the repository root", missing));
340
- }
341
- for (const name of REQUIRED_OPENSPEC_CODEX_SKILLS) {
342
- const skillPath = join(skillsRoot, name, "SKILL.md");
343
- if (!existsSync(skillPath))
344
- continue;
345
- const declared = read_skill_frontmatter_name(skillPath);
346
- if (declared !== name) {
347
- reasons.push(reason("openspec_native_surface_invalid", `OpenSpec native skill ${skillPath} has invalid front matter name ${repr(declared)}`, [skillPath]));
348
- }
349
- }
350
- return reasons;
333
+ export function openspec_init_reasons(_repoRoot) {
334
+ return runtime.openspec_cli_capability_reasons();
351
335
  }
352
336
  // D4 (audit G-2): check-init must notice when SuperSpec's own workflow skills are missing or
353
337
  // corrupted, otherwise a deleted .codex/skills/superspec-* surface stays invisible end to end.
package/dist/src/i18n.js CHANGED
@@ -9,6 +9,9 @@ const COMMAND_ZH = {
9
9
  "check-task-reopen": { label_zh: "任务重开检查", hint_zh: "检查被审查打回的任务是否满足重开条件。" },
10
10
  "check-task-edit": { label_zh: "任务编辑前检查", hint_zh: "检查任务在开始实现前是否满足测试与范围前置条件。" },
11
11
  "check-task-complete": { label_zh: "任务完成检查", hint_zh: "检查任务在勾完成前是否满足 GREEN 或替代验证要求。" },
12
+ "workflow-packet": { label_zh: "流程数据包", hint_zh: "只读生成当前流程 gate 的紧凑执行数据包。" },
13
+ "review-packet": { label_zh: "审查数据包", hint_zh: "只读生成审查角色或主线程的最小任务包。" },
14
+ "ledger-render": { label_zh: "问题清单渲染", hint_zh: "渲染审查轮次的确定性问题清单文本。" },
12
15
  "check-review-ready": { label_zh: "进入审查前检查", hint_zh: "检查实现阶段是否已经处理完成,可以进入审查阶段。" },
13
16
  "check-review-complete": { label_zh: "审查完成检查", hint_zh: "检查审查阶段证据是否齐备并允许通过。" },
14
17
  "check-verify-ready": { label_zh: "验证完成检查", hint_zh: "检查最终验证证据是否完整。" },
@@ -583,10 +586,8 @@ export function action_status_zh(status) {
583
586
  }
584
587
  export function action_label_zh(action) {
585
588
  const exact = {
586
- openspec_codex_skills: "检查 OpenSpec 配套技能文件",
589
+ openspec_cli_surface: "检查 OpenSpec CLI 能力",
587
590
  superspec_repo_local_roles: "检查 SuperSpec 本地角色与提示词",
588
- "openspec init --tools codex .": "运行 OpenSpec 初始化命令",
589
- "openspec update --force .": "运行 OpenSpec 更新命令",
590
591
  };
591
592
  if (exact[action])
592
593
  return exact[action];
@@ -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
  }