@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
|
@@ -162,13 +162,13 @@ function formatStepProfileHuman(profile) {
|
|
|
162
162
|
`;
|
|
163
163
|
}
|
|
164
164
|
export {
|
|
165
|
-
|
|
166
|
-
nonDryRuns,
|
|
167
|
-
nearestRankP50,
|
|
168
|
-
formatStepProfileHuman,
|
|
169
|
-
extractExecutions,
|
|
170
|
-
buildStepProfile,
|
|
171
|
-
buildRows,
|
|
165
|
+
DEFAULT_LAST,
|
|
172
166
|
DEFAULT_WINDOW_SEC,
|
|
173
|
-
|
|
167
|
+
buildRows,
|
|
168
|
+
buildStepProfile,
|
|
169
|
+
extractExecutions,
|
|
170
|
+
formatStepProfileHuman,
|
|
171
|
+
nearestRankP50,
|
|
172
|
+
nonDryRuns,
|
|
173
|
+
rowFlags
|
|
174
174
|
};
|
|
@@ -1,240 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Host-transcript
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Host-transcript adapter shared by session-timeline (session review) and run-summary (dev-run /
|
|
3
|
+
* dev-runall execution summary). Owns the host side: resolving the active transcript path from the
|
|
4
|
+
* environment, detecting the host row format, and parsing raw JSONL lines into timestamped rows.
|
|
5
|
+
* Reads Claude Code / pi JSONL rows read-only. Node builtins only.
|
|
6
|
+
*
|
|
7
|
+
* Task 1178 R2: the pure measurement (row/token/actor aggregation and its normalized types) lives
|
|
8
|
+
* in `packages/app/src/workflow/run-summary.ts` and is reached through the generated pure
|
|
9
|
+
* plan-projection entry. Normalize once here (`parseRows`) and hand the rows to that owner; the
|
|
10
|
+
* re-exports below keep the plugin's one import path for the whole surface.
|
|
5
11
|
*/
|
|
6
12
|
import { existsSync, readdirSync } from 'node:fs';
|
|
7
13
|
import { homedir } from 'node:os';
|
|
8
14
|
import { join } from 'node:path';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
id?: string; // pi: the row id doubles as the once-per-message usage key
|
|
37
|
-
/** pi `custom_message` body: a hook-relayed prompt's text. */
|
|
38
|
-
content?: string;
|
|
39
|
-
/** pi `custom_message` visibility; `false` keeps the row out of the operator-visible transcript. */
|
|
40
|
-
display?: boolean;
|
|
41
|
-
/** pi `custom_message` producer id (e.g. a hook), carried for diagnostics only. */
|
|
42
|
-
customType?: string;
|
|
43
|
-
message?: {
|
|
44
|
-
id?: string;
|
|
45
|
-
role?: string;
|
|
46
|
-
content?: unknown;
|
|
47
|
-
usage?: Record<string, number | undefined>;
|
|
48
|
-
toolCallId?: string;
|
|
49
|
-
/** pi toolResult rows name their tool here (1146 R4). */
|
|
50
|
-
toolName?: string;
|
|
51
|
-
/** pi toolResult error flag (1146 R4). */
|
|
52
|
-
isError?: boolean;
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Running measurement of one segment or window. */
|
|
57
|
-
export interface Acc {
|
|
58
|
-
last: number;
|
|
59
|
-
askMs: number;
|
|
60
|
-
asks: Map<string, number>;
|
|
61
|
-
toolIds: Set<string>;
|
|
62
|
-
messages: Map<string, Tokens>;
|
|
63
|
-
compactions: number;
|
|
64
|
-
}
|
|
15
|
+
import type { Row } from './plan-projection.generated.mjs';
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
type Acc,
|
|
19
|
+
type ActorSplit,
|
|
20
|
+
accumulate,
|
|
21
|
+
actorSplit,
|
|
22
|
+
type Block,
|
|
23
|
+
buildRunSummary,
|
|
24
|
+
formatDuration,
|
|
25
|
+
formatTokenSplit,
|
|
26
|
+
formatTokens,
|
|
27
|
+
type GateTime,
|
|
28
|
+
isCompaction,
|
|
29
|
+
newAcc,
|
|
30
|
+
type Progress,
|
|
31
|
+
promptText,
|
|
32
|
+
type Row,
|
|
33
|
+
type RunSummary,
|
|
34
|
+
type Span,
|
|
35
|
+
type SummaryRow,
|
|
36
|
+
skillInvocationPrompt,
|
|
37
|
+
sumSpans,
|
|
38
|
+
sumTokens,
|
|
39
|
+
type Tokens,
|
|
40
|
+
zeroTokens,
|
|
41
|
+
} from './plan-projection.generated.mjs';
|
|
65
42
|
|
|
66
43
|
const SESSION_ID = /^[A-Za-z0-9_-]+$/;
|
|
67
|
-
/**
|
|
68
|
-
const OPERATOR_TOOLS = new Set(['AskUserQuestion', 'ask_user_question']);
|
|
44
|
+
/** pi roles that prove a `type:"message"` row is a pi transcript, not Claude's `user`/`assistant`. */
|
|
69
45
|
const PI_ROLES = new Set(['user', 'assistant', 'toolResult']);
|
|
70
46
|
|
|
71
|
-
export const zeroTokens = (): Tokens => ({ input: 0, cacheCreate: 0, cacheRead: 0, output: 0 });
|
|
72
|
-
|
|
73
|
-
export function formatDuration(ms: number): string {
|
|
74
|
-
const s = Math.round(ms / 1000);
|
|
75
|
-
const h = Math.floor(s / 3600);
|
|
76
|
-
const m = Math.floor((s % 3600) / 60);
|
|
77
|
-
const ss = String(s % 60).padStart(2, '0');
|
|
78
|
-
return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Compact token units: `950`, `41k`, `28.4M`. */
|
|
82
|
-
export function formatTokens(n: number): string {
|
|
83
|
-
if (n >= 999_500) return `${(n / 1e6).toFixed(1)}M`;
|
|
84
|
-
return n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* `<total> / <non-cached>`: total = every input class + output; non-cached drops cache reads, the
|
|
89
|
-
* share re-served from the prompt cache, leaving the fresh input, cache writes and output.
|
|
90
|
-
*/
|
|
91
|
-
export function formatTokenSplit(t: Tokens | null): string {
|
|
92
|
-
if (!t) return 'n/a';
|
|
93
|
-
const nonCached = t.input + t.cacheCreate + t.output;
|
|
94
|
-
return `${formatTokens(nonCached + t.cacheRead)} / ${formatTokens(nonCached)}`;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Operator prompt text, or undefined for tool results, meta rows and compaction summaries.
|
|
99
|
-
*
|
|
100
|
-
* pi (1151 R1/R3): pi records the OPERATOR'S own `/skill` invocation as a user message that opens
|
|
101
|
-
* with `<skill name="X" …>` and whose arguments follow the closing tag — it does not inject skill
|
|
102
|
-
* bodies as user rows (a 2,452-row census of local transcripts found none; the one synthetic
|
|
103
|
-
* body-only fixture row had no real counterpart). Every such row is therefore an operator prompt,
|
|
104
|
-
* rendered `/X <args>`; an ARGUMENT-LESS wrapper is still an invocation and renders `/X` (the skill
|
|
105
|
-
* name is the stage label the review groups by, so arguments alone would lose which command ran).
|
|
106
|
-
*
|
|
107
|
-
* pi (1151 follow-up): a hook-relayed `custom_message` (`display: true`) is a real operator prompt —
|
|
108
|
-
* the host shows it and the agent answers it — so it opens a segment like typed text; a
|
|
109
|
-
* `display: false` row is a host-only notice and stays non-segmenting activity.
|
|
110
|
-
*/
|
|
111
|
-
export function promptText(row: Row): string | undefined {
|
|
112
|
-
if (row.type === 'custom_message') {
|
|
113
|
-
if (row.display === false) return undefined;
|
|
114
|
-
const text = typeof row.content === 'string' ? row.content.trim() : '';
|
|
115
|
-
return text === '' ? undefined : text;
|
|
116
|
-
}
|
|
117
|
-
if (row.type === 'message') {
|
|
118
|
-
// pi (1130): only a user-role message with text content is a prompt; toolResult rows are not.
|
|
119
|
-
if (row.message?.role !== 'user') return undefined;
|
|
120
|
-
const text = firstTextContent(row);
|
|
121
|
-
if (text === undefined) return undefined;
|
|
122
|
-
return skillInvocationPrompt(text) ?? text;
|
|
123
|
-
}
|
|
124
|
-
if (row.type !== 'user' || row.isMeta || row.isCompactSummary) return undefined;
|
|
125
|
-
const content = row.message?.content;
|
|
126
|
-
if (typeof content === 'string') return content;
|
|
127
|
-
if (!Array.isArray(content) || (content as Block[]).some((b) => b.type === 'tool_result')) return undefined;
|
|
128
|
-
return (content as Block[]).find((b) => b.type === 'text')?.text;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** First `type:text` block of a pi message, or its string content. */
|
|
132
|
-
function firstTextContent(row: Row): string | undefined {
|
|
133
|
-
const content = row.message?.content;
|
|
134
|
-
if (typeof content === 'string') return content;
|
|
135
|
-
if (!Array.isArray(content)) return undefined;
|
|
136
|
-
return (content as Block[]).find((b) => b.type === 'text')?.text;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* 1151 R1: `/X <args>` for a pi operator `/skill` invocation wrapper, undefined for anything else.
|
|
141
|
-
*
|
|
142
|
-
* The wrapper is `^\s*<skill name="X"` — the anchored, complete quoted name is the only signal;
|
|
143
|
-
* text that merely CONTAINS `<skill` later in the body stays plain operator text. Arguments are the
|
|
144
|
-
* text after the LAST `</skill>` (a body may itself quote the tag); a wrapper with no closing tag
|
|
145
|
-
* renders `/X` alone rather than dumping the body as the prompt. A malformed name (no closing quote)
|
|
146
|
-
* is not a wrapper.
|
|
147
|
-
*/
|
|
148
|
-
export function skillInvocationPrompt(text: string): string | undefined {
|
|
149
|
-
const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
|
|
150
|
-
if (name === undefined) return undefined;
|
|
151
|
-
const close = text.lastIndexOf('</skill>');
|
|
152
|
-
const args = close < 0 ? '' : text.slice(close + '</skill>'.length).trim();
|
|
153
|
-
return args === '' ? `/${name}` : `/${name} ${args}`;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export function sumTokens(all: Tokens[]): Tokens {
|
|
157
|
-
const total = zeroTokens();
|
|
158
|
-
for (const t of all) for (const k of Object.keys(total) as (keyof Tokens)[]) total[k] += t[k];
|
|
159
|
-
return total;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function sumSpans(spans: Span[]): Span {
|
|
163
|
-
return {
|
|
164
|
-
workMs: spans.reduce((n, s) => n + s.workMs, 0),
|
|
165
|
-
waitMs: spans.reduce((n, s) => n + s.waitMs, 0),
|
|
166
|
-
toolCalls: spans.reduce((n, s) => n + s.toolCalls, 0),
|
|
167
|
-
tokens: sumTokens(spans.map((s) => s.tokens)),
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export const newAcc = (ts: number): Acc => ({
|
|
172
|
-
last: ts,
|
|
173
|
-
askMs: 0,
|
|
174
|
-
asks: new Map(),
|
|
175
|
-
toolIds: new Set(),
|
|
176
|
-
messages: new Map(),
|
|
177
|
-
compactions: 0,
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
/** Fold one timestamped non-prompt record into a segment or window. */
|
|
181
|
-
export function accumulate(acc: Acc, row: Row, ts: number): void {
|
|
182
|
-
acc.last = Math.max(acc.last, ts);
|
|
183
|
-
if (row.type === 'message') {
|
|
184
|
-
// pi (1130): mirrors the importer's field semantics (piRole / normalizeOmpToolCall /
|
|
185
|
-
// ompToolResultTiming) — toolCall blocks on assistant rows, results paired by toolCallId.
|
|
186
|
-
const m = row.message;
|
|
187
|
-
if (m?.role === 'assistant') {
|
|
188
|
-
const blocks = Array.isArray(m.content) ? (m.content as Block[]) : [];
|
|
189
|
-
for (const b of blocks) {
|
|
190
|
-
if (b.type === 'toolCall' && b.id) {
|
|
191
|
-
acc.toolIds.add(b.id);
|
|
192
|
-
if (b.name && OPERATOR_TOOLS.has(b.name)) acc.asks.set(b.id, ts);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
const u = m.usage;
|
|
196
|
-
if (u && row.id)
|
|
197
|
-
acc.messages.set(row.id, {
|
|
198
|
-
input: u.input ?? 0,
|
|
199
|
-
cacheCreate: u.cacheWrite ?? 0,
|
|
200
|
-
cacheRead: u.cacheRead ?? 0,
|
|
201
|
-
output: u.output ?? 0,
|
|
202
|
-
});
|
|
203
|
-
} else if (m?.role === 'toolResult' && m.toolCallId && acc.asks.has(m.toolCallId)) {
|
|
204
|
-
acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
|
|
205
|
-
}
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
if (row.type === 'compaction') acc.compactions++; // pi-only row type (1130 R4)
|
|
209
|
-
const blocks = Array.isArray(row.message?.content) ? (row.message.content as Block[]) : [];
|
|
210
|
-
for (const b of blocks) {
|
|
211
|
-
if (b.type === 'tool_use' && b.id) {
|
|
212
|
-
acc.toolIds.add(b.id);
|
|
213
|
-
if (b.name && OPERATOR_TOOLS.has(b.name)) acc.asks.set(b.id, ts);
|
|
214
|
-
} else if (b.type === 'tool_result' && b.tool_use_id && acc.asks.has(b.tool_use_id)) {
|
|
215
|
-
acc.askMs += ts - (acc.asks.get(b.tool_use_id) ?? ts);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
const u = row.message?.usage;
|
|
219
|
-
// Split records of one response repeat its usage: keyed by message id, overwrite, never add.
|
|
220
|
-
if (row.type === 'assistant' && u && row.message?.id) {
|
|
221
|
-
acc.messages.set(row.message.id, {
|
|
222
|
-
input: u.input_tokens ?? 0,
|
|
223
|
-
cacheCreate: u.cache_creation_input_tokens ?? 0,
|
|
224
|
-
cacheRead: u.cache_read_input_tokens ?? 0,
|
|
225
|
-
output: u.output_tokens ?? 0,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* 1146 R4: a compaction boundary in either host shape — pi writes a `compaction` row, Claude a
|
|
232
|
-
* `system` row with `subtype:"compact_boundary"`.
|
|
233
|
-
*/
|
|
234
|
-
export function isCompaction(row: Row): boolean {
|
|
235
|
-
return row.type === 'compaction' || (row.type === 'system' && row.subtype === 'compact_boundary');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
47
|
/** First row matching a known shape decides: `type:"message"` with a pi role → pi, else Claude. */
|
|
239
48
|
export function sniffFormat(rows: Row[]): 'claude' | 'pi' | 'unknown' {
|
|
240
49
|
for (const row of rows) {
|
package/plugins/sp/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Spur — a local-first harness engineering toolkit that wraps mainstream coding agents with constraint checking, workflow orchestration, and history analytics.",
|
|
5
5
|
"extensions": {
|
|
6
6
|
"pi": ["./hooks/pi/guard-extension.ts"]
|