@gobing-ai/spur 0.4.0 → 0.4.2
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/config.global.yaml +4 -2
- package/config/decisions/gates.yaml +36 -0
- package/config/decisions/history-anatomy.yaml +35 -0
- package/config/decisions/idea-pipeline.yaml +38 -0
- package/config/plugin-scripts.json +15 -0
- package/config/rules/docs/sourced-api-claims.yaml +60 -0
- package/config/rules/recommended-pre-check.yaml +8 -3
- package/config/rules/structure/scratch-evidence-pointer.yaml +35 -0
- package/config/script-placement-baseline.json +2 -2
- package/config/templates/AGENTS.md +1 -1
- package/config/workflows/feature-lifecycle.yaml +9 -2
- package/config/workflows/history-anatomy.yaml +22 -0
- package/config/workflows/idea-pipeline.yaml +75 -0
- package/config/workflows/task-pipeline.yaml +216 -57
- package/config/workflows/wrapup-pipeline.yaml +107 -14
- package/package.json +10 -10
- package/plugins/sp/README.md +12 -3
- package/plugins/sp/agents/super-planner.md +16 -6
- package/plugins/sp/agents/super-reviewer.md +14 -0
- package/plugins/sp/bunfig.toml +8 -0
- package/plugins/sp/commands/dev-brainstorm.md +29 -16
- package/plugins/sp/commands/dev-find-way.md +80 -0
- package/plugins/sp/commands/dev-idea.md +6 -0
- package/plugins/sp/commands/dev-plan.md +6 -0
- package/plugins/sp/commands/dev-review-session.md +4 -1
- package/plugins/sp/commands/dev-run.md +12 -2
- package/plugins/sp/commands/dev-runall.md +32 -15
- package/plugins/sp/hooks/context-owner.ts +56 -0
- package/plugins/sp/hooks/context-post-tool.ts +23 -0
- package/plugins/sp/hooks/context-session-start.ts +49 -27
- package/plugins/sp/hooks/context-session-stop.ts +131 -30
- package/plugins/sp/hooks/pi/guard-extension.ts +21 -8
- package/plugins/sp/lib/idea-handoff.generated.mjs +43 -5
- package/plugins/sp/lib/inline-run-app.ts +28 -0
- package/plugins/sp/lib/inline-run.generated.d.mts +5 -0
- package/plugins/sp/lib/inline-run.generated.mjs +351 -46
- package/plugins/sp/lib/plan-projection.generated.d.mts +47 -0
- package/plugins/sp/lib/plan-projection.generated.mjs +368 -0
- package/plugins/sp/lib/quality-gate.generated.d.mts +35 -1
- package/plugins/sp/lib/quality-gate.generated.mjs +30296 -71
- package/plugins/sp/lib/residual-scan.generated.d.mts +19 -0
- package/plugins/sp/lib/residual-scan.generated.mjs +66 -0
- package/plugins/sp/lib/run-summary-core.ts +561 -0
- package/plugins/sp/lib/transcript.ts +150 -7
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/references/roles.md +1 -1
- package/plugins/sp/scripts/batch-plan.mjs +488 -0
- package/plugins/sp/scripts/batch-plan.ts +145 -0
- package/plugins/sp/scripts/batch-preflight.mjs +51 -3
- package/plugins/sp/scripts/batch-preflight.ts +72 -1
- package/plugins/sp/scripts/commit-guard.mjs +216 -0
- package/plugins/sp/scripts/commit-guard.ts +249 -0
- package/plugins/sp/scripts/inline-run-dispatch.mjs +130 -0
- package/plugins/sp/scripts/inline-run-dispatch.ts +135 -0
- package/plugins/sp/scripts/inline-run-setup.mjs +562 -80
- package/plugins/sp/scripts/inline-run-setup.ts +52 -78
- package/plugins/sp/scripts/persist-out-check.mjs +90 -41
- package/plugins/sp/scripts/persist-out-check.ts +112 -57
- package/plugins/sp/scripts/pr-reviewing.mjs +19 -5
- package/plugins/sp/scripts/pr-reviewing.ts +34 -9
- package/plugins/sp/scripts/quality-gate.mjs +30213 -61
- package/plugins/sp/scripts/quality-gate.ts +21 -5
- package/plugins/sp/scripts/residual-scan.mjs +86 -31
- package/plugins/sp/scripts/residual-scan.ts +28 -30
- package/plugins/sp/scripts/run-summary.mjs +432 -14
- package/plugins/sp/scripts/run-summary.ts +41 -177
- package/plugins/sp/scripts/session-timeline.mjs +109 -11
- package/plugins/sp/scripts/session-timeline.ts +57 -19
- package/plugins/sp/scripts/task-diffstat.mjs +32 -29
- package/plugins/sp/scripts/task-diffstat.ts +34 -34
- package/plugins/sp/scripts/wrapup-steps.mjs +150 -2
- package/plugins/sp/scripts/wrapup-steps.ts +203 -1
- package/plugins/sp/skills/brainstorm/SKILL.md +9 -47
- package/plugins/sp/skills/branch-workflow/SKILL.md +1 -0
- package/plugins/sp/skills/branch-workflow/references/worktree-patterns.md +1 -0
- package/plugins/sp/skills/code-implementation/SKILL.md +48 -1
- package/plugins/sp/skills/code-verification/SKILL.md +3 -4
- package/plugins/sp/skills/code-verification/references/secu-review.md +3 -0
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +68 -0
- package/plugins/sp/skills/fleet-join/SKILL.md +1 -1
- package/plugins/sp/skills/indexed-context/SKILL.md +14 -7
- package/plugins/sp/skills/parallel-execution/SKILL.md +2 -2
- package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +1 -1
- package/plugins/sp/skills/parallel-execution/references/fan-out-patterns.md +2 -2
- package/plugins/sp/skills/pr-reviewing/SKILL.md +7 -3
- package/plugins/sp/skills/session-review/SKILL.md +40 -7
- package/plugins/sp/skills/source-driven-development/SKILL.md +5 -0
- package/plugins/sp/skills/spec-decomposition/references/decomposition.md +5 -3
- package/plugins/sp/skills/spur-cli/SKILL.md +5 -2
- package/plugins/sp/skills/spur-cli/references/agent.md +20 -15
- package/plugins/sp/skills/spur-cli/references/decision.md +16 -8
- package/plugins/sp/skills/spur-cli/references/message.md +7 -7
- package/plugins/sp/skills/spur-cli/references/projects.md +7 -5
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +19 -3
- package/plugins/sp/skills/spur-cli/references/tasks.md +10 -7
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +7 -0
- package/plugins/sp/skills/spur-cli/references/workflows.md +7 -2
- package/plugins/sp/skills/spur-composer/SKILL.md +1 -1
- package/plugins/sp/skills/spur-dev/SKILL.md +20 -14
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +33 -3
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +49 -14
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +37 -17
- package/plugins/sp/skills/spur-dev/references/execution-batch-continuation.md +68 -0
- package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +199 -0
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +156 -1024
- package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +133 -0
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +6 -0
- package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +471 -0
- package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +379 -0
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +44 -12
- package/plugins/sp/skills/spur-dev/references/glossary.md +2 -2
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +340 -165
- package/plugins/sp/skills/spur-dev/references/structured-trace-emission.md +178 -0
- package/plugins/sp/skills/wayfinder/SKILL.md +10 -14
- package/schemas/state-machine-workflow.schema.json +23 -0
- package/spur.js +42402 -26889
- package/web/_astro/{BoardApp.VeoxxLBy.js → BoardApp.B3GQZvj8.js} +1 -1
- package/web/_astro/BoardApp.BT7L2kSb.js +324 -0
- package/web/_astro/TaskDetail.Cxov1_N_.js +1 -0
- package/web/_astro/{arc.f4e0OYLg.js → arc.vaT7tPOB.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.SSIvxi-Z.js → architectureDiagram-3BPJPVTR.DRMmTjkN.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.DSXIHOFf.js → blockDiagram-GPEHLZMM.cla0LO_q.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.DG9Ubu85.js → c4Diagram-AAUBKEIU.D4YAIBKn.js} +1 -1
- package/web/_astro/channel.D4nG9YLf.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.C7QU88Qh.js → chunk-2J33WTMH.BrSJbMRZ.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.26Z94xBj.js → chunk-4BX2VUAB.CuLMgKF5.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.DYRiBkqw.js → chunk-55IACEB6.zkvgu54F.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.D5Vfv5pe.js → chunk-727SXJPM.DxWkACh8.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.DXHV47bN.js → chunk-AQP2D5EJ.42XrmIlH.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.D1suAfOs.js → chunk-FMBD7UC4.Bao2wMK9.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.D6BmdHEY.js → chunk-ND2GUHAM.BhydojD1.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.rnPU-Ak-.js → chunk-QZHKN3VN.CmZ82g-H.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CvEfXIo1.js → classDiagram-4FO5ZUOK.Blr83Gt_.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CvEfXIo1.js → classDiagram-v2-Q7XG4LA2.Blr83Gt_.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.DkMisLSm.js → cose-bilkent-S5V4N54A.qmfv0SPn.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.C77Q-pF6.js → cynefin-OW5HDTMX.BtELhXPR.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.YaV8ce27.js → dagre-BM42HDAG.AWmaAvXo.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.CUyGEKge.js → diagram-2AECGRRQ.DdH34KDi.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.CRgd8dgj.js → diagram-5GNKFQAL.BmNPix1k.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.Bmzys9hq.js → diagram-KO2AKTUF.BNQd_Ppo.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.BvmJY4Lw.js → diagram-LMA3HP47.BAPgDi0e.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.0hEhD30d.js → diagram-OG6HWLK6.DZ4zyLdC.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.Bjt8lrnm.js → erDiagram-TEJ5UH35.Q1TySMmw.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.CJZTjRDV.js → flowDiagram-I6XJVG4X.BXhFBicG.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7._VqoVafc.js → ganttDiagram-6RSMTGT7.vHSgcuo2.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.CEDb7tVo.js → gitGraphDiagram-PVQCEYII.Bi1f73C7.js} +1 -1
- package/web/_astro/index.BhSwN8yY.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.CgwifPpK.js → infoDiagram-5YYISTIA.DPvXZEWm.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.B99wx2J1.js → ishikawaDiagram-YF4QCWOH.Cz829fsV.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.2hRwJbEo.js → journeyDiagram-JHISSGLW.enRnB9Me.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.BahJkKNC.js → kanban-definition-UN3LZRKU.DnTNk6d2.js} +1 -1
- package/web/_astro/{linear.DguUUe6V.js → linear.BzGnBwNL.js} +1 -1
- package/web/_astro/{mermaid.core.CvgfmqVr.js → mermaid.core.CSbTpIVz.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.B3qEA3x9.js → mindmap-definition-RKZ34NQL.DGJDy8zT.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.FlWA-_ru.js → pieDiagram-4H26LBE5.HSPdi5_8.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.9bVn35X7.js → quadrantDiagram-W4KKPZXB.6Gv8mz2Y.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.ChykIhct.js → requirementDiagram-4Y6WPE33.CnBCtFwa.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.Z2Y3hkYb.js → sankeyDiagram-5OEKKPKP.5M8VWAu8.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.J7JsHWDt.js → sequenceDiagram-3UESZ5HK.DN41XN-U.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.DAPbiCSS.js → stateDiagram-AJRCARHV.ErOO3W8j.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.D2jvSi0q.js → stateDiagram-v2-BHNVJYJU.B_ZHICM-.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.psW2ci29.js → timeline-definition-PNZ67QCA.CWdosr07.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DlILYVmg.js → vennDiagram-CIIHVFJN.BHzYjNV5.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.xNWwLJnn.js → wardleyDiagram-YWT4CUSO.h8x4IwPX.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.EU_BuH7V.js → xychartDiagram-2RQKCTM6.RTapW2X8.js} +1 -1
- package/web/index.html +2 -2
- package/plugins/sp/commands/dev-parallel.md +0 -31
- package/web/_astro/BoardApp.Bzr4OXrW.js +0 -324
- package/web/_astro/TaskDetail.LAyR_Pvn.js +0 -1
- package/web/_astro/channel.CUFdtWhm.js +0 -1
- package/web/_astro/index.Dg_3bOaJ.css +0 -1
|
@@ -16,14 +16,41 @@ export interface Span {
|
|
|
16
16
|
tokens: Tokens;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
export interface Block {
|
|
20
|
+
type?: string;
|
|
21
|
+
id?: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
text?: string;
|
|
24
|
+
tool_use_id?: string;
|
|
25
|
+
/** Claude tool_result error flag (1146 R4): a failed subagent call is counted. */
|
|
26
|
+
is_error?: boolean;
|
|
27
|
+
}
|
|
20
28
|
|
|
21
29
|
export interface Row {
|
|
22
30
|
type?: string;
|
|
31
|
+
/** Claude `system` rows carry the compaction subtype (1146 R4). */
|
|
32
|
+
subtype?: string;
|
|
23
33
|
timestamp?: string;
|
|
24
34
|
isMeta?: boolean;
|
|
25
35
|
isCompactSummary?: boolean;
|
|
26
|
-
|
|
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
|
+
};
|
|
27
54
|
}
|
|
28
55
|
|
|
29
56
|
/** Running measurement of one segment or window. */
|
|
@@ -33,11 +60,13 @@ export interface Acc {
|
|
|
33
60
|
asks: Map<string, number>;
|
|
34
61
|
toolIds: Set<string>;
|
|
35
62
|
messages: Map<string, Tokens>;
|
|
63
|
+
compactions: number;
|
|
36
64
|
}
|
|
37
65
|
|
|
38
66
|
const SESSION_ID = /^[A-Za-z0-9_-]+$/;
|
|
39
67
|
/** Host tools whose call→result gap is the operator answering, not the agent working. */
|
|
40
|
-
const OPERATOR_TOOLS = new Set(['AskUserQuestion']);
|
|
68
|
+
const OPERATOR_TOOLS = new Set(['AskUserQuestion', 'ask_user_question']);
|
|
69
|
+
const PI_ROLES = new Set(['user', 'assistant', 'toolResult']);
|
|
41
70
|
|
|
42
71
|
export const zeroTokens = (): Tokens => ({ input: 0, cacheCreate: 0, cacheRead: 0, output: 0 });
|
|
43
72
|
|
|
@@ -65,8 +94,33 @@ export function formatTokenSplit(t: Tokens | null): string {
|
|
|
65
94
|
return `${formatTokens(nonCached + t.cacheRead)} / ${formatTokens(nonCached)}`;
|
|
66
95
|
}
|
|
67
96
|
|
|
68
|
-
/**
|
|
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
|
+
*/
|
|
69
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
|
+
}
|
|
70
124
|
if (row.type !== 'user' || row.isMeta || row.isCompactSummary) return undefined;
|
|
71
125
|
const content = row.message?.content;
|
|
72
126
|
if (typeof content === 'string') return content;
|
|
@@ -74,6 +128,31 @@ export function promptText(row: Row): string | undefined {
|
|
|
74
128
|
return (content as Block[]).find((b) => b.type === 'text')?.text;
|
|
75
129
|
}
|
|
76
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
|
+
|
|
77
156
|
export function sumTokens(all: Tokens[]): Tokens {
|
|
78
157
|
const total = zeroTokens();
|
|
79
158
|
for (const t of all) for (const k of Object.keys(total) as (keyof Tokens)[]) total[k] += t[k];
|
|
@@ -95,11 +174,38 @@ export const newAcc = (ts: number): Acc => ({
|
|
|
95
174
|
asks: new Map(),
|
|
96
175
|
toolIds: new Set(),
|
|
97
176
|
messages: new Map(),
|
|
177
|
+
compactions: 0,
|
|
98
178
|
});
|
|
99
179
|
|
|
100
180
|
/** Fold one timestamped non-prompt record into a segment or window. */
|
|
101
181
|
export function accumulate(acc: Acc, row: Row, ts: number): void {
|
|
102
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)
|
|
103
209
|
const blocks = Array.isArray(row.message?.content) ? (row.message.content as Block[]) : [];
|
|
104
210
|
for (const b of blocks) {
|
|
105
211
|
if (b.type === 'tool_use' && b.id) {
|
|
@@ -121,8 +227,29 @@ export function accumulate(acc: Acc, row: Row, ts: number): void {
|
|
|
121
227
|
}
|
|
122
228
|
}
|
|
123
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
|
+
/** First row matching a known shape decides: `type:"message"` with a pi role → pi, else Claude. */
|
|
239
|
+
export function sniffFormat(rows: Row[]): 'claude' | 'pi' | 'unknown' {
|
|
240
|
+
for (const row of rows) {
|
|
241
|
+
if (row.type === 'message' && PI_ROLES.has(row.message?.role ?? '')) return 'pi';
|
|
242
|
+
if (row.type === 'user' || row.type === 'assistant') return 'claude';
|
|
243
|
+
}
|
|
244
|
+
return 'unknown';
|
|
245
|
+
}
|
|
246
|
+
|
|
124
247
|
/** Timestamped rows in file order; malformed lines counted, timestamp-less rows dropped. */
|
|
125
|
-
export function parseRows(lines: string[]): {
|
|
248
|
+
export function parseRows(lines: string[]): {
|
|
249
|
+
rows: [Row, number][];
|
|
250
|
+
skippedLines: number;
|
|
251
|
+
format: 'claude' | 'pi' | 'unknown';
|
|
252
|
+
} {
|
|
126
253
|
const rows: [Row, number][] = [];
|
|
127
254
|
let skippedLines = 0;
|
|
128
255
|
for (const line of lines) {
|
|
@@ -137,7 +264,7 @@ export function parseRows(lines: string[]): { rows: [Row, number][]; skippedLine
|
|
|
137
264
|
const ts = row.timestamp ? Date.parse(row.timestamp) : Number.NaN;
|
|
138
265
|
if (!Number.isNaN(ts)) rows.push([row, ts]);
|
|
139
266
|
}
|
|
140
|
-
return { rows, skippedLines };
|
|
267
|
+
return { rows, skippedLines, format: sniffFormat(rows.map(([r]) => r)) };
|
|
141
268
|
}
|
|
142
269
|
|
|
143
270
|
export type Resolved = { ok: true; path: string } | { ok: false; reason: string };
|
|
@@ -149,7 +276,23 @@ export function resolveTranscript(
|
|
|
149
276
|
): Resolved {
|
|
150
277
|
if (override) return existsSync(override) ? { ok: true, path: override } : { ok: false, reason: 'no transcript' };
|
|
151
278
|
const id = env.CLAUDE_CODE_SESSION_ID;
|
|
152
|
-
if (!id)
|
|
279
|
+
if (!id) {
|
|
280
|
+
// 1138 R1: pi exports PI_SESSION_FILE (an exact path) and no Claude session id. Order is
|
|
281
|
+
// explicit override → Claude id → PI_SESSION_FILE; PI_SESSION_ID is never used for lookup
|
|
282
|
+
// because pi's filename embeds a timestamp, so the path is not derivable from the id.
|
|
283
|
+
const piFile = env.PI_SESSION_FILE;
|
|
284
|
+
if (piFile !== undefined && piFile !== '') {
|
|
285
|
+
return existsSync(piFile)
|
|
286
|
+
? { ok: true, path: piFile }
|
|
287
|
+
: { ok: false, reason: `PI_SESSION_FILE ${piFile} does not exist` };
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
ok: false,
|
|
291
|
+
reason:
|
|
292
|
+
'no host session id; pass --transcript <path> (pi: ~/.pi/agent/sessions/<cwd-slug>/<file>.jsonl, ' +
|
|
293
|
+
'or set PI_SESSION_FILE)',
|
|
294
|
+
};
|
|
295
|
+
}
|
|
153
296
|
if (!SESSION_ID.test(id)) return { ok: false, reason: 'refusing a session id with path characters' };
|
|
154
297
|
if (!existsSync(projectsRoot)) return { ok: false, reason: `no transcript root ${projectsRoot}` };
|
|
155
298
|
for (const dir of readdirSync(projectsRoot)) {
|
package/plugins/sp/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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"]
|
|
@@ -62,7 +62,7 @@ roles:
|
|
|
62
62
|
stages: [verify, review, dogfood]
|
|
63
63
|
- id: planner
|
|
64
64
|
tier: capable-2
|
|
65
|
-
commands: [dev-plan, dev-refine, dev-brainstorm, dev-
|
|
65
|
+
commands: [dev-plan, dev-refine, dev-brainstorm, dev-find-way, dev-idea, dev-runall, dev-next, dev-job-resume, dev-arch, dev-refineall, dev-find-next, dev-feature-change]
|
|
66
66
|
stages: [plan, refine, brainstorm]
|
|
67
67
|
```
|
|
68
68
|
|