@peterxiaoyang/superspec 0.1.7 → 0.1.8
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/install-map.json +5 -0
- package/bin/superspec-hook.js +4 -0
- package/dist/src/archive.js +2 -0
- package/dist/src/cli_args.d.ts +5 -0
- package/dist/src/cli_args.js +41 -0
- package/dist/src/core.js +36 -0
- package/dist/src/evidence.js +3 -0
- package/dist/src/gates.js +2 -2
- package/dist/src/hooks/adapter.d.ts +5 -0
- package/dist/src/hooks/adapter.js +311 -0
- package/dist/src/hooks/guard_api.d.ts +12 -0
- package/dist/src/hooks/guard_api.js +797 -0
- package/dist/src/hooks/health.d.ts +4 -0
- package/dist/src/hooks/health.js +98 -0
- package/dist/src/hooks/policy_event.d.ts +41 -0
- package/dist/src/hooks/policy_event.js +2624 -0
- package/dist/src/hooks/types.d.ts +72 -0
- package/dist/src/hooks/types.js +1 -0
- package/dist/src/hooks/validation.d.ts +3 -0
- package/dist/src/hooks/validation.js +70 -0
- package/dist/src/packet_measure.js +67 -102
- package/dist/superspec_hook.d.ts +4 -0
- package/dist/superspec_hook.js +35 -0
- package/package.json +7 -2
- package/schemas/hook-event.schema.json +27 -0
- package/templates/hooks/codex-hooks.json +61 -0
- package/templates/workflow/skills/superspec-apply/SKILL.md +7 -0
- package/templates/workflow/skills/superspec-archive/SKILL.md +8 -0
- package/templates/workflow/skills/superspec-explore/SKILL.md +7 -0
- package/templates/workflow/skills/superspec-propose/SKILL.md +7 -0
- package/templates/workflow/skills/superspec-review/SKILL.md +7 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { JsonMap, Reason } from "../util.ts";
|
|
2
|
+
export declare const HOOK_ADAPTER_VERSION = "superspec-hook@2";
|
|
3
|
+
export type HookTrust = "trusted" | "audit-only" | "untrusted";
|
|
4
|
+
export type HookStrictProfile = "available" | "unavailable";
|
|
5
|
+
export type HookEvent = JsonMap & {
|
|
6
|
+
hook_event_name?: string;
|
|
7
|
+
session_id?: string;
|
|
8
|
+
turn_id?: string;
|
|
9
|
+
cwd?: string;
|
|
10
|
+
tool_name?: string;
|
|
11
|
+
tool_use_id?: string;
|
|
12
|
+
tool_input?: JsonMap;
|
|
13
|
+
tool_response?: JsonMap;
|
|
14
|
+
agent_id?: string;
|
|
15
|
+
agent_type?: string;
|
|
16
|
+
};
|
|
17
|
+
export type HookDecision = {
|
|
18
|
+
allowed: boolean;
|
|
19
|
+
decision: "allow" | "block" | "status";
|
|
20
|
+
change_id: string;
|
|
21
|
+
gate: string;
|
|
22
|
+
strict_profile: HookStrictProfile;
|
|
23
|
+
enforcement: "pass-through" | "guarded" | "deny" | "audit-only";
|
|
24
|
+
trust: HookTrust;
|
|
25
|
+
block_reasons: Reason[];
|
|
26
|
+
audit_only_reasons: Reason[];
|
|
27
|
+
target_paths?: string[];
|
|
28
|
+
tool_name?: string;
|
|
29
|
+
hook_event_name?: string;
|
|
30
|
+
hook_event_id?: string;
|
|
31
|
+
actions?: JsonMap[];
|
|
32
|
+
next_allowed_actions: string[];
|
|
33
|
+
trust_warnings: string[];
|
|
34
|
+
};
|
|
35
|
+
export type HookSessionRecord = {
|
|
36
|
+
schema_version: 2;
|
|
37
|
+
kind: "hook_active_session";
|
|
38
|
+
trust: HookTrust;
|
|
39
|
+
change_id: string;
|
|
40
|
+
repo_root: string;
|
|
41
|
+
session_id: string;
|
|
42
|
+
workflow: string;
|
|
43
|
+
started_at: string;
|
|
44
|
+
expires_at: string;
|
|
45
|
+
guard_version: string;
|
|
46
|
+
adapter_version: string;
|
|
47
|
+
hook_manifest_hash: string | null;
|
|
48
|
+
strict_profile: HookStrictProfile;
|
|
49
|
+
audit_only_reasons: Reason[];
|
|
50
|
+
};
|
|
51
|
+
export type HookRunlogRecord = {
|
|
52
|
+
schema_version: 2;
|
|
53
|
+
kind: "subagent_start" | "subagent_stop";
|
|
54
|
+
trust: HookTrust;
|
|
55
|
+
hook_event_id: string;
|
|
56
|
+
hook_event_name: string;
|
|
57
|
+
hook_provenance: JsonMap;
|
|
58
|
+
session_id: string;
|
|
59
|
+
turn_id?: string;
|
|
60
|
+
run_id: string;
|
|
61
|
+
agent_id: string;
|
|
62
|
+
agent_type: string;
|
|
63
|
+
cwd?: string;
|
|
64
|
+
prompt_hash?: string;
|
|
65
|
+
prompt_ref?: string;
|
|
66
|
+
output_ref?: string;
|
|
67
|
+
output_hash?: string;
|
|
68
|
+
status?: string;
|
|
69
|
+
error?: string;
|
|
70
|
+
started_at?: string;
|
|
71
|
+
stopped_at?: string;
|
|
72
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const HOOK_ADAPTER_VERSION = "superspec-hook@2";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { isObject, reason, safe_within, sha256_file } from "../util.js";
|
|
4
|
+
import { superspec_dir } from "../paths.js";
|
|
5
|
+
function runlogPath(changeRoot) {
|
|
6
|
+
return join(superspec_dir(changeRoot), "subagent-runlog.jsonl");
|
|
7
|
+
}
|
|
8
|
+
export function strictRoleRunlogReasons(changeRoot, ev) {
|
|
9
|
+
if (ev.trust !== "runtime-verified" && ev.requires_hook_runlog !== true)
|
|
10
|
+
return [];
|
|
11
|
+
const problems = [
|
|
12
|
+
reason("hook_runlog_strict_profile_unavailable", `${ev._path}: strict hook runlog validation is unavailable until R-1 deny and hook provenance pass`),
|
|
13
|
+
];
|
|
14
|
+
const target = runlogPath(changeRoot);
|
|
15
|
+
if (!existsSync(target) || !statSync(target).isFile()) {
|
|
16
|
+
problems.push(reason("hook_runlog_missing", `${ev._path}: strict role evidence requires SuperSpec hook subagent runlog`));
|
|
17
|
+
return problems;
|
|
18
|
+
}
|
|
19
|
+
const records = readFileSync(target, "utf8").split(/\r?\n/u).filter(Boolean).flatMap((line) => {
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(line);
|
|
22
|
+
return isObject(parsed) ? [parsed] : [];
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const agentId = String(ev.agent_id ?? "");
|
|
29
|
+
const role = String(ev.agent_role ?? "");
|
|
30
|
+
const starts = records.filter((item) => item.kind === "subagent_start" && item.agent_id === agentId && item.agent_type === role && item.trust === "trusted");
|
|
31
|
+
const stops = records.filter((item) => item.kind === "subagent_stop" && item.agent_id === agentId && item.agent_type === role && item.trust === "trusted");
|
|
32
|
+
if (starts.length === 0)
|
|
33
|
+
problems.push(reason("hook_runlog_start_missing", `${ev._path}: strict role evidence has no trusted matching SubagentStart`, [agentId]));
|
|
34
|
+
if (stops.length === 0)
|
|
35
|
+
problems.push(reason("hook_runlog_stop_missing", `${ev._path}: strict role evidence has no trusted matching SubagentStop`, [agentId]));
|
|
36
|
+
const outputRef = typeof ev.output_ref === "string" ? ev.output_ref : "";
|
|
37
|
+
const outputPath = outputRef ? safe_within(changeRoot, outputRef) : null;
|
|
38
|
+
const outputSha = outputPath && existsSync(outputPath) && statSync(outputPath).isFile() ? sha256_file(outputPath) : null;
|
|
39
|
+
if (outputSha && stops.length > 0 && !stops.some((item) => item.output_hash === outputSha || item.output_ref === outputRef)) {
|
|
40
|
+
problems.push(reason("hook_runlog_output_mismatch", `${ev._path}: strict role evidence output_ref does not match trusted SubagentStop output hash/ref`, [agentId]));
|
|
41
|
+
}
|
|
42
|
+
return problems;
|
|
43
|
+
}
|
|
44
|
+
export function strictRuntimeEvidenceReasons(ev) {
|
|
45
|
+
if (ev.trust !== "runtime-verified")
|
|
46
|
+
return [];
|
|
47
|
+
const problems = [
|
|
48
|
+
reason("runtime_evidence_strict_profile_unavailable", `${ev._path}: strict runtime evidence is unavailable until R-1 deny and hook provenance pass`),
|
|
49
|
+
];
|
|
50
|
+
if (typeof ev.hook_event_id !== "string" || !ev.hook_event_id)
|
|
51
|
+
problems.push(reason("runtime_evidence_missing_hook_event", `${ev._path}: runtime-verified evidence requires hook_event_id`));
|
|
52
|
+
if (!isObject(ev.hook_provenance) || ev.hook_provenance.validation !== "trusted")
|
|
53
|
+
problems.push(reason("runtime_evidence_untrusted_provenance", `${ev._path}: runtime-verified evidence requires trusted hook_provenance`));
|
|
54
|
+
if (!isObject(ev.token_binding))
|
|
55
|
+
problems.push(reason("runtime_evidence_missing_token", `${ev._path}: runtime-verified evidence requires Guard token_binding`));
|
|
56
|
+
if (typeof ev.command_fingerprint !== "string" || !ev.command_fingerprint.startsWith("sha256:"))
|
|
57
|
+
problems.push(reason("runtime_evidence_missing_command_fingerprint", `${ev._path}: runtime-verified evidence requires command_fingerprint`));
|
|
58
|
+
if (!Array.isArray(ev.raw_log_pinned_refs) || ev.raw_log_pinned_refs.length === 0)
|
|
59
|
+
problems.push(reason("runtime_evidence_missing_raw_log_pin", `${ev._path}: runtime-verified evidence requires raw_log_pinned_refs`));
|
|
60
|
+
if (typeof ev.exit_code !== "number") {
|
|
61
|
+
problems.push(reason("runtime_evidence_missing_exit_code", `${ev._path}: runtime-verified evidence requires numeric exit_code`));
|
|
62
|
+
}
|
|
63
|
+
else if ((ev.kind === "final_test" || ev.semantic_status === "expected_success") && ev.exit_code !== 0) {
|
|
64
|
+
problems.push(reason("runtime_evidence_exit_code_mismatch", `${ev._path}: expected_success/final_test runtime evidence requires exit_code=0`));
|
|
65
|
+
}
|
|
66
|
+
else if (ev.semantic_status === "expected_failure" && ev.exit_code === 0) {
|
|
67
|
+
problems.push(reason("runtime_evidence_exit_code_mismatch", `${ev._path}: RED runtime evidence requires non-zero exit_code`));
|
|
68
|
+
}
|
|
69
|
+
return problems;
|
|
70
|
+
}
|
|
@@ -1,118 +1,119 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { load_install_map } from "./install_engine.js";
|
|
4
5
|
import { dispatch_packet } from "./packet_render.js";
|
|
5
|
-
import { GuardError,
|
|
6
|
+
import { GuardError, runtime, } from "./util.js";
|
|
6
7
|
import { file_blob_sha } from "./git.js";
|
|
7
8
|
const REPRESENTATIVE_SCENARIOS = [
|
|
8
9
|
{
|
|
9
10
|
name: "explore_complete",
|
|
10
|
-
description: "
|
|
11
|
+
description: "Package payload surface for discovery authoring plus proposal-entry critic review.",
|
|
11
12
|
files: [
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
13
|
+
"templates/workflow/skills/superspec-explore/SKILL.md",
|
|
14
|
+
"templates/workflow/prompts/critic.md",
|
|
15
|
+
"adapters/codex/agents/critic.toml",
|
|
15
16
|
],
|
|
16
17
|
},
|
|
17
18
|
{
|
|
18
19
|
name: "proposal_reviewed",
|
|
19
|
-
description: "
|
|
20
|
+
description: "Package payload surface for proposal authoring and proposal critic review.",
|
|
20
21
|
files: [
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
22
|
+
"templates/workflow/skills/superspec-propose/SKILL.md",
|
|
23
|
+
"templates/workflow/prompts/critic.md",
|
|
24
|
+
"adapters/codex/agents/critic.toml",
|
|
24
25
|
],
|
|
25
26
|
},
|
|
26
27
|
{
|
|
27
28
|
name: "design_complete",
|
|
28
|
-
description: "
|
|
29
|
+
description: "Package payload surface for design authoring and the architect/critic/test-engineer review bundle.",
|
|
29
30
|
files: [
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
31
|
+
"templates/workflow/skills/superspec-propose/SKILL.md",
|
|
32
|
+
"templates/workflow/prompts/architect.md",
|
|
33
|
+
"templates/workflow/prompts/critic.md",
|
|
34
|
+
"templates/workflow/prompts/test-engineer.md",
|
|
35
|
+
"adapters/codex/agents/architect.toml",
|
|
36
|
+
"adapters/codex/agents/critic.toml",
|
|
37
|
+
"adapters/codex/agents/test-engineer.toml",
|
|
37
38
|
],
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
41
|
name: "test_contract_drafted",
|
|
41
|
-
description: "
|
|
42
|
+
description: "Package payload surface for test-contract authoring and its review lanes.",
|
|
42
43
|
files: [
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
44
|
+
"templates/workflow/skills/superspec-propose/SKILL.md",
|
|
45
|
+
"templates/workflow/prompts/critic.md",
|
|
46
|
+
"templates/workflow/prompts/test-engineer.md",
|
|
47
|
+
"adapters/codex/agents/critic.toml",
|
|
48
|
+
"adapters/codex/agents/test-engineer.toml",
|
|
48
49
|
],
|
|
49
50
|
},
|
|
50
51
|
{
|
|
51
52
|
name: "apply_ready",
|
|
52
|
-
description: "
|
|
53
|
+
description: "Package payload surface for apply orchestration and bounded executor handoff.",
|
|
53
54
|
files: [
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
55
|
+
"templates/workflow/skills/superspec-apply/SKILL.md",
|
|
56
|
+
"templates/workflow/prompts/executor.md",
|
|
57
|
+
"adapters/codex/agents/executor.toml",
|
|
57
58
|
],
|
|
58
59
|
},
|
|
59
60
|
{
|
|
60
61
|
name: "review_complete_allow",
|
|
61
|
-
description: "
|
|
62
|
+
description: "Package payload surface for merged review + final verification on the allow path.",
|
|
62
63
|
files: [
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
64
|
+
"templates/workflow/skills/superspec-review/SKILL.md",
|
|
65
|
+
"templates/workflow/prompts/code-reviewer.md",
|
|
66
|
+
"templates/workflow/prompts/architect.md",
|
|
67
|
+
"templates/workflow/prompts/critic.md",
|
|
68
|
+
"templates/workflow/prompts/verifier.md",
|
|
69
|
+
"adapters/codex/agents/code-reviewer.toml",
|
|
70
|
+
"adapters/codex/agents/architect.toml",
|
|
71
|
+
"adapters/codex/agents/critic.toml",
|
|
72
|
+
"adapters/codex/agents/verifier.toml",
|
|
72
73
|
],
|
|
73
74
|
},
|
|
74
75
|
{
|
|
75
76
|
name: "archive_ready",
|
|
76
|
-
description: "
|
|
77
|
+
description: "Package payload surface for archive handoff after review passes.",
|
|
77
78
|
files: [
|
|
78
|
-
"
|
|
79
|
+
"templates/workflow/skills/superspec-archive/SKILL.md",
|
|
79
80
|
],
|
|
80
81
|
},
|
|
81
82
|
{
|
|
82
83
|
name: "round2_reviewer_prompt",
|
|
83
|
-
description: "
|
|
84
|
+
description: "Package payload surface for a round>1 reviewer lane.",
|
|
84
85
|
files: [
|
|
85
|
-
"
|
|
86
|
-
"
|
|
86
|
+
"templates/workflow/prompts/critic.md",
|
|
87
|
+
"adapters/codex/agents/critic.toml",
|
|
87
88
|
],
|
|
88
89
|
},
|
|
89
90
|
{
|
|
90
91
|
name: "request_changes_reopen_tasks",
|
|
91
|
-
description: "
|
|
92
|
+
description: "Package payload surface for a review round that routes back to apply via reopen_tasks.",
|
|
92
93
|
files: [
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
94
|
+
"templates/workflow/skills/superspec-review/SKILL.md",
|
|
95
|
+
"templates/workflow/prompts/code-reviewer.md",
|
|
96
|
+
"templates/workflow/prompts/architect.md",
|
|
97
|
+
"templates/workflow/prompts/critic.md",
|
|
98
|
+
"adapters/codex/agents/code-reviewer.toml",
|
|
99
|
+
"adapters/codex/agents/architect.toml",
|
|
100
|
+
"adapters/codex/agents/critic.toml",
|
|
100
101
|
],
|
|
101
102
|
},
|
|
102
103
|
{
|
|
103
104
|
name: "task_reopen_to_resolved",
|
|
104
|
-
description: "
|
|
105
|
+
description: "Package payload surface for reopened apply work from revert through successor executor completion.",
|
|
105
106
|
files: [
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
107
|
+
"templates/workflow/skills/superspec-apply/SKILL.md",
|
|
108
|
+
"templates/workflow/prompts/executor.md",
|
|
109
|
+
"adapters/codex/agents/executor.toml",
|
|
109
110
|
],
|
|
110
111
|
},
|
|
111
112
|
{
|
|
112
113
|
name: "scope_expansion",
|
|
113
|
-
description: "
|
|
114
|
+
description: "Package payload surface for apply-side user confirmation when task scope expands.",
|
|
114
115
|
files: [
|
|
115
|
-
"
|
|
116
|
+
"templates/workflow/skills/superspec-apply/SKILL.md",
|
|
116
117
|
],
|
|
117
118
|
},
|
|
118
119
|
];
|
|
@@ -166,24 +167,6 @@ function ensureReadableTextFile(repoRoot, relPath) {
|
|
|
166
167
|
if (existsSync(absPath) && statSync(absPath).isFile()) {
|
|
167
168
|
return readFileSync(absPath, "utf8");
|
|
168
169
|
}
|
|
169
|
-
const skillMatch = /^\.codex\/skills\/([^/]+)\/SKILL\.md$/u.exec(relPath);
|
|
170
|
-
if (skillMatch) {
|
|
171
|
-
const fallback = join(repoRoot, "templates", "workflow", "skills", skillMatch[1], "SKILL.md");
|
|
172
|
-
if (existsSync(fallback) && statSync(fallback).isFile())
|
|
173
|
-
return readFileSync(fallback, "utf8");
|
|
174
|
-
}
|
|
175
|
-
const promptMatch = /^\.codex\/prompts\/([^/]+)\.md$/u.exec(relPath);
|
|
176
|
-
if (promptMatch) {
|
|
177
|
-
const fallback = join(repoRoot, "templates", "workflow", "prompts", `${promptMatch[1]}.md`);
|
|
178
|
-
if (existsSync(fallback) && statSync(fallback).isFile())
|
|
179
|
-
return readFileSync(fallback, "utf8");
|
|
180
|
-
}
|
|
181
|
-
const agentMatch = /^\.codex\/agents\/([^/]+)\.toml$/u.exec(relPath);
|
|
182
|
-
if (agentMatch) {
|
|
183
|
-
const fallback = join(repoRoot, "adapters", "codex", "agents", `${agentMatch[1]}.toml`);
|
|
184
|
-
if (existsSync(fallback) && statSync(fallback).isFile())
|
|
185
|
-
return readFileSync(fallback, "utf8");
|
|
186
|
-
}
|
|
187
170
|
throw new GuardError(`packet_measure_missing_file: ${relPath}`);
|
|
188
171
|
}
|
|
189
172
|
function dedupePaths(paths) {
|
|
@@ -365,39 +348,21 @@ function measureMaterializedSamples(specs) {
|
|
|
365
348
|
ctx.cleanup();
|
|
366
349
|
}
|
|
367
350
|
}
|
|
368
|
-
function
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
return
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
return REQUIRED_SUPERSPEC_AGENT_ROLES.map((name) => `adapters/codex/agents/${name}.toml`);
|
|
376
|
-
}
|
|
377
|
-
function repoLocalSkillPaths(repoRoot) {
|
|
378
|
-
return REQUIRED_SUPERSPEC_WORKFLOW_SKILLS
|
|
379
|
-
.map((name) => join(".codex", "skills", name, "SKILL.md"))
|
|
380
|
-
.filter((relPath) => existsSync(join(repoRoot, relPath)));
|
|
381
|
-
}
|
|
382
|
-
function runtimeBridgeSkillPaths() {
|
|
383
|
-
return [];
|
|
351
|
+
function installPayloadSourcePaths(repoRoot) {
|
|
352
|
+
const { mappings, problems } = load_install_map(repoRoot);
|
|
353
|
+
if (problems.length > 0)
|
|
354
|
+
throw new GuardError(`packet_measure_install_map_invalid: ${problems.join("; ")}`);
|
|
355
|
+
return mappings
|
|
356
|
+
.filter((mapping) => mapping.kind === "skill" || mapping.kind === "prompt" || mapping.kind === "agent")
|
|
357
|
+
.map((mapping) => mapping.source);
|
|
384
358
|
}
|
|
385
359
|
export function representative_scenarios() {
|
|
386
360
|
return REPRESENTATIVE_SCENARIOS;
|
|
387
361
|
}
|
|
388
362
|
export function measure_packet_surface_report(repoRoot) {
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
...adapterAgentPaths(),
|
|
393
|
-
...repoLocalSkillPaths(repoRoot),
|
|
394
|
-
]);
|
|
395
|
-
const fixedSurfaceRuntimeRequiredSubset = measureSurface(repoRoot, [
|
|
396
|
-
...templateWorkflowSkillPaths(),
|
|
397
|
-
...templatePromptPaths(),
|
|
398
|
-
...adapterAgentPaths(),
|
|
399
|
-
...runtimeBridgeSkillPaths(),
|
|
400
|
-
]);
|
|
363
|
+
const packagePayloadSourcePaths = installPayloadSourcePaths(repoRoot);
|
|
364
|
+
const fixedSurfaceInstallUpperBound = measureSurface(repoRoot, packagePayloadSourcePaths);
|
|
365
|
+
const fixedSurfaceRuntimeRequiredSubset = measureSurface(repoRoot, packagePayloadSourcePaths);
|
|
401
366
|
const scenarios = REPRESENTATIVE_SCENARIOS.map((scenario) => ({
|
|
402
367
|
name: scenario.name,
|
|
403
368
|
description: scenario.description,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "./src/core.js";
|
|
3
|
+
export * from "./src/hooks/adapter.js";
|
|
4
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { runHookAdapter } from "./src/hooks/adapter.js";
|
|
7
|
+
function realpathMaybe(filePath) {
|
|
8
|
+
try {
|
|
9
|
+
return realpathSync(filePath);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return resolve(filePath);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const currentFile = realpathMaybe(process.argv[1] ?? "");
|
|
16
|
+
export function main_hook(argv = process.argv.slice(2)) {
|
|
17
|
+
const stdin = readStdin();
|
|
18
|
+
const result = runHookAdapter(argv, stdin);
|
|
19
|
+
if (result.stdout)
|
|
20
|
+
process.stdout.write(result.stdout);
|
|
21
|
+
if (result.stderr)
|
|
22
|
+
process.stderr.write(result.stderr);
|
|
23
|
+
return result.code;
|
|
24
|
+
}
|
|
25
|
+
function readStdin() {
|
|
26
|
+
try {
|
|
27
|
+
return readFileSync(0, "utf8");
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (currentFile === realpathMaybe(new URL(import.meta.url).pathname)) {
|
|
34
|
+
process.exitCode = main_hook();
|
|
35
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peterxiaoyang/superspec",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "SuperSpec workflow package: guard runtime, generic workflow templates, and Codex adapter payload.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"bin": {
|
|
22
22
|
"superspec": "bin/superspec.js",
|
|
23
23
|
"superspec-guard": "bin/superspec-guard.js",
|
|
24
|
+
"superspec-hook": "bin/superspec-hook.js",
|
|
24
25
|
"superspec-init": "bin/superspec-init.js"
|
|
25
26
|
},
|
|
26
27
|
"exports": {
|
|
@@ -32,6 +33,10 @@
|
|
|
32
33
|
"types": "./dist/superspec_guard.d.ts",
|
|
33
34
|
"default": "./dist/superspec_guard.js"
|
|
34
35
|
},
|
|
36
|
+
"./superspec_hook": {
|
|
37
|
+
"types": "./dist/superspec_hook.d.ts",
|
|
38
|
+
"default": "./dist/superspec_hook.js"
|
|
39
|
+
},
|
|
35
40
|
"./superspec_init": {
|
|
36
41
|
"types": "./dist/superspec_init.d.ts",
|
|
37
42
|
"default": "./dist/superspec_init.js"
|
|
@@ -51,7 +56,7 @@
|
|
|
51
56
|
"scripts": {
|
|
52
57
|
"build": "node build.js",
|
|
53
58
|
"typecheck": "tsc --noEmit",
|
|
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",
|
|
59
|
+
"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_hooks.test.ts tests/test_superspec_skills.test.ts",
|
|
55
60
|
"prepack": "npm run build",
|
|
56
61
|
"prepublishOnly": "npm run build",
|
|
57
62
|
"pack:dry-run": "npm pack --dry-run"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://superspec.local/schemas/hook-event.schema.json",
|
|
4
|
+
"title": "SuperSpec Codex Hook Event",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["hook_event_name", "cwd"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"session_id": { "type": "string" },
|
|
9
|
+
"turn_id": { "type": "string" },
|
|
10
|
+
"transcript_path": { "type": ["string", "null"] },
|
|
11
|
+
"cwd": { "type": "string" },
|
|
12
|
+
"hook_event_name": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"enum": ["PreToolUse", "PostToolUse", "SubagentStart", "SubagentStop"]
|
|
15
|
+
},
|
|
16
|
+
"permission_mode": { "type": "string" },
|
|
17
|
+
"tool_name": { "type": "string" },
|
|
18
|
+
"tool_use_id": { "type": "string" },
|
|
19
|
+
"tool_input": { "type": "object" },
|
|
20
|
+
"tool_response": { "type": ["object", "array", "string", "number", "boolean", "null"] },
|
|
21
|
+
"agent_id": { "type": "string" },
|
|
22
|
+
"agent_type": { "type": "string" },
|
|
23
|
+
"agent_transcript_path": { "type": ["string", "null"] },
|
|
24
|
+
"last_assistant_message": { "type": ["string", "null"] }
|
|
25
|
+
},
|
|
26
|
+
"additionalProperties": true
|
|
27
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"superspec": {
|
|
3
|
+
"managed": true,
|
|
4
|
+
"adapter_version": "superspec-hook@2",
|
|
5
|
+
"strict_profile_default": "audit-only-until-r1-provenance-passes"
|
|
6
|
+
},
|
|
7
|
+
"hooks": {
|
|
8
|
+
"PreToolUse": [
|
|
9
|
+
{
|
|
10
|
+
"matcher": "Bash|apply_patch|Edit|Write|mcp__.*",
|
|
11
|
+
"hooks": [
|
|
12
|
+
{
|
|
13
|
+
"type": "command",
|
|
14
|
+
"command": "superspec-hook --change \"$SUPERSPEC_CHANGE\"",
|
|
15
|
+
"timeout": 10,
|
|
16
|
+
"statusMessage": "SuperSpec hook write policy"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"PostToolUse": [
|
|
22
|
+
{
|
|
23
|
+
"matcher": "Bash",
|
|
24
|
+
"hooks": [
|
|
25
|
+
{
|
|
26
|
+
"type": "command",
|
|
27
|
+
"command": "superspec-hook --change \"$SUPERSPEC_CHANGE\"",
|
|
28
|
+
"timeout": 30,
|
|
29
|
+
"statusMessage": "SuperSpec hook runtime evidence"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"SubagentStart": [
|
|
35
|
+
{
|
|
36
|
+
"matcher": ".*",
|
|
37
|
+
"hooks": [
|
|
38
|
+
{
|
|
39
|
+
"type": "command",
|
|
40
|
+
"command": "superspec-hook --change \"$SUPERSPEC_CHANGE\"",
|
|
41
|
+
"timeout": 30,
|
|
42
|
+
"statusMessage": "SuperSpec hook subagent start"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"SubagentStop": [
|
|
48
|
+
{
|
|
49
|
+
"matcher": ".*",
|
|
50
|
+
"hooks": [
|
|
51
|
+
{
|
|
52
|
+
"type": "command",
|
|
53
|
+
"command": "superspec-hook --change \"$SUPERSPEC_CHANGE\"",
|
|
54
|
+
"timeout": 30,
|
|
55
|
+
"statusMessage": "SuperSpec hook subagent stop"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -26,6 +26,13 @@ Apply 按 OpenSpec tasks 执行实现,负责 RED/GREEN 证据、任务勾选
|
|
|
26
26
|
|
|
27
27
|
## 第一条必跑命令
|
|
28
28
|
|
|
29
|
+
先尝试建立当前 change 的 SuperSpec hook session。R-1/provenance 未通过时该命令只会记录 audit-only lease 和降级诊断,不代表 mechanical enforcement 已启用:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
superspec guard hook-session-begin --change "<change>" --workflow superspec-apply --entrypoint-token "<fresh-entrypoint-token>" --format agent
|
|
33
|
+
superspec guard hook-session-status --change "<change>" --format agent
|
|
34
|
+
```
|
|
35
|
+
|
|
29
36
|
```text
|
|
30
37
|
superspec guard workflow-packet --change "<change>" --gate apply_ready --format agent
|
|
31
38
|
```
|
|
@@ -26,6 +26,13 @@ Archive 在 `review_complete` allowed 后收尾:确认 archive readiness、保
|
|
|
26
26
|
|
|
27
27
|
## 第一条必跑命令
|
|
28
28
|
|
|
29
|
+
先尝试建立当前 change 的 SuperSpec hook session。R-1/provenance 未通过时该命令只会记录 audit-only lease 和降级诊断,不代表 mechanical enforcement 已启用:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
superspec guard hook-session-begin --change "<change>" --workflow superspec-archive --entrypoint-token "<fresh-entrypoint-token>" --format agent
|
|
33
|
+
superspec guard hook-session-status --change "<change>" --format agent
|
|
34
|
+
```
|
|
35
|
+
|
|
29
36
|
```text
|
|
30
37
|
superspec guard workflow-packet --change "<change>" --gate archive_ready --format agent
|
|
31
38
|
```
|
|
@@ -60,6 +67,7 @@ openspec archive -y "<change>"
|
|
|
60
67
|
|
|
61
68
|
```text
|
|
62
69
|
superspec guard check-archived --change "<change>" --format agent
|
|
70
|
+
superspec guard hook-session-end --change "<change>" --reason archived --format agent
|
|
63
71
|
```
|
|
64
72
|
|
|
65
73
|
`.superspec/artifacts/business-invariants.md`、`.superspec/artifacts/test-contract.md`、review/verification evidence、RED/GREEN evidence 和 archive evidence 必须能从 preservation manifest 追溯。
|
|
@@ -26,6 +26,13 @@ Explore 只做需求澄清、代码事实调查、范围边界和风险记录。
|
|
|
26
26
|
|
|
27
27
|
## 第一条必跑命令
|
|
28
28
|
|
|
29
|
+
先尝试建立当前 change 的 SuperSpec hook session。R-1/provenance 未通过时该命令只会记录 audit-only lease 和降级诊断,不代表 mechanical enforcement 已启用:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
superspec guard hook-session-begin --change "<change>" --workflow superspec-explore --entrypoint-token "<fresh-entrypoint-token>" --format agent
|
|
33
|
+
superspec guard hook-session-status --change "<change>" --format agent
|
|
34
|
+
```
|
|
35
|
+
|
|
29
36
|
```text
|
|
30
37
|
superspec init --scope project --format agent
|
|
31
38
|
```
|
|
@@ -26,6 +26,13 @@ Propose 把 discovery 转成 OpenSpec proposal package,并补 SuperSpec 业务
|
|
|
26
26
|
|
|
27
27
|
## 第一条必跑命令
|
|
28
28
|
|
|
29
|
+
先尝试建立当前 change 的 SuperSpec hook session。R-1/provenance 未通过时该命令只会记录 audit-only lease 和降级诊断,不代表 mechanical enforcement 已启用:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
superspec guard hook-session-begin --change "<change>" --workflow superspec-propose --entrypoint-token "<fresh-entrypoint-token>" --format agent
|
|
33
|
+
superspec guard hook-session-status --change "<change>" --format agent
|
|
34
|
+
```
|
|
35
|
+
|
|
29
36
|
```text
|
|
30
37
|
superspec guard workflow-packet --change "<change>" --gate explore_complete --format agent
|
|
31
38
|
```
|