@gobing-ai/spur 0.4.7 → 0.4.9
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/.claude-plugin/marketplace.json +1 -1
- package/config/pipeline-budgets.json +2 -2
- package/config/rules/boundary/config-loading-ownership.yaml +1 -0
- package/config/rules/boundary/sp-plugin-standalone.yaml +1 -1
- package/config/rules/boundary/sp-script-placement.yaml +1 -1
- package/config/rules/docs/sourced-api-claims.yaml +45 -7
- package/config/rules/strict/runtime-boundaries.yaml +40 -1
- package/config/rules/structure/protected-files.yaml +1 -1
- package/config/rules/surface/check-cli-surface.yaml +33 -0
- package/config/rules/typescript/no-debugger.yaml +3 -0
- package/config/workflow-candidates.json +2 -2
- package/config/workflows/feature-verification.yaml +6 -11
- package/config/workflows/history-anatomy.yaml +14 -28
- package/config/workflows/idea-pipeline.yaml +6 -19
- package/config/workflows/pr-review.yaml +9 -16
- package/config/workflows/task-pipeline.yaml +16 -23
- package/config/workflows/wrapup-pipeline.yaml +21 -63
- package/package.json +3 -3
- package/plugins/sp/README.md +70 -37
- package/plugins/sp/agents/super-planner.md +5 -5
- package/plugins/sp/agents/super-reviewer.md +2 -2
- package/plugins/sp/commands/dev-changelog.md +1 -1
- package/plugins/sp/commands/dev-daily.md +1 -1
- package/plugins/sp/commands/dev-pr-review.md +1 -1
- package/plugins/sp/commands/dev-review-session.md +1 -1
- package/plugins/sp/commands/dev-runall.md +4 -4
- package/plugins/sp/commands/dev-verify.md +4 -6
- package/plugins/sp/commands/dev-verifyall.md +4 -5
- package/plugins/sp/hooks/hooks.json +11 -1
- package/plugins/sp/lib/context-post-tool.ts +1 -1
- package/plugins/sp/lib/context-session-start.ts +1 -1
- package/plugins/sp/lib/context-session-stop.ts +1 -1
- package/plugins/sp/lib/destructive-policy.ts +65 -1
- package/plugins/sp/plugin.json +2 -5
- package/plugins/sp/{hooks → scripts/hooks}/agent-lifecycle.ts +1 -1
- package/plugins/sp/scripts/hooks/careful-guard.ts +94 -0
- package/plugins/sp/{hooks → scripts/hooks}/context-post-tool.ts +2 -2
- package/plugins/sp/{hooks → scripts/hooks}/context-session-start.ts +1 -1
- package/plugins/sp/{hooks → scripts/hooks}/context-session-stop.ts +1 -1
- package/plugins/sp/{hooks → scripts/hooks}/fleet-guest-stop.ts +6 -4
- package/plugins/sp/{hooks → scripts/hooks}/task-write-guard.ts +38 -21
- package/plugins/sp/skills/code-implementation/SKILL.md +1 -1
- package/plugins/sp/skills/code-verification/SKILL.md +8 -8
- package/plugins/sp/skills/code-verification/references/secu-review.md +4 -3
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +9 -1
- package/plugins/sp/skills/daily-summary/SKILL.md +4 -5
- package/plugins/sp/skills/dogfood-testing/SKILL.md +9 -9
- package/plugins/sp/skills/dogfood-testing/references/report-template.md +1 -1
- package/plugins/sp/skills/history-anatomy/references/modes.md +2 -2
- package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
- package/plugins/sp/skills/parallel-execution/SKILL.md +1 -1
- package/plugins/sp/skills/pr-reviewing/SKILL.md +14 -15
- package/plugins/sp/skills/session-review/SKILL.md +3 -3
- package/plugins/sp/skills/source-driven-development/SKILL.md +7 -4
- package/plugins/sp/skills/spur-check/SKILL.md +14 -13
- package/plugins/sp/skills/spur-cli/SKILL.md +1 -1
- package/plugins/sp/skills/spur-cli/references/agent.md +12 -0
- package/plugins/sp/skills/spur-cli/references/builder.md +27 -6
- package/plugins/sp/skills/spur-cli/references/features.md +6 -1
- package/plugins/sp/skills/spur-cli/references/history.md +3 -0
- package/plugins/sp/skills/spur-cli/references/self.md +5 -3
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +3 -0
- package/plugins/sp/skills/spur-cli/references/tasks.md +13 -3
- package/plugins/sp/skills/spur-cli/references/workflows.md +36 -1
- package/plugins/sp/skills/spur-dev/SKILL.md +1 -1
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +1 -1
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +12 -12
- package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +10 -11
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +16 -17
- package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +1 -1
- package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +8 -11
- package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +6 -8
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -1
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +33 -40
- package/plugins/sp/skills/spur-dev/references/structured-trace-emission.md +24 -24
- package/plugins/sp/skills/spur-doctor/SKILL.md +5 -4
- package/spur.js +41901 -30598
- package/config/plugin-scripts.json +0 -116
- package/config/script-placement-baseline.json +0 -33
- package/plugins/sp/hooks/careful-guard.ts +0 -74
- package/plugins/sp/hooks/pi/guard-extension.ts +0 -304
- package/plugins/sp/lib/dogfood-pipeline.ts +0 -265
- package/plugins/sp/lib/dogfood-report.ts +0 -278
- package/plugins/sp/lib/history-anatomy.generated.d.mts +0 -112
- package/plugins/sp/lib/history-anatomy.generated.mjs +0 -691
- package/plugins/sp/lib/idea-handoff.generated.d.mts +0 -17
- package/plugins/sp/lib/idea-handoff.generated.mjs +0 -1446
- package/plugins/sp/lib/inline-run-app.ts +0 -68
- package/plugins/sp/lib/inline-run.generated.d.mts +0 -37
- package/plugins/sp/lib/inline-run.generated.mjs +0 -3103
- package/plugins/sp/lib/plan-projection.generated.d.mts +0 -197
- package/plugins/sp/lib/plan-projection.generated.mjs +0 -982
- package/plugins/sp/lib/quality-gate.generated.d.mts +0 -139
- package/plugins/sp/lib/quality-gate.generated.mjs +0 -30510
- package/plugins/sp/lib/residual-scan.generated.d.mts +0 -93
- package/plugins/sp/lib/residual-scan.generated.mjs +0 -383
- package/plugins/sp/lib/run-summary-core.ts +0 -225
- package/plugins/sp/lib/spur-bin.ts +0 -36
- package/plugins/sp/lib/step-profile.generated.d.mts +0 -71
- package/plugins/sp/lib/step-profile.generated.mjs +0 -174
- package/plugins/sp/lib/transcript.ts +0 -112
- package/plugins/sp/scripts/batch-plan.mjs +0 -1096
- package/plugins/sp/scripts/batch-plan.ts +0 -145
- package/plugins/sp/scripts/batch-preflight.mjs +0 -423
- package/plugins/sp/scripts/batch-preflight.ts +0 -270
- package/plugins/sp/scripts/commit-guard.mjs +0 -226
- package/plugins/sp/scripts/commit-guard.ts +0 -248
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +0 -626
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +0 -855
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +0 -211
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +0 -110
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +0 -282
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +0 -98
- package/plugins/sp/scripts/feature-verification-steps.mjs +0 -82
- package/plugins/sp/scripts/feature-verification-steps.ts +0 -164
- package/plugins/sp/scripts/history-anatomy-cache.mjs +0 -931
- package/plugins/sp/scripts/history-anatomy-cache.ts +0 -247
- package/plugins/sp/scripts/idea-handoff.mjs +0 -32
- package/plugins/sp/scripts/idea-handoff.ts +0 -47
- package/plugins/sp/scripts/inline-run-dispatch.mjs +0 -131
- package/plugins/sp/scripts/inline-run-dispatch.ts +0 -115
- package/plugins/sp/scripts/inline-run-setup.mjs +0 -746
- package/plugins/sp/scripts/inline-run-setup.ts +0 -198
- package/plugins/sp/scripts/persist-out-check.mjs +0 -217
- package/plugins/sp/scripts/persist-out-check.ts +0 -249
- package/plugins/sp/scripts/pr-reviewing.mjs +0 -712
- package/plugins/sp/scripts/pr-reviewing.ts +0 -952
- package/plugins/sp/scripts/quality-gate.mjs +0 -30491
- package/plugins/sp/scripts/quality-gate.ts +0 -80
- package/plugins/sp/scripts/residual-scan.mjs +0 -634
- package/plugins/sp/scripts/residual-scan.ts +0 -248
- package/plugins/sp/scripts/run-summary.mjs +0 -758
- package/plugins/sp/scripts/run-summary.ts +0 -107
- package/plugins/sp/scripts/script-root.mjs +0 -129
- package/plugins/sp/scripts/script-root.ts +0 -199
- package/plugins/sp/scripts/session-timeline.mjs +0 -359
- package/plugins/sp/scripts/session-timeline.ts +0 -190
- package/plugins/sp/scripts/task-diffstat.mjs +0 -181
- package/plugins/sp/scripts/task-diffstat.ts +0 -219
- package/plugins/sp/scripts/workflow-step-profile.mjs +0 -338
- package/plugins/sp/scripts/workflow-step-profile.ts +0 -165
- package/plugins/sp/scripts/wrapup-drift-probe.mjs +0 -190
- package/plugins/sp/scripts/wrapup-drift-probe.ts +0 -261
- package/plugins/sp/scripts/wrapup-steps.mjs +0 -627
- package/plugins/sp/scripts/wrapup-steps.ts +0 -891
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* history-anatomy-cache — CLI glue (task 1005 R4, ADR-130 lib rule) for the history-anatomy
|
|
4
|
-
* report cache (feature I8, HA-S1 0659 / ADR-079): eight verbs, byte-identical argv/stdout/exit.
|
|
5
|
-
* Logic lives in packages/app/src/services/history-anatomy.ts, reached through the generated
|
|
6
|
-
* standalone bundle `plugins/sp/lib/history-anatomy.generated.mjs`; twin runs under bare `node`
|
|
7
|
-
* (`bun run build:scripts`).
|
|
8
|
-
*/
|
|
9
|
-
import { spawnSync } from 'node:child_process';
|
|
10
|
-
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
|
-
import * as core from '../lib/history-anatomy.generated.mjs';
|
|
12
|
-
|
|
13
|
-
export * from '../lib/history-anatomy.generated.mjs';
|
|
14
|
-
|
|
15
|
-
const VALID_COMMANDS = 'digest, check, paths, assert-clean, probe, stamp, refresh, publish';
|
|
16
|
-
const PROBE_USAGE =
|
|
17
|
-
'<script> probe --artifact <a.json> --target <report.md> [--baseline <b.json>] [--mode daily|ad-hoc] ' +
|
|
18
|
-
'[--date <YYYY-MM-DD>] [--recompute true] [--out <prov.json>] [--skill-dir <d>] [--contract <f>] [--workflow <f>] [--helper <f>]';
|
|
19
|
-
|
|
20
|
-
/** `--key value` / `--flag` → record. Bare flags become `"true"` so `--recompute` needs no value. */
|
|
21
|
-
function parseFlags(args: string[]): Record<string, string | undefined> {
|
|
22
|
-
const out: Record<string, string | undefined> = {};
|
|
23
|
-
for (let i = 0; i < args.length; i++) {
|
|
24
|
-
const a = args[i] ?? '';
|
|
25
|
-
if (!a.startsWith('--')) continue;
|
|
26
|
-
const key = a.slice(2);
|
|
27
|
-
const next = args[i + 1];
|
|
28
|
-
if (next === undefined || next.startsWith('--')) {
|
|
29
|
-
out[key] = 'true';
|
|
30
|
-
} else {
|
|
31
|
-
out[key] = next;
|
|
32
|
-
i++;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return out;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Run the CLI with captured stdout/stderr (data, not process side-effects) so unit tests invoke
|
|
40
|
-
* it in-process without leaking into the test runner's own output.
|
|
41
|
-
*/
|
|
42
|
-
export function runCacheCli(argv: string[]): core.CacheCliResult {
|
|
43
|
-
const [cmd, a, b] = argv;
|
|
44
|
-
switch (cmd) {
|
|
45
|
-
case 'digest': {
|
|
46
|
-
if (a === undefined) {
|
|
47
|
-
return { exitCode: 1, stdout: '', stderr: 'usage: <script> digest <artifact.json>\n' };
|
|
48
|
-
}
|
|
49
|
-
let artifact: unknown;
|
|
50
|
-
try {
|
|
51
|
-
artifact = JSON.parse(readFileSync(a, 'utf8'));
|
|
52
|
-
} catch {
|
|
53
|
-
return { exitCode: 1, stdout: '', stderr: `could not parse artifact at ${a}\n` };
|
|
54
|
-
}
|
|
55
|
-
const digest = core.semanticArtifactDigest(artifact);
|
|
56
|
-
return { exitCode: 0, stdout: `${digest}\n`, stderr: '' };
|
|
57
|
-
}
|
|
58
|
-
case 'check': {
|
|
59
|
-
if (a === undefined) {
|
|
60
|
-
return { exitCode: 1, stdout: '', stderr: 'usage: <script> check <report.md>\n' };
|
|
61
|
-
}
|
|
62
|
-
const result = core.checkReportStructure(readFileSync(a, 'utf8'));
|
|
63
|
-
const stdout = `${result.ok ? 'PASS' : 'FAIL'}\n${result.problems.map((p) => `- ${p}\n`).join('')}`;
|
|
64
|
-
return { exitCode: result.ok ? 0 : 1, stdout, stderr: '' };
|
|
65
|
-
}
|
|
66
|
-
case 'publish': {
|
|
67
|
-
if (a === undefined || b === undefined) {
|
|
68
|
-
return { exitCode: 1, stdout: '', stderr: 'usage: <script> publish <candidate.md> <target.md>\n' };
|
|
69
|
-
}
|
|
70
|
-
core.publishAtomically(a, b);
|
|
71
|
-
return { exitCode: 0, stdout: '', stderr: '' };
|
|
72
|
-
}
|
|
73
|
-
case 'assert-clean': {
|
|
74
|
-
// 0676 R3: fingerprint diff around a model stage. `--baseline` holds the
|
|
75
|
-
// pre-stage `git status --porcelain`; every path present now but absent there
|
|
76
|
-
// must be one of the stage's declared outputs, else exit 1 naming each.
|
|
77
|
-
const f = parseFlags(argv.slice(1));
|
|
78
|
-
if (f.baseline === undefined) {
|
|
79
|
-
return {
|
|
80
|
-
exitCode: 1,
|
|
81
|
-
stdout: '',
|
|
82
|
-
stderr: 'usage: <script> assert-clean --baseline <porcelain.txt> [--expect <path>]...\n',
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
const expects = new Set<string>();
|
|
86
|
-
for (const arg of argv.slice(1)) {
|
|
87
|
-
if (arg.startsWith('--expect=')) expects.add(arg.slice('--expect='.length));
|
|
88
|
-
}
|
|
89
|
-
let now: string;
|
|
90
|
-
try {
|
|
91
|
-
now =
|
|
92
|
-
spawnSync('git', ['status', '--porcelain'], {
|
|
93
|
-
encoding: 'utf8',
|
|
94
|
-
...(f.cwd !== undefined ? { cwd: f.cwd } : {}),
|
|
95
|
-
}).stdout ?? '';
|
|
96
|
-
} catch {
|
|
97
|
-
return { exitCode: 0, stdout: '', stderr: 'assert-clean: git unavailable; skipped\n' };
|
|
98
|
-
}
|
|
99
|
-
const undeclared = core.diffPorcelain(readFileSync(f.baseline, 'utf8'), now, expects);
|
|
100
|
-
if (undeclared.length > 0) {
|
|
101
|
-
return {
|
|
102
|
-
exitCode: 1,
|
|
103
|
-
stdout: '',
|
|
104
|
-
stderr: undeclared.map((p) => `undeclared write: ${p}\n`).join(''),
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
return { exitCode: 0, stdout: 'clean\n', stderr: '' };
|
|
108
|
-
}
|
|
109
|
-
case 'paths': {
|
|
110
|
-
const f = parseFlags(argv.slice(1));
|
|
111
|
-
if (f.helper === undefined || f.out === undefined) {
|
|
112
|
-
return {
|
|
113
|
-
exitCode: 1,
|
|
114
|
-
stdout: '',
|
|
115
|
-
stderr: 'usage: <script> paths --helper <p> --out <env> [--report-dir <d>] [--date <d>] [--output <p>] [--mode <m>] [--since <s>] [--until <u>] [--focus <text>] [--recompute true|false] [--run-id <id>]\n',
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
const v = core.validateSelector({
|
|
119
|
-
mode: f.mode,
|
|
120
|
-
date: f.date,
|
|
121
|
-
since: f.since,
|
|
122
|
-
until: f.until,
|
|
123
|
-
focus: f.focus,
|
|
124
|
-
recompute: f.recompute,
|
|
125
|
-
output: f.output,
|
|
126
|
-
});
|
|
127
|
-
if (!v.ok) return { exitCode: 1, stdout: '', stderr: `${v.errors.join('\n')}\n` };
|
|
128
|
-
const env = core.resolvePaths({
|
|
129
|
-
helper: f.helper,
|
|
130
|
-
reportDir: f['report-dir'] ?? 'docs/report',
|
|
131
|
-
date: f.date,
|
|
132
|
-
output: f.output,
|
|
133
|
-
mode: v.mode,
|
|
134
|
-
since: f.since,
|
|
135
|
-
until: f.until,
|
|
136
|
-
});
|
|
137
|
-
writeFileSync(f.out, env);
|
|
138
|
-
// 0920: run-scoped selector observation artifact — written only after successful
|
|
139
|
-
// validation; historical selector files have incompatible shapes and are not consumed.
|
|
140
|
-
if (f['run-id'] !== undefined && f['run-id'] !== '') {
|
|
141
|
-
mkdirSync('.spur/run', { recursive: true });
|
|
142
|
-
const envVars = Object.fromEntries(
|
|
143
|
-
env
|
|
144
|
-
.trim()
|
|
145
|
-
.split('\n')
|
|
146
|
-
.map((l) => [l.slice(0, l.indexOf('=')), l.slice(l.indexOf('=') + 1)]),
|
|
147
|
-
);
|
|
148
|
-
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone ?? 'UTC';
|
|
149
|
-
writeFileSync(
|
|
150
|
-
`.spur/run/${f['run-id']}-selector.json`,
|
|
151
|
-
`${JSON.stringify(
|
|
152
|
-
{
|
|
153
|
-
mode: v.mode,
|
|
154
|
-
date: envVars.HA_DATE ?? null,
|
|
155
|
-
focus: v.focus,
|
|
156
|
-
since: envVars.HA_SINCE ?? null,
|
|
157
|
-
until: envVars.HA_UNTIL ?? null,
|
|
158
|
-
timezone: tz,
|
|
159
|
-
},
|
|
160
|
-
null,
|
|
161
|
-
2,
|
|
162
|
-
)}\n`,
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
return { exitCode: 0, stdout: '', stderr: '' };
|
|
166
|
-
}
|
|
167
|
-
case 'probe': {
|
|
168
|
-
const f = parseFlags(argv.slice(1));
|
|
169
|
-
if (f.artifact === undefined || f.target === undefined) {
|
|
170
|
-
return { exitCode: 1, stdout: '', stderr: `usage: ${PROBE_USAGE}\n` };
|
|
171
|
-
}
|
|
172
|
-
let result: ReturnType<typeof core.probe>;
|
|
173
|
-
try {
|
|
174
|
-
result = core.probe({
|
|
175
|
-
artifact: f.artifact,
|
|
176
|
-
target: f.target,
|
|
177
|
-
baseline: f.baseline,
|
|
178
|
-
mode: f.mode === 'ad-hoc' ? 'ad-hoc' : 'daily',
|
|
179
|
-
date: f.date,
|
|
180
|
-
recompute: f.recompute === 'true',
|
|
181
|
-
executor: f.executor,
|
|
182
|
-
model: f.model,
|
|
183
|
-
skillDir: f['skill-dir'],
|
|
184
|
-
contractFile: f.contract,
|
|
185
|
-
workflowFile: f.workflow,
|
|
186
|
-
helperFile: f.helper,
|
|
187
|
-
contractVersion: f['contract-version'],
|
|
188
|
-
runId: f['run-id'],
|
|
189
|
-
spurVersion: f['spur-version'],
|
|
190
|
-
});
|
|
191
|
-
} catch {
|
|
192
|
-
return { exitCode: 1, stdout: '', stderr: `could not read artifact at ${f.artifact}\n` };
|
|
193
|
-
}
|
|
194
|
-
if (f.out !== undefined) writeFileSync(f.out, `${JSON.stringify(result.current, null, 2)}\n`);
|
|
195
|
-
const reasons = result.decision.reasons.map((r) => `- ${r}\n`).join('');
|
|
196
|
-
return { exitCode: 0, stdout: `${result.decision.disposition}\n${reasons}`, stderr: '' };
|
|
197
|
-
}
|
|
198
|
-
case 'stamp': {
|
|
199
|
-
const f = parseFlags(argv.slice(1));
|
|
200
|
-
if (f.candidate === undefined || f.provenance === undefined || f.out === undefined) {
|
|
201
|
-
return {
|
|
202
|
-
exitCode: 1,
|
|
203
|
-
stdout: '',
|
|
204
|
-
stderr: 'usage: <script> stamp --candidate <c.md> --provenance <p.json> --out <o.md>\n',
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
try {
|
|
208
|
-
const p = JSON.parse(readFileSync(f.provenance, 'utf8')) as core.CacheProvenance;
|
|
209
|
-
writeFileSync(f.out, `${core.stampReport(readFileSync(f.candidate, 'utf8'), p)}\n`);
|
|
210
|
-
} catch {
|
|
211
|
-
return { exitCode: 1, stdout: '', stderr: 'stamp: could not read candidate or provenance\n' };
|
|
212
|
-
}
|
|
213
|
-
return { exitCode: 0, stdout: '', stderr: '' };
|
|
214
|
-
}
|
|
215
|
-
case 'refresh': {
|
|
216
|
-
const f = parseFlags(argv.slice(1));
|
|
217
|
-
if (f.report === undefined || f.out === undefined) {
|
|
218
|
-
return {
|
|
219
|
-
exitCode: 1,
|
|
220
|
-
stdout: '',
|
|
221
|
-
stderr: 'usage: <script> refresh --report <published.md> --out <o.md> [--disposition hit]\n',
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
try {
|
|
225
|
-
const disposition = (f.disposition ?? 'hit') as core.CacheDisposition;
|
|
226
|
-
const refreshed = core.refreshReport(
|
|
227
|
-
readFileSync(f.report, 'utf8'),
|
|
228
|
-
f['validated-at'] ?? new Date().toISOString(),
|
|
229
|
-
disposition,
|
|
230
|
-
);
|
|
231
|
-
writeFileSync(f.out, refreshed.endsWith('\n') ? refreshed : `${refreshed}\n`);
|
|
232
|
-
} catch {
|
|
233
|
-
return { exitCode: 1, stdout: '', stderr: `refresh: could not read report at ${f.report}\n` };
|
|
234
|
-
}
|
|
235
|
-
return { exitCode: 0, stdout: '', stderr: '' };
|
|
236
|
-
}
|
|
237
|
-
default:
|
|
238
|
-
return { exitCode: 1, stdout: '', stderr: `valid commands: ${VALID_COMMANDS}\n` };
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (import.meta.main) {
|
|
243
|
-
const { exitCode, stdout, stderr } = runCacheCli(process.argv.slice(2));
|
|
244
|
-
process.stdout.write(stdout);
|
|
245
|
-
process.stderr.write(stderr);
|
|
246
|
-
process.exitCode = exitCode;
|
|
247
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @bun
|
|
3
|
-
|
|
4
|
-
// plugins/sp/lib/env.ts
|
|
5
|
-
function getEnvVars() {
|
|
6
|
-
return process.env;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// plugins/sp/scripts/idea-handoff.ts
|
|
10
|
-
var libUrl = new URL("../lib/idea-handoff.generated.mjs", import.meta.url).href;
|
|
11
|
-
async function loadHandoffLib() {
|
|
12
|
-
return await import(libUrl);
|
|
13
|
-
}
|
|
14
|
-
var IDEA_HANDOFF_USAGE = "usage: idea-handoff.ts (env: __runId, featureId, optional spurBin) \u2014 no subcommands";
|
|
15
|
-
async function main(argv, env = getEnvVars()) {
|
|
16
|
-
if (argv.length > 0) {
|
|
17
|
-
process.stderr.write(`${IDEA_HANDOFF_USAGE}
|
|
18
|
-
`);
|
|
19
|
-
return 2;
|
|
20
|
-
}
|
|
21
|
-
const { runIdeaHandoffCli } = await loadHandoffLib();
|
|
22
|
-
const outcome = await runIdeaHandoffCli(env);
|
|
23
|
-
return outcome.exitCode;
|
|
24
|
-
}
|
|
25
|
-
{
|
|
26
|
-
process.exit(await main(process.argv.slice(2)));
|
|
27
|
-
}
|
|
28
|
-
export {
|
|
29
|
-
IDEA_HANDOFF_USAGE,
|
|
30
|
-
loadHandoffLib,
|
|
31
|
-
main
|
|
32
|
-
};
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import { getEnvVars } from '../lib/env';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* idea-handoff — portable entrypoint for the idea-pipeline finalization (task 0824, feature I21).
|
|
7
|
-
*
|
|
8
|
-
* ADR-065 standard plugin script: the workflow wrapper prefers the monorepo writer
|
|
9
|
-
* (`bun packages/app/src/workflow/idea-handoff-cli.ts`) and falls back to this script's
|
|
10
|
-
* registered `.mjs` twin under bare `node`, so a seeded project with no monorepo checkout
|
|
11
|
-
* still finalizes the handoff (run-scoped report + task-deps ordering + roster refresh) and
|
|
12
|
-
* fails closed (exit 1) when neither writer resolves.
|
|
13
|
-
*
|
|
14
|
-
* The behavior lives in the generated bundle `../lib/idea-handoff.generated.mjs`, produced by
|
|
15
|
-
* `bun run build:plugin-lib` from `packages/app/src/workflow/idea-handoff-cli.ts` — the same
|
|
16
|
-
* source the monorepo branch executes, so both branches share one contract (0604/0824).
|
|
17
|
-
* Declarations: `../lib/idea-handoff.generated.d.mts`. The bundle is imported by computed URL
|
|
18
|
-
* on purpose: `superskill script convert` must NOT inline it (its tree carries one
|
|
19
|
-
* `Bun.spawn` site in a ts-runtime streaming path this workflow never exercises), so the
|
|
20
|
-
* twin stays a thin node-runnable loader over the committed runtime asset.
|
|
21
|
-
*
|
|
22
|
-
* Environment: `__runId`, `featureId`, optional `spurBin`. Missing required vars exit 1
|
|
23
|
-
* (mis-invocation is failed-closed, never a silent skip).
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
const libUrl = new URL('../lib/idea-handoff.generated.mjs', import.meta.url).href;
|
|
27
|
-
|
|
28
|
-
export async function loadHandoffLib(): Promise<typeof import('../lib/idea-handoff.generated.mjs')> {
|
|
29
|
-
return (await import(libUrl)) as typeof import('../lib/idea-handoff.generated.mjs');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const IDEA_HANDOFF_USAGE =
|
|
33
|
-
'usage: idea-handoff.ts (env: __runId, featureId, optional spurBin) — no subcommands';
|
|
34
|
-
|
|
35
|
-
export async function main(argv: string[], env: NodeJS.ProcessEnv = getEnvVars()): Promise<number> {
|
|
36
|
-
if (argv.length > 0) {
|
|
37
|
-
process.stderr.write(`${IDEA_HANDOFF_USAGE}\n`);
|
|
38
|
-
return 2;
|
|
39
|
-
}
|
|
40
|
-
const { runIdeaHandoffCli } = await loadHandoffLib();
|
|
41
|
-
const outcome = await runIdeaHandoffCli(env as { __runId?: string; featureId?: string; spurBin?: string });
|
|
42
|
-
return outcome.exitCode;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (import.meta.main) {
|
|
46
|
-
process.exit(await main(process.argv.slice(2)));
|
|
47
|
-
}
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @bun
|
|
3
|
-
|
|
4
|
-
// plugins/sp/scripts/inline-run-dispatch.ts
|
|
5
|
-
import { spawnSync } from "child_process";
|
|
6
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
|
-
|
|
8
|
-
// plugins/sp/lib/env.ts
|
|
9
|
-
function getEnvVar(name, fallback) {
|
|
10
|
-
const raw = process.env[name];
|
|
11
|
-
return raw === undefined ? fallback : raw;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// plugins/sp/lib/inline-run-app.ts
|
|
15
|
-
import { existsSync } from "node:fs";
|
|
16
|
-
import { dirname, join, resolve } from "node:path";
|
|
17
|
-
import { fileURLToPath } from "node:url";
|
|
18
|
-
function resolveAppEntry(spurBin) {
|
|
19
|
-
const fallback = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
|
|
20
|
-
const candidates = spurBin !== "" ? [spurBin] : [fallback];
|
|
21
|
-
for (const candidate of candidates) {
|
|
22
|
-
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
23
|
-
const mainModule = [...tokens].reverse().find((t) => t.endsWith(".ts"));
|
|
24
|
-
if (mainModule === undefined || !existsSync(mainModule))
|
|
25
|
-
continue;
|
|
26
|
-
const repoRoot = resolve(dirname(mainModule), "..", "..", "..");
|
|
27
|
-
const appEntry = join(repoRoot, "packages", "app", "src", "index.ts");
|
|
28
|
-
if (existsSync(appEntry))
|
|
29
|
-
return { entry: appEntry, portable: false };
|
|
30
|
-
}
|
|
31
|
-
const entry = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
|
|
32
|
-
if (!existsSync(entry)) {
|
|
33
|
-
throw new Error("inline application bundle is missing — rebuild/install the sp plugin before running inline");
|
|
34
|
-
}
|
|
35
|
-
return { entry, portable: true };
|
|
36
|
-
}
|
|
37
|
-
async function loadInlineApp(spurBin, resolveAppEntry, required) {
|
|
38
|
-
const { entry, portable } = resolveAppEntry(spurBin);
|
|
39
|
-
const app = await import(entry);
|
|
40
|
-
const missing = portable ? required.filter((name) => typeof app[name] !== "function") : [];
|
|
41
|
-
if (missing.length > 0) {
|
|
42
|
-
throw new Error(`inline application bundle ${entry} is older than its scripts — missing ${missing.join(", ")}. Reinstall sp with a superskill release that stages lib/ (commit b42961b), or pass --spur-bin <spur checkout>/apps/cli/src/index.ts.`);
|
|
43
|
-
}
|
|
44
|
-
return { app, portable };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// plugins/sp/scripts/inline-run-dispatch.ts
|
|
48
|
-
function usage() {
|
|
49
|
-
console.error([
|
|
50
|
-
"Usage: bun plugins/sp/scripts/inline-run-dispatch.ts --attribution --run-id <id> --stage <state> [--executor <name>] [--agent <name>] [--model <m>] [--spur-bin <path>]",
|
|
51
|
-
" bun plugins/sp/scripts/inline-run-dispatch.ts --dispatch-failure --run-id <id> --stage <state> --decision <escalate|stop|host-inline> (--text <record> | --text-file <path>) [--executor <name>] [--agent <name>] [--model <m>] [--spur-bin <path>]",
|
|
52
|
-
" --attribution writes .spur/run/<run-id>-attribution.jsonl; an omitted/empty --executor records the explicit no-attribution marker.",
|
|
53
|
-
" --dispatch-failure classifies the record upstream, writes .spur/run/<run-id>-dispatch-fallback.json, and carries an attributed capacity exhaustion onto the durable availability path."
|
|
54
|
-
].join(`
|
|
55
|
-
`));
|
|
56
|
-
process.exit(2);
|
|
57
|
-
}
|
|
58
|
-
var SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
59
|
-
async function main() {
|
|
60
|
-
if (!process.versions.bun) {
|
|
61
|
-
const child = spawnSync("bun", [fileURLToPath2(import.meta.url), ...process.argv.slice(2)], {
|
|
62
|
-
stdio: "inherit"
|
|
63
|
-
});
|
|
64
|
-
if (child.error)
|
|
65
|
-
console.error(`inline-run-dispatch requires Bun on PATH: ${child.error.message}`);
|
|
66
|
-
process.exit(child.status ?? 1);
|
|
67
|
-
}
|
|
68
|
-
const flags = new Map;
|
|
69
|
-
let attribution = false;
|
|
70
|
-
let dispatchFailure = false;
|
|
71
|
-
let spurBin = getEnvVar("SPUR_BIN") ?? "";
|
|
72
|
-
const argv = process.argv.slice(2);
|
|
73
|
-
for (let i = 0;i < argv.length; i++) {
|
|
74
|
-
const flag = argv[i];
|
|
75
|
-
if (flag === "--attribution")
|
|
76
|
-
attribution = true;
|
|
77
|
-
else if (flag === "--dispatch-failure")
|
|
78
|
-
dispatchFailure = true;
|
|
79
|
-
else if (flag === "--spur-bin")
|
|
80
|
-
spurBin = argv[++i] ?? spurBin;
|
|
81
|
-
else if (flag !== undefined)
|
|
82
|
-
flags.set(flag, argv[++i] ?? "");
|
|
83
|
-
}
|
|
84
|
-
const runId = flags.get("--run-id") ?? "";
|
|
85
|
-
const stage = flags.get("--stage") ?? "";
|
|
86
|
-
const decision = flags.get("--decision") ?? "";
|
|
87
|
-
const text = flags.get("--text") ?? "";
|
|
88
|
-
const textFile = flags.get("--text-file") ?? "";
|
|
89
|
-
const executor = flags.get("--executor") ?? "";
|
|
90
|
-
const agent = flags.get("--agent") ?? "";
|
|
91
|
-
const model = flags.get("--model") ?? "";
|
|
92
|
-
if (attribution === dispatchFailure)
|
|
93
|
-
usage();
|
|
94
|
-
if (runId.trim() === "" || stage.trim() === "" || !SAFE_RUN_ID_RE.test(runId))
|
|
95
|
-
usage();
|
|
96
|
-
const identity = {
|
|
97
|
-
executor: executor.trim() === "" ? null : executor,
|
|
98
|
-
agent: agent.trim() === "" ? null : agent,
|
|
99
|
-
...model.trim() === "" ? {} : { model }
|
|
100
|
-
};
|
|
101
|
-
const { app, portable } = await loadInlineApp(spurBin, resolveAppEntry, [
|
|
102
|
-
"recordInlineRunAttribution",
|
|
103
|
-
"runInlineRunDispatchFailure"
|
|
104
|
-
]);
|
|
105
|
-
if (attribution) {
|
|
106
|
-
app.recordInlineRunAttribution(process.cwd(), runId, {
|
|
107
|
-
stage,
|
|
108
|
-
...identity,
|
|
109
|
-
observedAt: new Date().toISOString()
|
|
110
|
-
});
|
|
111
|
-
process.stdout.write(`${JSON.stringify({ ok: true, runId, stage })}
|
|
112
|
-
`);
|
|
113
|
-
process.exit(0);
|
|
114
|
-
}
|
|
115
|
-
if (decision.trim() === "" || text === "" === (textFile === ""))
|
|
116
|
-
usage();
|
|
117
|
-
const { app: lib } = await loadInlineApp(spurBin, () => ({ entry: fileURLToPath2(new URL("../lib/inline-run.generated.mjs", import.meta.url)), portable }), ["loadSpurConfig"]);
|
|
118
|
-
const spurConfig = await lib.loadSpurConfig(process.cwd(), portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined);
|
|
119
|
-
process.exit(await app.runInlineRunDispatchFailure({
|
|
120
|
-
runId,
|
|
121
|
-
stage,
|
|
122
|
-
decision,
|
|
123
|
-
...text === "" ? { textFile } : { text },
|
|
124
|
-
...identity,
|
|
125
|
-
spurConfig
|
|
126
|
-
}));
|
|
127
|
-
}
|
|
128
|
-
main().catch((e) => {
|
|
129
|
-
console.error(`inline-run-dispatch: FAIL \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
130
|
-
process.exit(1);
|
|
131
|
-
});
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/** Inline dispatch fail-fast facade (task 1134 R1–R4): argv/env only. The classification, the
|
|
3
|
-
* decision artifact and the durable availability write all live in the app runners
|
|
4
|
-
* (`recordInlineRunAttribution`, `runInlineRunDispatchFailure`), reached through the same
|
|
5
|
-
* `loadInlineApp` seam the setup facade uses. It is a SEPARATE script from `inline-run-setup.ts`
|
|
6
|
-
* because it is a separate operation and the ADR-130 glue budget is per script. */
|
|
7
|
-
import { spawnSync } from 'node:child_process';
|
|
8
|
-
import { fileURLToPath } from 'node:url';
|
|
9
|
-
// Type-only namespace import (erased before bundling; the smoke scanner exempts `import type`).
|
|
10
|
-
import type * as spurApp from '@gobing-ai/spur-app';
|
|
11
|
-
import { getEnvVar } from '../lib/env';
|
|
12
|
-
import { loadInlineApp, resolveAppEntry } from '../lib/inline-run-app';
|
|
13
|
-
|
|
14
|
-
// EMBEDDED_SPUR_SCHEMAS is declared by the bundle twin, never exported by the barrel.
|
|
15
|
-
type InlineApp = typeof spurApp & { EMBEDDED_SPUR_SCHEMAS?: ReadonlyMap<string, string> };
|
|
16
|
-
|
|
17
|
-
function usage(): never {
|
|
18
|
-
console.error(
|
|
19
|
-
[
|
|
20
|
-
'Usage: bun plugins/sp/scripts/inline-run-dispatch.ts --attribution --run-id <id> --stage <state> [--executor <name>] [--agent <name>] [--model <m>] [--spur-bin <path>]',
|
|
21
|
-
' bun plugins/sp/scripts/inline-run-dispatch.ts --dispatch-failure --run-id <id> --stage <state> --decision <escalate|stop|host-inline> (--text <record> | --text-file <path>) [--executor <name>] [--agent <name>] [--model <m>] [--spur-bin <path>]',
|
|
22
|
-
' --attribution writes .spur/run/<run-id>-attribution.jsonl; an omitted/empty --executor records the explicit no-attribution marker.',
|
|
23
|
-
' --dispatch-failure classifies the record upstream, writes .spur/run/<run-id>-dispatch-fallback.json, and carries an attributed capacity exhaustion onto the durable availability path.',
|
|
24
|
-
].join('\n'),
|
|
25
|
-
);
|
|
26
|
-
process.exit(2);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Run ids become filenames under `.spur/run/` — single safe component only (0804 R8). */
|
|
30
|
-
const SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
31
|
-
|
|
32
|
-
/** Resolve the app entry: --spur-bin/SPUR_BIN main module if it proves a repo checkout, else the bundle. */
|
|
33
|
-
|
|
34
|
-
async function main(): Promise<void> {
|
|
35
|
-
// The portable Node twin has no workspace imports; the app bundle still uses Spur's Bun runtime.
|
|
36
|
-
if (!process.versions.bun) {
|
|
37
|
-
const child = spawnSync('bun', [fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
|
|
38
|
-
stdio: 'inherit',
|
|
39
|
-
});
|
|
40
|
-
if (child.error) console.error(`inline-run-dispatch requires Bun on PATH: ${child.error.message}`);
|
|
41
|
-
process.exit(child.status ?? 1);
|
|
42
|
-
}
|
|
43
|
-
const flags = new Map<string, string>();
|
|
44
|
-
let attribution = false;
|
|
45
|
-
let dispatchFailure = false;
|
|
46
|
-
let spurBin = getEnvVar('SPUR_BIN') ?? '';
|
|
47
|
-
const argv = process.argv.slice(2);
|
|
48
|
-
for (let i = 0; i < argv.length; i++) {
|
|
49
|
-
const flag = argv[i];
|
|
50
|
-
if (flag === '--attribution') attribution = true;
|
|
51
|
-
else if (flag === '--dispatch-failure') dispatchFailure = true;
|
|
52
|
-
else if (flag === '--spur-bin') spurBin = argv[++i] ?? spurBin;
|
|
53
|
-
else if (flag !== undefined) flags.set(flag, argv[++i] ?? '');
|
|
54
|
-
}
|
|
55
|
-
const runId = flags.get('--run-id') ?? '';
|
|
56
|
-
const stage = flags.get('--stage') ?? '';
|
|
57
|
-
const decision = flags.get('--decision') ?? '';
|
|
58
|
-
const text = flags.get('--text') ?? '';
|
|
59
|
-
const textFile = flags.get('--text-file') ?? '';
|
|
60
|
-
const executor = flags.get('--executor') ?? '';
|
|
61
|
-
const agent = flags.get('--agent') ?? '';
|
|
62
|
-
const model = flags.get('--model') ?? '';
|
|
63
|
-
|
|
64
|
-
if (attribution === dispatchFailure) usage();
|
|
65
|
-
if (runId.trim() === '' || stage.trim() === '' || !SAFE_RUN_ID_RE.test(runId)) usage();
|
|
66
|
-
// R3: the executor identity the inline stage actually used. An omitted/empty `--executor` is the
|
|
67
|
-
// explicit no-attribution marker — never a silent drop.
|
|
68
|
-
const identity = {
|
|
69
|
-
executor: executor.trim() === '' ? null : executor,
|
|
70
|
-
agent: agent.trim() === '' ? null : agent,
|
|
71
|
-
...(model.trim() === '' ? {} : { model }),
|
|
72
|
-
};
|
|
73
|
-
const { app, portable } = await loadInlineApp<InlineApp>(spurBin, resolveAppEntry, [
|
|
74
|
-
'recordInlineRunAttribution',
|
|
75
|
-
'runInlineRunDispatchFailure',
|
|
76
|
-
]);
|
|
77
|
-
|
|
78
|
-
if (attribution) {
|
|
79
|
-
app.recordInlineRunAttribution(process.cwd(), runId, {
|
|
80
|
-
stage,
|
|
81
|
-
...identity,
|
|
82
|
-
observedAt: new Date().toISOString(),
|
|
83
|
-
});
|
|
84
|
-
process.stdout.write(`${JSON.stringify({ ok: true, runId, stage })}\n`);
|
|
85
|
-
process.exit(0);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (decision.trim() === '' || (text === '') === (textFile === '')) usage();
|
|
89
|
-
// ADR-082: this facade is the composition root, so it loads the effective config once and
|
|
90
|
-
// threads the merged result into the app runner (never a per-slice load in packages/app).
|
|
91
|
-
const { app: lib } = await loadInlineApp<typeof import('../lib/inline-run.generated.mjs')>(
|
|
92
|
-
spurBin,
|
|
93
|
-
() => ({ entry: fileURLToPath(new URL('../lib/inline-run.generated.mjs', import.meta.url)), portable }),
|
|
94
|
-
['loadSpurConfig'],
|
|
95
|
-
);
|
|
96
|
-
const spurConfig = await lib.loadSpurConfig(
|
|
97
|
-
process.cwd(),
|
|
98
|
-
portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined,
|
|
99
|
-
);
|
|
100
|
-
process.exit(
|
|
101
|
-
await app.runInlineRunDispatchFailure({
|
|
102
|
-
runId,
|
|
103
|
-
stage,
|
|
104
|
-
decision: decision as 'escalate' | 'stop' | 'host-inline',
|
|
105
|
-
...(text === '' ? { textFile } : { text }),
|
|
106
|
-
...identity,
|
|
107
|
-
spurConfig,
|
|
108
|
-
}),
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
main().catch((e: unknown) => {
|
|
113
|
-
console.error(`inline-run-dispatch: FAIL — ${e instanceof Error ? e.message : String(e)}`);
|
|
114
|
-
process.exit(1);
|
|
115
|
-
});
|