@gobing-ai/spur 0.4.2 → 0.4.4
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/decisions/maker-liveness.yaml +39 -0
- package/config/plugin-scripts.json +0 -4
- package/config/rules/README.md +1 -0
- package/config/rules/docs/sourced-api-claims.yaml +56 -12
- package/config/rules/docs/verdict-confidence.yaml +68 -0
- package/config/rules/quality/coverage-gate.yaml +13 -0
- package/config/rules/recommended-pre-check.yaml +6 -2
- package/config/rules/strict/runtime-boundaries.yaml +10 -0
- package/config/rules/surface/check-cli-surface.yaml +1 -3
- package/config/script-placement-baseline.json +1 -19
- package/config/templates/AGENTS.md +7 -0
- package/config/workflows/feature-lifecycle.yaml +46 -1
- package/config/workflows/feature-verification.yaml +20 -1
- package/config/workflows/task-lifecycle.yaml +50 -0
- package/config/workflows/task-pipeline.yaml +15 -9
- package/package.json +10 -10
- package/plugins/sp/README.md +22 -16
- package/plugins/sp/agents/super-reviewer.md +46 -0
- package/plugins/sp/bunfig.toml +8 -5
- package/plugins/sp/commands/dev-changelog.md +1 -1
- package/plugins/sp/commands/dev-gitmsg.md +6 -2
- package/plugins/sp/commands/dev-handover.md +1 -0
- package/plugins/sp/commands/dev-job-dump.md +2 -1
- package/plugins/sp/commands/dev-refine.md +14 -2
- package/plugins/sp/commands/dev-refineall.md +7 -1
- package/plugins/sp/commands/dev-review.md +1 -0
- package/plugins/sp/hooks/careful-guard.ts +1 -1
- package/plugins/sp/hooks/context-post-tool.ts +7 -427
- package/plugins/sp/hooks/context-session-start.ts +5 -213
- package/plugins/sp/hooks/context-session-stop.ts +5 -191
- package/plugins/sp/hooks/pi/guard-extension.ts +6 -5
- package/plugins/sp/hooks/task-write-guard.ts +1 -1
- package/plugins/sp/{hooks → lib}/agent-hint.ts +1 -1
- package/plugins/sp/lib/context-post-tool.ts +432 -0
- package/plugins/sp/lib/context-session-start.ts +214 -0
- package/plugins/sp/lib/context-session-stop.ts +196 -0
- package/plugins/sp/lib/dogfood-pipeline.ts +265 -0
- package/plugins/sp/lib/dogfood-report.ts +278 -0
- package/plugins/sp/lib/history-anatomy.generated.mjs +18 -18
- package/plugins/sp/lib/idea-handoff.generated.mjs +91 -91
- package/plugins/sp/lib/inline-run-app.ts +40 -0
- package/plugins/sp/lib/inline-run.generated.d.mts +3 -3
- package/plugins/sp/lib/inline-run.generated.mjs +1425 -212
- package/plugins/sp/lib/plan-projection.generated.d.mts +150 -0
- package/plugins/sp/lib/plan-projection.generated.mjs +623 -9
- package/plugins/sp/lib/quality-gate.generated.d.mts +1 -0
- package/plugins/sp/lib/quality-gate.generated.mjs +13809 -13969
- package/plugins/sp/lib/residual-scan.generated.d.mts +20 -1
- package/plugins/sp/lib/residual-scan.generated.mjs +120 -25
- package/plugins/sp/lib/run-summary-core.ts +34 -370
- package/plugins/sp/lib/step-profile.generated.mjs +8 -8
- package/plugins/sp/lib/transcript.ts +37 -228
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/batch-plan.mjs +619 -11
- package/plugins/sp/scripts/batch-preflight.mjs +57 -13
- package/plugins/sp/scripts/batch-preflight.ts +35 -295
- package/plugins/sp/scripts/commit-guard.mjs +6 -6
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +127 -108
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +111 -104
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +13 -12
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +7 -265
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +7 -4
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +6 -272
- package/plugins/sp/scripts/feature-verification-steps.mjs +20 -125
- package/plugins/sp/scripts/feature-verification-steps.ts +57 -181
- package/plugins/sp/scripts/history-anatomy-cache.mjs +19 -19
- package/plugins/sp/scripts/idea-handoff.mjs +2 -2
- package/plugins/sp/scripts/inline-run-dispatch.mjs +25 -24
- package/plugins/sp/scripts/inline-run-dispatch.ts +1 -21
- package/plugins/sp/scripts/inline-run-setup.mjs +164 -137
- package/plugins/sp/scripts/inline-run-setup.ts +1 -21
- package/plugins/sp/scripts/persist-out-check.mjs +5 -5
- package/plugins/sp/scripts/pr-reviewing.mjs +13 -13
- package/plugins/sp/scripts/quality-gate.mjs +13730 -13887
- package/plugins/sp/scripts/residual-scan.mjs +129 -31
- package/plugins/sp/scripts/residual-scan.ts +16 -13
- package/plugins/sp/scripts/run-summary.mjs +166 -112
- package/plugins/sp/scripts/run-summary.ts +7 -5
- package/plugins/sp/scripts/script-root.mjs +6 -6
- package/plugins/sp/scripts/session-timeline.mjs +62 -35
- package/plugins/sp/scripts/task-diffstat.mjs +19 -9
- package/plugins/sp/scripts/task-diffstat.ts +8 -39
- package/plugins/sp/scripts/workflow-step-profile.mjs +12 -12
- package/plugins/sp/scripts/wrapup-drift-probe.mjs +9 -9
- package/plugins/sp/scripts/wrapup-steps.mjs +25 -25
- package/plugins/sp/skills/code-implementation/SKILL.md +7 -1
- package/plugins/sp/skills/code-verification/references/secu-review.md +2 -1
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +4 -0
- package/plugins/sp/skills/dogfood-testing/SKILL.md +2 -2
- package/plugins/sp/skills/next-feature/SKILL.md +1 -1
- package/plugins/sp/skills/next-router/SKILL.md +3 -1
- package/plugins/sp/skills/next-router/references/routing-table.md +9 -3
- package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +1 -1
- package/plugins/sp/skills/source-driven-development/SKILL.md +7 -4
- package/plugins/sp/skills/spur-cli/references/builder.md +3 -2
- package/plugins/sp/skills/spur-cli/references/decision.md +32 -7
- package/plugins/sp/skills/spur-cli/references/features.md +46 -5
- package/plugins/sp/skills/spur-cli/references/tasks.md +24 -4
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +8 -1
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +37 -11
- package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +1 -1
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +19 -6
- package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +22 -0
- package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +14 -0
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +4 -2
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +49 -12
- package/schemas/section-matrix.schema.json +3 -2
- package/spur.js +66591 -73262
- package/web/_astro/{BoardApp.B3GQZvj8.js → BoardApp.Niubpe6z.js} +1 -1
- package/web/_astro/{BoardApp.BT7L2kSb.js → BoardApp.hF0RAIrg.js} +150 -150
- package/web/_astro/TaskDetail.DJpapt38.js +1 -0
- package/web/_astro/{arc.vaT7tPOB.js → arc.BgKcK0i8.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.DRMmTjkN.js → architectureDiagram-3BPJPVTR.ChP-Ig0V.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.cla0LO_q.js → blockDiagram-GPEHLZMM.BVaCBoeN.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.D4YAIBKn.js → c4Diagram-AAUBKEIU.CHSDpEO5.js} +1 -1
- package/web/_astro/channel.DkpvN2tD.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.BrSJbMRZ.js → chunk-2J33WTMH.MYRcYVRa.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.CuLMgKF5.js → chunk-4BX2VUAB.S5j22Zqa.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.zkvgu54F.js → chunk-55IACEB6.gciRPvmZ.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DxWkACh8.js → chunk-727SXJPM.CdqrSQkX.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.42XrmIlH.js → chunk-AQP2D5EJ.CyokdHT3.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.Bao2wMK9.js → chunk-FMBD7UC4.Bg9KJhjW.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.BhydojD1.js → chunk-ND2GUHAM.1gxxI-vD.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CmZ82g-H.js → chunk-QZHKN3VN.DDEYtCKU.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.Blr83Gt_.js → classDiagram-4FO5ZUOK.Ch3zi37T.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.Blr83Gt_.js → classDiagram-v2-Q7XG4LA2.Ch3zi37T.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.qmfv0SPn.js → cose-bilkent-S5V4N54A.D2q5AINI.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.BtELhXPR.js → cynefin-OW5HDTMX.BA9kC2YT.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.AWmaAvXo.js → dagre-BM42HDAG.CHO08nRp.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.DdH34KDi.js → diagram-2AECGRRQ.BfFQt0LW.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.BmNPix1k.js → diagram-5GNKFQAL.CHMwMTe4.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.BNQd_Ppo.js → diagram-KO2AKTUF.BJ-7gK-w.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.BAPgDi0e.js → diagram-LMA3HP47.C5xqcc0t.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.DZ4zyLdC.js → diagram-OG6HWLK6.C2pQjfrC.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.Q1TySMmw.js → erDiagram-TEJ5UH35._FoNyxgC.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.BXhFBicG.js → flowDiagram-I6XJVG4X.B_ENkT2I.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.vHSgcuo2.js → ganttDiagram-6RSMTGT7.B3q8h77C.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.Bi1f73C7.js → gitGraphDiagram-PVQCEYII.QWZFDboZ.js} +1 -1
- package/web/_astro/index.BFyW13JJ.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.DPvXZEWm.js → infoDiagram-5YYISTIA.Cs6eEc92.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.Cz829fsV.js → ishikawaDiagram-YF4QCWOH.BU9DJMHA.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.enRnB9Me.js → journeyDiagram-JHISSGLW.D35k6Jpe.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.DnTNk6d2.js → kanban-definition-UN3LZRKU.DBqcAT4m.js} +1 -1
- package/web/_astro/{linear.BzGnBwNL.js → linear.D-LSNK8W.js} +1 -1
- package/web/_astro/{mermaid.core.CSbTpIVz.js → mermaid.core.CSruWVOa.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.DGJDy8zT.js → mindmap-definition-RKZ34NQL.BiFaUsfj.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.HSPdi5_8.js → pieDiagram-4H26LBE5.D1KTKqb9.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.6Gv8mz2Y.js → quadrantDiagram-W4KKPZXB.DBHflsMG.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.CnBCtFwa.js → requirementDiagram-4Y6WPE33.hwX9CNdk.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.5M8VWAu8.js → sankeyDiagram-5OEKKPKP.DkI8mJiE.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DN41XN-U.js → sequenceDiagram-3UESZ5HK.DMz7jBDx.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.ErOO3W8j.js → stateDiagram-AJRCARHV.B6Ef2xJ1.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.B_ZHICM-.js → stateDiagram-v2-BHNVJYJU.CgoaufB9.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.CWdosr07.js → timeline-definition-PNZ67QCA.B3c8h1t-.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.BHzYjNV5.js → vennDiagram-CIIHVFJN.BdHPAxAb.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.h8x4IwPX.js → wardleyDiagram-YWT4CUSO.BrJXPPAj.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.RTapW2X8.js → xychartDiagram-2RQKCTM6.CMH5yFsV.js} +1 -1
- package/web/index.html +2 -2
- package/web/spur_logo.svg +1 -1
- package/config/proportional-route-table.ts +0 -155
- package/plugins/sp/scripts/daily-summary/logger.ts +0 -28
- package/web/_astro/TaskDetail.Cxov1_N_.js +0 -1
- package/web/_astro/channel.D4nG9YLf.js +0 -1
- package/web/_astro/index.BhSwN8yY.css +0 -1
- /package/plugins/sp/{hooks → lib}/context-owner.ts +0 -0
- /package/plugins/sp/{hooks → lib}/destructive-policy.ts +0 -0
- /package/plugins/sp/{hooks → lib}/task-file-policy.ts +0 -0
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
* `loadInlineApp` seam the setup facade uses. It is a SEPARATE script from `inline-run-setup.ts`
|
|
6
6
|
* because it is a separate operation and the ADR-130 glue budget is per script. */
|
|
7
7
|
import { spawnSync } from 'node:child_process';
|
|
8
|
-
import { existsSync } from 'node:fs';
|
|
9
|
-
import { dirname, join, resolve } from 'node:path';
|
|
10
8
|
import { fileURLToPath } from 'node:url';
|
|
11
9
|
// Type-only namespace import (erased before bundling; the smoke scanner exempts `import type`).
|
|
12
10
|
import type * as spurApp from '@gobing-ai/spur-app';
|
|
13
11
|
import { getEnvVar } from '../lib/env';
|
|
14
|
-
import { loadInlineApp } from '../lib/inline-run-app';
|
|
12
|
+
import { loadInlineApp, resolveAppEntry } from '../lib/inline-run-app';
|
|
15
13
|
|
|
16
14
|
// EMBEDDED_SPUR_SCHEMAS is declared by the bundle twin, never exported by the barrel.
|
|
17
15
|
type InlineApp = typeof spurApp & { EMBEDDED_SPUR_SCHEMAS?: ReadonlyMap<string, string> };
|
|
@@ -32,24 +30,6 @@ function usage(): never {
|
|
|
32
30
|
const SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
33
31
|
|
|
34
32
|
/** Resolve the app entry: --spur-bin/SPUR_BIN main module if it proves a repo checkout, else the bundle. */
|
|
35
|
-
function resolveAppEntry(spurBin: string): { entry: string; portable: boolean } {
|
|
36
|
-
const fallback = fileURLToPath(new URL('../../../apps/cli/src/index.ts', import.meta.url));
|
|
37
|
-
const candidates = spurBin !== '' ? [spurBin] : [fallback];
|
|
38
|
-
for (const candidate of candidates) {
|
|
39
|
-
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
40
|
-
// Main module = last path-like token; only a .ts source entry proves a repo checkout.
|
|
41
|
-
const mainModule = [...tokens].reverse().find((t) => t.endsWith('.ts'));
|
|
42
|
-
if (mainModule === undefined || !existsSync(mainModule)) continue;
|
|
43
|
-
const repoRoot = resolve(dirname(mainModule), '..', '..', '..');
|
|
44
|
-
const appEntry = join(repoRoot, 'packages', 'app', 'src', 'index.ts');
|
|
45
|
-
if (existsSync(appEntry)) return { entry: appEntry, portable: false };
|
|
46
|
-
}
|
|
47
|
-
const entry = fileURLToPath(new URL('../lib/inline-run.generated.mjs', import.meta.url));
|
|
48
|
-
if (!existsSync(entry)) {
|
|
49
|
-
throw new Error('inline application bundle is missing — rebuild/install the sp plugin before running inline');
|
|
50
|
-
}
|
|
51
|
-
return { entry, portable: true };
|
|
52
|
-
}
|
|
53
33
|
|
|
54
34
|
async function main(): Promise<void> {
|
|
55
35
|
// The portable Node twin has no workspace imports; the app bundle still uses Spur's Bun runtime.
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
// plugins/sp/scripts/inline-run-setup.ts
|
|
5
5
|
import { spawnSync } from "child_process";
|
|
6
|
-
import {
|
|
7
|
-
import { dirname, join as join3, resolve } from "path";
|
|
8
|
-
import { fileURLToPath } from "url";
|
|
6
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9
7
|
|
|
10
8
|
// plugins/sp/lib/env.ts
|
|
11
9
|
function getEnvVar(name, fallback) {
|
|
@@ -14,6 +12,28 @@ function getEnvVar(name, fallback) {
|
|
|
14
12
|
}
|
|
15
13
|
|
|
16
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
|
+
}
|
|
17
37
|
async function loadInlineApp(spurBin, resolveAppEntry, required) {
|
|
18
38
|
const { entry, portable } = resolveAppEntry(spurBin);
|
|
19
39
|
const app = await import(entry);
|
|
@@ -30,16 +50,14 @@ function getEnvVars() {
|
|
|
30
50
|
}
|
|
31
51
|
|
|
32
52
|
// plugins/sp/lib/run-summary-core.ts
|
|
33
|
-
import { existsSync as
|
|
34
|
-
import { join as
|
|
53
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
54
|
+
import { join as join3 } from "node:path";
|
|
35
55
|
|
|
36
|
-
// plugins/sp/lib/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
|
|
42
|
-
var PI_ROLES = new Set(["user", "assistant", "toolResult"]);
|
|
56
|
+
// plugins/sp/lib/plan-projection.generated.mjs
|
|
57
|
+
var NON_ABORTING_PREFLIGHT_CODES = new Set([
|
|
58
|
+
"L4.scenario-unverified",
|
|
59
|
+
"L4.verifying-incomplete-tasks"
|
|
60
|
+
]);
|
|
43
61
|
var zeroTokens = () => ({ input: 0, cacheCreate: 0, cacheRead: 0, output: 0 });
|
|
44
62
|
function formatDuration(ms) {
|
|
45
63
|
const s = Math.round(ms / 1000);
|
|
@@ -59,6 +77,26 @@ function formatTokenSplit(t) {
|
|
|
59
77
|
const nonCached = t.input + t.cacheCreate + t.output;
|
|
60
78
|
return `${formatTokens(nonCached + t.cacheRead)} / ${formatTokens(nonCached)}`;
|
|
61
79
|
}
|
|
80
|
+
var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
|
|
81
|
+
function isCompaction(row) {
|
|
82
|
+
return row.type === "compaction" || row.type === "system" && row.subtype === "compact_boundary";
|
|
83
|
+
}
|
|
84
|
+
function firstTextContent(row) {
|
|
85
|
+
const content = row.message?.content;
|
|
86
|
+
if (typeof content === "string")
|
|
87
|
+
return content;
|
|
88
|
+
if (!Array.isArray(content))
|
|
89
|
+
return;
|
|
90
|
+
return content.find((b) => b.type === "text")?.text;
|
|
91
|
+
}
|
|
92
|
+
function skillInvocationPrompt(text) {
|
|
93
|
+
const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
|
|
94
|
+
if (name === undefined)
|
|
95
|
+
return;
|
|
96
|
+
const close = text.lastIndexOf("</skill>");
|
|
97
|
+
const args = close < 0 ? "" : text.slice(close + "</skill>".length).trim();
|
|
98
|
+
return args === "" ? `/${name}` : `/${name} ${args}`;
|
|
99
|
+
}
|
|
62
100
|
function promptText(row) {
|
|
63
101
|
if (row.type === "custom_message") {
|
|
64
102
|
if (row.display === false)
|
|
@@ -83,22 +121,6 @@ function promptText(row) {
|
|
|
83
121
|
return;
|
|
84
122
|
return content.find((b) => b.type === "text")?.text;
|
|
85
123
|
}
|
|
86
|
-
function firstTextContent(row) {
|
|
87
|
-
const content = row.message?.content;
|
|
88
|
-
if (typeof content === "string")
|
|
89
|
-
return content;
|
|
90
|
-
if (!Array.isArray(content))
|
|
91
|
-
return;
|
|
92
|
-
return content.find((b) => b.type === "text")?.text;
|
|
93
|
-
}
|
|
94
|
-
function skillInvocationPrompt(text) {
|
|
95
|
-
const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
|
|
96
|
-
if (name === undefined)
|
|
97
|
-
return;
|
|
98
|
-
const close = text.lastIndexOf("</skill>");
|
|
99
|
-
const args = close < 0 ? "" : text.slice(close + "</skill>".length).trim();
|
|
100
|
-
return args === "" ? `/${name}` : `/${name} ${args}`;
|
|
101
|
-
}
|
|
102
124
|
function sumTokens(all) {
|
|
103
125
|
const total = zeroTokens();
|
|
104
126
|
for (const t of all)
|
|
@@ -127,21 +149,21 @@ function accumulate(acc, row, ts) {
|
|
|
127
149
|
if (row.type === "message") {
|
|
128
150
|
const m = row.message;
|
|
129
151
|
if (m?.role === "assistant") {
|
|
130
|
-
const
|
|
131
|
-
for (const b of
|
|
152
|
+
const blocks = Array.isArray(m.content) ? m.content : [];
|
|
153
|
+
for (const b of blocks) {
|
|
132
154
|
if (b.type === "toolCall" && b.id) {
|
|
133
155
|
acc.toolIds.add(b.id);
|
|
134
156
|
if (b.name && OPERATOR_TOOLS.has(b.name))
|
|
135
157
|
acc.asks.set(b.id, ts);
|
|
136
158
|
}
|
|
137
159
|
}
|
|
138
|
-
const
|
|
139
|
-
if (
|
|
160
|
+
const u = m.usage;
|
|
161
|
+
if (u && row.id)
|
|
140
162
|
acc.messages.set(row.id, {
|
|
141
|
-
input:
|
|
142
|
-
cacheCreate:
|
|
143
|
-
cacheRead:
|
|
144
|
-
output:
|
|
163
|
+
input: u.input ?? 0,
|
|
164
|
+
cacheCreate: u.cacheWrite ?? 0,
|
|
165
|
+
cacheRead: u.cacheRead ?? 0,
|
|
166
|
+
output: u.output ?? 0
|
|
145
167
|
});
|
|
146
168
|
} else if (m?.role === "toolResult" && m.toolCallId && acc.asks.has(m.toolCallId)) {
|
|
147
169
|
acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
|
|
@@ -170,72 +192,14 @@ function accumulate(acc, row, ts) {
|
|
|
170
192
|
});
|
|
171
193
|
}
|
|
172
194
|
}
|
|
173
|
-
function isCompaction(row) {
|
|
174
|
-
return row.type === "compaction" || row.type === "system" && row.subtype === "compact_boundary";
|
|
175
|
-
}
|
|
176
|
-
function sniffFormat(rows) {
|
|
177
|
-
for (const row of rows) {
|
|
178
|
-
if (row.type === "message" && PI_ROLES.has(row.message?.role ?? ""))
|
|
179
|
-
return "pi";
|
|
180
|
-
if (row.type === "user" || row.type === "assistant")
|
|
181
|
-
return "claude";
|
|
182
|
-
}
|
|
183
|
-
return "unknown";
|
|
184
|
-
}
|
|
185
|
-
function parseRows(lines) {
|
|
186
|
-
const rows = [];
|
|
187
|
-
let skippedLines = 0;
|
|
188
|
-
for (const line of lines) {
|
|
189
|
-
if (!line.trim())
|
|
190
|
-
continue;
|
|
191
|
-
let row;
|
|
192
|
-
try {
|
|
193
|
-
row = JSON.parse(line);
|
|
194
|
-
} catch {
|
|
195
|
-
skippedLines++;
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
const ts = row.timestamp ? Date.parse(row.timestamp) : Number.NaN;
|
|
199
|
-
if (!Number.isNaN(ts))
|
|
200
|
-
rows.push([row, ts]);
|
|
201
|
-
}
|
|
202
|
-
return { rows, skippedLines, format: sniffFormat(rows.map(([r]) => r)) };
|
|
203
|
-
}
|
|
204
|
-
function resolveTranscript(env, projectsRoot = join(homedir(), ".claude", "projects"), override) {
|
|
205
|
-
if (override)
|
|
206
|
-
return existsSync(override) ? { ok: true, path: override } : { ok: false, reason: "no transcript" };
|
|
207
|
-
const id = env.CLAUDE_CODE_SESSION_ID;
|
|
208
|
-
if (!id) {
|
|
209
|
-
const piFile = env.PI_SESSION_FILE;
|
|
210
|
-
if (piFile !== undefined && piFile !== "") {
|
|
211
|
-
return existsSync(piFile) ? { ok: true, path: piFile } : { ok: false, reason: `PI_SESSION_FILE ${piFile} does not exist` };
|
|
212
|
-
}
|
|
213
|
-
return {
|
|
214
|
-
ok: false,
|
|
215
|
-
reason: "no host session id; pass --transcript <path> (pi: ~/.pi/agent/sessions/<cwd-slug>/<file>.jsonl, " + "or set PI_SESSION_FILE)"
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
if (!SESSION_ID.test(id))
|
|
219
|
-
return { ok: false, reason: "refusing a session id with path characters" };
|
|
220
|
-
if (!existsSync(projectsRoot))
|
|
221
|
-
return { ok: false, reason: `no transcript root ${projectsRoot}` };
|
|
222
|
-
for (const dir of readdirSync(projectsRoot)) {
|
|
223
|
-
const path = join(projectsRoot, dir, `${id}.jsonl`);
|
|
224
|
-
if (existsSync(path))
|
|
225
|
-
return { ok: true, path };
|
|
226
|
-
}
|
|
227
|
-
return { ok: false, reason: `no transcript for session ${id}` };
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// plugins/sp/lib/run-summary-core.ts
|
|
231
195
|
var IDLE_GAP_MS = 300000;
|
|
232
196
|
var SHELL_TOOLS = new Set(["bash", "shell", "exec", "exec_command", "run_command", "command"]);
|
|
233
197
|
var SUBAGENT_TOOLS = new Set(["agent", "task", "invoke_subagent", "subagent", "dispatch_agent"]);
|
|
234
|
-
var
|
|
198
|
+
var LOWERCASE_OPERATOR_TOOLS = new Set(["askuserquestion", "ask_user_question"]);
|
|
235
199
|
var ACTOR_PRECEDENCE = ["operator", "subagent", "shell", "other"];
|
|
236
200
|
function actorOf(tool) {
|
|
237
201
|
const t = tool.toLowerCase().replace(/[^a-z_]/g, "");
|
|
238
|
-
if (
|
|
202
|
+
if (LOWERCASE_OPERATOR_TOOLS.has(t))
|
|
239
203
|
return "operator";
|
|
240
204
|
if (SUBAGENT_TOOLS.has(t))
|
|
241
205
|
return "subagent";
|
|
@@ -385,7 +349,8 @@ function summaryRow(stage, status, span, measured) {
|
|
|
385
349
|
token: formatTokenSplit(measured ? span.tokens : null)
|
|
386
350
|
};
|
|
387
351
|
}
|
|
388
|
-
function buildRunSummary(
|
|
352
|
+
function buildRunSummary(rows, inputs, opts = {}) {
|
|
353
|
+
const { since, until } = opts;
|
|
389
354
|
const windows = [];
|
|
390
355
|
for (const { label, progress } of inputs) {
|
|
391
356
|
const states = (progress.states ?? []).flatMap((s) => {
|
|
@@ -411,7 +376,6 @@ function buildRunSummary(lines, inputs, since, until) {
|
|
|
411
376
|
}
|
|
412
377
|
}
|
|
413
378
|
windows.sort((a, b) => a.start - b.start);
|
|
414
|
-
const { rows, skippedLines } = parseRows(lines);
|
|
415
379
|
const measure = (start, end) => {
|
|
416
380
|
const acc = newAcc(start);
|
|
417
381
|
for (const [row, ts] of rows)
|
|
@@ -459,9 +423,87 @@ function buildRunSummary(lines, inputs, since, until) {
|
|
|
459
423
|
total,
|
|
460
424
|
actors: actorSplit(rows, totalStart, totalEnd),
|
|
461
425
|
gate: { ms: null, reason: "no matching check receipt" },
|
|
462
|
-
skippedLines
|
|
426
|
+
skippedLines: opts.skippedLines ?? 0
|
|
463
427
|
};
|
|
464
428
|
}
|
|
429
|
+
var SENSITIVE_PREFIXES = ["drizzle/", "packages/config/", ".github/", "plugins/sp/hooks/"];
|
|
430
|
+
var SENSITIVE_GLOBS = ["apps/server/src/**/auth*", "**/*secret*", "**/*.sql"];
|
|
431
|
+
function globToRegex(glob) {
|
|
432
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("**/", "\x00").replaceAll("**", "\x01").replaceAll("*", "[^/]*").replaceAll("\x00", "(?:.*/)?").replaceAll("\x01", ".*");
|
|
433
|
+
return new RegExp(`^${escaped}$`, "s");
|
|
434
|
+
}
|
|
435
|
+
var SENSITIVE_MATCHERS = [
|
|
436
|
+
...SENSITIVE_PREFIXES.map((prefix) => ({
|
|
437
|
+
label: prefix,
|
|
438
|
+
test: (path) => path.startsWith(prefix)
|
|
439
|
+
})),
|
|
440
|
+
...SENSITIVE_GLOBS.map((glob) => {
|
|
441
|
+
const regex = globToRegex(glob);
|
|
442
|
+
return { label: glob, test: (path) => regex.test(path) };
|
|
443
|
+
})
|
|
444
|
+
];
|
|
445
|
+
|
|
446
|
+
// plugins/sp/lib/transcript.ts
|
|
447
|
+
import { existsSync as existsSync2, readdirSync } from "node:fs";
|
|
448
|
+
import { homedir } from "node:os";
|
|
449
|
+
import { join as join2 } from "node:path";
|
|
450
|
+
var SESSION_ID = /^[A-Za-z0-9_-]+$/;
|
|
451
|
+
var PI_ROLES = new Set(["user", "assistant", "toolResult"]);
|
|
452
|
+
function sniffFormat(rows) {
|
|
453
|
+
for (const row of rows) {
|
|
454
|
+
if (row.type === "message" && PI_ROLES.has(row.message?.role ?? ""))
|
|
455
|
+
return "pi";
|
|
456
|
+
if (row.type === "user" || row.type === "assistant")
|
|
457
|
+
return "claude";
|
|
458
|
+
}
|
|
459
|
+
return "unknown";
|
|
460
|
+
}
|
|
461
|
+
function parseRows(lines) {
|
|
462
|
+
const rows = [];
|
|
463
|
+
let skippedLines = 0;
|
|
464
|
+
for (const line of lines) {
|
|
465
|
+
if (!line.trim())
|
|
466
|
+
continue;
|
|
467
|
+
let row;
|
|
468
|
+
try {
|
|
469
|
+
row = JSON.parse(line);
|
|
470
|
+
} catch {
|
|
471
|
+
skippedLines++;
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
const ts = row.timestamp ? Date.parse(row.timestamp) : Number.NaN;
|
|
475
|
+
if (!Number.isNaN(ts))
|
|
476
|
+
rows.push([row, ts]);
|
|
477
|
+
}
|
|
478
|
+
return { rows, skippedLines, format: sniffFormat(rows.map(([r]) => r)) };
|
|
479
|
+
}
|
|
480
|
+
function resolveTranscript(env, projectsRoot = join2(homedir(), ".claude", "projects"), override) {
|
|
481
|
+
if (override)
|
|
482
|
+
return existsSync2(override) ? { ok: true, path: override } : { ok: false, reason: "no transcript" };
|
|
483
|
+
const id = env.CLAUDE_CODE_SESSION_ID;
|
|
484
|
+
if (!id) {
|
|
485
|
+
const piFile = env.PI_SESSION_FILE;
|
|
486
|
+
if (piFile !== undefined && piFile !== "") {
|
|
487
|
+
return existsSync2(piFile) ? { ok: true, path: piFile } : { ok: false, reason: `PI_SESSION_FILE ${piFile} does not exist` };
|
|
488
|
+
}
|
|
489
|
+
return {
|
|
490
|
+
ok: false,
|
|
491
|
+
reason: "no host session id; pass --transcript <path> (pi: ~/.pi/agent/sessions/<cwd-slug>/<file>.jsonl, " + "or set PI_SESSION_FILE)"
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
if (!SESSION_ID.test(id))
|
|
495
|
+
return { ok: false, reason: "refusing a session id with path characters" };
|
|
496
|
+
if (!existsSync2(projectsRoot))
|
|
497
|
+
return { ok: false, reason: `no transcript root ${projectsRoot}` };
|
|
498
|
+
for (const dir of readdirSync(projectsRoot)) {
|
|
499
|
+
const path = join2(projectsRoot, dir, `${id}.jsonl`);
|
|
500
|
+
if (existsSync2(path))
|
|
501
|
+
return { ok: true, path };
|
|
502
|
+
}
|
|
503
|
+
return { ok: false, reason: `no transcript for session ${id}` };
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// plugins/sp/lib/run-summary-core.ts
|
|
465
507
|
function renderSummaryMarkdown(summary) {
|
|
466
508
|
const line = (r, bold = false) => {
|
|
467
509
|
const stage = bold ? `**${r.stage}**` : r.stage;
|
|
@@ -493,9 +535,9 @@ function renderSummaryMarkdown(summary) {
|
|
|
493
535
|
}
|
|
494
536
|
function writeCloseSummary(run, opts = {}) {
|
|
495
537
|
const cwd = opts.cwd ?? process.cwd();
|
|
496
|
-
const runDir =
|
|
497
|
-
const rel =
|
|
498
|
-
const abs =
|
|
538
|
+
const runDir = join3(cwd, ".spur", "run");
|
|
539
|
+
const rel = join3(".spur", "run", `${run.runId}-summary.md`);
|
|
540
|
+
const abs = join3(cwd, rel);
|
|
499
541
|
const na = (reason) => {
|
|
500
542
|
mkdirSync(runDir, { recursive: true });
|
|
501
543
|
writeFileSync(abs, `Execution summary: n/a (${reason})
|
|
@@ -508,15 +550,19 @@ function writeCloseSummary(run, opts = {}) {
|
|
|
508
550
|
const resolved = resolveTranscript(opts.env ?? getEnvVars(), opts.projectsRoot);
|
|
509
551
|
if (!resolved.ok)
|
|
510
552
|
return na(resolved.reason);
|
|
511
|
-
const
|
|
512
|
-
`);
|
|
513
|
-
const summary = buildRunSummary(
|
|
553
|
+
const { rows, skippedLines } = parseRows(readFileSync(resolved.path, "utf8").split(`
|
|
554
|
+
`));
|
|
555
|
+
const summary = buildRunSummary(rows, [{ progress: run.progress }], {
|
|
556
|
+
since: run.startedAt,
|
|
557
|
+
until: run.completedAt,
|
|
558
|
+
skippedLines
|
|
559
|
+
});
|
|
514
560
|
if (summary.rows.length === 0)
|
|
515
561
|
return na("no timed attempts");
|
|
516
562
|
mkdirSync(runDir, { recursive: true });
|
|
517
563
|
writeFileSync(abs, `${renderSummaryMarkdown(summary)}
|
|
518
564
|
`);
|
|
519
|
-
writeFileSync(
|
|
565
|
+
writeFileSync(join3(runDir, `${run.runId}-summary.json`), JSON.stringify({ ...summary, runId: run.runId, startedAt: run.startedAt, completedAt: run.completedAt }, null, 2));
|
|
520
566
|
return rel;
|
|
521
567
|
} catch (error) {
|
|
522
568
|
return na(error instanceof Error ? error.message : String(error));
|
|
@@ -552,29 +598,10 @@ function refuseUnsafeRunId(runId) {
|
|
|
552
598
|
console.error(" The run id must be a single safe filename component (alphanumeric/._-, no leading dot, no path separators, interpolation or traversal; same class as the task-pipeline route-reason guard, task 0804 R8). Allocate a fresh run id (uuid or timestamp slug) and retry.");
|
|
553
599
|
process.exit(1);
|
|
554
600
|
}
|
|
555
|
-
function resolveAppEntry(spurBin) {
|
|
556
|
-
const fallback = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
|
|
557
|
-
const candidates = spurBin !== "" ? [spurBin] : [fallback];
|
|
558
|
-
for (const candidate of candidates) {
|
|
559
|
-
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
560
|
-
const mainModule = [...tokens].reverse().find((t) => t.endsWith(".ts"));
|
|
561
|
-
if (mainModule === undefined || !existsSync3(mainModule))
|
|
562
|
-
continue;
|
|
563
|
-
const repoRoot = resolve(dirname(mainModule), "..", "..", "..");
|
|
564
|
-
const appEntry = join3(repoRoot, "packages", "app", "src", "index.ts");
|
|
565
|
-
if (existsSync3(appEntry))
|
|
566
|
-
return { entry: appEntry, portable: false };
|
|
567
|
-
}
|
|
568
|
-
const entry = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
|
|
569
|
-
if (!existsSync3(entry)) {
|
|
570
|
-
throw new Error("inline application bundle is missing \u2014 rebuild/install the sp plugin before running inline");
|
|
571
|
-
}
|
|
572
|
-
return { entry, portable: true };
|
|
573
|
-
}
|
|
574
601
|
var SETUP_EXPORTS = ["readInstalledInventory", "runInlineRunSetup", "writeInlineRunOutcome"];
|
|
575
602
|
async function main() {
|
|
576
603
|
if (!process.versions.bun) {
|
|
577
|
-
const child = spawnSync("bun", [
|
|
604
|
+
const child = spawnSync("bun", [fileURLToPath2(import.meta.url), ...process.argv.slice(2)], {
|
|
578
605
|
stdio: "inherit"
|
|
579
606
|
});
|
|
580
607
|
if (child.error)
|
|
@@ -631,8 +658,8 @@ async function main() {
|
|
|
631
658
|
if (fingerprint) {
|
|
632
659
|
if (runId !== "" || file !== "" || taskFiles.length !== 1 || (taskFiles[0] ?? "").trim() === "")
|
|
633
660
|
usage();
|
|
634
|
-
const { app
|
|
635
|
-
process.exit(await
|
|
661
|
+
const { app } = await loadInlineApp(spurBin, resolveAppEntry, ["runInlineRunFingerprint"]);
|
|
662
|
+
process.exit(await app.runInlineRunFingerprint({ taskFile: taskFiles[0] ?? "", featureFile }));
|
|
636
663
|
}
|
|
637
664
|
if (decide) {
|
|
638
665
|
if (action || close || fingerprint || file !== "" || taskFiles.length > 0)
|
|
@@ -642,14 +669,14 @@ async function main() {
|
|
|
642
669
|
if (!SAFE_RUN_ID_RE.test(runId))
|
|
643
670
|
refuseUnsafeRunId(runId);
|
|
644
671
|
try {
|
|
645
|
-
const { app
|
|
646
|
-
const { app: lib } = await loadInlineApp(spurBin, () => ({ entry:
|
|
647
|
-
const loadOpts =
|
|
672
|
+
const { app, portable } = await loadInlineApp(spurBin, resolveAppEntry, ["runInlineRunDecide"]);
|
|
673
|
+
const { app: lib } = await loadInlineApp(spurBin, () => ({ entry: fileURLToPath2(new URL("../lib/inline-run.generated.mjs", import.meta.url)), portable }), ["resolveDecideDecisionMakerEnabled", "loadSpurConfig"]);
|
|
674
|
+
const loadOpts = portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined;
|
|
648
675
|
const [enabled, spurConfig] = await Promise.all([
|
|
649
676
|
lib.resolveDecideDecisionMakerEnabled(process.cwd(), loadOpts),
|
|
650
677
|
lib.loadSpurConfig(process.cwd(), loadOpts)
|
|
651
678
|
]);
|
|
652
|
-
process.exit(await
|
|
679
|
+
process.exit(await app.runInlineRunDecide({ runId, node, optionsFile: optionsJson, enabled, spurConfig }));
|
|
653
680
|
} catch (error) {
|
|
654
681
|
const message = error instanceof Error ? error.message : String(error);
|
|
655
682
|
process.stdout.write(`${JSON.stringify({ ok: false, runId, error: message })}
|
|
@@ -662,15 +689,15 @@ async function main() {
|
|
|
662
689
|
usage();
|
|
663
690
|
if (from.trim() === "" || taskFiles.some((taskFile) => taskFile.trim() === ""))
|
|
664
691
|
usage();
|
|
665
|
-
const { app
|
|
666
|
-
process.exit(await
|
|
692
|
+
const { app } = await loadInlineApp(spurBin, resolveAppEntry, ["runInlineRunPersistOut"]);
|
|
693
|
+
process.exit(await app.runInlineRunPersistOut({ from, taskFiles }));
|
|
667
694
|
}
|
|
668
695
|
if (nodeEnter || actionsFile !== "" || action || close) {
|
|
669
696
|
if (!SAFE_RUN_ID_RE.test(runId))
|
|
670
697
|
refuseUnsafeRunId(runId);
|
|
671
698
|
const mode = nodeEnter ? "node-enter" : actionsFile !== "" ? "actions-file" : close ? "close" : "action";
|
|
672
|
-
const { app
|
|
673
|
-
const code = await
|
|
699
|
+
const { app } = await loadInlineApp(spurBin, resolveAppEntry, ["runInlineRunTraceMode"]);
|
|
700
|
+
const code = await app.runInlineRunTraceMode({
|
|
674
701
|
mode,
|
|
675
702
|
runId,
|
|
676
703
|
node,
|
|
@@ -699,7 +726,7 @@ async function main() {
|
|
|
699
726
|
inventory = await app.readInstalledInventory({
|
|
700
727
|
file,
|
|
701
728
|
spurBin,
|
|
702
|
-
localCli:
|
|
729
|
+
localCli: fileURLToPath2(new URL("../../../apps/cli/src/index.ts", import.meta.url))
|
|
703
730
|
});
|
|
704
731
|
} catch (error) {
|
|
705
732
|
const message = `could not resolve the workflow definition: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
* handshake, mode dispatch. Mode bodies live in the bundled app service (`runInlineRun*` exports); the `.mjs`
|
|
4
4
|
* twin re-enters Bun in `main()` (SQLite) and imports the bundle dynamically, so bare `node` never loads app code. */
|
|
5
5
|
import { spawnSync } from 'node:child_process';
|
|
6
|
-
import { existsSync } from 'node:fs';
|
|
7
|
-
import { dirname, join, resolve } from 'node:path';
|
|
8
6
|
import { fileURLToPath } from 'node:url';
|
|
9
7
|
// Type-only namespace import (erased before bundling; the smoke scanner exempts `import type`).
|
|
10
8
|
import type * as spurApp from '@gobing-ai/spur-app';
|
|
11
9
|
import { getEnvVar } from '../lib/env';
|
|
12
|
-
import { loadInlineApp } from '../lib/inline-run-app';
|
|
10
|
+
import { loadInlineApp, resolveAppEntry } from '../lib/inline-run-app';
|
|
13
11
|
import { writeCloseSummary } from './run-summary';
|
|
14
12
|
|
|
15
13
|
// EMBEDDED_SPUR_SCHEMAS: declared by the bundle twin, never exported by the barrel — optional
|
|
@@ -47,24 +45,6 @@ function refuseUnsafeRunId(runId: string): never {
|
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
/** Resolve the app entry: --spur-bin/SPUR_BIN main module if it proves a repo checkout, else the adjacent bundle. */
|
|
50
|
-
function resolveAppEntry(spurBin: string): { entry: string; portable: boolean } {
|
|
51
|
-
const fallback = fileURLToPath(new URL('../../../apps/cli/src/index.ts', import.meta.url));
|
|
52
|
-
const candidates = spurBin !== '' ? [spurBin] : [fallback];
|
|
53
|
-
for (const candidate of candidates) {
|
|
54
|
-
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
55
|
-
// Main module = last path-like token; only a .ts source entry proves a repo checkout.
|
|
56
|
-
const mainModule = [...tokens].reverse().find((t) => t.endsWith('.ts'));
|
|
57
|
-
if (mainModule === undefined || !existsSync(mainModule)) continue;
|
|
58
|
-
const repoRoot = resolve(dirname(mainModule), '..', '..', '..');
|
|
59
|
-
const appEntry = join(repoRoot, 'packages', 'app', 'src', 'index.ts');
|
|
60
|
-
if (existsSync(appEntry)) return { entry: appEntry, portable: false };
|
|
61
|
-
}
|
|
62
|
-
const entry = fileURLToPath(new URL('../lib/inline-run.generated.mjs', import.meta.url));
|
|
63
|
-
if (!existsSync(entry)) {
|
|
64
|
-
throw new Error('inline application bundle is missing — rebuild/install the sp plugin before running inline');
|
|
65
|
-
}
|
|
66
|
-
return { entry, portable: true };
|
|
67
|
-
}
|
|
68
48
|
|
|
69
49
|
const SETUP_EXPORTS = ['readInstalledInventory', 'runInlineRunSetup', 'writeInlineRunOutcome'] as const;
|
|
70
50
|
|
|
@@ -204,10 +204,10 @@ persist-out-check: --from must be an existing worktree directory
|
|
|
204
204
|
process.exit(main(process.argv.slice(2)));
|
|
205
205
|
}
|
|
206
206
|
export {
|
|
207
|
-
|
|
208
|
-
persistOutCheckUsage,
|
|
209
|
-
main,
|
|
210
|
-
listObligations,
|
|
207
|
+
compareTrees,
|
|
211
208
|
defaultInvokeRoot,
|
|
212
|
-
|
|
209
|
+
listObligations,
|
|
210
|
+
main,
|
|
211
|
+
persistOutCheckUsage,
|
|
212
|
+
wbsOfTaskFile
|
|
213
213
|
};
|
|
@@ -695,18 +695,18 @@ function main(argv) {
|
|
|
695
695
|
process.exit(main(process.argv.slice(2)));
|
|
696
696
|
}
|
|
697
697
|
export {
|
|
698
|
-
|
|
699
|
-
setCommandRunner,
|
|
700
|
-
scanHygiene,
|
|
701
|
-
renderFindings,
|
|
702
|
-
parseArgs,
|
|
703
|
-
normalizeFindings,
|
|
704
|
-
main,
|
|
705
|
-
isHeadReviewed,
|
|
706
|
-
isExplicitCleanReview,
|
|
707
|
-
isCodexAuthor,
|
|
708
|
-
hasCurrentCleanReview,
|
|
709
|
-
hasCodeReviewRules,
|
|
698
|
+
buildRequestBody,
|
|
710
699
|
extractSeverity,
|
|
711
|
-
|
|
700
|
+
hasCodeReviewRules,
|
|
701
|
+
hasCurrentCleanReview,
|
|
702
|
+
isCodexAuthor,
|
|
703
|
+
isExplicitCleanReview,
|
|
704
|
+
isHeadReviewed,
|
|
705
|
+
main,
|
|
706
|
+
normalizeFindings,
|
|
707
|
+
parseArgs,
|
|
708
|
+
renderFindings,
|
|
709
|
+
scanHygiene,
|
|
710
|
+
setCommandRunner,
|
|
711
|
+
spawnRunner
|
|
712
712
|
};
|