@peterxiaoyang/superspec 0.1.4 → 0.1.6
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/executor.toml +13 -0
- package/adapters/codex/agents/test-engineer.toml +4 -154
- package/adapters/codex/agents/test-runner.toml +13 -0
- package/adapters/codex/agents/verifier.toml +4 -110
- package/adapters/codex/install-map.json +20 -0
- package/dist/src/apply_worker_chain.d.ts +57 -0
- package/dist/src/apply_worker_chain.js +1188 -0
- package/dist/src/cli.js +13 -0
- package/dist/src/cli_args.d.ts +13 -1
- package/dist/src/cli_args.js +237 -12
- package/dist/src/core.d.ts +1 -0
- package/dist/src/core.js +1 -0
- package/dist/src/evidence.js +152 -0
- package/dist/src/gates.d.ts +2 -1
- package/dist/src/gates.js +275 -21
- package/dist/src/i18n.js +4 -3
- package/dist/src/install_engine.d.ts +17 -0
- package/dist/src/install_engine.js +125 -2
- package/dist/src/packet_measure.d.ts +43 -0
- package/dist/src/packet_measure.js +417 -0
- package/dist/src/packet_render.d.ts +4 -0
- package/dist/src/packet_render.js +1623 -0
- package/dist/src/packet_schema.d.ts +56 -0
- package/dist/src/packet_schema.js +1 -0
- package/dist/src/project_init.js +7 -49
- package/dist/src/tasks.d.ts +10 -0
- package/dist/src/tasks.js +86 -0
- package/dist/src/util.d.ts +11 -3
- package/dist/src/util.js +27 -6
- package/package.json +2 -2
- package/schemas/install-manifest.schema.json +17 -0
- package/templates/workflow/prompts/architect.md +16 -109
- package/templates/workflow/prompts/code-reviewer.md +20 -134
- package/templates/workflow/prompts/critic.md +18 -75
- package/templates/workflow/prompts/executor.md +32 -0
- package/templates/workflow/prompts/test-engineer.md +16 -126
- package/templates/workflow/prompts/test-runner.md +33 -0
- package/templates/workflow/prompts/verifier.md +20 -77
- package/templates/workflow/skills/superspec-apply/SKILL.md +102 -78
- package/templates/workflow/skills/superspec-archive/SKILL.md +41 -37
- package/templates/workflow/skills/superspec-explore/SKILL.md +63 -77
- package/templates/workflow/skills/superspec-propose/SKILL.md +64 -85
- package/templates/workflow/skills/superspec-review/SKILL.md +76 -233
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type PacketOutputFormat = "agent" | "prompt";
|
|
2
|
+
export type PinnedRef = {
|
|
3
|
+
root: "repo" | "change";
|
|
4
|
+
path: string;
|
|
5
|
+
blob_sha: string;
|
|
6
|
+
};
|
|
7
|
+
export type FindingSelector = {
|
|
8
|
+
evidence_id: string;
|
|
9
|
+
finding_uid: string;
|
|
10
|
+
evidence_ref: PinnedRef;
|
|
11
|
+
};
|
|
12
|
+
export type DecisionSelector = {
|
|
13
|
+
evidence_id: string;
|
|
14
|
+
decision_scope_key: string;
|
|
15
|
+
evidence_ref: PinnedRef;
|
|
16
|
+
};
|
|
17
|
+
export type WorkflowPacket = {
|
|
18
|
+
stage: string;
|
|
19
|
+
current_gate: string;
|
|
20
|
+
task_id?: string;
|
|
21
|
+
status: "allowed" | "blocked";
|
|
22
|
+
top_blockers?: string[];
|
|
23
|
+
blocker_count?: number;
|
|
24
|
+
has_more_blockers?: boolean;
|
|
25
|
+
next_action: string;
|
|
26
|
+
next_command?: string;
|
|
27
|
+
openspec_cli_surfaces?: string[];
|
|
28
|
+
must_read_refs: PinnedRef[];
|
|
29
|
+
must_read_verbatim_findings?: FindingSelector[];
|
|
30
|
+
must_read_verbatim_decisions?: DecisionSelector[];
|
|
31
|
+
diagnostic_command?: string;
|
|
32
|
+
};
|
|
33
|
+
export type ReviewPacket = {
|
|
34
|
+
consumer: "role" | "main-thread";
|
|
35
|
+
gate: string;
|
|
36
|
+
role: string;
|
|
37
|
+
round: number;
|
|
38
|
+
target_refs: PinnedRef[];
|
|
39
|
+
source_refs: PinnedRef[];
|
|
40
|
+
required_load_refs?: PinnedRef[];
|
|
41
|
+
required_claim_ids?: string[];
|
|
42
|
+
must_read_verbatim_findings?: FindingSelector[];
|
|
43
|
+
must_read_verbatim_decisions?: DecisionSelector[];
|
|
44
|
+
required_output_kind: string;
|
|
45
|
+
output_contract_fields: string[];
|
|
46
|
+
required_review_scope?: string[];
|
|
47
|
+
stop_conditions: string[];
|
|
48
|
+
};
|
|
49
|
+
export type ApplyWorkerPacket = Record<string, any>;
|
|
50
|
+
export type PacketDispatchResult = {
|
|
51
|
+
output_format: "agent";
|
|
52
|
+
payload: WorkflowPacket | ReviewPacket | ApplyWorkerPacket;
|
|
53
|
+
} | {
|
|
54
|
+
output_format: "prompt";
|
|
55
|
+
payload: string;
|
|
56
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/src/project_init.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import { REQUIRED_SUPERSPEC_AGENT_ROLES,
|
|
3
|
+
import { REQUIRED_SUPERSPEC_AGENT_ROLES, REQUIRED_OPENSPEC_MIN_VERSION, block, commandExists, openspec_cli_probe, reason, read_agent_toml_name, } from "./core.js";
|
|
4
4
|
import { install_workflow } from "./install_engine.js";
|
|
5
|
-
import { system_failure_zh } from "./i18n.js";
|
|
6
5
|
export const OPENSPEC_NPM_PACKAGE = "@fission-ai/openspec";
|
|
7
6
|
export const OPENSPEC_INSTALL_DOC_URL = "https://github.com/Fission-AI/OpenSpec#readme";
|
|
8
7
|
const ROLE_DESCRIPTIONS = {
|
|
@@ -12,9 +11,6 @@ const ROLE_DESCRIPTIONS = {
|
|
|
12
11
|
"code-reviewer": "代码 / 规格 / 安全审查",
|
|
13
12
|
verifier: "最终完成证据与验证审查",
|
|
14
13
|
};
|
|
15
|
-
function commandFailure(proc) {
|
|
16
|
-
return system_failure_zh((proc.error?.message ?? (proc.stderr || proc.stdout)).trim(), proc.status !== null && proc.status !== undefined ? `命令执行失败(退出状态码 ${proc.status})。` : "命令执行失败,请查看终端日志后重试。");
|
|
17
|
-
}
|
|
18
14
|
function renderCommand(cmd, args) {
|
|
19
15
|
return [cmd, ...args].join(" ");
|
|
20
16
|
}
|
|
@@ -59,20 +55,6 @@ export function openspec_cli_requirement_message(probe, opts = {}) {
|
|
|
59
55
|
}
|
|
60
56
|
return `${probe.message}。请先安装或升级 @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION}(${OPENSPEC_INSTALL_DOC_URL}),然后重新运行 \`superspec init --scope project\`。`;
|
|
61
57
|
}
|
|
62
|
-
function openspecSkillProblems(repoRoot) {
|
|
63
|
-
const skillsRoot = join(repoRoot, ".codex", "skills");
|
|
64
|
-
const problems = [];
|
|
65
|
-
for (const name of REQUIRED_OPENSPEC_CODEX_SKILLS) {
|
|
66
|
-
const skillPath = join(skillsRoot, name, "SKILL.md");
|
|
67
|
-
if (!existsSync(skillPath) || !statSync(skillPath).isFile()) {
|
|
68
|
-
problems.push(name);
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (read_skill_frontmatter_name(skillPath) !== name)
|
|
72
|
-
problems.push(name);
|
|
73
|
-
}
|
|
74
|
-
return problems.sort();
|
|
75
|
-
}
|
|
76
58
|
function writeSuperSpecAgent(repoRoot, name) {
|
|
77
59
|
const filePath = join(repoRoot, ".codex", "agents", `${name}.toml`);
|
|
78
60
|
mkdirSync(join(repoRoot, ".codex", "agents"), { recursive: true });
|
|
@@ -105,38 +87,14 @@ function writeSuperSpecPrompt(repoRoot, name) {
|
|
|
105
87
|
].join("\n"), "utf8");
|
|
106
88
|
return filePath;
|
|
107
89
|
}
|
|
108
|
-
function
|
|
90
|
+
function ensureOpenSpecCliSurface(repoRoot, actions) {
|
|
109
91
|
const probe = openspec_cli_probe({ cwd: repoRoot });
|
|
110
|
-
if (!probe.ok)
|
|
111
|
-
return [openspec_cli_requirement_message(probe, { cwd: repoRoot })];
|
|
112
|
-
let problems = openspecSkillProblems(repoRoot);
|
|
113
|
-
if (problems.length === 0) {
|
|
114
|
-
actions.push({ action: "openspec_codex_skills", status: "ok" });
|
|
115
|
-
return [];
|
|
116
|
-
}
|
|
117
|
-
const init = runCommand("openspec", ["init", "--tools", "codex", "."], { cwd: repoRoot, timeout: 60_000 });
|
|
118
|
-
actions.push({
|
|
119
|
-
action: "openspec init --tools codex .",
|
|
120
|
-
status: init.status === 0 ? "updated" : "failed",
|
|
121
|
-
refs: problems,
|
|
122
|
-
detail: init.status === 0 ? undefined : commandFailure(init),
|
|
123
|
-
});
|
|
124
|
-
if (init.error || init.status !== 0)
|
|
125
|
-
return [`openspec init 执行失败:${commandFailure(init)}`];
|
|
126
|
-
problems = openspecSkillProblems(repoRoot);
|
|
127
|
-
if (problems.length === 0)
|
|
128
|
-
return [];
|
|
129
|
-
const update = runCommand("openspec", ["update", "--force", "."], { cwd: repoRoot, timeout: 60_000 });
|
|
130
92
|
actions.push({
|
|
131
|
-
action: "
|
|
132
|
-
status:
|
|
133
|
-
|
|
134
|
-
detail: update.status === 0 ? undefined : commandFailure(update),
|
|
93
|
+
action: "openspec_cli_surface",
|
|
94
|
+
status: probe.ok ? "ok" : "failed",
|
|
95
|
+
detail: probe.message,
|
|
135
96
|
});
|
|
136
|
-
|
|
137
|
-
return [`openspec update 执行失败:${commandFailure(update)}`];
|
|
138
|
-
problems = openspecSkillProblems(repoRoot);
|
|
139
|
-
return problems.length === 0 ? [] : [`OpenSpec 配套技能文件仍然缺失或无效:${problems.join(", ")}`];
|
|
97
|
+
return probe.ok ? [] : [openspec_cli_requirement_message(probe, { cwd: repoRoot })];
|
|
140
98
|
}
|
|
141
99
|
function ensureSuperSpecRoles(repoRoot, actions) {
|
|
142
100
|
const problems = [];
|
|
@@ -182,7 +140,7 @@ export function project_init(repoRootRaw = process.cwd(), opts = {}) {
|
|
|
182
140
|
const repoRoot = resolve(repoRootRaw);
|
|
183
141
|
const actions = [];
|
|
184
142
|
const problems = [
|
|
185
|
-
...
|
|
143
|
+
...ensureOpenSpecCliSurface(repoRoot, actions),
|
|
186
144
|
...ensureSuperSpecWorkflow(repoRoot, actions, opts.force === true),
|
|
187
145
|
...ensureSuperSpecRoles(repoRoot, actions),
|
|
188
146
|
];
|
package/dist/src/tasks.d.ts
CHANGED
|
@@ -5,12 +5,22 @@ export type TestContractRecord = {
|
|
|
5
5
|
scenario_ref: string;
|
|
6
6
|
invariant_refs: string[];
|
|
7
7
|
};
|
|
8
|
+
export type TestCommandResolution = {
|
|
9
|
+
test_id: string;
|
|
10
|
+
command: string | null;
|
|
11
|
+
source: "test_command" | "test_command_ref" | null;
|
|
12
|
+
command_ref?: string;
|
|
13
|
+
expected_failure_signature?: string;
|
|
14
|
+
expected_failure_classifier?: string;
|
|
15
|
+
blockers: Reason[];
|
|
16
|
+
};
|
|
8
17
|
export declare function parse_tasks(changeRoot: string): Record<string, TaskInfo>;
|
|
9
18
|
export declare function tasks_structure_hash(changeRoot: string): string | null;
|
|
10
19
|
export declare function splitList(value: string): string[];
|
|
11
20
|
export declare function parse_test_contract_records(changeRoot: string): TestContractRecord[];
|
|
12
21
|
export declare function parse_test_contract_ids(changeRoot: string): Set<string>;
|
|
13
22
|
export declare function test_contract_text(changeRoot: string): string;
|
|
23
|
+
export declare function resolve_test_contract_command(changeRoot: string, testId: string, config?: JsonMap): TestCommandResolution;
|
|
14
24
|
export declare function parse_spec_scenarios(changeRoot: string): string[];
|
|
15
25
|
export declare function test_contract_covers_scenario(changeRoot: string, scenario: string): boolean;
|
|
16
26
|
export declare function test_contract_invariant_refs_by_test(changeRoot: string): Map<string, Set<string>>;
|
package/dist/src/tasks.js
CHANGED
|
@@ -120,6 +120,92 @@ export function test_contract_text(changeRoot) {
|
|
|
120
120
|
return "";
|
|
121
121
|
return readFileSync(filePath, "utf8");
|
|
122
122
|
}
|
|
123
|
+
function test_contract_section_lines(changeRoot, testId) {
|
|
124
|
+
const lines = test_contract_text(changeRoot).split(/\r?\n/);
|
|
125
|
+
const start = lines.findIndex((line) => {
|
|
126
|
+
const match = line.trim().match(/^###\s+(\S+)\s*$/);
|
|
127
|
+
return match?.[1] === testId;
|
|
128
|
+
});
|
|
129
|
+
if (start < 0)
|
|
130
|
+
return [];
|
|
131
|
+
const out = [];
|
|
132
|
+
for (const line of lines.slice(start + 1)) {
|
|
133
|
+
if (/^###\s+\S+/.test(line.trim()) || /^##\s+/.test(line.trim()))
|
|
134
|
+
break;
|
|
135
|
+
out.push(line);
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
function contractField(line) {
|
|
140
|
+
const match = line.match(/^\s*-\s+`?([A-Za-z0-9_-]+)`?\s*:\s*(.*)$/);
|
|
141
|
+
if (!match)
|
|
142
|
+
return null;
|
|
143
|
+
return { key: match[1], value: match[2].trim() };
|
|
144
|
+
}
|
|
145
|
+
export function resolve_test_contract_command(changeRoot, testId, config = {}) {
|
|
146
|
+
const blockers = [];
|
|
147
|
+
const lines = test_contract_section_lines(changeRoot, testId);
|
|
148
|
+
if (lines.length === 0) {
|
|
149
|
+
return { test_id: testId, command: null, source: null, blockers: [reason("missing_test_contract_section", `test contract missing ### ${testId} section`)] };
|
|
150
|
+
}
|
|
151
|
+
const fields = new Map();
|
|
152
|
+
for (const line of lines) {
|
|
153
|
+
const field = contractField(line);
|
|
154
|
+
if (!field)
|
|
155
|
+
continue;
|
|
156
|
+
const values = fields.get(field.key) ?? [];
|
|
157
|
+
values.push(field.value);
|
|
158
|
+
fields.set(field.key, values);
|
|
159
|
+
}
|
|
160
|
+
const commands = fields.get("test_command") ?? [];
|
|
161
|
+
const refs = fields.get("test_command_ref") ?? [];
|
|
162
|
+
if (commands.length === 0 && refs.length === 0)
|
|
163
|
+
blockers.push(reason("missing_test_command", `test contract ${testId} requires exactly one test_command or test_command_ref`));
|
|
164
|
+
if (commands.length > 1 || refs.length > 1)
|
|
165
|
+
blockers.push(reason("duplicate_test_command", `test contract ${testId} has duplicate test command fields`));
|
|
166
|
+
if (commands.length > 0 && refs.length > 0)
|
|
167
|
+
blockers.push(reason("ambiguous_test_command", `test contract ${testId} must not mix test_command and test_command_ref`));
|
|
168
|
+
const expected_failure_signature = (fields.get("expected_failure_signature") ?? [])[0];
|
|
169
|
+
const expected_failure_classifier = (fields.get("expected_failure_classifier") ?? [])[0];
|
|
170
|
+
if (commands.length === 1 && refs.length === 0) {
|
|
171
|
+
const command = commands[0];
|
|
172
|
+
if (!command)
|
|
173
|
+
blockers.push(reason("missing_test_command", `test contract ${testId} has empty test_command`));
|
|
174
|
+
return {
|
|
175
|
+
test_id: testId,
|
|
176
|
+
command: command || null,
|
|
177
|
+
source: command ? "test_command" : null,
|
|
178
|
+
expected_failure_signature,
|
|
179
|
+
expected_failure_classifier,
|
|
180
|
+
blockers,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (refs.length === 1 && commands.length === 0) {
|
|
184
|
+
const refValue = refs[0];
|
|
185
|
+
const match = refValue.match(/^config\.commands\.([A-Za-z0-9_.-]+)$/);
|
|
186
|
+
if (!match) {
|
|
187
|
+
blockers.push(reason("untrusted_test_command_ref", `test contract ${testId} test_command_ref must be config.commands.<id>`));
|
|
188
|
+
return { test_id: testId, command: null, source: null, command_ref: refValue, expected_failure_signature, expected_failure_classifier, blockers };
|
|
189
|
+
}
|
|
190
|
+
const commandId = match[1];
|
|
191
|
+
const commandsConfig = config.commands;
|
|
192
|
+
const command = commandsConfig && typeof commandsConfig === "object" ? commandsConfig[commandId] : undefined;
|
|
193
|
+
if (typeof command !== "string" || !command.trim()) {
|
|
194
|
+
blockers.push(reason("untrusted_test_command_ref", `test contract ${testId} test_command_ref ${refValue} does not resolve to config.commands.${commandId}`));
|
|
195
|
+
return { test_id: testId, command: null, source: null, command_ref: refValue, expected_failure_signature, expected_failure_classifier, blockers };
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
test_id: testId,
|
|
199
|
+
command,
|
|
200
|
+
source: "test_command_ref",
|
|
201
|
+
command_ref: refValue,
|
|
202
|
+
expected_failure_signature,
|
|
203
|
+
expected_failure_classifier,
|
|
204
|
+
blockers,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return { test_id: testId, command: null, source: null, expected_failure_signature, expected_failure_classifier, blockers };
|
|
208
|
+
}
|
|
123
209
|
export function parse_spec_scenarios(changeRoot) {
|
|
124
210
|
const specs = join(changeRoot, "specs");
|
|
125
211
|
const scenarios = [];
|
package/dist/src/util.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export type Decision = {
|
|
|
40
40
|
workflow_terms_zh?: WorkflowTermHint[];
|
|
41
41
|
};
|
|
42
42
|
export type DecisionOutputFormat = "json" | "agent" | "user";
|
|
43
|
+
export type PacketOutputFormat = "agent" | "prompt";
|
|
43
44
|
export type AgentWorkflowAction = "continue" | "fix_artifacts" | "ask_user_confirmation" | "collect_review_evidence" | "collect_test_evidence" | "repair_evidence" | "rerun_check" | "inspect_diagnostics";
|
|
44
45
|
export type TaskInfo = {
|
|
45
46
|
task_id: string;
|
|
@@ -72,10 +73,10 @@ export declare const TASK_REOPEN_RESOLVED_REQUIRED_FIELDS: readonly ["reopen_evi
|
|
|
72
73
|
export declare const TASK_REOPEN_INVALIDITY_CLASSES: readonly ["insufficient_completion_evidence"];
|
|
73
74
|
export declare const FORBIDDEN_FIELDS: Set<string>;
|
|
74
75
|
export declare const OPENSPEC_ARTIFACTS: Set<string>;
|
|
75
|
-
export declare const REQUIRED_OPENSPEC_CODEX_SKILLS: readonly [
|
|
76
|
+
export declare const REQUIRED_OPENSPEC_CODEX_SKILLS: readonly [];
|
|
76
77
|
export declare const REQUIRED_SUPERSPEC_WORKFLOW_SKILLS: readonly ["superspec-explore", "superspec-propose", "superspec-apply", "superspec-review", "superspec-archive"];
|
|
77
|
-
export declare const REQUIRED_SUPERSPEC_AGENT_ROLES: readonly ["architect", "critic", "test-engineer", "code-reviewer", "verifier"];
|
|
78
|
-
export declare const REQUIRED_OPENSPEC_CLI_SURFACES: readonly [readonly ["instructions", "--help"], readonly ["archive", "--help"], readonly ["validate", "--help"], readonly ["status", "--help"]];
|
|
78
|
+
export declare const REQUIRED_SUPERSPEC_AGENT_ROLES: readonly ["architect", "critic", "executor", "test-runner", "test-engineer", "code-reviewer", "verifier"];
|
|
79
|
+
export declare const REQUIRED_OPENSPEC_CLI_SURFACES: readonly [readonly ["list", "--help"], readonly ["instructions", "--help"], readonly ["archive", "--help"], readonly ["validate", "--help"], readonly ["status", "--help"]];
|
|
79
80
|
export declare const ARTIFACT_ENTER_GATE: Record<string, string>;
|
|
80
81
|
export declare const ROUTE_ORDER: Record<string, number>;
|
|
81
82
|
export declare const ROUTE_ALIASES: Record<string, string>;
|
|
@@ -85,6 +86,9 @@ export declare class GuardError extends Error {
|
|
|
85
86
|
}
|
|
86
87
|
export declare const runtime: JsonMap;
|
|
87
88
|
export declare function parseDecisionOutputFormat(raw: string): DecisionOutputFormat;
|
|
89
|
+
export declare function parsePacketOutputFormat(raw: string, opts?: {
|
|
90
|
+
allowPrompt?: boolean;
|
|
91
|
+
}): PacketOutputFormat;
|
|
88
92
|
export declare function reason(code: string, message: string, refs?: string[] | null): Reason;
|
|
89
93
|
export declare function pinned_ref_key(item: JsonMap): string;
|
|
90
94
|
export declare function trustWarnings(): string[];
|
|
@@ -113,6 +117,10 @@ export declare function printDecision(decision: JsonMap, opts?: {
|
|
|
113
117
|
command?: string;
|
|
114
118
|
format?: DecisionOutputFormat;
|
|
115
119
|
}): void;
|
|
120
|
+
export declare function printPacket(payload: JsonMap | string, opts: {
|
|
121
|
+
format: PacketOutputFormat;
|
|
122
|
+
}): void;
|
|
123
|
+
export declare function printPacketError(code: string, message: string): void;
|
|
116
124
|
export declare function runCommand(cmd: string, args: string[], opts?: {
|
|
117
125
|
cwd?: string;
|
|
118
126
|
timeout?: number;
|
package/dist/src/util.js
CHANGED
|
@@ -64,6 +64,7 @@ export const EVIDENCE_KINDS = new Set([
|
|
|
64
64
|
"test_run",
|
|
65
65
|
"alternative_verification",
|
|
66
66
|
"manual_verification",
|
|
67
|
+
"apply_worker_chain",
|
|
67
68
|
"task_reopen",
|
|
68
69
|
"task_reopen_resolved",
|
|
69
70
|
"human_confirmation",
|
|
@@ -146,12 +147,10 @@ export const FORBIDDEN_FIELDS = new Set([
|
|
|
146
147
|
"artifact_status",
|
|
147
148
|
]);
|
|
148
149
|
export const OPENSPEC_ARTIFACTS = new Set(["proposal", "specs", "design", "tasks"]);
|
|
149
|
-
export
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"openspec-archive-change",
|
|
154
|
-
];
|
|
150
|
+
// Compatibility export for callers that imported the old repo-local OpenSpec bridge list.
|
|
151
|
+
// Phase 4 makes the OpenSpec CLI surface the runtime truth, so no repo-local OpenSpec skills are
|
|
152
|
+
// required by SuperSpec init or health checks.
|
|
153
|
+
export const REQUIRED_OPENSPEC_CODEX_SKILLS = [];
|
|
155
154
|
// D4 (audit G-2): SuperSpec's own workflow skills are part of the init health surface — a deleted
|
|
156
155
|
// or renamed superspec-* skill must be visible at check-init, not discovered mid-workflow.
|
|
157
156
|
export const REQUIRED_SUPERSPEC_WORKFLOW_SKILLS = [
|
|
@@ -164,11 +163,14 @@ export const REQUIRED_SUPERSPEC_WORKFLOW_SKILLS = [
|
|
|
164
163
|
export const REQUIRED_SUPERSPEC_AGENT_ROLES = [
|
|
165
164
|
"architect",
|
|
166
165
|
"critic",
|
|
166
|
+
"executor",
|
|
167
|
+
"test-runner",
|
|
167
168
|
"test-engineer",
|
|
168
169
|
"code-reviewer",
|
|
169
170
|
"verifier",
|
|
170
171
|
];
|
|
171
172
|
export const REQUIRED_OPENSPEC_CLI_SURFACES = [
|
|
173
|
+
["list", "--help"],
|
|
172
174
|
["instructions", "--help"],
|
|
173
175
|
["archive", "--help"],
|
|
174
176
|
["validate", "--help"],
|
|
@@ -241,6 +243,13 @@ export function parseDecisionOutputFormat(raw) {
|
|
|
241
243
|
return raw;
|
|
242
244
|
throw new GuardError("--format 只允许 json、agent 或 user");
|
|
243
245
|
}
|
|
246
|
+
export function parsePacketOutputFormat(raw, opts = {}) {
|
|
247
|
+
if (raw === "agent")
|
|
248
|
+
return raw;
|
|
249
|
+
if (opts.allowPrompt && raw === "prompt")
|
|
250
|
+
return raw;
|
|
251
|
+
throw new GuardError(opts.allowPrompt ? "--format 只允许 agent 或 prompt" : "--format 只允许 agent");
|
|
252
|
+
}
|
|
244
253
|
export function reason(code, message, refs = null) {
|
|
245
254
|
const zh = reason_zh(code);
|
|
246
255
|
return { code, message, refs: refs ?? [], label_zh: zh.label_zh, hint_zh: zh.hint_zh };
|
|
@@ -588,6 +597,18 @@ export function printDecision(decision, opts = {}) {
|
|
|
588
597
|
}
|
|
589
598
|
process.stdout.write(`${JSON.stringify(sanitizeDecisionForOutput(decorated), null, 2)}\n`);
|
|
590
599
|
}
|
|
600
|
+
export function printPacket(payload, opts) {
|
|
601
|
+
if (opts.format === "prompt") {
|
|
602
|
+
process.stdout.write(String(payload));
|
|
603
|
+
if (!String(payload).endsWith("\n"))
|
|
604
|
+
process.stdout.write("\n");
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
608
|
+
}
|
|
609
|
+
export function printPacketError(code, message) {
|
|
610
|
+
process.stdout.write(`${JSON.stringify({ status: "error", error_code: code, message }, null, 2)}\n`);
|
|
611
|
+
}
|
|
591
612
|
export function runCommand(cmd, args, opts = {}) {
|
|
592
613
|
const platform = opts.platform ?? process.platform;
|
|
593
614
|
const invocation = platform === "win32" ? windowsCommandInvocation(cmd, args, opts.cwd) : { cmd, args };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peterxiaoyang/superspec",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "SuperSpec workflow package: guard runtime, generic workflow templates, and Codex adapter payload.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"scripts": {
|
|
52
52
|
"build": "node build.js",
|
|
53
53
|
"typecheck": "tsc --noEmit",
|
|
54
|
-
"test": "node --test tests/test_install_engine.test.ts tests/test_real_openspec_smoke.test.ts tests/test_superspec_cli.test.ts tests/
|
|
54
|
+
"test": "node --test tests/test_install_engine.test.ts tests/test_packet_measure.test.ts tests/test_real_openspec_smoke.test.ts tests/test_superspec_cli.test.ts tests/test_superspec_guard_output.test.ts tests/test_superspec_guard_core.test.ts tests/test_superspec_guard_review.test.ts tests/test_superspec_guard_request_reopen.test.ts tests/test_superspec_guard_archive.test.ts tests/test_superspec_guard_packet.test.ts tests/test_superspec_guard_disclosure.test.ts tests/test_superspec_skills.test.ts",
|
|
55
55
|
"prepack": "npm run build",
|
|
56
56
|
"prepublishOnly": "npm run build",
|
|
57
57
|
"pack:dry-run": "npm pack --dry-run"
|
|
@@ -75,6 +75,23 @@
|
|
|
75
75
|
"type": "string",
|
|
76
76
|
"minLength": 1
|
|
77
77
|
}
|
|
78
|
+
},
|
|
79
|
+
"configPatch": {
|
|
80
|
+
"type": "object",
|
|
81
|
+
"additionalProperties": false,
|
|
82
|
+
"required": ["path", "retainedOnUninstall", "managed"],
|
|
83
|
+
"properties": {
|
|
84
|
+
"path": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"minLength": 1
|
|
87
|
+
},
|
|
88
|
+
"retainedOnUninstall": {
|
|
89
|
+
"const": true
|
|
90
|
+
},
|
|
91
|
+
"managed": {
|
|
92
|
+
"const": false
|
|
93
|
+
}
|
|
94
|
+
}
|
|
78
95
|
}
|
|
79
96
|
}
|
|
80
97
|
}
|
|
@@ -1,120 +1,27 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "
|
|
3
|
-
argument-hint: "
|
|
2
|
+
description: "架构与边界审查角色"
|
|
3
|
+
argument-hint: "任务说明或 review-packet prompt_ref"
|
|
4
4
|
---
|
|
5
|
-
<identity>
|
|
6
|
-
你是 Architect(Oracle)。你基于文件证据做诊断、分析和建议。你只读,不修改文件。
|
|
7
|
-
</identity>
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
- 所有用户可见输出必须使用简体中文。
|
|
11
|
-
- 命令、路径、JSON/schema 字段、代码标识符、gate 名称、任务/测试 id、协议字面值在需要精确表达时保持原样。
|
|
12
|
-
- 最终文本不要使用英文分节标题,例如 "Summary"、"Analysis"、"Root Cause"、"Recommendations";整份报告用中文写。
|
|
13
|
-
- 转述工作流或 guard 概念时,用中文解释,不要直接粘贴英文模板原句。
|
|
14
|
-
</language>
|
|
15
|
-
|
|
16
|
-
<constraints>
|
|
17
|
-
<scope_guard>
|
|
18
|
-
- Never write or edit files.
|
|
19
|
-
- Never judge code you have not opened.
|
|
20
|
-
- Never give generic advice detached from this codebase.
|
|
21
|
-
- Acknowledge uncertainty instead of speculating.
|
|
22
|
-
</scope_guard>
|
|
23
|
-
|
|
24
|
-
<ask_gate>
|
|
25
|
-
- Default to outcome-first, evidence-dense analysis; add depth only when it materially improves the result, evidence, or stop condition.
|
|
26
|
-
- Treat newer user task updates as local overrides for the active analysis thread while preserving earlier non-conflicting constraints.
|
|
27
|
-
- Ask only when the next step materially changes scope or requires a business decision.
|
|
28
|
-
</ask_gate>
|
|
29
|
-
</constraints>
|
|
30
|
-
|
|
31
|
-
<execution_loop>
|
|
32
|
-
1. 先收集上下文。
|
|
33
|
-
2. 形成假设。
|
|
34
|
-
3. 用代码事实交叉验证。
|
|
35
|
-
4. 返回摘要、根因、建议和取舍。
|
|
36
|
-
|
|
37
|
-
<success_criteria>
|
|
38
|
-
- 每条重要结论都要附 file:line 证据。
|
|
39
|
-
- 要指出根因,而不只是症状。
|
|
40
|
-
- 建议必须具体且可执行。
|
|
41
|
-
- 必须说明取舍。
|
|
42
|
-
- 在 ralplan 共识审查中,要包含反论、张力和综合方案。
|
|
43
|
-
- 在 `superspec-review` 中,要输出基于来源证据的架构 guidance 和升级点;最终判断由主流程完成,不由本角色直接下判。
|
|
44
|
-
</success_criteria>
|
|
45
|
-
|
|
46
|
-
<verification_loop>
|
|
47
|
-
- 默认投入强度:高。
|
|
48
|
-
- 当诊断和建议已经有证据支撑时停止。
|
|
49
|
-
- 在分析真正落地前持续阅读。
|
|
50
|
-
- 如果是 ralplan 共识审查,要明确写出取舍张力与综合方案。
|
|
51
|
-
</verification_loop>
|
|
52
|
-
|
|
53
|
-
<tool_persistence>
|
|
54
|
-
只要 file:line 证据还缺失,就不要停在“看起来合理”的猜测上。
|
|
55
|
-
</tool_persistence>
|
|
56
|
-
</execution_loop>
|
|
57
|
-
|
|
58
|
-
<tools>
|
|
59
|
-
- 并行使用 Glob/Grep/Read。
|
|
60
|
-
- 当诊断会因此更扎实时,再使用诊断工具和 git 历史。
|
|
61
|
-
- 如果需要更宽的审查范围,就向上汇报,不要自行横向改派。
|
|
62
|
-
</tools>
|
|
6
|
+
# Architect
|
|
63
7
|
|
|
64
|
-
|
|
65
|
-
<output_contract>
|
|
66
|
-
默认最终输出形态:结果优先、证据密集;直接给出结论、支撑证据、验证或引用状态,以及停止条件,不要铺垫。
|
|
8
|
+
## 角色身份
|
|
67
9
|
|
|
68
|
-
|
|
69
|
-
[2-3 句:发现了什么、主建议是什么]
|
|
10
|
+
你是 Architect。你审查系统边界、接口契约、数据流、长期维护风险、回滚难度和设计取舍。你提供架构 guidance,不替代主流程做最终判断。
|
|
70
11
|
|
|
71
|
-
##
|
|
72
|
-
[详细发现,带 file:line 引用]
|
|
12
|
+
## 读写边界
|
|
73
13
|
|
|
74
|
-
|
|
75
|
-
|
|
14
|
+
- 默认只读;不要修改文件。
|
|
15
|
+
- 不评价没有打开或没有被 packet/source refs 指向的材料。
|
|
16
|
+
- 如果需要扩大审查范围,向主流程说明缺口,不要自行改派或改代码。
|
|
76
17
|
|
|
77
|
-
##
|
|
78
|
-
1. [最高优先级] - [工作量] - [影响]
|
|
79
|
-
2. [下一优先级] - [工作量] - [影响]
|
|
18
|
+
## SuperSpec Packet 规则
|
|
80
19
|
|
|
81
|
-
|
|
82
|
-
- 关键架构判断
|
|
83
|
-
- 建议直接加载的 source refs
|
|
84
|
-
- 建议升级或后续动作
|
|
20
|
+
在 `superspec-review` 或 disclosure review 中,先读取主流程提供的 `review-packet` 或 `prompt_ref`。以 packet 中的 `target_refs`、`source_refs`、`required_output_kind`、`output_contract_fields`、`required_review_scope` 和 `stop_conditions` 为准;不要依赖本 prompt 记忆输出 schema。
|
|
85
21
|
|
|
86
|
-
##
|
|
87
|
-
| 方案 | 优点 | 代价 |
|
|
88
|
-
|------|------|------|
|
|
89
|
-
| A | ... | ... |
|
|
90
|
-
| B | ... | ... |
|
|
22
|
+
## 输出风格
|
|
91
23
|
|
|
92
|
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
|
|
97
|
-
## 引用
|
|
98
|
-
- `path/to/file.ts:42` - [该处证明了什么]
|
|
99
|
-
- `path/to/other.ts:108` - [该处证明了什么]
|
|
100
|
-
</output_contract>
|
|
101
|
-
|
|
102
|
-
<scenario_handling>
|
|
103
|
-
- **正确示例:** 用户在你已经定位到高概率根因后说 `continue`。继续补齐缺失的 file:line 证据。
|
|
104
|
-
|
|
105
|
-
- **正确示例:** 分析完成后,用户说 `make a PR`。把它当成下游流程上下文,而不是稀释分析深度的理由。
|
|
106
|
-
|
|
107
|
-
- **正确示例:** 用户说 `merge if CI green`。把它当成后续操作条件,而不是跳过剩余证据的理由。
|
|
108
|
-
|
|
109
|
-
- **错误示例:** 用户说 `continue`,你却重新开始分析,或把之前已经拿到的证据丢掉。
|
|
110
|
-
</scenario_handling>
|
|
111
|
-
|
|
112
|
-
<final_checklist>
|
|
113
|
-
- 我是否在下结论前读过代码?
|
|
114
|
-
- 每个关键发现是否都附了 file:line 证据?
|
|
115
|
-
- 根因是否说清楚了?
|
|
116
|
-
- 建议是否足够具体可执行?
|
|
117
|
-
- 我是否说明了取舍?
|
|
118
|
-
- 如果这是 ralplan 共识审查,我是否包含了反论、张力与综合方案?
|
|
119
|
-
</final_checklist>
|
|
120
|
-
</style>
|
|
24
|
+
- 所有用户可见输出必须使用简体中文。
|
|
25
|
+
- 命令、路径、JSON/schema 字段、gate 名称、任务/测试 id、代码标识符保留原文。
|
|
26
|
+
- 结论先行,按严重度列出问题,给出文件/行号证据。
|
|
27
|
+
- 无阻塞问题时明确写“无阻塞问题”,并列残余风险或未验证项。
|