context-mode 1.0.110 → 1.0.112
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 +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/index.ts +3 -2
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +152 -34
- package/bin/statusline.mjs +144 -127
- package/build/adapters/base.d.ts +8 -5
- package/build/adapters/base.js +8 -18
- package/build/adapters/claude-code/index.d.ts +24 -3
- package/build/adapters/claude-code/index.js +44 -11
- package/build/adapters/codex/hooks.d.ts +10 -5
- package/build/adapters/codex/hooks.js +10 -5
- package/build/adapters/codex/index.d.ts +17 -5
- package/build/adapters/codex/index.js +337 -37
- package/build/adapters/codex/paths.d.ts +1 -0
- package/build/adapters/codex/paths.js +12 -0
- package/build/adapters/cursor/index.d.ts +6 -0
- package/build/adapters/cursor/index.js +83 -2
- package/build/adapters/detect.d.ts +1 -1
- package/build/adapters/detect.js +29 -6
- package/build/adapters/omp/index.d.ts +65 -0
- package/build/adapters/omp/index.js +182 -0
- package/build/adapters/omp/plugin.d.ts +75 -0
- package/build/adapters/omp/plugin.js +220 -0
- package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
- package/build/adapters/openclaw/mcp-tools.js +198 -0
- package/build/adapters/openclaw/plugin.d.ts +130 -0
- package/build/adapters/openclaw/plugin.js +629 -0
- package/build/adapters/openclaw/workspace-router.d.ts +29 -0
- package/build/adapters/openclaw/workspace-router.js +64 -0
- package/build/adapters/opencode/plugin.d.ts +145 -0
- package/build/adapters/opencode/plugin.js +457 -0
- package/build/adapters/pi/extension.d.ts +26 -0
- package/build/adapters/pi/extension.js +552 -0
- package/build/adapters/pi/index.d.ts +57 -0
- package/build/adapters/pi/index.js +173 -0
- package/build/adapters/pi/mcp-bridge.d.ts +113 -0
- package/build/adapters/pi/mcp-bridge.js +251 -0
- package/build/adapters/types.d.ts +11 -6
- package/build/cli.js +186 -170
- package/build/db-base.d.ts +15 -2
- package/build/db-base.js +50 -5
- package/build/executor.d.ts +2 -0
- package/build/executor.js +15 -2
- package/build/opencode-plugin.js +1 -1
- package/build/runPool.d.ts +36 -0
- package/build/runPool.js +51 -0
- package/build/runtime.js +64 -5
- package/build/search/auto-memory.js +6 -4
- package/build/security.js +30 -10
- package/build/server.d.ts +23 -1
- package/build/server.js +652 -174
- package/build/session/analytics.d.ts +404 -1
- package/build/session/analytics.js +1347 -42
- package/build/session/db.d.ts +114 -5
- package/build/session/db.js +275 -27
- package/build/session/event-emit.d.ts +48 -0
- package/build/session/event-emit.js +101 -0
- package/build/session/extract.d.ts +1 -0
- package/build/session/extract.js +79 -12
- package/build/session/purge.d.ts +111 -0
- package/build/session/purge.js +138 -0
- package/build/store.d.ts +7 -0
- package/build/store.js +69 -6
- package/build/util/claude-config.d.ts +26 -0
- package/build/util/claude-config.js +91 -0
- package/build/util/hook-config.d.ts +4 -0
- package/build/util/hook-config.js +39 -0
- package/cli.bundle.mjs +411 -208
- package/configs/antigravity/GEMINI.md +0 -3
- package/configs/claude-code/CLAUDE.md +1 -4
- package/configs/codex/AGENTS.md +1 -4
- package/configs/codex/config.toml +3 -0
- package/configs/codex/hooks.json +8 -0
- package/configs/cursor/context-mode.mdc +0 -3
- package/configs/gemini-cli/GEMINI.md +0 -3
- package/configs/jetbrains-copilot/copilot-instructions.md +0 -3
- package/configs/kilo/AGENTS.md +0 -3
- package/configs/kiro/KIRO.md +0 -3
- package/configs/omp/SYSTEM.md +85 -0
- package/configs/omp/mcp.json +7 -0
- package/configs/openclaw/AGENTS.md +0 -3
- package/configs/opencode/AGENTS.md +0 -3
- package/configs/pi/AGENTS.md +0 -3
- package/configs/qwen-code/QWEN.md +1 -4
- package/configs/vscode-copilot/copilot-instructions.md +0 -3
- package/configs/zed/AGENTS.md +0 -3
- package/hooks/codex/posttooluse.mjs +9 -2
- package/hooks/codex/precompact.mjs +69 -0
- package/hooks/codex/sessionstart.mjs +13 -9
- package/hooks/codex/stop.mjs +1 -2
- package/hooks/codex/userpromptsubmit.mjs +1 -2
- package/hooks/core/routing.mjs +237 -18
- package/hooks/cursor/afteragentresponse.mjs +1 -1
- package/hooks/cursor/hooks.json +31 -0
- package/hooks/cursor/posttooluse.mjs +1 -1
- package/hooks/cursor/sessionstart.mjs +5 -5
- package/hooks/cursor/stop.mjs +1 -1
- package/hooks/ensure-deps.mjs +12 -13
- package/hooks/gemini-cli/aftertool.mjs +1 -1
- package/hooks/gemini-cli/beforeagent.mjs +1 -1
- package/hooks/gemini-cli/precompress.mjs +3 -2
- package/hooks/gemini-cli/sessionstart.mjs +9 -9
- package/hooks/jetbrains-copilot/posttooluse.mjs +1 -1
- package/hooks/jetbrains-copilot/precompact.mjs +3 -2
- package/hooks/jetbrains-copilot/sessionstart.mjs +9 -9
- package/hooks/kiro/agentspawn.mjs +5 -5
- package/hooks/kiro/posttooluse.mjs +2 -2
- package/hooks/kiro/userpromptsubmit.mjs +1 -1
- package/hooks/posttooluse.mjs +45 -0
- package/hooks/precompact.mjs +17 -0
- package/hooks/pretooluse.mjs +23 -0
- package/hooks/routing-block.mjs +0 -12
- package/hooks/run-hook.mjs +16 -3
- package/hooks/session-db.bundle.mjs +27 -18
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-helpers.mjs +101 -64
- package/hooks/sessionstart.mjs +51 -2
- package/hooks/vscode-copilot/posttooluse.mjs +1 -1
- package/hooks/vscode-copilot/precompact.mjs +3 -2
- package/hooks/vscode-copilot/sessionstart.mjs +9 -9
- package/openclaw.plugin.json +1 -1
- package/package.json +14 -8
- package/server.bundle.mjs +349 -147
- package/skills/UPSTREAM-CREDITS.md +0 -51
- package/skills/context-mode-ops/SKILL.md +0 -299
- package/skills/context-mode-ops/agent-teams.md +0 -198
- package/skills/context-mode-ops/communication.md +0 -224
- package/skills/context-mode-ops/marketing.md +0 -124
- package/skills/context-mode-ops/release.md +0 -214
- package/skills/context-mode-ops/review-pr.md +0 -269
- package/skills/context-mode-ops/tdd.md +0 -329
- package/skills/context-mode-ops/triage-issue.md +0 -266
- package/skills/context-mode-ops/validation.md +0 -307
- package/skills/diagnose/SKILL.md +0 -122
- package/skills/diagnose/scripts/hitl-loop.template.sh +0 -41
- package/skills/grill-me/SKILL.md +0 -15
- package/skills/grill-with-docs/ADR-FORMAT.md +0 -47
- package/skills/grill-with-docs/CONTEXT-FORMAT.md +0 -77
- package/skills/grill-with-docs/SKILL.md +0 -93
- package/skills/improve-codebase-architecture/DEEPENING.md +0 -37
- package/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +0 -44
- package/skills/improve-codebase-architecture/LANGUAGE.md +0 -53
- package/skills/improve-codebase-architecture/SKILL.md +0 -76
- package/skills/tdd/SKILL.md +0 -114
- package/skills/tdd/deep-modules.md +0 -33
- package/skills/tdd/interface-design.md +0 -31
- package/skills/tdd/mocking.md +0 -59
- package/skills/tdd/refactoring.md +0 -10
- package/skills/tdd/tests.md +0 -61
|
@@ -48,6 +48,59 @@ export interface McpToolUsageRow {
|
|
|
48
48
|
median_concurrency: number | null;
|
|
49
49
|
max_concurrency: number | null;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Conversation-scoped stats — aggregated from `session_events` for a single
|
|
53
|
+
* `session_id` across every worktree DB plus the compact-rescue snapshot from
|
|
54
|
+
* `session_resume`. Replaces the broken in-memory `tool_call_counter` that
|
|
55
|
+
* only saw `ctx_*` MCP calls and reset to 0 every time the MCP server PID
|
|
56
|
+
* changed (which is what made hours-of-work conversations show "1 call · 5 KB").
|
|
57
|
+
*/
|
|
58
|
+
export interface ConversationStats {
|
|
59
|
+
/** session_id this aggregate covers (the current Claude Code conversation). */
|
|
60
|
+
sessionId: string;
|
|
61
|
+
/** Total event count for this session_id, summed across all DBs. */
|
|
62
|
+
events: number;
|
|
63
|
+
/** Distinct DB files this session_id appeared in (a rotation indicator). */
|
|
64
|
+
dbCount: number;
|
|
65
|
+
/** Wall-clock days from first to last event. Captures real activity length. */
|
|
66
|
+
daysAlive: number;
|
|
67
|
+
/** Bytes restored from the compact snapshot for this session_id. 0 if no compact. */
|
|
68
|
+
snapshotBytes: number;
|
|
69
|
+
/** Number of compact snapshots consumed for this session_id. */
|
|
70
|
+
snapshotsConsumed: number;
|
|
71
|
+
/** Category breakdown for this session_id. */
|
|
72
|
+
byCategory: Array<{
|
|
73
|
+
category: string;
|
|
74
|
+
count: number;
|
|
75
|
+
label: string;
|
|
76
|
+
}>;
|
|
77
|
+
/**
|
|
78
|
+
* Earliest event timestamp (ms epoch) for this session_id across every DB.
|
|
79
|
+
* Used by the section-1 "started X" line in the narrative renderer. 0 when
|
|
80
|
+
* the session has no events yet. Optional for back-compat with older
|
|
81
|
+
* callers / fixtures that pre-date the narrative layout.
|
|
82
|
+
*/
|
|
83
|
+
firstEventMs?: number;
|
|
84
|
+
/** Latest event timestamp (ms epoch) — pairs with firstEventMs. */
|
|
85
|
+
lastEventMs?: number;
|
|
86
|
+
/**
|
|
87
|
+
* Wall-clock timestamp of the most recent /compact rescue for this session.
|
|
88
|
+
* Drives the "On <datetime>, /compact fired" line in section 1. Undefined
|
|
89
|
+
* when the conversation has never been compacted.
|
|
90
|
+
*/
|
|
91
|
+
lastRescueMs?: number;
|
|
92
|
+
/**
|
|
93
|
+
* Per-day capture breakdown for the section-1 horizontal timeline. Each
|
|
94
|
+
* entry is one calendar day (UTC midnight ms) with that day's event count
|
|
95
|
+
* + optional rescueBytes when /compact fired on that day. Empty array
|
|
96
|
+
* when no events recorded yet.
|
|
97
|
+
*/
|
|
98
|
+
byDay?: Array<{
|
|
99
|
+
ms: number;
|
|
100
|
+
count: number;
|
|
101
|
+
rescueBytes?: number;
|
|
102
|
+
}>;
|
|
103
|
+
}
|
|
51
104
|
/** Runtime stats tracked by server.ts during a live session. */
|
|
52
105
|
export interface RuntimeStats {
|
|
53
106
|
bytesReturned: Record<string, number>;
|
|
@@ -114,7 +167,15 @@ export interface FullReport {
|
|
|
114
167
|
}>;
|
|
115
168
|
};
|
|
116
169
|
}
|
|
117
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* Human-readable labels for event categories.
|
|
172
|
+
*
|
|
173
|
+
* Each label is a sentence-case phrase that reads like a benefit, not a
|
|
174
|
+
* column name. The user shouldn't see raw schema words like "external-ref"
|
|
175
|
+
* or "agent-finding" — those leak the database into the UX. When a new
|
|
176
|
+
* category lands without an entry here, the renderer falls through to the
|
|
177
|
+
* raw category id; that's a copy-debt signal, fix it here.
|
|
178
|
+
*/
|
|
118
179
|
export declare const categoryLabels: Record<string, string>;
|
|
119
180
|
/** Explains why each category matters for continuity. */
|
|
120
181
|
export declare const categoryHints: Record<string, string>;
|
|
@@ -177,6 +238,40 @@ export declare class AnalyticsEngine {
|
|
|
177
238
|
*/
|
|
178
239
|
queryAll(runtimeStats: RuntimeStats): FullReport;
|
|
179
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Where one adapter stores its context-mode sidecars on disk. Mirrors the
|
|
243
|
+
* map in `src/adapters/detect.ts:92-111` (`getSessionDirSegments`) so we
|
|
244
|
+
* never go out of sync as a single source of truth.
|
|
245
|
+
*
|
|
246
|
+
* `sessionsDir` = `<home>/<segments>/context-mode/sessions`
|
|
247
|
+
* `contentDir` = `<home>/<segments>/context-mode/content`
|
|
248
|
+
*
|
|
249
|
+
* Why duplicated here: `getSessionDirSegments` returns segments relative to
|
|
250
|
+
* `homedir()`; analytics needs the absolute joined paths for both `sessions`
|
|
251
|
+
* and `content` siblings. Keeping a parallel hard-coded list avoids importing
|
|
252
|
+
* detect.ts (which pulls in adapter loaders) into the stats path.
|
|
253
|
+
*/
|
|
254
|
+
export interface AdapterDirEntry {
|
|
255
|
+
/** Adapter id matching `src/adapters/detect.ts` PlatformId. */
|
|
256
|
+
name: string;
|
|
257
|
+
/** Absolute path to `<home>/<segments>/context-mode/sessions`. */
|
|
258
|
+
sessionsDir: string;
|
|
259
|
+
/** Absolute path to `<home>/<segments>/context-mode/content`. */
|
|
260
|
+
contentDir: string;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Enumerate every known adapter's sessions + content dirs under `home`.
|
|
264
|
+
* Used by `getMultiAdapterLifetimeStats` and `getMultiAdapterRealBytesStats`
|
|
265
|
+
* so a single call surfaces "your work everywhere on this machine across
|
|
266
|
+
* all AI tools" (the marketing line).
|
|
267
|
+
*
|
|
268
|
+
* Returns ALL 15 adapters even when the dir doesn't exist on disk — the
|
|
269
|
+
* scanner functions filter to existing dirs. That keeps the enumeration
|
|
270
|
+
* pure / testable without filesystem dependencies.
|
|
271
|
+
*/
|
|
272
|
+
export declare function enumerateAdapterDirs(opts?: {
|
|
273
|
+
home?: string;
|
|
274
|
+
}): AdapterDirEntry[];
|
|
180
275
|
/** Aggregated stats spanning every SessionDB + auto-memory under the user's profile. */
|
|
181
276
|
export interface LifetimeStats {
|
|
182
277
|
totalEvents: number;
|
|
@@ -192,6 +287,24 @@ export interface LifetimeStats {
|
|
|
192
287
|
* sidecar has any events. Optional for back-compat with older fixtures.
|
|
193
288
|
*/
|
|
194
289
|
categoryCounts: Record<string, number>;
|
|
290
|
+
/**
|
|
291
|
+
* Total bytes restored from compact-rescue snapshots across every DB on
|
|
292
|
+
* disk. Adds the rescue benefit to lifetime $ so the headline isn't
|
|
293
|
+
* silently undercounting the killer feature. 0 when no compact has fired
|
|
294
|
+
* or older fixtures don't pass this. Optional for back-compat with tests.
|
|
295
|
+
*/
|
|
296
|
+
rescueBytes?: number;
|
|
297
|
+
/**
|
|
298
|
+
* Earliest event timestamp (ms epoch) across every DB. Used for the
|
|
299
|
+
* "since 2026-04-14" lifetime narrative. 0 when unknown. Optional.
|
|
300
|
+
*/
|
|
301
|
+
firstEventMs?: number;
|
|
302
|
+
/**
|
|
303
|
+
* Distinct project_dir count across every DB. Different from
|
|
304
|
+
* `autoMemoryProjects` (which only counts dirs with auto-memory files).
|
|
305
|
+
* Captures every cwd context-mode has ever seen events for. Optional.
|
|
306
|
+
*/
|
|
307
|
+
distinctProjects?: number;
|
|
195
308
|
}
|
|
196
309
|
/**
|
|
197
310
|
* Aggregate lifetime stats from all SessionDB files in `sessionsDir` and
|
|
@@ -206,6 +319,258 @@ export declare function getLifetimeStats(opts?: {
|
|
|
206
319
|
/** Override for tests — defaults to db-base loadDatabase(). */
|
|
207
320
|
loadDatabase?: () => unknown;
|
|
208
321
|
}): LifetimeStats;
|
|
322
|
+
/**
|
|
323
|
+
* Aggregate every event for one `session_id` across all SessionDB files in
|
|
324
|
+
* `sessionsDir` plus the compact-rescue snapshot bytes from `session_resume`.
|
|
325
|
+
*
|
|
326
|
+
* Why this exists: the Claude Code session_id can persist across days while
|
|
327
|
+
* the underlying DB file rotates (size cap), and a compact-rescue snapshot
|
|
328
|
+
* carries hundreds of KB of context that would otherwise have been lost. The
|
|
329
|
+
* old in-memory `tool_call_counter` saw none of this — it counted only `ctx_*`
|
|
330
|
+
* MCP calls against the current MCP server PID and reset on every restart.
|
|
331
|
+
* Reading from `session_events` + `session_resume` is the source-of-truth
|
|
332
|
+
* version that matches what users actually experienced.
|
|
333
|
+
*/
|
|
334
|
+
export declare function getConversationStats(opts: {
|
|
335
|
+
sessionId: string;
|
|
336
|
+
sessionsDir?: string;
|
|
337
|
+
/** Optional worktree filename prefix (sha256(cwd)[:16]). When omitted, scans every DB. */
|
|
338
|
+
worktreeHash?: string;
|
|
339
|
+
loadDatabase?: () => unknown;
|
|
340
|
+
}): ConversationStats;
|
|
341
|
+
/**
|
|
342
|
+
* Real-bytes counter the renderer uses to replace the conservative
|
|
343
|
+
* `events × 256` token estimate. Reads four sources from disk and
|
|
344
|
+
* returns the sum the renderer divides by 4 to get tokens.
|
|
345
|
+
*
|
|
346
|
+
* - `eventDataBytes` = SUM(LENGTH(data)) FROM session_events
|
|
347
|
+
* - `bytesAvoided` = SUM(bytes_avoided) FROM session_events
|
|
348
|
+
* - `bytesReturned` = SUM(bytes_returned) FROM session_events
|
|
349
|
+
* - `snapshotBytes` = SUM(LENGTH(snapshot)) FROM session_resume
|
|
350
|
+
* - `totalSavedTokens` = (eventDataBytes + bytesAvoided + snapshotBytes) / 4
|
|
351
|
+
*
|
|
352
|
+
* `bytesReturned` is reported but NOT folded into `totalSavedTokens`
|
|
353
|
+
* because it represents bytes the model already paid for — adding it
|
|
354
|
+
* would double-count what's already on the user's invoice.
|
|
355
|
+
*/
|
|
356
|
+
export interface RealBytesStats {
|
|
357
|
+
eventDataBytes: number;
|
|
358
|
+
bytesAvoided: number;
|
|
359
|
+
bytesReturned: number;
|
|
360
|
+
snapshotBytes: number;
|
|
361
|
+
totalSavedTokens: number;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Compute real-bytes stats across one session, one project (worktree
|
|
365
|
+
* filter), or every session on disk (lifetime).
|
|
366
|
+
*
|
|
367
|
+
* - Pass `sessionId` for the conversation tier.
|
|
368
|
+
* - Pass `worktreeHash` to filter `*.db` files by name prefix
|
|
369
|
+
* (per-project lifetime — `sha256(cwd).slice(0, 16)`).
|
|
370
|
+
* - Pass neither — full lifetime aggregate.
|
|
371
|
+
*
|
|
372
|
+
* Best-effort: returns zeroes when the dir is missing, the DB is
|
|
373
|
+
* corrupt, or the session has no events. Never throws — same
|
|
374
|
+
* contract as `getConversationStats` / `getLifetimeStats` so the
|
|
375
|
+
* stats-render path can never crash on a bad sidecar.
|
|
376
|
+
*/
|
|
377
|
+
export declare function getRealBytesStats(opts: {
|
|
378
|
+
sessionId?: string;
|
|
379
|
+
sessionsDir?: string;
|
|
380
|
+
worktreeHash?: string;
|
|
381
|
+
loadDatabase?: () => unknown;
|
|
382
|
+
}): RealBytesStats;
|
|
383
|
+
/**
|
|
384
|
+
* Real-usage filter thresholds. Decided in the B3a /diagnose conversation
|
|
385
|
+
* to suppress fixture-noise dirs (test runs that touched ~/.X but never
|
|
386
|
+
* carried real user work).
|
|
387
|
+
*
|
|
388
|
+
* An adapter is `isReal=true` iff ALL four hold:
|
|
389
|
+
* eventCount >= 100
|
|
390
|
+
* distinctProjects >= 5
|
|
391
|
+
* lastActivity within 30 days
|
|
392
|
+
* avgEventBytes >= 50
|
|
393
|
+
*
|
|
394
|
+
* Tuneable via `getMultiAdapterLifetimeStats({ filter })` for testing.
|
|
395
|
+
*/
|
|
396
|
+
export interface RealUsageFilter {
|
|
397
|
+
minEvents?: number;
|
|
398
|
+
minProjects?: number;
|
|
399
|
+
recencyMs?: number;
|
|
400
|
+
minAvgBytes?: number;
|
|
401
|
+
/** Fixed "now" timestamp for deterministic testing. Defaults to Date.now(). */
|
|
402
|
+
nowMs?: number;
|
|
403
|
+
}
|
|
404
|
+
/** Per-adapter scan result returned by {@link scanOneAdapter}. */
|
|
405
|
+
export interface AdapterScanResult {
|
|
406
|
+
/** Adapter id (matches `enumerateAdapterDirs().name`). */
|
|
407
|
+
name: string;
|
|
408
|
+
/** Total event rows across every `*.db` in this adapter's sessions dir. */
|
|
409
|
+
eventCount: number;
|
|
410
|
+
/** Total distinct session_meta rows across every db. */
|
|
411
|
+
sessionCount: number;
|
|
412
|
+
/** Sum of LENGTH(data) across every session_event row. */
|
|
413
|
+
dataBytes: number;
|
|
414
|
+
/** Sum of LENGTH(snapshot) across consumed compact-rescue snapshots. */
|
|
415
|
+
rescueBytes: number;
|
|
416
|
+
/** Reserved for future content/ scan (B3b). 0 today. */
|
|
417
|
+
contentBytes: number;
|
|
418
|
+
/** Distinct session_id count across all dbs (alias of sessionCount). */
|
|
419
|
+
uuidConvs: number;
|
|
420
|
+
/** Distinct project_dir values across all session_events. */
|
|
421
|
+
projectDirs: string[];
|
|
422
|
+
/** Earliest event ms epoch (Number.POSITIVE_INFINITY when no events). */
|
|
423
|
+
firstMs: number;
|
|
424
|
+
/** Latest event ms epoch (0 when no events). */
|
|
425
|
+
lastMs: number;
|
|
426
|
+
/** Real-usage flag — see {@link RealUsageFilter}. */
|
|
427
|
+
isReal: boolean;
|
|
428
|
+
}
|
|
429
|
+
/** Aggregated multi-adapter lifetime stats. */
|
|
430
|
+
export interface MultiAdapterLifetimeStats {
|
|
431
|
+
/** Sum of eventCount across every adapter that exists on disk. */
|
|
432
|
+
totalEvents: number;
|
|
433
|
+
/** Sum of sessionCount across every adapter. */
|
|
434
|
+
totalSessions: number;
|
|
435
|
+
/** Sum of dataBytes + rescueBytes across every adapter. */
|
|
436
|
+
totalBytes: number;
|
|
437
|
+
/** Per-adapter rows for adapters that have >= one .db file. */
|
|
438
|
+
perAdapter: AdapterScanResult[];
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Aggregate lifetime stats across every adapter dir under `home`.
|
|
442
|
+
* The marketing line — "your work everywhere on this machine across all
|
|
443
|
+
* AI tools" — depends on this. Existing `getLifetimeStats` (single dir)
|
|
444
|
+
* is untouched; this is purely additive.
|
|
445
|
+
*/
|
|
446
|
+
export declare function getMultiAdapterLifetimeStats(opts?: {
|
|
447
|
+
home?: string;
|
|
448
|
+
loadDatabase?: () => unknown;
|
|
449
|
+
filter?: RealUsageFilter;
|
|
450
|
+
}): MultiAdapterLifetimeStats;
|
|
451
|
+
/** Aggregated multi-adapter real-bytes stats. */
|
|
452
|
+
export interface MultiAdapterRealBytesStats extends RealBytesStats {
|
|
453
|
+
/** Per-adapter row in the same shape as {@link RealBytesStats}, keyed by name. */
|
|
454
|
+
perAdapter: Array<RealBytesStats & {
|
|
455
|
+
name: string;
|
|
456
|
+
}>;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Aggregate real-bytes stats across every adapter dir under `home`.
|
|
460
|
+
* Mirrors `getRealBytesStats` (single dir, analytics.ts:887-989) but
|
|
461
|
+
* iterates {@link enumerateAdapterDirs}. Optional `sessionId` /
|
|
462
|
+
* `worktreeHash` filters apply uniformly to every dir.
|
|
463
|
+
*/
|
|
464
|
+
export declare function getMultiAdapterRealBytesStats(opts?: {
|
|
465
|
+
home?: string;
|
|
466
|
+
sessionId?: string;
|
|
467
|
+
worktreeHash?: string;
|
|
468
|
+
loadDatabase?: () => unknown;
|
|
469
|
+
}): MultiAdapterRealBytesStats;
|
|
470
|
+
/**
|
|
471
|
+
* Marketing-grade labels for auto-memory file prefixes. The renderer sees raw
|
|
472
|
+
* filename prefixes (`project_codex_hooks.md` → `project`) — without this map
|
|
473
|
+
* the user gets schema words in the UI, which leaks the database into UX.
|
|
474
|
+
*/
|
|
475
|
+
export declare const autoMemoryLabels: Record<string, string>;
|
|
476
|
+
/**
|
|
477
|
+
* Marketing-grade labels for adapter ids surfaced by
|
|
478
|
+
* {@link enumerateAdapterDirs} / {@link getMultiAdapterLifetimeStats}.
|
|
479
|
+
* The renderer never shows raw IDs — UX uses the names users see in
|
|
480
|
+
* each tool's own surface area.
|
|
481
|
+
*/
|
|
482
|
+
export declare const adapterLabels: Record<string, string>;
|
|
483
|
+
/**
|
|
484
|
+
* Locale + IANA-timezone detection for the narrative renderer.
|
|
485
|
+
*
|
|
486
|
+
* Cascade (each level overrides the next):
|
|
487
|
+
* 1. CONTEXT_MODE_LOCALE / CONTEXT_MODE_TZ env overrides
|
|
488
|
+
* (used by tests + by users who want to pin output regardless of OS).
|
|
489
|
+
* 2. macOS `defaults read -g AppleLocale` → `en_TR` style → `en-TR`.
|
|
490
|
+
* 3. Linux `LANG` / `LC_TIME` env vars.
|
|
491
|
+
* 4. Fallback: `Intl.DateTimeFormat().resolvedOptions().locale`.
|
|
492
|
+
*
|
|
493
|
+
* Timezone always uses `Intl.DateTimeFormat().resolvedOptions().timeZone`
|
|
494
|
+
* — that one's always available and correct regardless of platform.
|
|
495
|
+
*/
|
|
496
|
+
export declare function detectLocaleAndTz(): {
|
|
497
|
+
locale: string;
|
|
498
|
+
tz: string;
|
|
499
|
+
};
|
|
500
|
+
/**
|
|
501
|
+
* Render the section-4 "For example: what would that cost?" block.
|
|
502
|
+
*
|
|
503
|
+
* Translates a lifetime token total into a relatable Opus-4 dollar figure
|
|
504
|
+
* + 3 tangible comparisons (Cursor Pro / Claude Max / weekends of API
|
|
505
|
+
* coding) + 10-dev team scale projection + alternate-model scale row,
|
|
506
|
+
* capped with an EXAMPLES disclaimer. The renderer is intentionally
|
|
507
|
+
* liberal with rounding (whole-month Cursor counts, integer weekends)
|
|
508
|
+
* because this section is illustrative — the EXAMPLES line tells users
|
|
509
|
+
* not to confuse it for a bill.
|
|
510
|
+
*
|
|
511
|
+
* Returns [] when there's nothing to scale (lifetimeTokens === 0) so
|
|
512
|
+
* the section disappears cleanly on a fresh install.
|
|
513
|
+
*
|
|
514
|
+
* Math constants:
|
|
515
|
+
* Opus 4 = $15.00 per 1M input tokens (matches OPUS_INPUT_PRICE_PER_TOKEN)
|
|
516
|
+
* Sonnet 4 = $3.00 per 1M input tokens
|
|
517
|
+
* GPT-4o = $2.50 per 1M input tokens
|
|
518
|
+
* Gemini 2 = $1.25 per 1M input tokens
|
|
519
|
+
* Haiku 4 = $0.80 per 1M input tokens
|
|
520
|
+
* Cursor Pro = $20 / month → "X months of Cursor Pro"
|
|
521
|
+
* Claude Max = $200 / month → "X.X months of Claude Max"
|
|
522
|
+
* Weekend coding ≈ $73.67 → "X weekends of nonstop API coding"
|
|
523
|
+
* Team multiplier = 10× → "At a 10-dev team scale: ~$X over Y days, or ~$Z/year"
|
|
524
|
+
*/
|
|
525
|
+
export declare function renderCostExample(lifetimeBytes: number, lifetimeTokens: number, lifetimeDays: number): string[];
|
|
526
|
+
/**
|
|
527
|
+
* One day on the horizontal narrative timeline. `ms` is midnight-UTC of
|
|
528
|
+
* the day (caller is responsible for normalising); `count` is captures
|
|
529
|
+
* for that day; `rescueBytes` (when >0) overlays the ◆ /compact glyph.
|
|
530
|
+
*/
|
|
531
|
+
export interface TimelineDay {
|
|
532
|
+
ms: number;
|
|
533
|
+
count: number;
|
|
534
|
+
rescueBytes?: number;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Render the proportional-spacing horizontal day strip used in section 1
|
|
538
|
+
* of the 5-section narrative. Returns the lines verbatim ready to splice
|
|
539
|
+
* into the formatReport line buffer:
|
|
540
|
+
*
|
|
541
|
+
* apr 28 ●──────────────────────●────█──────────────────────◆────● may 10
|
|
542
|
+
*
|
|
543
|
+
* apr 28 277 captures
|
|
544
|
+
* may 4 438 captures ← peak
|
|
545
|
+
* may 9 261 captures ◆ /compact rescued 1552 KB
|
|
546
|
+
* may 10 100 captures
|
|
547
|
+
*
|
|
548
|
+
* ● active day █ peak day ◆ /compact rescue
|
|
549
|
+
*
|
|
550
|
+
* The strip body is exactly 56 chars wide. Day positions are computed as
|
|
551
|
+
* `round((day - first) / (last - first) * 55)`. Glyph priority for a
|
|
552
|
+
* column: rescue (◆) > peak (█) > active (●). Filler is the box-drawing
|
|
553
|
+
* `─` character so the strip reads cleanly in monospace terminals.
|
|
554
|
+
*/
|
|
555
|
+
export declare function renderHorizontalTimeline(days: TimelineDay[], locale: string, tz: string): string[];
|
|
556
|
+
/**
|
|
557
|
+
* Render a UTC ms timestamp as a human-readable local datetime string in
|
|
558
|
+
* the canonical Mert-approved format:
|
|
559
|
+
*
|
|
560
|
+
* "28 Apr 2026 at 12:16 (Europe/Istanbul)"
|
|
561
|
+
*
|
|
562
|
+
* Used by the 5-section narrative renderer (formatReport) so users see
|
|
563
|
+
* exactly when their conversation started + when /compact rescues fired
|
|
564
|
+
* in their wall-clock timezone — never UTC, never ambiguous.
|
|
565
|
+
*
|
|
566
|
+
* - 24-hour clock with zero-padded minutes ("20:54", not "8:54 PM").
|
|
567
|
+
* - Day is NOT zero-padded ("9 May", not "09 May") to match the target.
|
|
568
|
+
* - IANA timezone is appended verbatim in parentheses regardless of
|
|
569
|
+
* locale so users never misread Istanbul-time as UTC.
|
|
570
|
+
* - Returns "" for ms === 0 or NaN so callers can guard the rendered
|
|
571
|
+
* line ("started …") without an extra timestamp-validity check.
|
|
572
|
+
*/
|
|
573
|
+
export declare function formatLocalDateTime(ms: number, locale: string, tz: string): string;
|
|
209
574
|
/** Opus 4 input price: $15 per 1M tokens. */
|
|
210
575
|
export declare const OPUS_INPUT_PRICE_PER_TOKEN: number;
|
|
211
576
|
/** Convert a token count to a USD string at the Opus input rate. */
|
|
@@ -223,4 +588,42 @@ export declare function tokensToUsd(tokens: number): string;
|
|
|
223
588
|
export declare function formatReport(report: FullReport, version?: string, latestVersion?: string | null, opts?: {
|
|
224
589
|
lifetime?: LifetimeStats;
|
|
225
590
|
mcpUsage?: McpToolUsageRow[];
|
|
591
|
+
conversation?: ConversationStats;
|
|
592
|
+
/**
|
|
593
|
+
* Phase 8 of D2 PRD — pass realBytes pre-aggregated from
|
|
594
|
+
* `getRealBytesStats(...)` and the renderer will use those numbers
|
|
595
|
+
* for the $ math instead of the conservative `events × 256` estimate.
|
|
596
|
+
*
|
|
597
|
+
* - `realBytes.lifetime` overrides `lifetimeTokensWithout`.
|
|
598
|
+
* - `realBytes.conversation` overrides `conversationTokens`.
|
|
599
|
+
* - Either may be omitted independently — missing values fall back
|
|
600
|
+
* to the legacy estimate so this feature can never produce
|
|
601
|
+
* a smaller number than before (Mert: stats only go up).
|
|
602
|
+
* - When the new value is SMALLER than the legacy estimate (fresh
|
|
603
|
+
* sessions before any sandbox events emit), we keep the larger
|
|
604
|
+
* number to honour the same monotonic-growth invariant.
|
|
605
|
+
*/
|
|
606
|
+
realBytes?: {
|
|
607
|
+
lifetime?: RealBytesStats;
|
|
608
|
+
conversation?: RealBytesStats;
|
|
609
|
+
};
|
|
610
|
+
/**
|
|
611
|
+
* B3b — multi-adapter aggregation surfaced by
|
|
612
|
+
* `getMultiAdapterLifetimeStats(...)` (analytics.ts:1248). When present,
|
|
613
|
+
* the renderer adds a "Where it came from" sub-block under the receipt,
|
|
614
|
+
* promotes the headline to "across N AI tools" when >= 2 real adapters
|
|
615
|
+
* are detected, and renames the all-work block to "All your work
|
|
616
|
+
* everywhere". Backward compat: omitting this opt preserves the legacy
|
|
617
|
+
* single-adapter renderer output unchanged.
|
|
618
|
+
*/
|
|
619
|
+
multiAdapter?: MultiAdapterLifetimeStats;
|
|
620
|
+
/**
|
|
621
|
+
* 5-section narrative renderer overrides. Defaults to ambient
|
|
622
|
+
* `process.cwd()` + `Date.now()` + `detectLocaleAndTz()` for production
|
|
623
|
+
* use; tests inject deterministic values so output is byte-stable.
|
|
624
|
+
*/
|
|
625
|
+
cwd?: string;
|
|
626
|
+
now?: number;
|
|
627
|
+
locale?: string;
|
|
628
|
+
tz?: string;
|
|
226
629
|
}): string;
|