@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
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @bun
|
|
3
|
-
import { createRequire } from "node:module";
|
|
4
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
5
3
|
|
|
6
4
|
// plugins/sp/scripts/daily-summary/daily-summary.ts
|
|
7
5
|
import { spawn, spawnSync } from "child_process";
|
|
8
|
-
import { existsSync, mkdirSync, readlinkSync, writeFileSync } from "fs";
|
|
9
|
-
import { dirname, isAbsolute, join, resolve } from "path";
|
|
6
|
+
import { existsSync as existsSync2, mkdirSync, readlinkSync, writeFileSync } from "fs";
|
|
7
|
+
import { dirname as dirname2, isAbsolute, join as join2, resolve as resolve2 } from "path";
|
|
8
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10
9
|
|
|
11
10
|
// plugins/sp/lib/env.ts
|
|
12
11
|
function getEnvVar(name, fallback) {
|
|
@@ -17,20 +16,54 @@ function getEnvVars() {
|
|
|
17
16
|
return process.env;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
// plugins/sp/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
// plugins/sp/lib/inline-run-app.ts
|
|
20
|
+
import { existsSync } from "node:fs";
|
|
21
|
+
import { dirname, join, resolve } from "node:path";
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
23
|
+
function resolveAppEntry(spurBin) {
|
|
24
|
+
const fallback = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
|
|
25
|
+
const candidates = spurBin !== "" ? [spurBin] : [fallback];
|
|
26
|
+
for (const candidate of candidates) {
|
|
27
|
+
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
28
|
+
const mainModule = [...tokens].reverse().find((t) => t.endsWith(".ts"));
|
|
29
|
+
if (mainModule === undefined || !existsSync(mainModule))
|
|
30
|
+
continue;
|
|
31
|
+
const repoRoot = resolve(dirname(mainModule), "..", "..", "..");
|
|
32
|
+
const appEntry = join(repoRoot, "packages", "app", "src", "index.ts");
|
|
33
|
+
if (existsSync(appEntry))
|
|
34
|
+
return { entry: appEntry, portable: false };
|
|
35
|
+
}
|
|
36
|
+
const entry = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
|
|
37
|
+
if (!existsSync(entry)) {
|
|
38
|
+
throw new Error("inline application bundle is missing — rebuild/install the sp plugin before running inline");
|
|
39
|
+
}
|
|
40
|
+
return { entry, portable: true };
|
|
41
|
+
}
|
|
42
|
+
async function loadInlineApp(spurBin, resolveAppEntry, required) {
|
|
43
|
+
const { entry, portable } = resolveAppEntry(spurBin);
|
|
44
|
+
const app = await import(entry);
|
|
45
|
+
const missing = portable ? required.filter((name) => typeof app[name] !== "function") : [];
|
|
46
|
+
if (missing.length > 0) {
|
|
47
|
+
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.`);
|
|
48
|
+
}
|
|
49
|
+
return { app, portable };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// plugins/sp/scripts/daily-summary/daily-summary.ts
|
|
53
|
+
var consoleEnabled = true;
|
|
54
|
+
function enableLogger(consoleOn) {
|
|
55
|
+
consoleEnabled = consoleOn;
|
|
56
|
+
}
|
|
57
|
+
function logAt(stream, message) {
|
|
58
|
+
if (!consoleEnabled)
|
|
24
59
|
return;
|
|
25
60
|
console[stream](message);
|
|
26
61
|
}
|
|
27
62
|
var logger = {
|
|
28
|
-
info: (message) =>
|
|
29
|
-
warn: (message) =>
|
|
30
|
-
error: (message) =>
|
|
63
|
+
info: (message) => logAt("log", message),
|
|
64
|
+
warn: (message) => logAt("warn", message),
|
|
65
|
+
error: (message) => logAt("error", message)
|
|
31
66
|
};
|
|
32
|
-
|
|
33
|
-
// plugins/sp/scripts/daily-summary/daily-summary.ts
|
|
34
67
|
var DAILY_DIR = "docs/daily";
|
|
35
68
|
var DEFAULT_DATE = "today";
|
|
36
69
|
function parseArgs(argv = process.argv.slice(2)) {
|
|
@@ -108,7 +141,7 @@ function getDateRange(dateStr) {
|
|
|
108
141
|
return { start, end };
|
|
109
142
|
}
|
|
110
143
|
var defaultProcessSpawner = (cmd, args, env) => {
|
|
111
|
-
return new Promise((
|
|
144
|
+
return new Promise((resolve, reject) => {
|
|
112
145
|
try {
|
|
113
146
|
const proc = spawn(cmd, args, {
|
|
114
147
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -119,7 +152,7 @@ var defaultProcessSpawner = (cmd, args, env) => {
|
|
|
119
152
|
proc.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.from(chunk)));
|
|
120
153
|
proc.stderr.on("data", (chunk) => stderrChunks.push(Buffer.from(chunk)));
|
|
121
154
|
proc.on("close", (exitCode) => {
|
|
122
|
-
|
|
155
|
+
resolve({
|
|
123
156
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
124
157
|
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
125
158
|
exitCode: exitCode ?? 0
|
|
@@ -158,72 +191,49 @@ async function getCcusageData(date) {
|
|
|
158
191
|
return null;
|
|
159
192
|
}
|
|
160
193
|
}
|
|
194
|
+
function composeHistoryHealth(reading) {
|
|
195
|
+
const loops = reading.loops.map((row) => ({
|
|
196
|
+
toolName: row.toolName,
|
|
197
|
+
argsDigest: row.argsDigest || "repeated execution",
|
|
198
|
+
repeats: row.repeats,
|
|
199
|
+
sessionId: row.sessionId,
|
|
200
|
+
fromSeq: row.firstSeq,
|
|
201
|
+
toSeq: row.lastSeq,
|
|
202
|
+
wastedTokens: row.repeats * 250
|
|
203
|
+
}));
|
|
204
|
+
const remediationProposals = [];
|
|
205
|
+
for (const lp of loops.slice(0, 5)) {
|
|
206
|
+
const cleanTool = lp.toolName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
207
|
+
const key = `repetition:${cleanTool}:${lp.argsDigest.slice(0, 16)}`;
|
|
208
|
+
const title = `Break execution loop in ${lp.toolName} (${lp.repeats} repeats)`;
|
|
209
|
+
const command = `spur task create "<title>" --feature <id> && spur task update <wbs> --section Plan --from-file <path>`;
|
|
210
|
+
remediationProposals.push({ key, title, command });
|
|
211
|
+
}
|
|
212
|
+
if (reading.toolCalls > 0 && reading.errorRatePct > 10) {
|
|
213
|
+
remediationProposals.push({
|
|
214
|
+
key: "reliability:tooling:high-error-rate",
|
|
215
|
+
title: `Investigate high tool error rate (${reading.errorRatePct.toFixed(1)}%)`,
|
|
216
|
+
command: `spur task create "<title>" --feature <id> && spur task update <wbs> --section Plan --from-file <path>`
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
toolCalls: reading.toolCalls,
|
|
221
|
+
toolErrors: reading.toolErrors,
|
|
222
|
+
errorRatePct: reading.errorRatePct,
|
|
223
|
+
loops,
|
|
224
|
+
redundantCalls: loops.reduce((acc, l) => acc + Math.max(0, l.repeats - 1), 0),
|
|
225
|
+
wastedTokens: loops.reduce((acc, l) => acc + l.wastedTokens, 0),
|
|
226
|
+
remediationProposals
|
|
227
|
+
};
|
|
228
|
+
}
|
|
161
229
|
async function getSpurHistoryHealth(date, dbPath = ".spur/spur.db") {
|
|
162
230
|
try {
|
|
163
|
-
const resolvedPath =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
const loopTable = db.query("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get("history_board_loop_findings");
|
|
171
|
-
let loops = [];
|
|
172
|
-
if (loopTable) {
|
|
173
|
-
const rows = db.query(`SELECT tool_name, args_digest, repeats, session_id, first_seq, last_seq, started_at
|
|
174
|
-
FROM history_board_loop_findings
|
|
175
|
-
WHERE started_at IS NULL OR started_at LIKE ?
|
|
176
|
-
ORDER BY repeats DESC
|
|
177
|
-
LIMIT 20`).all(`${date}%`);
|
|
178
|
-
loops = rows.map((r) => ({
|
|
179
|
-
toolName: r.tool_name,
|
|
180
|
-
argsDigest: r.args_digest || "repeated execution",
|
|
181
|
-
repeats: r.repeats,
|
|
182
|
-
sessionId: r.session_id,
|
|
183
|
-
fromSeq: r.first_seq,
|
|
184
|
-
toSeq: r.last_seq,
|
|
185
|
-
wastedTokens: r.repeats * 250
|
|
186
|
-
}));
|
|
187
|
-
}
|
|
188
|
-
let toolCalls = 0;
|
|
189
|
-
let toolErrors = 0;
|
|
190
|
-
const toolTable = db.query("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get("history_board_tool_5m");
|
|
191
|
-
if (toolTable) {
|
|
192
|
-
const stats = db.query(`SELECT SUM(calls) AS calls, SUM(errors) AS errors
|
|
193
|
-
FROM history_board_tool_5m
|
|
194
|
-
WHERE bucket_start LIKE ?`).get(`${date}%`);
|
|
195
|
-
toolCalls = stats?.calls ?? 0;
|
|
196
|
-
toolErrors = stats?.errors ?? 0;
|
|
197
|
-
}
|
|
198
|
-
const redundantCalls = loops.reduce((acc, l) => acc + Math.max(0, l.repeats - 1), 0);
|
|
199
|
-
const wastedTokens = loops.reduce((acc, l) => acc + l.wastedTokens, 0);
|
|
200
|
-
const errorRatePct = toolCalls > 0 ? toolErrors / toolCalls * 100 : 0;
|
|
201
|
-
const remediationProposals = [];
|
|
202
|
-
for (const lp of loops.slice(0, 5)) {
|
|
203
|
-
const cleanTool = lp.toolName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
204
|
-
const key = `repetition:${cleanTool}:${lp.argsDigest.slice(0, 16)}`;
|
|
205
|
-
const title = `Break execution loop in ${lp.toolName} (${lp.repeats} repeats)`;
|
|
206
|
-
const command = `spur task create "<title>" --feature <id> && spur task update <wbs> --section Plan --from-file <path>`;
|
|
207
|
-
remediationProposals.push({ key, title, command });
|
|
208
|
-
}
|
|
209
|
-
if (toolCalls > 0 && errorRatePct > 10) {
|
|
210
|
-
const key = "reliability:tooling:high-error-rate";
|
|
211
|
-
const title = `Investigate high tool error rate (${errorRatePct.toFixed(1)}%)`;
|
|
212
|
-
const command = `spur task create "<title>" --feature <id> && spur task update <wbs> --section Plan --from-file <path>`;
|
|
213
|
-
remediationProposals.push({ key, title, command });
|
|
214
|
-
}
|
|
215
|
-
return {
|
|
216
|
-
toolCalls,
|
|
217
|
-
toolErrors,
|
|
218
|
-
errorRatePct,
|
|
219
|
-
loops,
|
|
220
|
-
redundantCalls,
|
|
221
|
-
wastedTokens,
|
|
222
|
-
remediationProposals
|
|
223
|
-
};
|
|
224
|
-
} finally {
|
|
225
|
-
db.close();
|
|
226
|
-
}
|
|
231
|
+
const resolvedPath = resolve2(process.cwd(), dbPath);
|
|
232
|
+
const { app } = await loadInlineApp(getEnvVar("SPUR_BIN") ?? "", resolveAppEntry, [
|
|
233
|
+
"readHistoryHealth"
|
|
234
|
+
]);
|
|
235
|
+
const reading = await app.readHistoryHealth({ date, dbPath: resolvedPath });
|
|
236
|
+
return reading === null ? null : composeHistoryHealth(reading);
|
|
227
237
|
} catch {
|
|
228
238
|
return null;
|
|
229
239
|
}
|
|
@@ -307,12 +317,12 @@ Please provide the following (press Enter to skip):
|
|
|
307
317
|
chunks.push(chunk);
|
|
308
318
|
}
|
|
309
319
|
const buffered = Buffer.concat(chunks).toString("utf-8");
|
|
310
|
-
const [
|
|
320
|
+
const [learnings = "", issuesFixed = "", pending = ""] = buffered.split(`
|
|
311
321
|
`);
|
|
312
322
|
return {
|
|
313
|
-
learnings:
|
|
314
|
-
issuesFixed:
|
|
315
|
-
pending:
|
|
323
|
+
learnings: learnings.trim(),
|
|
324
|
+
issuesFixed: issuesFixed.trim(),
|
|
325
|
+
pending: pending.trim()
|
|
316
326
|
};
|
|
317
327
|
}
|
|
318
328
|
const readline = await import("readline");
|
|
@@ -320,9 +330,9 @@ Please provide the following (press Enter to skip):
|
|
|
320
330
|
input: process.stdin,
|
|
321
331
|
output: process.stdout
|
|
322
332
|
});
|
|
323
|
-
const question = (prompt) => new Promise((
|
|
333
|
+
const question = (prompt) => new Promise((resolve) => {
|
|
324
334
|
rl.question(prompt, (answer) => {
|
|
325
|
-
|
|
335
|
+
resolve(answer.trim());
|
|
326
336
|
});
|
|
327
337
|
});
|
|
328
338
|
const learnings = await question(`1. What did you learn today? (optional)
|
|
@@ -468,26 +478,26 @@ function generateMarkdown(summary) {
|
|
|
468
478
|
`);
|
|
469
479
|
}
|
|
470
480
|
function ensureDir(path) {
|
|
471
|
-
if (!
|
|
481
|
+
if (!existsSync2(path)) {
|
|
472
482
|
mkdirSync(path, { recursive: true });
|
|
473
483
|
}
|
|
474
484
|
}
|
|
475
485
|
function writeSummary(markdown, options) {
|
|
476
486
|
const filename = `summary_${options.date.replace(/-/g, "")}.md`;
|
|
477
|
-
const outputPath = options.outputPath ||
|
|
478
|
-
ensureDir(
|
|
487
|
+
const outputPath = options.outputPath || join2(DAILY_DIR, filename);
|
|
488
|
+
ensureDir(join2(outputPath, ".."));
|
|
479
489
|
writeFileSync(outputPath, markdown, "utf-8");
|
|
480
490
|
return outputPath;
|
|
481
491
|
}
|
|
482
492
|
function resolveHistoryReportPath() {
|
|
483
|
-
const pointer =
|
|
484
|
-
if (!
|
|
493
|
+
const pointer = resolve2(process.cwd(), ".spur", "reports", "history", "latest.json");
|
|
494
|
+
if (!existsSync2(pointer)) {
|
|
485
495
|
return;
|
|
486
496
|
}
|
|
487
497
|
try {
|
|
488
498
|
const target = readlinkSync(pointer);
|
|
489
|
-
const resolved = isAbsolute(target) ? target :
|
|
490
|
-
return
|
|
499
|
+
const resolved = isAbsolute(target) ? target : resolve2(dirname2(pointer), target);
|
|
500
|
+
return existsSync2(resolved) ? resolved : undefined;
|
|
491
501
|
} catch {
|
|
492
502
|
return;
|
|
493
503
|
}
|
|
@@ -540,13 +550,21 @@ async function buildDailySummary(options) {
|
|
|
540
550
|
result.gitActivity = gitActivity;
|
|
541
551
|
}
|
|
542
552
|
const historyHealth = await getSpurHistoryHealth(options.date);
|
|
543
|
-
if (historyHealth
|
|
553
|
+
if (historyHealth) {
|
|
544
554
|
result.historyHealth = historyHealth;
|
|
545
555
|
platforms.push("Spur History");
|
|
546
556
|
}
|
|
547
557
|
return result;
|
|
548
558
|
}
|
|
549
559
|
async function main() {
|
|
560
|
+
if (!process.versions.bun) {
|
|
561
|
+
const child = spawnSync("bun", [fileURLToPath2(import.meta.url), ...process.argv.slice(2)], {
|
|
562
|
+
stdio: "inherit"
|
|
563
|
+
});
|
|
564
|
+
if (!child.error)
|
|
565
|
+
process.exit(child.status ?? 1);
|
|
566
|
+
console.error(`daily-summary: Bun not on PATH (${child.error.message}); history health unavailable`);
|
|
567
|
+
}
|
|
550
568
|
const options = parseArgs();
|
|
551
569
|
logger.info(`Generating daily summary for ${options.date}...`);
|
|
552
570
|
try {
|
|
@@ -588,20 +606,21 @@ ${markdown}
|
|
|
588
606
|
});
|
|
589
607
|
}
|
|
590
608
|
export {
|
|
591
|
-
|
|
592
|
-
writeSummary,
|
|
593
|
-
todayLocal,
|
|
594
|
-
setProcessSpawner,
|
|
595
|
-
promptUser,
|
|
596
|
-
printUsage,
|
|
597
|
-
parseArgs,
|
|
598
|
-
main,
|
|
599
|
-
getSpurHistoryHealth,
|
|
600
|
-
getGitCommits,
|
|
601
|
-
getDateRange,
|
|
602
|
-
getCcusageData,
|
|
603
|
-
generateMarkdown,
|
|
604
|
-
ensureDir,
|
|
609
|
+
buildDailySummary,
|
|
605
610
|
defaultProcessSpawner,
|
|
606
|
-
|
|
611
|
+
enableLogger,
|
|
612
|
+
ensureDir,
|
|
613
|
+
generateMarkdown,
|
|
614
|
+
getCcusageData,
|
|
615
|
+
getDateRange,
|
|
616
|
+
getGitCommits,
|
|
617
|
+
getSpurHistoryHealth,
|
|
618
|
+
main,
|
|
619
|
+
parseArgs,
|
|
620
|
+
printUsage,
|
|
621
|
+
promptUser,
|
|
622
|
+
setProcessSpawner,
|
|
623
|
+
todayLocal,
|
|
624
|
+
writeSummary,
|
|
625
|
+
yesterdayLocal
|
|
607
626
|
};
|
|
@@ -11,9 +11,31 @@
|
|
|
11
11
|
import { spawn, spawnSync } from 'node:child_process';
|
|
12
12
|
import { existsSync, mkdirSync, readlinkSync, writeFileSync } from 'node:fs';
|
|
13
13
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
14
15
|
import { getEnvVar, getEnvVars } from '../../lib/env';
|
|
16
|
+
import { loadInlineApp, resolveAppEntry } from '../../lib/inline-run-app';
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
// ─── In-script logger ────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
// The former `logger.ts` helper exposed a file channel nothing ever implemented, so the used part
|
|
21
|
+
// — a console toggle the tests need — lives here instead of a one-caller module (task 1173, plan P7).
|
|
22
|
+
let consoleEnabled = true;
|
|
23
|
+
|
|
24
|
+
/** Silence or restore this script's console output (test hook). */
|
|
25
|
+
export function enableLogger(consoleOn: boolean): void {
|
|
26
|
+
consoleEnabled = consoleOn;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function logAt(stream: 'log' | 'warn' | 'error', message: string): void {
|
|
30
|
+
if (!consoleEnabled) return;
|
|
31
|
+
console[stream](message);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const logger = {
|
|
35
|
+
info: (message: string): void => logAt('log', message),
|
|
36
|
+
warn: (message: string): void => logAt('warn', message),
|
|
37
|
+
error: (message: string): void => logAt('error', message),
|
|
38
|
+
};
|
|
17
39
|
|
|
18
40
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
19
41
|
|
|
@@ -287,113 +309,85 @@ export async function getCcusageData(date: string): Promise<CcusageData | null>
|
|
|
287
309
|
|
|
288
310
|
// ─── Spur History Health Integration ──────────────────────────────────────────
|
|
289
311
|
|
|
312
|
+
/** The day's raw history health read back across the generated inline app entry (task 1178 R3). */
|
|
313
|
+
interface HistoryHealthReading {
|
|
314
|
+
toolCalls: number;
|
|
315
|
+
toolErrors: number;
|
|
316
|
+
errorRatePct: number;
|
|
317
|
+
loops: Array<{
|
|
318
|
+
toolName: string;
|
|
319
|
+
argsDigest: string;
|
|
320
|
+
repeats: number;
|
|
321
|
+
sessionId: string;
|
|
322
|
+
firstSeq: number;
|
|
323
|
+
lastSeq: number;
|
|
324
|
+
startedAt: string | null;
|
|
325
|
+
}>;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** The generated inline app entry's history-health export. */
|
|
329
|
+
interface InlineHistoryApp {
|
|
330
|
+
readHistoryHealth(input: { date: string; dbPath: string }): Promise<HistoryHealthReading | null>;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Compose the daily report's health summary from the app reading. `wastedTokens` stays an
|
|
335
|
+
* explicit `repeats * 250` estimate; remediation proposals stay plain text (task 1178 R3).
|
|
336
|
+
*/
|
|
337
|
+
function composeHistoryHealth(reading: HistoryHealthReading): HistoryHealthSummary {
|
|
338
|
+
const loops: HistoryLoopFinding[] = reading.loops.map((row) => ({
|
|
339
|
+
toolName: row.toolName,
|
|
340
|
+
argsDigest: row.argsDigest || 'repeated execution',
|
|
341
|
+
repeats: row.repeats,
|
|
342
|
+
sessionId: row.sessionId,
|
|
343
|
+
fromSeq: row.firstSeq,
|
|
344
|
+
toSeq: row.lastSeq,
|
|
345
|
+
wastedTokens: row.repeats * 250,
|
|
346
|
+
}));
|
|
347
|
+
|
|
348
|
+
const remediationProposals: Array<{ key: string; title: string; command: string }> = [];
|
|
349
|
+
for (const lp of loops.slice(0, 5)) {
|
|
350
|
+
const cleanTool = lp.toolName.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
351
|
+
const key = `repetition:${cleanTool}:${lp.argsDigest.slice(0, 16)}`;
|
|
352
|
+
const title = `Break execution loop in ${lp.toolName} (${lp.repeats} repeats)`;
|
|
353
|
+
const command = `spur task create "<title>" --feature <id> && spur task update <wbs> --section Plan --from-file <path>`;
|
|
354
|
+
remediationProposals.push({ key, title, command });
|
|
355
|
+
}
|
|
356
|
+
if (reading.toolCalls > 0 && reading.errorRatePct > 10) {
|
|
357
|
+
remediationProposals.push({
|
|
358
|
+
key: 'reliability:tooling:high-error-rate',
|
|
359
|
+
title: `Investigate high tool error rate (${reading.errorRatePct.toFixed(1)}%)`,
|
|
360
|
+
command: `spur task create "<title>" --feature <id> && spur task update <wbs> --section Plan --from-file <path>`,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return {
|
|
365
|
+
toolCalls: reading.toolCalls,
|
|
366
|
+
toolErrors: reading.toolErrors,
|
|
367
|
+
errorRatePct: reading.errorRatePct,
|
|
368
|
+
loops,
|
|
369
|
+
redundantCalls: loops.reduce((acc, l) => acc + Math.max(0, l.repeats - 1), 0),
|
|
370
|
+
wastedTokens: loops.reduce((acc, l) => acc + l.wastedTokens, 0),
|
|
371
|
+
remediationProposals,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Read the day's history health through the app owner (task 1178 R3): this adapter opens no
|
|
377
|
+
* SQLite and carries no SQL. A missing database file or required table returns `null`
|
|
378
|
+
* (unavailable); an existing empty table returns a measured-zero summary, never `n/a`.
|
|
379
|
+
*/
|
|
290
380
|
export async function getSpurHistoryHealth(
|
|
291
381
|
date: string,
|
|
292
382
|
dbPath = '.spur/spur.db',
|
|
293
383
|
): Promise<HistoryHealthSummary | null> {
|
|
294
384
|
try {
|
|
295
385
|
const resolvedPath = resolve(process.cwd(), dbPath);
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const db = new Database(resolvedPath, { readonly: true });
|
|
302
|
-
|
|
303
|
-
try {
|
|
304
|
-
// 1. Query execution loop findings
|
|
305
|
-
const loopTable = db
|
|
306
|
-
.query<{ name: string }, [string]>("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
|
|
307
|
-
.get('history_board_loop_findings');
|
|
308
|
-
|
|
309
|
-
let loops: HistoryLoopFinding[] = [];
|
|
310
|
-
if (loopTable) {
|
|
311
|
-
const rows = db
|
|
312
|
-
.query<
|
|
313
|
-
{
|
|
314
|
-
tool_name: string;
|
|
315
|
-
args_digest: string;
|
|
316
|
-
repeats: number;
|
|
317
|
-
session_id: string;
|
|
318
|
-
first_seq: number;
|
|
319
|
-
last_seq: number;
|
|
320
|
-
started_at: string | null;
|
|
321
|
-
},
|
|
322
|
-
[string]
|
|
323
|
-
>(
|
|
324
|
-
`SELECT tool_name, args_digest, repeats, session_id, first_seq, last_seq, started_at
|
|
325
|
-
FROM history_board_loop_findings
|
|
326
|
-
WHERE started_at IS NULL OR started_at LIKE ?
|
|
327
|
-
ORDER BY repeats DESC
|
|
328
|
-
LIMIT 20`,
|
|
329
|
-
)
|
|
330
|
-
.all(`${date}%`);
|
|
331
|
-
|
|
332
|
-
loops = rows.map((r) => ({
|
|
333
|
-
toolName: r.tool_name,
|
|
334
|
-
argsDigest: r.args_digest || 'repeated execution',
|
|
335
|
-
repeats: r.repeats,
|
|
336
|
-
sessionId: r.session_id,
|
|
337
|
-
fromSeq: r.first_seq,
|
|
338
|
-
toSeq: r.last_seq,
|
|
339
|
-
wastedTokens: r.repeats * 250,
|
|
340
|
-
}));
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// 2. Query tool calls and errors
|
|
344
|
-
let toolCalls = 0;
|
|
345
|
-
let toolErrors = 0;
|
|
346
|
-
const toolTable = db
|
|
347
|
-
.query<{ name: string }, [string]>("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
|
|
348
|
-
.get('history_board_tool_5m');
|
|
349
|
-
|
|
350
|
-
if (toolTable) {
|
|
351
|
-
const stats = db
|
|
352
|
-
.query<{ calls: number | null; errors: number | null }, [string]>(
|
|
353
|
-
`SELECT SUM(calls) AS calls, SUM(errors) AS errors
|
|
354
|
-
FROM history_board_tool_5m
|
|
355
|
-
WHERE bucket_start LIKE ?`,
|
|
356
|
-
)
|
|
357
|
-
.get(`${date}%`);
|
|
358
|
-
|
|
359
|
-
toolCalls = stats?.calls ?? 0;
|
|
360
|
-
toolErrors = stats?.errors ?? 0;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const redundantCalls = loops.reduce((acc, l) => acc + Math.max(0, l.repeats - 1), 0);
|
|
364
|
-
const wastedTokens = loops.reduce((acc, l) => acc + l.wastedTokens, 0);
|
|
365
|
-
const errorRatePct = toolCalls > 0 ? (toolErrors / toolCalls) * 100 : 0;
|
|
366
|
-
|
|
367
|
-
// 3. Generate auto-healing remediation proposals
|
|
368
|
-
const remediationProposals: Array<{ key: string; title: string; command: string }> = [];
|
|
369
|
-
|
|
370
|
-
for (const lp of loops.slice(0, 5)) {
|
|
371
|
-
const cleanTool = lp.toolName.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
372
|
-
const key = `repetition:${cleanTool}:${lp.argsDigest.slice(0, 16)}`;
|
|
373
|
-
const title = `Break execution loop in ${lp.toolName} (${lp.repeats} repeats)`;
|
|
374
|
-
const command = `spur task create "<title>" --feature <id> && spur task update <wbs> --section Plan --from-file <path>`;
|
|
375
|
-
remediationProposals.push({ key, title, command });
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (toolCalls > 0 && errorRatePct > 10) {
|
|
379
|
-
const key = 'reliability:tooling:high-error-rate';
|
|
380
|
-
const title = `Investigate high tool error rate (${errorRatePct.toFixed(1)}%)`;
|
|
381
|
-
const command = `spur task create "<title>" --feature <id> && spur task update <wbs> --section Plan --from-file <path>`;
|
|
382
|
-
remediationProposals.push({ key, title, command });
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return {
|
|
386
|
-
toolCalls,
|
|
387
|
-
toolErrors,
|
|
388
|
-
errorRatePct,
|
|
389
|
-
loops,
|
|
390
|
-
redundantCalls,
|
|
391
|
-
wastedTokens,
|
|
392
|
-
remediationProposals,
|
|
393
|
-
};
|
|
394
|
-
} finally {
|
|
395
|
-
db.close();
|
|
396
|
-
}
|
|
386
|
+
const { app } = await loadInlineApp<InlineHistoryApp>(getEnvVar('SPUR_BIN') ?? '', resolveAppEntry, [
|
|
387
|
+
'readHistoryHealth',
|
|
388
|
+
]);
|
|
389
|
+
const reading = await app.readHistoryHealth({ date, dbPath: resolvedPath });
|
|
390
|
+
return reading === null ? null : composeHistoryHealth(reading);
|
|
397
391
|
} catch {
|
|
398
392
|
return null;
|
|
399
393
|
}
|
|
@@ -795,9 +789,11 @@ export async function buildDailySummary(options: CliOptions): Promise<DailySumma
|
|
|
795
789
|
result.gitActivity = gitActivity;
|
|
796
790
|
}
|
|
797
791
|
|
|
798
|
-
// Query Spur history health (loops, tool errors, and auto-healing proposals)
|
|
792
|
+
// Query Spur history health (loops, tool errors, and auto-healing proposals). A measured
|
|
793
|
+
// empty day is a real zero and keeps its "Clean" section; an unavailable database (missing
|
|
794
|
+
// file/table) stays absent — absence and zero never collapse (task 1178 R3).
|
|
799
795
|
const historyHealth = await getSpurHistoryHealth(options.date);
|
|
800
|
-
if (historyHealth
|
|
796
|
+
if (historyHealth) {
|
|
801
797
|
result.historyHealth = historyHealth;
|
|
802
798
|
platforms.push('Spur History');
|
|
803
799
|
}
|
|
@@ -806,6 +802,17 @@ export async function buildDailySummary(options: CliOptions): Promise<DailySumma
|
|
|
806
802
|
}
|
|
807
803
|
|
|
808
804
|
export async function main(): Promise<void> {
|
|
805
|
+
// The generated app entry read by getSpurHistoryHealth needs Bun (SQLite); the installed
|
|
806
|
+
// `.mjs` twin may be launched with plain node, so re-enter Bun before any app import.
|
|
807
|
+
// Without Bun on PATH, keep running under node: the history reader degrades to unavailable
|
|
808
|
+
// (n/a) instead of failing the whole report (task 1178 review P4).
|
|
809
|
+
if (!process.versions.bun) {
|
|
810
|
+
const child = spawnSync('bun', [fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
|
|
811
|
+
stdio: 'inherit',
|
|
812
|
+
});
|
|
813
|
+
if (!child.error) process.exit(child.status ?? 1);
|
|
814
|
+
console.error(`daily-summary: Bun not on PATH (${child.error.message}); history health unavailable`);
|
|
815
|
+
}
|
|
809
816
|
const options = parseArgs();
|
|
810
817
|
|
|
811
818
|
logger.info(`Generating daily summary for ${options.date}...`);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
4
|
|
|
5
|
-
// plugins/sp/
|
|
5
|
+
// plugins/sp/lib/dogfood-pipeline.ts
|
|
6
6
|
var PIPELINE_TOKENS = [
|
|
7
7
|
"--next",
|
|
8
8
|
"dev-runall",
|
|
@@ -125,6 +125,7 @@ function evaluateDogfoodGate(testee, options = {}) {
|
|
|
125
125
|
exitCode: 0
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
|
+
// plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts
|
|
128
129
|
function parseCliArgs(argv) {
|
|
129
130
|
let testee = null;
|
|
130
131
|
let maxRetryPresent = false;
|
|
@@ -195,16 +196,16 @@ function runCli(argv) {
|
|
|
195
196
|
process.exit(exitCode);
|
|
196
197
|
}
|
|
197
198
|
export {
|
|
198
|
-
|
|
199
|
-
parseCliArgs,
|
|
200
|
-
isImplementHeavyStep,
|
|
201
|
-
hasMutatingFixMode,
|
|
202
|
-
evaluateDogfoodGate,
|
|
203
|
-
detectPipelineDriving,
|
|
204
|
-
detectImplementHeavy,
|
|
205
|
-
PIPELINE_TOKENS,
|
|
206
|
-
PIPELINE_DRIVING_REFUSE_MESSAGE,
|
|
207
|
-
MUTATING_FIX_REFUSE_MESSAGE,
|
|
199
|
+
CLI_USAGE,
|
|
208
200
|
IMPLEMENT_HEAVY_ADVISORY_MESSAGE,
|
|
209
|
-
|
|
201
|
+
MUTATING_FIX_REFUSE_MESSAGE,
|
|
202
|
+
PIPELINE_DRIVING_REFUSE_MESSAGE,
|
|
203
|
+
PIPELINE_TOKENS,
|
|
204
|
+
detectImplementHeavy,
|
|
205
|
+
detectPipelineDriving,
|
|
206
|
+
evaluateDogfoodGate,
|
|
207
|
+
hasMutatingFixMode,
|
|
208
|
+
isImplementHeavyStep,
|
|
209
|
+
parseCliArgs,
|
|
210
|
+
runCli
|
|
210
211
|
};
|