@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.
- package/adapters/codex/agents/architect.toml +4 -148
- package/adapters/codex/agents/code-reviewer.toml +4 -166
- package/adapters/codex/agents/critic.toml +5 -106
- package/adapters/codex/agents/test-engineer.toml +4 -154
- package/adapters/codex/agents/verifier.toml +4 -110
- package/dist/src/cli.js +15 -2
- package/dist/src/cli_args.d.ts +6 -0
- package/dist/src/cli_args.js +143 -16
- package/dist/src/gates.d.ts +1 -1
- package/dist/src/gates.js +3 -19
- package/dist/src/i18n.js +4 -3
- package/dist/src/init_cli.d.ts +2 -1
- package/dist/src/init_cli.js +31 -17
- package/dist/src/packet_measure.d.ts +43 -0
- package/dist/src/packet_measure.js +395 -0
- package/dist/src/packet_render.d.ts +4 -0
- package/dist/src/packet_render.js +652 -0
- package/dist/src/packet_schema.d.ts +55 -0
- package/dist/src/packet_schema.js +1 -0
- package/dist/src/project_init.js +7 -49
- package/dist/src/util.d.ts +21 -2
- package/dist/src/util.js +238 -6
- package/dist/superspec.js +4 -3
- package/package.json +2 -2
- package/templates/workflow/prompts/architect.md +16 -109
- package/templates/workflow/prompts/code-reviewer.md +17 -137
- package/templates/workflow/prompts/critic.md +18 -75
- package/templates/workflow/prompts/test-engineer.md +16 -126
- package/templates/workflow/prompts/verifier.md +17 -80
- package/templates/workflow/skills/superspec-apply/SKILL.md +65 -77
- package/templates/workflow/skills/superspec-archive/SKILL.md +42 -36
- package/templates/workflow/skills/superspec-explore/SKILL.md +64 -76
- package/templates/workflow/skills/superspec-propose/SKILL.md +65 -84
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/dist/src/cli_args.d.ts
CHANGED
|
@@ -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;
|
package/dist/src/cli_args.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
};
|
package/dist/src/gates.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Decision, JsonMap, Reason } from "./util.ts";
|
|
2
|
-
export declare function openspec_init_reasons(
|
|
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,
|
|
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(
|
|
334
|
-
|
|
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
|
-
|
|
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];
|
package/dist/src/init_cli.d.ts
CHANGED
|
@@ -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
|
};
|
package/dist/src/init_cli.js
CHANGED
|
@@ -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
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|