freeharness 0.4.1 → 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/README.md +8 -7
- package/canonical/HARVEST.yaml +25 -14
- package/canonical/PROVENANCE.yaml +1 -1
- package/canonical/agents/README.md +77 -36
- package/canonical/agents/agentic-architect.md +2 -1
- package/canonical/agents/database-reviewer.md +6 -8
- package/canonical/agents/explorer-ai-engineer-analyst.md +2 -6
- package/canonical/agents/implementer.md +30 -0
- package/canonical/agents/pr-reviewer.md +9 -11
- package/canonical/agents/python-reviewer.md +7 -9
- package/canonical/agents/report-formatter.md +7 -3
- package/canonical/agents/security-reviewer.md +7 -8
- package/canonical/agents/test-writer.md +5 -2
- package/canonical/hooks/cost-prices.json +11 -9
- package/canonical/hooks/freeharness-config-guard.mjs +8072 -87
- package/canonical/hooks/freeharness-context-monitor.mjs +88 -54
- package/canonical/hooks/freeharness-converge.mjs +78 -23
- package/canonical/hooks/freeharness-cost-tracker.mjs +1129 -218
- package/canonical/hooks/freeharness-memory-guard.mjs +20 -23
- package/canonical/hooks/freeharness-precompact.mjs +217 -87
- package/canonical/hooks/freeharness-quota-tap.mjs +173 -0
- package/canonical/hooks/freeharness-reload-context.mjs +197 -0
- package/canonical/hooks/freeharness-repeat-tool.mjs +1 -1
- package/canonical/hooks/freeharness-sdd-context.mjs +970 -0
- package/canonical/hooks/freeharness-session-start +297 -59
- package/canonical/hooks/hooks.claude-code.json +1 -1
- package/canonical/hooks/hooks.config-guard.claude-code.json +3 -2
- package/canonical/hooks/hooks.context-monitor.claude-code.json +1 -1
- package/canonical/hooks/hooks.converge.claude-code.json +1 -1
- package/canonical/hooks/hooks.memory-guard.claude-code.json +3 -2
- package/canonical/hooks/hooks.reload-context.claude-code.json +15 -0
- package/canonical/openspec/schema.spec-driven.yaml +29 -8
- package/canonical/openspec/templates/follow-ups.md +3 -2
- package/canonical/openspec/templates/proposal.md +1 -1
- package/canonical/openspec/templates/tasks.md +8 -0
- package/canonical/rules/CLAUDE.md +5 -4
- package/canonical/rules/routing.md +10 -9
- package/canonical/skills/agent-evaluation/SKILL.md +17 -6
- package/canonical/skills/agent-evaluation/evals/cases.yaml +38 -23
- package/canonical/skills/agentic-architecture/SKILL.md +4 -0
- package/canonical/skills/agentic-architecture/evals/cases.yaml +27 -0
- package/canonical/skills/code-review/SKILL.md +9 -2
- package/canonical/skills/code-review/evals/cases.yaml +1118 -27
- package/canonical/skills/git-workflow/SKILL.md +2 -0
- package/canonical/skills/local-code/SKILL.md +2 -2
- package/canonical/skills/local-code/evals/cases.yaml +1 -1
- package/canonical/skills/report-formatting/SKILL.md +10 -2
- package/canonical/skills/report-formatting/reference/ARCHITECTURE.md +3 -3
- package/canonical/skills/report-formatting/reference/HELPERS.md +10 -5
- package/canonical/skills/report-formatting/reference/RELEASE.md +1 -2
- package/canonical/skills/report-formatting/reference/TEMPLATES.md +3 -2
- package/canonical/skills/report-formatting/reference/authoring.md +25 -37
- package/canonical/skills/report-formatting/scripts/build-architecture.js +1 -1
- package/canonical/skills/report-formatting/scripts/build-md-claro.js +5 -8
- package/canonical/skills/report-formatting/scripts/check-env.js +0 -4
- package/canonical/skills/report-formatting/scripts/docmodel.js +3 -11
- package/canonical/skills/report-formatting/scripts/example-build.js +8 -10
- package/canonical/skills/report-formatting/scripts/example_build_ArchitectureReview_docx.js +8 -10
- package/canonical/skills/report-formatting/scripts/example_build_ExecutiveReport_docx.js +8 -10
- package/canonical/skills/report-formatting/scripts/theme-claro.js +39 -10
- package/canonical/skills/subagent-driven-development/SKILL.md +35 -28
- package/canonical/skills/subagent-driven-development/evals/cases.yaml +3496 -19
- package/canonical/skills/subagent-driven-development/reference/brief.md +185 -35
- package/canonical/skills/subagent-driven-development/reference/handoff.md +82 -2
- package/canonical/skills/subagent-driven-development/reference/loop.md +57 -0
- package/canonical/skills/subagent-driven-development/reference/trees.md +40 -0
- package/canonical/skills/subagent-driven-development/reference/workspace.md +31 -0
- package/canonical/skills/subagent-driven-development/scripts/validate-handoff.mjs +4771 -326
- package/dist/adapters/harness/claude-code.d.ts +19 -3
- package/dist/adapters/harness/claude-code.js +204 -39
- package/dist/adapters/harness/opencode.js +16 -2
- package/dist/adapters/harness/plugin.js +28 -17
- package/dist/adapters/memory/doctor.d.ts +174 -29
- package/dist/adapters/memory/doctor.js +758 -134
- package/dist/adapters/memory/migrate.d.ts +95 -0
- package/dist/adapters/memory/migrate.js +497 -0
- package/dist/adapters/memory/paths.d.ts +22 -0
- package/dist/adapters/memory/paths.js +85 -0
- package/dist/adapters/memory/vendored.d.ts +56 -29
- package/dist/adapters/memory/vendored.js +191 -154
- package/dist/adapters/sync/diff.js +4 -1
- package/dist/cli/cleanup-prompt.d.ts +35 -0
- package/dist/cli/cleanup-prompt.js +68 -0
- package/dist/cli/cleanup.d.ts +22 -24
- package/dist/cli/cleanup.js +182 -68
- package/dist/cli/cost-quota.d.ts +158 -0
- package/dist/cli/cost-quota.js +645 -0
- package/dist/cli/cost.d.ts +408 -46
- package/dist/cli/cost.js +1731 -57
- package/dist/cli/doctor.d.ts +52 -13
- package/dist/cli/doctor.js +193 -44
- package/dist/cli/hard-cleanup.d.ts +93 -0
- package/dist/cli/hard-cleanup.js +552 -0
- package/dist/cli/install-facts.d.ts +28 -0
- package/dist/cli/install-facts.js +66 -0
- package/dist/cli/json-key-edit.d.ts +18 -0
- package/dist/cli/json-key-edit.js +118 -0
- package/dist/cli/memory-import.d.ts +74 -0
- package/dist/cli/memory-import.js +469 -0
- package/dist/cli/plugin-validate.d.ts +12 -0
- package/dist/cli/plugin-validate.js +43 -0
- package/dist/cli/uninstall.d.ts +1 -1
- package/dist/cli/uninstall.js +2 -2
- package/dist/cli/upstream-launchers.d.ts +49 -0
- package/dist/cli/upstream-launchers.js +287 -0
- package/dist/index.js +247 -70
- package/dist/infra/fsutil.d.ts +31 -3
- package/dist/infra/fsutil.js +69 -6
- package/dist/infra/manifest.d.ts +2 -1
- package/dist/infra/manifest.js +3 -2
- package/dist/infra/prices.d.ts +18 -0
- package/dist/infra/prices.js +64 -0
- package/node_modules/@freeharness/core/dist/tiers.d.ts +10 -1
- package/node_modules/@freeharness/core/dist/tiers.js +24 -2
- package/node_modules/@freeharness/core/package.json +1 -1
- package/node_modules/@freeharness/sdd/PROVENANCE.yaml +279 -2
- package/node_modules/@freeharness/sdd/dist/src/bin/openspec.js +34 -8
- package/node_modules/@freeharness/sdd/dist/src/shim/archive.d.ts +7 -6
- package/node_modules/@freeharness/sdd/dist/src/shim/archive.js +72 -64
- package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.d.ts +48 -16
- package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.js +168 -70
- package/node_modules/@freeharness/sdd/dist/src/shim/instructions.d.ts +36 -6
- package/node_modules/@freeharness/sdd/dist/src/shim/instructions.js +110 -49
- package/node_modules/@freeharness/sdd/dist/src/shim/list.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/list.js +57 -14
- package/node_modules/@freeharness/sdd/dist/src/shim/nested-change.d.ts +7 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/nested-change.js +118 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/new-change.js +3 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/proposal-capabilities.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/proposal-capabilities.js +94 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/shared.d.ts +17 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/shared.js +182 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/status.d.ts +2 -2
- package/node_modules/@freeharness/sdd/dist/src/shim/status.js +34 -5
- package/node_modules/@freeharness/sdd/dist/src/shim/validate.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/validate.js +49 -41
- package/node_modules/@freeharness/sdd/dist/src/shim/warnings.d.ts +21 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/warnings.js +66 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/line-endings.d.ts +4 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/line-endings.js +17 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/specs-apply.js +95 -22
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.d.ts +6 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.js +14 -6
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.js +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.d.ts +3 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.js +24 -4
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/task-progress.js +4 -7
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/types.js +13 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/apply-change.js +21 -6
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/archive-change.js +80 -19
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/explore.js +42 -22
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/project-root.d.ts +32 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/project-root.js +39 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/propose.js +16 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/sync-specs.js +36 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/update-change.js +65 -30
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.js +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.d.ts +8 -13
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.js +79 -102
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/markdown-parser.js +4 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.d.ts +50 -10
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.js +165 -37
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.d.ts +24 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.js +55 -8
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/spec-structure.js +4 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/purpose-placeholder.js +19 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.d.ts +10 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.js +115 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.d.ts +3 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.js +20 -20
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.d.ts +6 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.js +75 -14
- package/node_modules/@freeharness/sdd/package.json +1 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/schema.upstream.yaml +47 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-apply-change/SKILL.md +25 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-archive-change/SKILL.md +50 -11
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-bulk-archive-change/SKILL.md +48 -10
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-continue-change/SKILL.md +27 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-explore/SKILL.md +25 -12
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-ff-change/SKILL.md +17 -5
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-new-change/SKILL.md +12 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-onboard/SKILL.md +47 -33
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-propose/SKILL.md +17 -5
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-sync-specs/SKILL.md +29 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-update-change/SKILL.md +31 -13
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-verify-change/SKILL.md +75 -27
- package/package.json +6 -4
- package/provenance/canonical.PROVENANCE.yaml +1 -1
- package/provenance/memory.PROVENANCE.yaml +458 -6
- package/provenance/sdd.PROVENANCE.yaml +279 -2
- package/canonical/hooks/freeharness-auto-memory.mjs +0 -158
- package/canonical/hooks/hooks.auto-memory.claude-code.json +0 -16
package/dist/cli/cost.d.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
import type { EffortLevel } from "@freeharness/core";
|
|
1
2
|
import type { PrefixItem } from "./prefix.js";
|
|
2
|
-
|
|
3
|
+
/** Token counts priced by `priceBucket`; legacy has no `cache_write_1h`. */
|
|
4
|
+
export interface Bucket {
|
|
3
5
|
input: number;
|
|
4
6
|
output: number;
|
|
5
7
|
cache_read: number;
|
|
6
8
|
cache_write: number;
|
|
9
|
+
cache_write_1h?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface UsageBucket extends Bucket {
|
|
7
12
|
messages?: number;
|
|
8
13
|
est_usd?: number;
|
|
9
14
|
}
|
|
10
15
|
export interface AgentUsage {
|
|
11
16
|
model: string;
|
|
12
|
-
usage: {
|
|
13
|
-
input: number;
|
|
14
|
-
output: number;
|
|
15
|
-
cache_read: number;
|
|
16
|
-
cache_write: number;
|
|
17
|
+
usage: Bucket & {
|
|
17
18
|
total: number;
|
|
18
19
|
};
|
|
19
20
|
messages: number;
|
|
@@ -48,16 +49,14 @@ export interface UsageRecord {
|
|
|
48
49
|
}>;
|
|
49
50
|
/** Additive; absent on older lines. */
|
|
50
51
|
by_agent?: Record<string, AgentUsage>;
|
|
51
|
-
subagents?: {
|
|
52
|
-
input: number;
|
|
53
|
-
output: number;
|
|
54
|
-
cache_read: number;
|
|
55
|
-
cache_write: number;
|
|
52
|
+
subagents?: Bucket & {
|
|
56
53
|
total: number;
|
|
57
54
|
};
|
|
58
55
|
context_window?: number;
|
|
59
56
|
model_suffix?: string | null;
|
|
60
57
|
context_window_source?: "model" | "settings" | "inferred";
|
|
58
|
+
/** The tracker priced some of `est_usd` by the family step. */
|
|
59
|
+
price_estimated?: true;
|
|
61
60
|
}
|
|
62
61
|
export interface PrunedRecord {
|
|
63
62
|
kind: "pruned";
|
|
@@ -78,6 +77,10 @@ export interface CostRow {
|
|
|
78
77
|
cache_read: number;
|
|
79
78
|
cache_write: number;
|
|
80
79
|
est_usd: number;
|
|
80
|
+
/** Some of the row's USD came from the family step: the table has no row for that model id. */
|
|
81
|
+
price_estimated?: true;
|
|
82
|
+
/** Cache-read tokens per session hour; per-project and total rows only, as session hours do not split by model. */
|
|
83
|
+
cache_read_per_hour?: number;
|
|
81
84
|
}
|
|
82
85
|
export interface CostReport {
|
|
83
86
|
log: string;
|
|
@@ -89,6 +92,14 @@ export interface CostReport {
|
|
|
89
92
|
byModel: CostRow[];
|
|
90
93
|
total: CostRow;
|
|
91
94
|
last?: string;
|
|
95
|
+
pricing: PricingMeta;
|
|
96
|
+
}
|
|
97
|
+
/** Read-time pricing metadata for a report's header. */
|
|
98
|
+
export interface PricingMeta {
|
|
99
|
+
table: string;
|
|
100
|
+
loaded: boolean;
|
|
101
|
+
stored_estimate: number;
|
|
102
|
+
without_split: number;
|
|
92
103
|
}
|
|
93
104
|
/** Filters accepted by `cost report`. */
|
|
94
105
|
export interface CostFilter {
|
|
@@ -116,8 +127,8 @@ export declare function readLog(file: string): {
|
|
|
116
127
|
* in first-seen order.
|
|
117
128
|
*/
|
|
118
129
|
export declare function latestPerSession(records: UsageRecord[]): UsageRecord[];
|
|
119
|
-
/** Aggregates usage lines into per-project and per-model rows plus a total
|
|
120
|
-
export declare function aggregate(records: UsageRecord[], filter?: CostFilter, log?: string): CostReport;
|
|
130
|
+
/** Aggregates usage lines into per-project and per-model rows plus a total, sorted by USD descending; USD comes from `priceRecord`. */
|
|
131
|
+
export declare function aggregate(records: UsageRecord[], filter?: CostFilter, log?: string, prices?: Record<string, PriceRow>, pricesFile?: string): CostReport;
|
|
121
132
|
/** Formats a token count compactly (1.2k, 3.4M). */
|
|
122
133
|
export declare function fmtTokens(n: number): string;
|
|
123
134
|
/** Renders the report as aligned text tables. */
|
|
@@ -149,6 +160,7 @@ export interface SinkRow {
|
|
|
149
160
|
tool_output_share: number;
|
|
150
161
|
pruned_bytes: number;
|
|
151
162
|
est_usd: number;
|
|
163
|
+
price_estimated?: true;
|
|
152
164
|
}
|
|
153
165
|
export interface ToolRow {
|
|
154
166
|
tool: string;
|
|
@@ -169,13 +181,14 @@ export interface SinksReport {
|
|
|
169
181
|
tools: ToolRow[];
|
|
170
182
|
notes: string[];
|
|
171
183
|
}[];
|
|
184
|
+
pricing: PricingMeta;
|
|
172
185
|
}
|
|
173
186
|
/**
|
|
174
187
|
* Ranks token sinks per project: sessions (or task classes) by total tokens with the
|
|
175
188
|
* tool-output share and the pruned bytes recovered, and tools by output. Sessions without a
|
|
176
|
-
* task class are `untagged` and counted
|
|
189
|
+
* task class are `untagged` and counted; USD is repriced per row like `aggregate`.
|
|
177
190
|
*/
|
|
178
|
-
export declare function sinks(records: UsageRecord[], filter?: SinksFilter, log?: string): SinksReport;
|
|
191
|
+
export declare function sinks(records: UsageRecord[], filter?: SinksFilter, log?: string, prices?: Record<string, PriceRow>, pricesFile?: string): SinksReport;
|
|
179
192
|
/** Renders the sinks report. */
|
|
180
193
|
export declare function formatSinks(report: SinksReport): string[];
|
|
181
194
|
/** One `.freeharness/telemetry.jsonl` record written by the cost-tracker hook on SessionEnd (data-contract.md Sink Schema). */
|
|
@@ -190,19 +203,16 @@ export interface TelemetryRecord {
|
|
|
190
203
|
task: string | null;
|
|
191
204
|
tier: string | null;
|
|
192
205
|
task_class: string;
|
|
206
|
+
/** Main-context tokens by model; absent joins a `usage.jsonl` snapshot instead. */
|
|
207
|
+
by_model?: Record<string, Bucket>;
|
|
208
|
+
/** Session's activity class; absent is inferred from token totals. */
|
|
209
|
+
activity?: string;
|
|
210
|
+
end_reason?: string | null;
|
|
193
211
|
sinks: {
|
|
194
|
-
main: {
|
|
195
|
-
input: number;
|
|
196
|
-
output: number;
|
|
197
|
-
cache_read: number;
|
|
198
|
-
cache_write: number;
|
|
212
|
+
main: Bucket & {
|
|
199
213
|
total: number;
|
|
200
214
|
};
|
|
201
|
-
subagents: {
|
|
202
|
-
input: number;
|
|
203
|
-
output: number;
|
|
204
|
-
cache_read: number;
|
|
205
|
-
cache_write: number;
|
|
215
|
+
subagents: Bucket & {
|
|
206
216
|
total: number;
|
|
207
217
|
};
|
|
208
218
|
by_agent: Record<string, AgentUsage>;
|
|
@@ -222,6 +232,12 @@ export interface TelemetryRecord {
|
|
|
222
232
|
context_max: number;
|
|
223
233
|
context_window: number;
|
|
224
234
|
warnings: string[];
|
|
235
|
+
/** The session's class; records written before the class existed lack it. */
|
|
236
|
+
session_class?: string;
|
|
237
|
+
session_classes?: string[];
|
|
238
|
+
changes?: string[];
|
|
239
|
+
/** Claude Code version; its presence marks a record written after subagent transcripts were discovered. */
|
|
240
|
+
cc_version?: string | null;
|
|
225
241
|
}
|
|
226
242
|
/**
|
|
227
243
|
* Reads `.freeharness/telemetry.jsonl`: a line that is not valid JSON or lacks
|
|
@@ -244,32 +260,77 @@ export interface ChangeRow {
|
|
|
244
260
|
totalObserver: number;
|
|
245
261
|
est_usd: number;
|
|
246
262
|
tierMix: string;
|
|
263
|
+
price_estimated?: true;
|
|
247
264
|
}
|
|
265
|
+
export type ActivityClass = "measured" | "no-transcript" | "no-model-calls";
|
|
248
266
|
/**
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* observer tokens, summed est USD, and the tier mix (`standard 2, fast 1`, tiers with
|
|
252
|
-
* no session omitted). One row per change, ranked by total main tokens descending.
|
|
267
|
+
* Excluded-session counts; the activity keys are always present. `subagents-unread` is set only
|
|
268
|
+
* when some record hit it, so a report over current records keeps its two-key shape.
|
|
253
269
|
*/
|
|
254
|
-
export
|
|
255
|
-
|
|
256
|
-
|
|
270
|
+
export type ExcludedCounts = Record<Exclude<ActivityClass, "measured">, number> & {
|
|
271
|
+
"subagents-unread"?: number;
|
|
272
|
+
};
|
|
273
|
+
export declare const SESSION_CLASS_NAMES: readonly ["sdd", "plan", "archive", "review", "debug", "untagged"];
|
|
274
|
+
/** Measured sessions per class; `legacy` holds the records written before `session_class` existed. */
|
|
275
|
+
export type ClassCounts = Record<(typeof SESSION_CLASS_NAMES)[number], number> & {
|
|
276
|
+
legacy: number;
|
|
277
|
+
};
|
|
278
|
+
/** `changes()`'s result: rows, the pricing header, the excluded counts, the class mix and the records `--days` dropped for an unparseable `ts`. */
|
|
279
|
+
export interface ChangesReport {
|
|
280
|
+
rows: ChangeRow[];
|
|
281
|
+
pricing: PricingMeta;
|
|
282
|
+
excluded: ExcludedCounts;
|
|
283
|
+
classes: ClassCounts;
|
|
284
|
+
skippedTs: number;
|
|
285
|
+
}
|
|
286
|
+
/** Untagged over the measured records that carry a class, so legacy records cannot dilute the share the gate reads; null with none. */
|
|
287
|
+
export declare function untaggedShare(classes: ClassCounts): number | null;
|
|
288
|
+
/**
|
|
289
|
+
* Groups telemetry records by tagged `change` (`untagged` catch-all): session count and
|
|
290
|
+
* median tokens over `measured` records only; total tokens, est USD (repriced at read
|
|
291
|
+
* time) and the tier mix over every record. Every change with at least one record gets a
|
|
292
|
+
* row (sessions/medians 0 with none `measured`); ranked by total main tokens descending.
|
|
293
|
+
* `opts.days` keeps records whose `ts` falls in the last N days of `opts.now`; an unparseable
|
|
294
|
+
* `ts` cannot be placed in the window, so it is counted in `skippedTs` instead of guessed at.
|
|
295
|
+
*/
|
|
296
|
+
export declare function changes(records: TelemetryRecord[], usage?: UsageRecord[], prices?: Record<string, PriceRow>, pricesFile?: string, opts?: {
|
|
297
|
+
days?: number;
|
|
298
|
+
now?: number;
|
|
299
|
+
}): ChangesReport;
|
|
300
|
+
/** Renders the `--changes` report; `skipped` counts lines `readTelemetry` dropped (malformed or without `session_id`); `classes` adds the class line and `skippedTs` (set under `--days`) the unparseable-ts line. */
|
|
301
|
+
export declare function formatChanges(rows: ChangeRow[], file: string, skipped: number, pricing: PricingMeta, excluded: ExcludedCounts, classes?: ClassCounts, skippedTs?: number): string[];
|
|
257
302
|
/** Price row per MTok as read from `cost-prices.json` (see `canonical/hooks/cost-prices.json`). */
|
|
258
303
|
interface PriceRow {
|
|
259
304
|
input: number;
|
|
260
305
|
output: number;
|
|
261
306
|
cache_read: number;
|
|
262
307
|
cache_write: number;
|
|
308
|
+
/** The 1-hour cache-write rate; absent rows derive it at 1.6x `cache_write`. */
|
|
309
|
+
cache_write_1h?: number;
|
|
263
310
|
window?: number;
|
|
264
311
|
}
|
|
312
|
+
/** USD for one bucket at `row`'s rates (0 with no row); mirrors the hook's `estimate`, deriving the 1-hour rate at 1.6x `cache_write` when absent. */
|
|
313
|
+
export declare function priceBucket(row: PriceRow | undefined, bucket: Bucket): number;
|
|
265
314
|
/**
|
|
266
|
-
* Loads the price table
|
|
267
|
-
* yields an empty table so the estimate degrades to 0 instead of throwing.
|
|
315
|
+
* Loads the price table every `cost report` mode reprices from; a missing or malformed
|
|
316
|
+
* file yields an empty table so the estimate degrades to 0 instead of throwing.
|
|
268
317
|
* Mirrors `loadPrices` in canonical/hooks/freeharness-cost-tracker.mjs; kept as a
|
|
269
318
|
* separate copy since the hook is a standalone, dependency-free .mjs the CLI does not
|
|
270
319
|
* import from. Returns model name -> price row (per MTok).
|
|
271
320
|
*/
|
|
272
321
|
export declare function loadPriceTable(file: string): Record<string, PriceRow>;
|
|
322
|
+
/** A price lookup's result: the matched key (null when no row matches), its row, and whether the family step chose it. */
|
|
323
|
+
export interface PriceMatch {
|
|
324
|
+
key: string | null;
|
|
325
|
+
row: PriceRow | undefined;
|
|
326
|
+
estimated: boolean;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Exact key, else the longest prefix key, else the id's family's newest key (estimated), else
|
|
330
|
+
* `default`. Mirrors `priceMatch` in canonical/hooks/freeharness-cost-tracker.mjs (same
|
|
331
|
+
* separate-copy rationale as `loadPriceTable`); a parity test holds the two together.
|
|
332
|
+
*/
|
|
333
|
+
export declare function priceMatch(prices: Record<string, PriceRow>, model: string): PriceMatch;
|
|
273
334
|
/** One `cost report --agents` row (ranked by total tokens). */
|
|
274
335
|
export interface AgentRow {
|
|
275
336
|
name: string;
|
|
@@ -284,21 +345,322 @@ export interface AgentRow {
|
|
|
284
345
|
cache_write: number;
|
|
285
346
|
tokens: number;
|
|
286
347
|
est_usd: number;
|
|
348
|
+
/** Some of the row's USD came from the family step: the table has no row for that model id. */
|
|
349
|
+
price_estimated?: true;
|
|
287
350
|
}
|
|
288
351
|
/**
|
|
289
|
-
* Ranks subagent dispatches by total tokens from each session's last `by_agent`
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
* and an est USD from the price table. Rows ranked by total tokens descending.
|
|
352
|
+
* Ranks subagent dispatches by total tokens from each session's last `by_agent` snapshot:
|
|
353
|
+
* per agent name, the model with the most tokens (ties broken by name, mirroring the hook's
|
|
354
|
+
* `mergeAgentUsage`), `dispatches`/`messages` summed across sessions (no `dispatches` field
|
|
355
|
+
* counts as 1), USD from `priceBucket` per entry summed per name. Ranked by tokens
|
|
356
|
+
* descending, paired with the pricing header `formatAgents` reads.
|
|
295
357
|
*/
|
|
296
|
-
export declare function agents(records: UsageRecord[], prices?: Record<string, PriceRow
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
358
|
+
export declare function agents(records: UsageRecord[], prices?: Record<string, PriceRow>, pricesFile?: string): {
|
|
359
|
+
rows: AgentRow[];
|
|
360
|
+
pricing: PricingMeta;
|
|
361
|
+
};
|
|
362
|
+
/** Renders the `--agents` report; `pricing.loaded: false` prints a warning and every row shows 0.00. */
|
|
363
|
+
export declare function formatAgents(rows: AgentRow[], pricing: PricingMeta): string[];
|
|
302
364
|
/** Per-project telemetry file path: `<dir>/.freeharness/telemetry.jsonl` (may not exist yet); `dir` defaults to the current working directory. */
|
|
303
365
|
export declare function telemetryPath(dir?: string): string;
|
|
366
|
+
/** One role-filtered (impl/fix/review) ledger line for the routing report. */
|
|
367
|
+
export interface RoutingLedgerLine {
|
|
368
|
+
change: string;
|
|
369
|
+
ts: string;
|
|
370
|
+
role: "impl" | "fix" | "review";
|
|
371
|
+
task: string;
|
|
372
|
+
model: string;
|
|
373
|
+
/** Null when absent (a legacy line), outside EFFORT_LEVELS, or read from the agent definition. */
|
|
374
|
+
effort: EffortLevel | null;
|
|
375
|
+
input: number;
|
|
376
|
+
output: number;
|
|
377
|
+
cache_read: number;
|
|
378
|
+
cache_write: number;
|
|
379
|
+
cache_write_1h?: number;
|
|
380
|
+
/** Null or absent makes the line its own dispatch. */
|
|
381
|
+
agent_id?: string | null;
|
|
382
|
+
/** A Stop/SessionEnd backfill line. */
|
|
383
|
+
late?: boolean;
|
|
384
|
+
/** `agent`: a legacy backfill line whose effort came from the agent definition; its effort is read as null. */
|
|
385
|
+
effort_source?: "payload" | "transcript" | "agent" | null;
|
|
386
|
+
/** The subagent transcript, whose prompt names the brief the dispatch ran under. */
|
|
387
|
+
transcript?: string;
|
|
388
|
+
/** The `-r<n>` of the dispatch name; null when the name carries none, so such a fix is a round of its own. */
|
|
389
|
+
round?: number | null;
|
|
390
|
+
/** The lead session that ran the dispatch, which weighs a multi-change lead's share. */
|
|
391
|
+
session?: string | null;
|
|
392
|
+
/** Read unvalidated; `ccBand` decides whether it names a version. */
|
|
393
|
+
cc_version?: string | null;
|
|
394
|
+
}
|
|
395
|
+
/** Model families a subscription meters apart; a model id naming none is `other`, never dropped. */
|
|
396
|
+
export type ModelFamily = "opus" | "sonnet" | "haiku" | "fable" | "other";
|
|
397
|
+
/** Cache reads and fresh tokens (input, every cache write, output) kept apart, since a window meters them at different weights. */
|
|
398
|
+
export interface TokenGroup {
|
|
399
|
+
cache_read: number;
|
|
400
|
+
fresh: number;
|
|
401
|
+
}
|
|
402
|
+
/** The family a model id names as a whole `-`/`.`/`_`-delimited segment, so `claude-opusx-1` is not Opus. */
|
|
403
|
+
export declare function familyOf(model: string): ModelFamily;
|
|
404
|
+
/** Sums buckets per model into the two groups per family, in `FAMILIES` order; `cache_write_1h` is part of `cache_write`, so it is never added twice. */
|
|
405
|
+
export declare function tokenGroups(usageByModel: Record<string, Bucket>): Partial<Record<ModelFamily, TokenGroup>>;
|
|
406
|
+
/** `routingReport`'s row per implementer configuration and task class. */
|
|
407
|
+
export interface RoutingRow {
|
|
408
|
+
model: string;
|
|
409
|
+
effort: EffortLevel | "unknown";
|
|
410
|
+
/** The brief's `Class:` header, or `unclassified`. */
|
|
411
|
+
class: string;
|
|
412
|
+
tasks: number;
|
|
413
|
+
accepted: number;
|
|
414
|
+
/** Accepted tasks with no `fix` dispatch; `--json` names it `first_pass`. */
|
|
415
|
+
firstPass: number;
|
|
416
|
+
impl: number;
|
|
417
|
+
fix: number;
|
|
418
|
+
review: number;
|
|
419
|
+
escalated: number;
|
|
420
|
+
usd: number;
|
|
421
|
+
/** Accepted tasks' lines summed, divided by the accepted count; null with zero accepted. */
|
|
422
|
+
usdPerAccepted: number | null;
|
|
423
|
+
/** Some line of the row was priced by the family step. */
|
|
424
|
+
price_estimated?: true;
|
|
425
|
+
/** The same dispatches' tokens per accepted task by family; empty with zero accepted. `--json`: `tokens_per_accepted`. */
|
|
426
|
+
tokensPerAccepted: Partial<Record<ModelFamily, TokenGroup>>;
|
|
427
|
+
/** Seven-day window percent per accepted task from the fitted weights; present only when a calibration holds a seven-day window. `--json`: `seven_day_pct_per_accepted`. */
|
|
428
|
+
sevenDayPctPerAccepted?: number | null;
|
|
429
|
+
/** Distinct fix rounds averaged over accepted tasks; null with zero accepted. `--json`: `mean_fix_rounds`. */
|
|
430
|
+
meanFixRounds: number | null;
|
|
431
|
+
/** Accepted tasks with more than one distinct fix round. `--json`: `tasks_over_one_round`. */
|
|
432
|
+
tasksOverOneRound: number;
|
|
433
|
+
/** The follow-ups this row's accepted tasks left; zero without a follow-ups.md, absent only without the `followUps` option. `--json`: `follow_ups_per_accepted`. */
|
|
434
|
+
followUpsPerAccepted?: FollowUpsPerAccepted;
|
|
435
|
+
}
|
|
436
|
+
/** A task's acceptance, keyed `<change>\u0000<task>`; absent means not accepted. */
|
|
437
|
+
type AcceptanceMap = Map<string, boolean>;
|
|
438
|
+
/** A task brief's routing facts. */
|
|
439
|
+
export interface TaskBrief {
|
|
440
|
+
/** The brief's `Task:` header lists the ledger task's `N.N` id. */
|
|
441
|
+
declared: boolean;
|
|
442
|
+
class: string;
|
|
443
|
+
}
|
|
444
|
+
/** Task briefs keyed like `AcceptanceMap`; absent means no brief. */
|
|
445
|
+
export type BriefMap = Map<string, TaskBrief>;
|
|
446
|
+
/** Joins a change and task into an `AcceptanceMap` key. */
|
|
447
|
+
export declare function acceptanceKey(change: string, task: string): string;
|
|
448
|
+
/** Reads one change's `ledger.jsonl` into role-filtered lines; invalid JSON, an impl/fix/review line without `ts`, `task` or `model`, and a line with any other non-null role (or none) are malformed and counted; a `role: null` line (a non-SDD dispatch) is skipped uncounted. */
|
|
449
|
+
export declare function readLedgerLines(file: string, change: string): {
|
|
450
|
+
lines: RoutingLedgerLine[];
|
|
451
|
+
malformed: number;
|
|
452
|
+
skipped: boolean;
|
|
453
|
+
};
|
|
454
|
+
/** Task ids (leading `N.N` numbering) checked (`x`/`X`) in a tasks.md body; mirrors the vendored reader's marker set (dash, star, plus, ordered). */
|
|
455
|
+
export declare function checkedTaskIds(content: string): Set<string>;
|
|
456
|
+
/**
|
|
457
|
+
* The tasks.md task a dispatch's task id counts under, so splitting a task into sub-tasks never
|
|
458
|
+
* raises the task count: the id itself or its longest prefix (cut at a dot) listed as a task line.
|
|
459
|
+
* @param task The ledger task id.
|
|
460
|
+
* @param tasksMdIds Every task line id of the change's tasks.md.
|
|
461
|
+
* @returns The listed id, or null when no prefix is listed or the id is not dotted numbers.
|
|
462
|
+
*/
|
|
463
|
+
export declare function rollUpTask(task: string, tasksMdIds: Set<string>): string | null;
|
|
464
|
+
/**
|
|
465
|
+
* The change a dispatch's prompt names by its brief path, read from the first user message of its
|
|
466
|
+
* transcript, so a dispatch in several ledgers counts where the cost tracker filed it.
|
|
467
|
+
* @param transcriptPath The ledger line's `transcript` path.
|
|
468
|
+
* @param projectRoot The project root an absolute prompt path must sit under; without it only a relative path counts.
|
|
469
|
+
* @returns The named change, in flight or archived, or null for an unusable transcript or no valid path.
|
|
470
|
+
*/
|
|
471
|
+
export declare function promptBriefChange(transcriptPath: string, projectRoot?: string): string | null;
|
|
472
|
+
/** Claude Code before this version recorded placeholder output tokens in subagent transcripts. */
|
|
473
|
+
declare const CC_BAND_FLOOR = "2.1.307";
|
|
474
|
+
export type CcBand = `${typeof CC_BAND_FLOOR} or later` | "older" | "unknown";
|
|
475
|
+
/** Dispatch counts per version band. `--json`: `cc_version_bands`. */
|
|
476
|
+
export type CcBands = Record<CcBand, number>;
|
|
477
|
+
/**
|
|
478
|
+
* Whether a dispatch's subagent fresh tokens can be trusted: recorded on 2.1.307 or later. A
|
|
479
|
+
* pre-release suffix is ignored, since it carries the same recording fix as its release.
|
|
480
|
+
*/
|
|
481
|
+
export declare function ccBand(version: string | null): CcBand;
|
|
482
|
+
/** `routingReport`'s project inputs beyond the ledgers. */
|
|
483
|
+
export interface RoutingOptions {
|
|
484
|
+
/** The task each ledger task id (keyed like `AcceptanceMap`) counts under, null for a sub-task with no tasks.md ancestor; an absent key keeps the id. */
|
|
485
|
+
rollUp?: Map<string, string | null>;
|
|
486
|
+
/** The change a transcript's prompt names, for dispatches in several ledgers. */
|
|
487
|
+
promptChange?: (transcript: string) => string | null;
|
|
488
|
+
/** A review dispatch's criterion, keyed by its own change and task; an absent key reads `unrecorded`. */
|
|
489
|
+
reviewCriteria?: Map<string, string>;
|
|
490
|
+
/** Each change's follow-ups.md items; when set, every row carries its own follow-up figure. */
|
|
491
|
+
followUps?: Map<string, FollowUpItem[]>;
|
|
492
|
+
/** A transcript's Claude Code version, for a dispatch whose line carries none; omitted, such a dispatch bands unknown. */
|
|
493
|
+
transcriptVersion?: (transcript: string) => string | null;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Version bands kept beside the rows rather than on them, index-aligned with `rows` and
|
|
497
|
+
* `reviewRows`, so the row objects stay as callers already compare them.
|
|
498
|
+
*/
|
|
499
|
+
export interface RoutingVersionBands {
|
|
500
|
+
rows: CcBands[];
|
|
501
|
+
/** Older or unknown dispatches behind each row's fresh figures (its accepted tasks'). */
|
|
502
|
+
rowsLowerBound: number[];
|
|
503
|
+
reviewRows: CcBands[];
|
|
504
|
+
/** Every dispatch counted in some row, once. */
|
|
505
|
+
total: CcBands;
|
|
506
|
+
}
|
|
507
|
+
/** A follow-ups.md item's type and `(from <task>)` label. */
|
|
508
|
+
type FollowUpItem = {
|
|
509
|
+
type: string;
|
|
510
|
+
from: string | null;
|
|
511
|
+
};
|
|
512
|
+
/** One row per reviewer configuration (model, effort) and `Review tier:` criterion. */
|
|
513
|
+
export interface ReviewRow {
|
|
514
|
+
model: string;
|
|
515
|
+
effort: EffortLevel | "unknown";
|
|
516
|
+
/** The `S<n>` of the task's review package, or `unrecorded`. */
|
|
517
|
+
criterion: string;
|
|
518
|
+
dispatches: number;
|
|
519
|
+
/** Distinct tasks reviewed, sub-tasks rolled up. `--json`: `tasks`. */
|
|
520
|
+
tasks: number;
|
|
521
|
+
/** Review tokens over the distinct tasks reviewed. `--json`: `tokens_per_task`. */
|
|
522
|
+
tokensPerTask: Partial<Record<ModelFamily, TokenGroup>>;
|
|
523
|
+
}
|
|
524
|
+
/** The orchestrating sessions' main-transcript tokens, from telemetry tagged with a reported change. */
|
|
525
|
+
export interface LeadRow {
|
|
526
|
+
sessions: number;
|
|
527
|
+
tokens: Partial<Record<ModelFamily, TokenGroup>>;
|
|
528
|
+
}
|
|
529
|
+
/** Tokens per accepted task with the lead counted, since the lead session carries most cache reads. */
|
|
530
|
+
export interface TotalsPerAccepted {
|
|
531
|
+
accepted: number;
|
|
532
|
+
dispatches: Partial<Record<ModelFamily, TokenGroup>>;
|
|
533
|
+
lead: Partial<Record<ModelFamily, TokenGroup>>;
|
|
534
|
+
total: Partial<Record<ModelFamily, TokenGroup>>;
|
|
535
|
+
}
|
|
536
|
+
/** Follow-up items left by accepted tasks, per accepted task; `total` is null with zero accepted. `--json`: `by_type`. */
|
|
537
|
+
export interface FollowUpsPerAccepted {
|
|
538
|
+
total: number | null;
|
|
539
|
+
byType: Record<string, number>;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Groups dispatches by task, assigns each task the (model, effort) of its first `impl` dispatch
|
|
543
|
+
* (none excludes it), and aggregates per (model, effort, class): role counts, first-pass tasks,
|
|
544
|
+
* `escalated` (a later impl/fix on another model), USD, and, over accepted tasks only, tokens by
|
|
545
|
+
* family and distinct fix rounds.
|
|
546
|
+
*
|
|
547
|
+
* Sub-task dispatches count under the task `options.rollUp` maps them to, so splitting a task
|
|
548
|
+
* never raises the task count; an id mapped to null counts in no row. A rolled-up task's class is
|
|
549
|
+
* the brief class of the sub-task its first impl dispatch ran, the configuration that did the work.
|
|
550
|
+
* @param options Roll-up map and prompt-path resolver; omitted, every id is its own task and
|
|
551
|
+
* duplicate dispatches follow the declaring-brief rule.
|
|
552
|
+
*/
|
|
553
|
+
export declare function routingReport(ledgers: RoutingLedgerLine[], acceptance: AcceptanceMap, prices: Record<string, PriceRow>, briefs?: BriefMap, options?: RoutingOptions): RoutingRow[];
|
|
554
|
+
/** Distinct dispatch `agent_id`s per lead session, of one change's ledger lines. */
|
|
555
|
+
export type SessionAgents = Map<string, Set<string>>;
|
|
556
|
+
/**
|
|
557
|
+
* A record's main-context buckets split across the changes it worked on, so a lead session that
|
|
558
|
+
* worked on several changes counts its context once in total rather than once per change. Each
|
|
559
|
+
* change's weight is the distinct dispatches the session ran in its ledger; with none anywhere the
|
|
560
|
+
* shares are equal. Parts are whole tokens that sum exactly to the record's figures, whichever
|
|
561
|
+
* changes a report reads.
|
|
562
|
+
* @param agentsOf Each named change's dispatches per session.
|
|
563
|
+
* @returns Per named change, its fraction (to scale a USD figure) and its buckets.
|
|
564
|
+
*/
|
|
565
|
+
export declare function leadShares(record: TelemetryRecord, buckets: Record<string, Bucket>, agentsOf: (change: string) => SessionAgents): Map<string, {
|
|
566
|
+
share: number;
|
|
567
|
+
buckets: Record<string, Bucket>;
|
|
568
|
+
}>;
|
|
569
|
+
/** `routingReportForProject`'s result; `skippedFiles` are project-relative. */
|
|
570
|
+
export interface RoutingReportResult {
|
|
571
|
+
rows: RoutingRow[];
|
|
572
|
+
malformedLines: number;
|
|
573
|
+
missingTasksFiles: string[];
|
|
574
|
+
skippedFiles: string[];
|
|
575
|
+
pricing: Pick<PricingMeta, "table" | "loaded">;
|
|
576
|
+
/** Distinct sub-task ids with no tasks.md ancestor, counted in no row. */
|
|
577
|
+
unmatchedSubTasks?: number;
|
|
578
|
+
/** The figures below the rows; optional so a result built by hand renders the rows alone. */
|
|
579
|
+
reviewRows?: ReviewRow[];
|
|
580
|
+
lead?: LeadRow;
|
|
581
|
+
totalsPerAccepted?: TotalsPerAccepted;
|
|
582
|
+
followUpsPerAccepted?: FollowUpsPerAccepted;
|
|
583
|
+
/** The seven-day fit behind the row estimates; absent when no calibration holds a seven-day window. */
|
|
584
|
+
sevenDay?: {
|
|
585
|
+
residual: number;
|
|
586
|
+
unreliable: boolean;
|
|
587
|
+
};
|
|
588
|
+
/** Why a calibration file was ignored, printed as one line. */
|
|
589
|
+
calibrationWarning?: string;
|
|
590
|
+
versionBands?: RoutingVersionBands;
|
|
591
|
+
/** In first-dispatch order; `--json` only, as `accepted_tasks`. */
|
|
592
|
+
acceptedTaskRecords?: AcceptedTaskRecord[];
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* One accepted task the rows count, sub-tasks rolled up, so a median over tasks recorded wholly on
|
|
596
|
+
* a trusted Claude Code version reads straight from the JSON; the rows only hold per-row means.
|
|
597
|
+
*/
|
|
598
|
+
export interface AcceptedTaskRecord {
|
|
599
|
+
change: string;
|
|
600
|
+
task: string;
|
|
601
|
+
class: string;
|
|
602
|
+
firstTs: string;
|
|
603
|
+
/** Impl, fix and review dispatches, each counted once. */
|
|
604
|
+
dispatches: number;
|
|
605
|
+
bands: CcBands;
|
|
606
|
+
/** Every dispatch is in the 2.1.307 or later band. */
|
|
607
|
+
fullyLater: boolean;
|
|
608
|
+
tokens: Partial<Record<ModelFamily, TokenGroup>>;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Discovers every change under `.freeharness/sdd/` with a `ledger.jsonl` (or just `changeFilter`),
|
|
612
|
+
* resolves each task's acceptance against its tasks.md, and runs `routingReport` over the union.
|
|
613
|
+
* The review packages, the telemetry tagged with a reported change and its follow-ups.md add the
|
|
614
|
+
* review rows, the lead row and the per-accepted-task totals, since a gate counting dispatches
|
|
615
|
+
* alone would pass by moving cost into the lead session. A quota calibration with a seven-day
|
|
616
|
+
* window adds the window estimate to every row; an invalid one is ignored with a warning.
|
|
617
|
+
*/
|
|
618
|
+
export declare function routingReportForProject(projectRoot: string, changeFilter?: string, prices?: Record<string, PriceRow>, pricesFile?: string, calibrationFile?: string): RoutingReportResult;
|
|
619
|
+
/** The `--json` shape of a routing report: rows keyed as the data contract names them, the figures beside them after the existing fields. */
|
|
620
|
+
export declare function routingReportJson(result: RoutingReportResult): Record<string, unknown>;
|
|
621
|
+
/** Escapes DEL and C1 (U+007F to U+009F), which `JSON.stringify` leaves raw, as `\u00XX`; the parsed value is unchanged. */
|
|
622
|
+
export declare function escapeJsonControls(json: string): string;
|
|
623
|
+
/** Renders the `--routing` report; a notice replaces the table when no ledger has an `impl` dispatch, and the trailers print either way. */
|
|
624
|
+
export declare function formatRoutingReport(result: RoutingReportResult): string[];
|
|
625
|
+
/** One change's retrospective figures. */
|
|
626
|
+
export interface RetroFigures {
|
|
627
|
+
ledger: true;
|
|
628
|
+
change: string;
|
|
629
|
+
line: string;
|
|
630
|
+
date: string;
|
|
631
|
+
dispatches: number;
|
|
632
|
+
impl: number;
|
|
633
|
+
review: number;
|
|
634
|
+
fix: number;
|
|
635
|
+
other: number;
|
|
636
|
+
dispatchCacheRead: number;
|
|
637
|
+
cacheReadByFamily: Partial<Record<ModelFamily, number>>;
|
|
638
|
+
fresh: number;
|
|
639
|
+
freshByFamily: Partial<Record<ModelFamily, number>>;
|
|
640
|
+
/** Null when the bundled validator could not be loaded or its summary failed. */
|
|
641
|
+
tasksAtOrOverCeiling: number | null;
|
|
642
|
+
scopeStops: number | null;
|
|
643
|
+
fixRounds: number;
|
|
644
|
+
usd: number;
|
|
645
|
+
leadSessions: number;
|
|
646
|
+
leadCacheRead: number;
|
|
647
|
+
leadUsd: number;
|
|
648
|
+
leadClasses: Partial<ClassCounts>;
|
|
649
|
+
ccVersionBands: CcBands;
|
|
650
|
+
}
|
|
651
|
+
/** A change without a ledger carries only its notice. */
|
|
652
|
+
export type RetroResult = RetroFigures | {
|
|
653
|
+
ledger: false;
|
|
654
|
+
change: string;
|
|
655
|
+
line: string;
|
|
656
|
+
};
|
|
657
|
+
/**
|
|
658
|
+
* The retrospective line for one change. Dispatches are the routing collapse over every ledger,
|
|
659
|
+
* kept where the collapse files them, so a dispatch copied into another change's ledger counts
|
|
660
|
+
* once; the command reads only and writes nothing.
|
|
661
|
+
* @param now The moment whose local date the line carries.
|
|
662
|
+
*/
|
|
663
|
+
export declare function retroLine(projectRoot: string, change: string, now?: Date): Promise<RetroResult>;
|
|
664
|
+
/** The `--json` shape of the retrospective: the line first, every figure snake-cased beside it. */
|
|
665
|
+
export declare function retroJson(r: RetroFigures): Record<string, unknown>;
|
|
304
666
|
export {};
|