@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
|
@@ -0,0 +1,55 @@
|
|
|
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 PacketDispatchResult = {
|
|
50
|
+
output_format: "agent";
|
|
51
|
+
payload: WorkflowPacket | ReviewPacket;
|
|
52
|
+
} | {
|
|
53
|
+
output_format: "prompt";
|
|
54
|
+
payload: string;
|
|
55
|
+
};
|
|
@@ -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/util.d.ts
CHANGED
|
@@ -39,6 +39,9 @@ export type Decision = {
|
|
|
39
39
|
trust_warnings_zh?: string[];
|
|
40
40
|
workflow_terms_zh?: WorkflowTermHint[];
|
|
41
41
|
};
|
|
42
|
+
export type DecisionOutputFormat = "json" | "agent" | "user";
|
|
43
|
+
export type PacketOutputFormat = "agent" | "prompt";
|
|
44
|
+
export type AgentWorkflowAction = "continue" | "fix_artifacts" | "ask_user_confirmation" | "collect_review_evidence" | "collect_test_evidence" | "repair_evidence" | "rerun_check" | "inspect_diagnostics";
|
|
42
45
|
export type TaskInfo = {
|
|
43
46
|
task_id: string;
|
|
44
47
|
checked: boolean;
|
|
@@ -70,10 +73,10 @@ export declare const TASK_REOPEN_RESOLVED_REQUIRED_FIELDS: readonly ["reopen_evi
|
|
|
70
73
|
export declare const TASK_REOPEN_INVALIDITY_CLASSES: readonly ["insufficient_completion_evidence"];
|
|
71
74
|
export declare const FORBIDDEN_FIELDS: Set<string>;
|
|
72
75
|
export declare const OPENSPEC_ARTIFACTS: Set<string>;
|
|
73
|
-
export declare const REQUIRED_OPENSPEC_CODEX_SKILLS: readonly [
|
|
76
|
+
export declare const REQUIRED_OPENSPEC_CODEX_SKILLS: readonly [];
|
|
74
77
|
export declare const REQUIRED_SUPERSPEC_WORKFLOW_SKILLS: readonly ["superspec-explore", "superspec-propose", "superspec-apply", "superspec-review", "superspec-archive"];
|
|
75
78
|
export declare const REQUIRED_SUPERSPEC_AGENT_ROLES: readonly ["architect", "critic", "test-engineer", "code-reviewer", "verifier"];
|
|
76
|
-
export declare const REQUIRED_OPENSPEC_CLI_SURFACES: readonly [readonly ["instructions", "--help"], readonly ["archive", "--help"], readonly ["validate", "--help"], readonly ["status", "--help"]];
|
|
79
|
+
export declare const REQUIRED_OPENSPEC_CLI_SURFACES: readonly [readonly ["list", "--help"], readonly ["instructions", "--help"], readonly ["archive", "--help"], readonly ["validate", "--help"], readonly ["status", "--help"]];
|
|
77
80
|
export declare const ARTIFACT_ENTER_GATE: Record<string, string>;
|
|
78
81
|
export declare const ROUTE_ORDER: Record<string, number>;
|
|
79
82
|
export declare const ROUTE_ALIASES: Record<string, string>;
|
|
@@ -82,6 +85,10 @@ export declare const GATE_ROUTE: Record<string, string>;
|
|
|
82
85
|
export declare class GuardError extends Error {
|
|
83
86
|
}
|
|
84
87
|
export declare const runtime: JsonMap;
|
|
88
|
+
export declare function parseDecisionOutputFormat(raw: string): DecisionOutputFormat;
|
|
89
|
+
export declare function parsePacketOutputFormat(raw: string, opts?: {
|
|
90
|
+
allowPrompt?: boolean;
|
|
91
|
+
}): PacketOutputFormat;
|
|
85
92
|
export declare function reason(code: string, message: string, refs?: string[] | null): Reason;
|
|
86
93
|
export declare function pinned_ref_key(item: JsonMap): string;
|
|
87
94
|
export declare function trustWarnings(): string[];
|
|
@@ -99,9 +106,21 @@ export declare function block(change: string, gate: string, reasons: Reason[], o
|
|
|
99
106
|
export declare function decorateDecision(decision: JsonMap, opts?: {
|
|
100
107
|
command?: string;
|
|
101
108
|
}): JsonMap;
|
|
109
|
+
export declare function workflowActionForReasonCodes(reasonCodes: string[], allowed?: boolean): AgentWorkflowAction;
|
|
110
|
+
export declare function renderAgentDecision(decision: JsonMap, opts?: {
|
|
111
|
+
command?: string;
|
|
112
|
+
}): JsonMap;
|
|
113
|
+
export declare function renderUserFacingDecision(decision: JsonMap, opts?: {
|
|
114
|
+
command?: string;
|
|
115
|
+
}): string;
|
|
102
116
|
export declare function printDecision(decision: JsonMap, opts?: {
|
|
103
117
|
command?: string;
|
|
118
|
+
format?: DecisionOutputFormat;
|
|
119
|
+
}): void;
|
|
120
|
+
export declare function printPacket(payload: JsonMap | string, opts: {
|
|
121
|
+
format: PacketOutputFormat;
|
|
104
122
|
}): void;
|
|
123
|
+
export declare function printPacketError(code: string, message: string): void;
|
|
105
124
|
export declare function runCommand(cmd: string, args: string[], opts?: {
|
|
106
125
|
cwd?: string;
|
|
107
126
|
timeout?: number;
|
package/dist/src/util.js
CHANGED
|
@@ -146,12 +146,10 @@ export const FORBIDDEN_FIELDS = new Set([
|
|
|
146
146
|
"artifact_status",
|
|
147
147
|
]);
|
|
148
148
|
export const OPENSPEC_ARTIFACTS = new Set(["proposal", "specs", "design", "tasks"]);
|
|
149
|
-
export
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"openspec-archive-change",
|
|
154
|
-
];
|
|
149
|
+
// Compatibility export for callers that imported the old repo-local OpenSpec bridge list.
|
|
150
|
+
// Phase 4 makes the OpenSpec CLI surface the runtime truth, so no repo-local OpenSpec skills are
|
|
151
|
+
// required by SuperSpec init or health checks.
|
|
152
|
+
export const REQUIRED_OPENSPEC_CODEX_SKILLS = [];
|
|
155
153
|
// D4 (audit G-2): SuperSpec's own workflow skills are part of the init health surface — a deleted
|
|
156
154
|
// or renamed superspec-* skill must be visible at check-init, not discovered mid-workflow.
|
|
157
155
|
export const REQUIRED_SUPERSPEC_WORKFLOW_SKILLS = [
|
|
@@ -169,6 +167,7 @@ export const REQUIRED_SUPERSPEC_AGENT_ROLES = [
|
|
|
169
167
|
"verifier",
|
|
170
168
|
];
|
|
171
169
|
export const REQUIRED_OPENSPEC_CLI_SURFACES = [
|
|
170
|
+
["list", "--help"],
|
|
172
171
|
["instructions", "--help"],
|
|
173
172
|
["archive", "--help"],
|
|
174
173
|
["validate", "--help"],
|
|
@@ -236,6 +235,18 @@ export const GATE_ROUTE = {
|
|
|
236
235
|
export class GuardError extends Error {
|
|
237
236
|
}
|
|
238
237
|
export const runtime = {};
|
|
238
|
+
export function parseDecisionOutputFormat(raw) {
|
|
239
|
+
if (raw === "json" || raw === "agent" || raw === "user")
|
|
240
|
+
return raw;
|
|
241
|
+
throw new GuardError("--format 只允许 json、agent 或 user");
|
|
242
|
+
}
|
|
243
|
+
export function parsePacketOutputFormat(raw, opts = {}) {
|
|
244
|
+
if (raw === "agent")
|
|
245
|
+
return raw;
|
|
246
|
+
if (opts.allowPrompt && raw === "prompt")
|
|
247
|
+
return raw;
|
|
248
|
+
throw new GuardError(opts.allowPrompt ? "--format 只允许 agent 或 prompt" : "--format 只允许 agent");
|
|
249
|
+
}
|
|
239
250
|
export function reason(code, message, refs = null) {
|
|
240
251
|
const zh = reason_zh(code);
|
|
241
252
|
return { code, message, refs: refs ?? [], label_zh: zh.label_zh, hint_zh: zh.hint_zh };
|
|
@@ -370,10 +381,231 @@ function sanitizeDecisionForOutput(decision) {
|
|
|
370
381
|
actions,
|
|
371
382
|
};
|
|
372
383
|
}
|
|
384
|
+
function userFacingLine(value) {
|
|
385
|
+
return String(value ?? "").replace(/\s+/gu, " ").trim();
|
|
386
|
+
}
|
|
387
|
+
const SAFE_TEXT_REPLACEMENTS = [
|
|
388
|
+
[/\bneeds_user_decision_pending\b/giu, "等待用户确认"],
|
|
389
|
+
[/\bneeds_user_decision\b/giu, "等待用户确认"],
|
|
390
|
+
[/\buser_review_decision\b/giu, "用户确认记录"],
|
|
391
|
+
[/\bmain_review_digest\b/giu, "审查问题记录"],
|
|
392
|
+
[/\breview_standing_authorization\b/giu, "长期授权记录"],
|
|
393
|
+
[/\bdecision_scope_key\b/giu, "确认范围"],
|
|
394
|
+
[/\bfinding_uid\b/giu, "问题标识"],
|
|
395
|
+
[/\bexport\s+function\s+[A-Za-z_$][\w$]*\s*\([^)]*\)/gu, "内部实现细节"],
|
|
396
|
+
[/\bfunction\s+[A-Za-z_$][\w$]*\s*\([^)]*\)/gu, "内部实现细节"],
|
|
397
|
+
[/\b[A-Za-z_$][\w$]*\s*\([^)]*\)/gu, "内部调用细节"],
|
|
398
|
+
[/裁决/gu, "确认"],
|
|
399
|
+
];
|
|
400
|
+
function safeDisplayText(value) {
|
|
401
|
+
let out = userFacingLine(value);
|
|
402
|
+
for (const [pattern, replacement] of SAFE_TEXT_REPLACEMENTS)
|
|
403
|
+
out = out.replace(pattern, replacement);
|
|
404
|
+
return out;
|
|
405
|
+
}
|
|
406
|
+
function fallbackReasonForWorkflowAction(action) {
|
|
407
|
+
const fallbacks = {
|
|
408
|
+
continue: "当前检查已通过。",
|
|
409
|
+
ask_user_confirmation: "需要用户确认后才能继续。",
|
|
410
|
+
collect_review_evidence: "需要补齐审查或验证复核记录。",
|
|
411
|
+
collect_test_evidence: "需要补齐测试或校验证据。",
|
|
412
|
+
fix_artifacts: "需要修正方案、任务或证据结构。",
|
|
413
|
+
repair_evidence: "需要修复证据记录。",
|
|
414
|
+
rerun_check: "需要重新运行当前检查。",
|
|
415
|
+
inspect_diagnostics: "需要查看诊断输出后处理。",
|
|
416
|
+
};
|
|
417
|
+
return fallbacks[action];
|
|
418
|
+
}
|
|
419
|
+
function safeReasonText(_item, action = "inspect_diagnostics") {
|
|
420
|
+
return fallbackReasonForWorkflowAction(action);
|
|
421
|
+
}
|
|
422
|
+
const WORKFLOW_ACTION_BY_REASON = [
|
|
423
|
+
[new Set([
|
|
424
|
+
"needs_user_decision_pending",
|
|
425
|
+
"user_decision_unbound",
|
|
426
|
+
"missing_review_digest",
|
|
427
|
+
"missing_human_confirmation",
|
|
428
|
+
"apply_isolation_unconfirmed",
|
|
429
|
+
"scope_expansion_unconfirmed",
|
|
430
|
+
"finding_unresolved",
|
|
431
|
+
"round_budget_exhausted",
|
|
432
|
+
]), "ask_user_confirmation"],
|
|
433
|
+
[new Set([
|
|
434
|
+
"missing_source_guidance",
|
|
435
|
+
"missing_verification_review",
|
|
436
|
+
"missing_final_verification_review",
|
|
437
|
+
"missing_roles",
|
|
438
|
+
"missing_native_subagent_evidence",
|
|
439
|
+
"missing_invariant_review",
|
|
440
|
+
"missing_test_contract_review",
|
|
441
|
+
"missing_architect_review",
|
|
442
|
+
"missing_critic_review",
|
|
443
|
+
"missing_test-engineer_review",
|
|
444
|
+
"missing_code-reviewer_review",
|
|
445
|
+
"missing_verifier_review",
|
|
446
|
+
"missing_main_adjudication",
|
|
447
|
+
"proposal_reviewed_failed",
|
|
448
|
+
"review_not_ready",
|
|
449
|
+
"missing_proposal_review",
|
|
450
|
+
]), "collect_review_evidence"],
|
|
451
|
+
[new Set([
|
|
452
|
+
"missing_red_evidence",
|
|
453
|
+
"missing_green_evidence",
|
|
454
|
+
"missing_characterization",
|
|
455
|
+
"test_contract_not_honored",
|
|
456
|
+
"validate_failed",
|
|
457
|
+
"missing_final_tests",
|
|
458
|
+
"verify_failure_unconfirmed",
|
|
459
|
+
]), "collect_test_evidence"],
|
|
460
|
+
[new Set([
|
|
461
|
+
"missing_discovery",
|
|
462
|
+
"missing_proposal",
|
|
463
|
+
"missing_design",
|
|
464
|
+
"missing_tasks",
|
|
465
|
+
"invalid_task_graph",
|
|
466
|
+
"invalid_business_invariants",
|
|
467
|
+
"review_finding_invalid",
|
|
468
|
+
"review_digest_invalid",
|
|
469
|
+
"user_decision_invalid",
|
|
470
|
+
"human_confirmation_invalid",
|
|
471
|
+
"standing_authorization_invalid",
|
|
472
|
+
"evidence_unknown_kind",
|
|
473
|
+
"evidence_missing_field",
|
|
474
|
+
"artifact_update_required",
|
|
475
|
+
"rereview_required",
|
|
476
|
+
]), "fix_artifacts"],
|
|
477
|
+
[new Set([
|
|
478
|
+
"state_concurrent_update",
|
|
479
|
+
"state_fingerprint_stale",
|
|
480
|
+
]), "rerun_check"],
|
|
481
|
+
[new Set([
|
|
482
|
+
"state_corrupt",
|
|
483
|
+
"openspec_cli_unavailable",
|
|
484
|
+
"openspec_native_surface_missing",
|
|
485
|
+
"dirty_worktree_unavailable",
|
|
486
|
+
"guard_error",
|
|
487
|
+
"guard_internal_error",
|
|
488
|
+
"unknown_gate",
|
|
489
|
+
"unknown_artifact",
|
|
490
|
+
"not_openspec_artifact",
|
|
491
|
+
]), "inspect_diagnostics"],
|
|
492
|
+
];
|
|
493
|
+
export function workflowActionForReasonCodes(reasonCodes, allowed = false) {
|
|
494
|
+
if (allowed)
|
|
495
|
+
return "continue";
|
|
496
|
+
const codes = new Set(reasonCodes);
|
|
497
|
+
for (const [matches, action] of WORKFLOW_ACTION_BY_REASON) {
|
|
498
|
+
for (const code of matches) {
|
|
499
|
+
if (codes.has(code))
|
|
500
|
+
return action;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return "inspect_diagnostics";
|
|
504
|
+
}
|
|
505
|
+
function summaryForWorkflowAction(action, allowed) {
|
|
506
|
+
if (allowed || action === "continue")
|
|
507
|
+
return "当前检查已通过,可以继续下一步。";
|
|
508
|
+
const summaries = {
|
|
509
|
+
ask_user_confirmation: "当前阶段需要用户确认一个范围或处理方式选择后才能继续。",
|
|
510
|
+
collect_review_evidence: "当前阶段缺少必要审查或验证复核记录,补齐后再继续。",
|
|
511
|
+
collect_test_evidence: "当前阶段缺少测试或校验证据,补齐后再继续。",
|
|
512
|
+
fix_artifacts: "当前阶段的方案、任务或证据结构需要修正后再继续。",
|
|
513
|
+
repair_evidence: "当前证据记录需要修复后再继续。",
|
|
514
|
+
rerun_check: "当前检查需要在输入稳定后重新运行。",
|
|
515
|
+
inspect_diagnostics: "当前检查需要查看诊断输出后处理。",
|
|
516
|
+
};
|
|
517
|
+
return summaries[action];
|
|
518
|
+
}
|
|
519
|
+
function nextStepsForWorkflowAction(action, allowed) {
|
|
520
|
+
if (allowed || action === "continue")
|
|
521
|
+
return ["继续执行下一步。"];
|
|
522
|
+
const steps = {
|
|
523
|
+
ask_user_confirmation: ["向用户展示待确认的问题与选项,记录选择后重新运行检查。"],
|
|
524
|
+
collect_review_evidence: ["补齐所需审查或验证复核记录,然后重新运行检查。"],
|
|
525
|
+
collect_test_evidence: ["补齐失败/通过测试或校验证据,然后重新运行检查。"],
|
|
526
|
+
fix_artifacts: ["修正相关方案、任务或证据结构,然后重新运行检查。"],
|
|
527
|
+
repair_evidence: ["修复证据记录中的结构或引用问题,然后重新运行检查。"],
|
|
528
|
+
rerun_check: ["等待输入稳定后重新运行当前检查。"],
|
|
529
|
+
inspect_diagnostics: ["使用诊断输出查看内部细节,再按对应问题处理。"],
|
|
530
|
+
};
|
|
531
|
+
return steps[action];
|
|
532
|
+
}
|
|
533
|
+
const DIAGNOSTIC_HINT = "需要排查内部细节时使用 --format json。";
|
|
534
|
+
export function renderAgentDecision(decision, opts = {}) {
|
|
535
|
+
const decorated = sanitizeDecisionForOutput(decorateDecision(decision, opts));
|
|
536
|
+
const reasons = Array.isArray(decorated.block_reasons) ? decorated.block_reasons : [];
|
|
537
|
+
const reasonCodes = reasons.map((item) => String(item.code ?? ""));
|
|
538
|
+
const allowed = Boolean(decorated.allowed);
|
|
539
|
+
const workflowAction = workflowActionForReasonCodes(reasonCodes, allowed);
|
|
540
|
+
const renderedReasons = reasons.map((item) => safeReasonText(item, workflowAction)).filter(Boolean);
|
|
541
|
+
return {
|
|
542
|
+
allowed,
|
|
543
|
+
status: allowed ? "allowed" : "blocked",
|
|
544
|
+
workflow_action: workflowAction,
|
|
545
|
+
stage_label_zh: safeDisplayText(decorated.gate_label_zh) || "当前阶段",
|
|
546
|
+
check_label_zh: safeDisplayText(decorated.command_label_zh) || "当前检查",
|
|
547
|
+
summary_zh: safeDisplayText(summaryForWorkflowAction(workflowAction, allowed)),
|
|
548
|
+
reasons_zh: renderedReasons.length > 0 ? renderedReasons : undefined,
|
|
549
|
+
next_steps_zh: nextStepsForWorkflowAction(workflowAction, allowed).map(safeDisplayText),
|
|
550
|
+
diagnostic_hint: DIAGNOSTIC_HINT,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
export function renderUserFacingDecision(decision, opts = {}) {
|
|
554
|
+
const decorated = sanitizeDecisionForOutput(decorateDecision(decision, opts));
|
|
555
|
+
const agentView = renderAgentDecision(decorated, opts);
|
|
556
|
+
const gate = safeDisplayText(decorated.gate_label_zh) || "当前检查";
|
|
557
|
+
const command = safeDisplayText(decorated.command_label_zh);
|
|
558
|
+
const lines = [];
|
|
559
|
+
if (decorated.allowed) {
|
|
560
|
+
lines.push(`检查通过:${gate}。`);
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
lines.push(`暂时不能继续:${gate}。`);
|
|
564
|
+
}
|
|
565
|
+
if (command && command !== gate) {
|
|
566
|
+
lines.push(`检查项:${command}。`);
|
|
567
|
+
}
|
|
568
|
+
const reasons = Array.isArray(decorated.block_reasons) ? decorated.block_reasons : [];
|
|
569
|
+
if (!decorated.allowed && reasons.length > 0) {
|
|
570
|
+
lines.push("原因:");
|
|
571
|
+
for (const item of reasons) {
|
|
572
|
+
const reasonText = safeReasonText(item, agentView.workflow_action);
|
|
573
|
+
lines.push(`- ${reasonText}`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
const nextActions = Array.isArray(agentView.next_steps_zh) ? agentView.next_steps_zh.map(safeDisplayText).filter(Boolean) : [];
|
|
577
|
+
if (nextActions.length > 0) {
|
|
578
|
+
lines.push("下一步:");
|
|
579
|
+
for (const action of nextActions)
|
|
580
|
+
lines.push(`- ${action}`);
|
|
581
|
+
}
|
|
582
|
+
lines.push(`诊断:${DIAGNOSTIC_HINT}`);
|
|
583
|
+
return `${lines.join("\n")}\n`;
|
|
584
|
+
}
|
|
373
585
|
export function printDecision(decision, opts = {}) {
|
|
374
586
|
const decorated = decorateDecision(decision, opts);
|
|
587
|
+
if (opts.format === "user") {
|
|
588
|
+
process.stdout.write(renderUserFacingDecision(decorated, opts));
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (opts.format === "agent") {
|
|
592
|
+
process.stdout.write(`${JSON.stringify(renderAgentDecision(decorated, opts), null, 2)}\n`);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
375
595
|
process.stdout.write(`${JSON.stringify(sanitizeDecisionForOutput(decorated), null, 2)}\n`);
|
|
376
596
|
}
|
|
597
|
+
export function printPacket(payload, opts) {
|
|
598
|
+
if (opts.format === "prompt") {
|
|
599
|
+
process.stdout.write(String(payload));
|
|
600
|
+
if (!String(payload).endsWith("\n"))
|
|
601
|
+
process.stdout.write("\n");
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
605
|
+
}
|
|
606
|
+
export function printPacketError(code, message) {
|
|
607
|
+
process.stdout.write(`${JSON.stringify({ status: "error", error_code: code, message }, null, 2)}\n`);
|
|
608
|
+
}
|
|
377
609
|
export function runCommand(cmd, args, opts = {}) {
|
|
378
610
|
const platform = opts.platform ?? process.platform;
|
|
379
611
|
const invocation = platform === "win32" ? windowsCommandInvocation(cmd, args, opts.cwd) : { cmd, args };
|
package/dist/superspec.js
CHANGED
|
@@ -30,16 +30,16 @@ function help() {
|
|
|
30
30
|
" version print SuperSpec CLI version",
|
|
31
31
|
"",
|
|
32
32
|
"examples:",
|
|
33
|
-
" superspec init --scope project",
|
|
33
|
+
" superspec init --scope project --format agent",
|
|
34
34
|
" superspec init --scope user",
|
|
35
|
-
" superspec guard check-init --change <change>",
|
|
35
|
+
" superspec guard check-init --change <change> --format agent",
|
|
36
36
|
" superspec doctor",
|
|
37
37
|
"",
|
|
38
38
|
].join("\n");
|
|
39
39
|
}
|
|
40
40
|
function updateHelp() {
|
|
41
41
|
return [
|
|
42
|
-
"usage: superspec update [--scope {project,user}] [--path PATH] [--codex-home PATH] [--local-only]",
|
|
42
|
+
"usage: superspec update [--scope {project,user}] [--path PATH] [--codex-home PATH] [--format {json,agent,user}] [--local-only]",
|
|
43
43
|
"",
|
|
44
44
|
"updates the global SuperSpec CLI from npm, then updates manifest-managed SuperSpec surfaces.",
|
|
45
45
|
"",
|
|
@@ -49,6 +49,7 @@ function updateHelp() {
|
|
|
49
49
|
" --user, --global equivalent to --scope user",
|
|
50
50
|
" --path PATH project root for project scope (default: current directory)",
|
|
51
51
|
" --codex-home PATH Codex user home for user scope (default: $CODEX_HOME or ~/.codex)",
|
|
52
|
+
" --format {json,agent,user} output format; use agent for workflow consumption",
|
|
52
53
|
" --local-only skip npm self-update and use the currently installed package",
|
|
53
54
|
" -h, --help show this help",
|
|
54
55
|
"",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peterxiaoyang/superspec",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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"
|
|
@@ -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
|
+
- 无阻塞问题时明确写“无阻塞问题”,并列残余风险或未验证项。
|