@nanonets/graft 0.13.0 → 0.16.0
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 +26 -10
- package/dist/ai/crux.d.ts +14 -1
- package/dist/ai/crux.d.ts.map +1 -1
- package/dist/ai/crux.js +39 -51
- package/dist/ai/crux.js.map +1 -1
- package/dist/ai/failure.d.ts +13 -2
- package/dist/ai/failure.d.ts.map +1 -1
- package/dist/ai/failure.js +16 -4
- package/dist/ai/failure.js.map +1 -1
- package/dist/ai/llm/factory.d.ts +10 -1
- package/dist/ai/llm/factory.d.ts.map +1 -1
- package/dist/ai/llm/factory.js +16 -0
- package/dist/ai/llm/factory.js.map +1 -1
- package/dist/ai/llm/litellm.d.ts +34 -0
- package/dist/ai/llm/litellm.d.ts.map +1 -0
- package/dist/ai/llm/litellm.js +45 -0
- package/dist/ai/llm/litellm.js.map +1 -0
- package/dist/ai/llm/openai.d.ts.map +1 -1
- package/dist/ai/llm/openai.js +66 -7
- package/dist/ai/llm/openai.js.map +1 -1
- package/dist/ai/llm/orcarouter.d.ts +34 -0
- package/dist/ai/llm/orcarouter.d.ts.map +1 -0
- package/dist/ai/llm/orcarouter.js +45 -0
- package/dist/ai/llm/orcarouter.js.map +1 -0
- package/dist/ai/llm/recover-tool.d.ts +19 -0
- package/dist/ai/llm/recover-tool.d.ts.map +1 -0
- package/dist/ai/llm/recover-tool.js +109 -0
- package/dist/ai/llm/recover-tool.js.map +1 -0
- package/dist/ai/providers.d.ts.map +1 -1
- package/dist/ai/providers.js +15 -3
- package/dist/ai/providers.js.map +1 -1
- package/dist/ai/synthesize.d.ts.map +1 -1
- package/dist/ai/synthesize.js +19 -5
- package/dist/ai/synthesize.js.map +1 -1
- package/dist/app/checkout.d.ts +28 -0
- package/dist/app/checkout.d.ts.map +1 -0
- package/dist/app/checkout.js +100 -0
- package/dist/app/checkout.js.map +1 -0
- package/dist/app/events.d.ts +37 -0
- package/dist/app/events.d.ts.map +1 -0
- package/dist/app/events.js +59 -0
- package/dist/app/events.js.map +1 -0
- package/dist/app/identity.d.ts +54 -0
- package/dist/app/identity.d.ts.map +1 -0
- package/dist/app/identity.js +96 -0
- package/dist/app/identity.js.map +1 -0
- package/dist/app/main.d.ts +2 -0
- package/dist/app/main.d.ts.map +1 -0
- package/dist/app/main.js +56 -0
- package/dist/app/main.js.map +1 -0
- package/dist/app/pages.d.ts +39 -0
- package/dist/app/pages.d.ts.map +1 -0
- package/dist/app/pages.js +86 -0
- package/dist/app/pages.js.map +1 -0
- package/dist/app/queue.d.ts +40 -0
- package/dist/app/queue.d.ts.map +1 -0
- package/dist/app/queue.js +68 -0
- package/dist/app/queue.js.map +1 -0
- package/dist/app/review.d.ts +23 -0
- package/dist/app/review.d.ts.map +1 -0
- package/dist/app/review.js +139 -0
- package/dist/app/review.js.map +1 -0
- package/dist/app/server.d.ts +40 -0
- package/dist/app/server.d.ts.map +1 -0
- package/dist/app/server.js +117 -0
- package/dist/app/server.js.map +1 -0
- package/dist/ask/ask.d.ts +35 -0
- package/dist/ask/ask.d.ts.map +1 -1
- package/dist/ask/ask.js +427 -63
- package/dist/ask/ask.js.map +1 -1
- package/dist/ask/file-rank.d.ts +72 -0
- package/dist/ask/file-rank.d.ts.map +1 -0
- package/dist/ask/file-rank.js +160 -0
- package/dist/ask/file-rank.js.map +1 -0
- package/dist/ask/file-selection.d.ts +4 -0
- package/dist/ask/file-selection.d.ts.map +1 -1
- package/dist/ask/file-selection.js +25 -0
- package/dist/ask/file-selection.js.map +1 -1
- package/dist/ask/fuse.d.ts +16 -0
- package/dist/ask/fuse.d.ts.map +1 -1
- package/dist/ask/fuse.js +21 -4
- package/dist/ask/fuse.js.map +1 -1
- package/dist/ask/graphrank.d.ts +18 -0
- package/dist/ask/graphrank.d.ts.map +1 -1
- package/dist/ask/graphrank.js +65 -19
- package/dist/ask/graphrank.js.map +1 -1
- package/dist/blast/blast-cli.d.ts +5 -0
- package/dist/blast/blast-cli.d.ts.map +1 -1
- package/dist/blast/blast-cli.js +21 -30
- package/dist/blast/blast-cli.js.map +1 -1
- package/dist/blast/blast.d.ts +9 -0
- package/dist/blast/blast.d.ts.map +1 -1
- package/dist/blast/blast.js.map +1 -1
- package/dist/blast/name.d.ts +13 -0
- package/dist/blast/name.d.ts.map +1 -1
- package/dist/blast/name.js +36 -0
- package/dist/blast/name.js.map +1 -1
- package/dist/blast/owners.d.ts +85 -0
- package/dist/blast/owners.d.ts.map +1 -0
- package/dist/blast/owners.js +272 -0
- package/dist/blast/owners.js.map +1 -0
- package/dist/blast/render.d.ts.map +1 -1
- package/dist/blast/render.js +105 -0
- package/dist/blast/render.js.map +1 -1
- package/dist/blast/viz.d.ts.map +1 -1
- package/dist/blast/viz.js +10 -0
- package/dist/blast/viz.js.map +1 -1
- package/dist/claude/format.js +1 -1
- package/dist/claude/format.js.map +1 -1
- package/dist/claude/hooks.d.ts.map +1 -1
- package/dist/claude/hooks.js +132 -22
- package/dist/claude/hooks.js.map +1 -1
- package/dist/claude/init.d.ts +1 -0
- package/dist/claude/init.d.ts.map +1 -1
- package/dist/claude/init.js +1 -1
- package/dist/claude/init.js.map +1 -1
- package/dist/claude/session-metrics.d.ts +63 -0
- package/dist/claude/session-metrics.d.ts.map +1 -0
- package/dist/claude/session-metrics.js +181 -0
- package/dist/claude/session-metrics.js.map +1 -0
- package/dist/claude/settings-merge.d.ts +18 -1
- package/dist/claude/settings-merge.d.ts.map +1 -1
- package/dist/claude/settings-merge.js +70 -27
- package/dist/claude/settings-merge.js.map +1 -1
- package/dist/claude/state.d.ts +25 -0
- package/dist/claude/state.d.ts.map +1 -1
- package/dist/claude/state.js +14 -1
- package/dist/claude/state.js.map +1 -1
- package/dist/claude/statusline.d.ts +5 -3
- package/dist/claude/statusline.d.ts.map +1 -1
- package/dist/claude/statusline.js +6 -4
- package/dist/claude/statusline.js.map +1 -1
- package/dist/claude/tally.d.ts +23 -0
- package/dist/claude/tally.d.ts.map +1 -0
- package/dist/claude/tally.js +138 -0
- package/dist/claude/tally.js.map +1 -0
- package/dist/cli.js +145 -4
- package/dist/cli.js.map +1 -1
- package/dist/context/build.d.ts +11 -0
- package/dist/context/build.d.ts.map +1 -1
- package/dist/context/build.js +49 -9
- package/dist/context/build.js.map +1 -1
- package/dist/context/check.d.ts.map +1 -1
- package/dist/context/check.js +5 -12
- package/dist/context/check.js.map +1 -1
- package/dist/context/node-file.d.ts +2 -1
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +70 -3
- package/dist/context/node-file.js.map +1 -1
- package/dist/context/savings.d.ts +8 -0
- package/dist/context/savings.d.ts.map +1 -1
- package/dist/context/savings.js +14 -0
- package/dist/context/savings.js.map +1 -1
- package/dist/engine.d.ts +4 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +2 -0
- package/dist/engine.js.map +1 -1
- package/dist/graph/bindings.d.ts +6 -5
- package/dist/graph/bindings.d.ts.map +1 -1
- package/dist/graph/bindings.js +117 -5
- package/dist/graph/bindings.js.map +1 -1
- package/dist/graph/build.d.ts +4 -0
- package/dist/graph/build.d.ts.map +1 -1
- package/dist/graph/build.js +7 -4
- package/dist/graph/build.js.map +1 -1
- package/dist/graph/cards.d.ts +4 -3
- package/dist/graph/cards.d.ts.map +1 -1
- package/dist/graph/cards.js +34 -6
- package/dist/graph/cards.js.map +1 -1
- package/dist/graph/check.d.ts.map +1 -1
- package/dist/graph/check.js +36 -6
- package/dist/graph/check.js.map +1 -1
- package/dist/graph/enrich.d.ts +4 -4
- package/dist/graph/enrich.d.ts.map +1 -1
- package/dist/graph/enrich.js +32 -6
- package/dist/graph/enrich.js.map +1 -1
- package/dist/graph/extract.d.ts +9 -2
- package/dist/graph/extract.d.ts.map +1 -1
- package/dist/graph/extract.js +356 -12
- package/dist/graph/extract.js.map +1 -1
- package/dist/graph/fingerprint.d.ts +6 -1
- package/dist/graph/fingerprint.d.ts.map +1 -1
- package/dist/graph/fingerprint.js +7 -3
- package/dist/graph/fingerprint.js.map +1 -1
- package/dist/graph/generic.d.ts +10 -0
- package/dist/graph/generic.d.ts.map +1 -1
- package/dist/graph/generic.js +20 -3
- package/dist/graph/generic.js.map +1 -1
- package/dist/graph/invariants.d.ts +3 -2
- package/dist/graph/invariants.d.ts.map +1 -1
- package/dist/graph/invariants.js +6 -3
- package/dist/graph/invariants.js.map +1 -1
- package/dist/graph/refresh.d.ts.map +1 -1
- package/dist/graph/refresh.js +6 -2
- package/dist/graph/refresh.js.map +1 -1
- package/dist/graph/resolve.d.ts +1 -1
- package/dist/graph/resolve.d.ts.map +1 -1
- package/dist/graph/resolve.js +106 -5
- package/dist/graph/resolve.js.map +1 -1
- package/dist/graph/scopes.d.ts.map +1 -1
- package/dist/graph/scopes.js +2 -1
- package/dist/graph/scopes.js.map +1 -1
- package/dist/graph/source-files.d.ts +7 -2
- package/dist/graph/source-files.d.ts.map +1 -1
- package/dist/graph/source-files.js +19 -6
- package/dist/graph/source-files.js.map +1 -1
- package/dist/graph/workspace-cli.d.ts +2 -0
- package/dist/graph/workspace-cli.d.ts.map +1 -1
- package/dist/graph/workspace-cli.js +3 -0
- package/dist/graph/workspace-cli.js.map +1 -1
- package/dist/graph/workspace.d.ts +2 -0
- package/dist/graph/workspace.d.ts.map +1 -1
- package/dist/graph/workspace.js +173 -6
- package/dist/graph/workspace.js.map +1 -1
- package/dist/hosts/antigravity.d.ts +2 -2
- package/dist/hosts/antigravity.d.ts.map +1 -1
- package/dist/hosts/codex-hooks.d.ts +2 -6
- package/dist/hosts/codex-hooks.d.ts.map +1 -1
- package/dist/hosts/codex-hooks.js +7 -30
- package/dist/hosts/codex-hooks.js.map +1 -1
- package/dist/hosts/config-write.d.ts +36 -0
- package/dist/hosts/config-write.d.ts.map +1 -0
- package/dist/hosts/config-write.js +63 -0
- package/dist/hosts/config-write.js.map +1 -0
- package/dist/hosts/cursor-hooks.d.ts +18 -0
- package/dist/hosts/cursor-hooks.d.ts.map +1 -0
- package/dist/hosts/cursor-hooks.js +100 -0
- package/dist/hosts/cursor-hooks.js.map +1 -0
- package/dist/hosts/init.d.ts +2 -2
- package/dist/hosts/init.d.ts.map +1 -1
- package/dist/hosts/init.js +8 -2
- package/dist/hosts/init.js.map +1 -1
- package/dist/hosts/mcp-config.d.ts +15 -5
- package/dist/hosts/mcp-config.d.ts.map +1 -1
- package/dist/hosts/mcp-config.js +51 -15
- package/dist/hosts/mcp-config.js.map +1 -1
- package/dist/hosts/plan.d.ts.map +1 -1
- package/dist/hosts/plan.js +2 -0
- package/dist/hosts/plan.js.map +1 -1
- package/dist/hosts/retract.d.ts +47 -0
- package/dist/hosts/retract.d.ts.map +1 -0
- package/dist/hosts/retract.js +473 -0
- package/dist/hosts/retract.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/ingest/fs.d.ts +39 -5
- package/dist/ingest/fs.d.ts.map +1 -1
- package/dist/ingest/fs.js +104 -11
- package/dist/ingest/fs.js.map +1 -1
- package/dist/mcp/tool-names.d.ts +20 -0
- package/dist/mcp/tool-names.d.ts.map +1 -0
- package/dist/mcp/tool-names.js +36 -0
- package/dist/mcp/tool-names.js.map +1 -0
- package/dist/mcp/tools.d.ts +11 -2
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +9 -21
- package/dist/mcp/tools.js.map +1 -1
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +17 -3
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/telemetry/flush.d.ts.map +1 -1
- package/dist/telemetry/flush.js +1 -0
- package/dist/telemetry/flush.js.map +1 -1
- package/dist/telemetry/identity.d.ts +7 -0
- package/dist/telemetry/identity.d.ts.map +1 -1
- package/dist/telemetry/identity.js.map +1 -1
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +2 -2
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/sessions.d.ts +19 -2
- package/dist/telemetry/sessions.d.ts.map +1 -1
- package/dist/telemetry/sessions.js +57 -34
- package/dist/telemetry/sessions.js.map +1 -1
- package/dist/telemetry/track.d.ts +22 -3
- package/dist/telemetry/track.d.ts.map +1 -1
- package/dist/telemetry/track.js +34 -5
- package/dist/telemetry/track.js.map +1 -1
- package/dist/upkeep-run.js +1 -1
- package/dist/upkeep-run.js.map +1 -1
- package/dist/upkeep.d.ts +7 -4
- package/dist/upkeep.d.ts.map +1 -1
- package/dist/upkeep.js +2 -1
- package/dist/upkeep.js.map +1 -1
- package/dist/util/state.d.ts +12 -2
- package/dist/util/state.d.ts.map +1 -1
- package/dist/util/state.js +4 -0
- package/dist/util/state.js.map +1 -1
- package/dist/viewer/app.js +2 -1
- package/dist/viewer/style.css +14 -0
- package/dist/viz/assemble.d.ts +17 -0
- package/dist/viz/assemble.d.ts.map +1 -1
- package/dist/viz/assemble.js.map +1 -1
- package/package.json +12 -4
- package/scripts/graph-quality.mjs +3 -3
- package/scripts/postinstall.mjs +42 -3
- package/scripts/run-tests.mjs +14 -0
- package/scripts/tally-audit.mjs +140 -0
- package/dist/ai/llm/claude-cli.d.ts +0 -60
- package/dist/ai/llm/claude-cli.d.ts.map +0 -1
- package/dist/ai/llm/claude-cli.js +0 -417
- package/dist/ai/llm/claude-cli.js.map +0 -1
- package/dist/util/num.d.ts +0 -3
- package/dist/util/num.d.ts.map +0 -1
- package/dist/util/num.js +0 -20
- package/dist/util/num.js.map +0 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session usage accounting, shared across every host (Claude Code, Cursor, and
|
|
3
|
+
* plain MCP). It answers the one question the README rests on: when an agent had
|
|
4
|
+
* both graft and grep in front of it, which did it reach for, and how much did
|
|
5
|
+
* that save?
|
|
6
|
+
*
|
|
7
|
+
* The counters (`graftReads`, `sourceReads`, `savedTokens`) already live on
|
|
8
|
+
* {@link SessionState}; before this module nothing ever incremented the first
|
|
9
|
+
* two, so `session_summary` telemetry shipped 0/0 for every session. Everything
|
|
10
|
+
* here is a pure classify + read-modify-write over `graft/.cache/session/`, so a
|
|
11
|
+
* host adapter is a few lines: parse its payload, call {@link recordToolUse}.
|
|
12
|
+
*
|
|
13
|
+
* Two payload shapes feed it, confirmed against the vendor docs rather than
|
|
14
|
+
* guessed (the matcher depends on getting the names right):
|
|
15
|
+
*
|
|
16
|
+
* - Claude Code `PostToolUse` — `{ tool_name, tool_input: { command }, tool_response }`.
|
|
17
|
+
* A graft retrieval prints a `[graft] tokens saved ≈ N` footer into
|
|
18
|
+
* `tool_response`, which is itself proof graft ran.
|
|
19
|
+
* - Cursor `postToolUse` — `{ tool_name, tool_input, tool_output, conversation_id }`
|
|
20
|
+
* (https://cursor.com/docs/hooks). `tool_output` is the JSON-stringified
|
|
21
|
+
* result; a Shell `graft …` call carries the same footer inside its stdout.
|
|
22
|
+
* - Cursor `afterMCPExecution` — `{ tool_name, tool_input, result_json, duration }`.
|
|
23
|
+
* Fires only for MCP tools, so a graft tool is recognised by its name and
|
|
24
|
+
* its savings read out of `result_json`.
|
|
25
|
+
*
|
|
26
|
+
* MCP tool names arrive host-prefixed in different shapes (`graft_find_code`,
|
|
27
|
+
* `MCP:graft_find_code`, `mcp__graft__graft_find_code`). {@link isGraftMcpTool}
|
|
28
|
+
* strips the prefix to the last delimited segment and checks it against the real
|
|
29
|
+
* name list (`GRAFT_MCP_TOOL_NAMES`), so a graft tool is recognised across every
|
|
30
|
+
* host format while a third-party tool that merely contains "graft" is not.
|
|
31
|
+
*/
|
|
32
|
+
import { statSync } from 'node:fs';
|
|
33
|
+
import { join } from 'node:path';
|
|
34
|
+
import { sumSavingsFooters } from '../context/savings.js';
|
|
35
|
+
import { readSession, writeSession, sessionDir, listSessionIds } from './state.js';
|
|
36
|
+
import { GRAFT_MCP_TOOL_NAMES } from '../mcp/tool-names.js';
|
|
37
|
+
/** Native tools that mean "the agent went to read source itself" — the grep/read
|
|
38
|
+
* path graft is meant to replace. These are the host tool NAMES (Claude Code and
|
|
39
|
+
* Cursor both use Read/Grep/Glob; Cursor adds Search); shell builtins like `ls`
|
|
40
|
+
* are commands, never tool names, so they belong to the Shell branch, not here.
|
|
41
|
+
* Compared lower-cased. */
|
|
42
|
+
const SOURCE_TOOLS = new Set(['read', 'grep', 'glob', 'search']);
|
|
43
|
+
/** The host's shell tool, whose `command` we then inspect for a graft invocation. */
|
|
44
|
+
function isShellTool(t) {
|
|
45
|
+
return t === 'bash' || t === 'shell';
|
|
46
|
+
}
|
|
47
|
+
/** An MCP-style tool name (server-prefixed), as opposed to a native Read/Shell.
|
|
48
|
+
* Used to hand graft MCP calls to the dedicated MCP hook so a host that fires
|
|
49
|
+
* BOTH a generic post-tool hook and an MCP hook can't double-count them. */
|
|
50
|
+
export function isMcpToolName(toolName) {
|
|
51
|
+
const t = toolName.toLowerCase();
|
|
52
|
+
return t.startsWith('mcp') || t.includes(':') || t.includes('__');
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Whether a tool name is one of graft's MCP tools, across every host prefix.
|
|
56
|
+
* Anchored on the real name list (`graft_find_code`, its legacy aliases, …), not
|
|
57
|
+
* a loose `includes('graft')`: a third-party MCP tool whose name merely contains
|
|
58
|
+
* "graft" (or a repo working on graft itself) must not count as a graft read —
|
|
59
|
+
* the same care `commandInvokesGraft` takes for the CLI side. Strips the host
|
|
60
|
+
* prefix first (`mcp__graft__graft_find_code`, `MCP:graft_find_code`) by taking
|
|
61
|
+
* the last delimited segment. */
|
|
62
|
+
export function isGraftMcpTool(toolName) {
|
|
63
|
+
const t = toolName.toLowerCase();
|
|
64
|
+
const bare = t.split(/[:./]|__/).pop() ?? t;
|
|
65
|
+
return GRAFT_MCP_TOOL_NAMES.has(bare);
|
|
66
|
+
}
|
|
67
|
+
/** Whether a shell command line invokes the graft CLI (any of its install shapes). */
|
|
68
|
+
export function commandInvokesGraft(command) {
|
|
69
|
+
const c = command.trim();
|
|
70
|
+
if (/dist[/\\]cli\.js/.test(c))
|
|
71
|
+
return true; // running from a source checkout
|
|
72
|
+
// `graft …`, `graft-dev …`, `npx [-y] [@nanonets/]graft …`, at the start of the
|
|
73
|
+
// line or of a &&/;/| segment — not "mygraft" or a path that merely contains it.
|
|
74
|
+
return /(^|[|&;]\s*)(npx\s+(-y\s+)?(@nanonets\/)?)?graft(-dev)?\b/i.test(c);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Classify one tool use as a graft retrieval, a source read, or neither
|
|
78
|
+
* (an edit, a task spawn, an unrelated shell command — no retrieval signal).
|
|
79
|
+
* `command` is the shell command line when `toolName` is the host's shell tool.
|
|
80
|
+
*/
|
|
81
|
+
export function classifyToolUse(toolName, command) {
|
|
82
|
+
const t = (toolName ?? '').toLowerCase();
|
|
83
|
+
if (!t)
|
|
84
|
+
return null;
|
|
85
|
+
if (isGraftMcpTool(t))
|
|
86
|
+
return 'graft';
|
|
87
|
+
if (SOURCE_TOOLS.has(t))
|
|
88
|
+
return 'source';
|
|
89
|
+
if (isShellTool(t) && command && commandInvokesGraft(command))
|
|
90
|
+
return 'graft';
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/** Sum every `[graft] tokens saved ≈ N` footer in a blob of tool output. Thin
|
|
94
|
+
* alias over {@link sumSavingsFooters}, which lives next to the code that writes
|
|
95
|
+
* the footer so the two can't drift. */
|
|
96
|
+
export function parseSavings(blob) {
|
|
97
|
+
return sumSavingsFooters(blob);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Fold one tool use into a session's counters. A no-op when there is nothing to
|
|
101
|
+
* record (kind null and no savings), so a host can call it unconditionally after
|
|
102
|
+
* every tool without first checking whether the tool was interesting — the
|
|
103
|
+
* no-write path is what keeps it cheap on the Write/Edit/Task majority.
|
|
104
|
+
*
|
|
105
|
+
* Best-effort read-modify-write, not locked — mirroring `patchStats` in
|
|
106
|
+
* util/state.ts. Two tool-use hooks racing on the same session file can lose a
|
|
107
|
+
* count; that is acceptable for an episodic usage estimate (worst case is an
|
|
108
|
+
* undercount by one, never corruption thanks to the atomic write), and a lock
|
|
109
|
+
* here would contend with the build lock these hooks also touch.
|
|
110
|
+
*/
|
|
111
|
+
export function recordToolUse(dir, sessionId, use) {
|
|
112
|
+
const saved = use.savedTokens ?? 0;
|
|
113
|
+
if (!use.kind && saved <= 0)
|
|
114
|
+
return;
|
|
115
|
+
const id = sessionId || 'default';
|
|
116
|
+
const s = readSession(dir, id);
|
|
117
|
+
if (use.kind === 'graft')
|
|
118
|
+
s.graftReads = (s.graftReads ?? 0) + 1;
|
|
119
|
+
else if (use.kind === 'source')
|
|
120
|
+
s.sourceReads = (s.sourceReads ?? 0) + 1;
|
|
121
|
+
if (saved > 0)
|
|
122
|
+
s.savedTokens = (s.savedTokens ?? 0) + saved;
|
|
123
|
+
// A graft use owes a tally in this turn's reply; the Stop hook (countTallyTurn)
|
|
124
|
+
// resolves whether it got one and clears the flag. A flag, not a count — a turn
|
|
125
|
+
// with several graft calls is still one reply to the user.
|
|
126
|
+
if (use.kind === 'graft')
|
|
127
|
+
s.turnUsedGraft = true;
|
|
128
|
+
// Stamp the host once; the first tool use that lands owns the attribution.
|
|
129
|
+
if (use.host && !s.host)
|
|
130
|
+
s.host = use.host;
|
|
131
|
+
writeSession(dir, id, s);
|
|
132
|
+
}
|
|
133
|
+
/** The most-recently-touched session file for a repo, or null when none exist —
|
|
134
|
+
* what `graft stats` shows by default: the session you were just in. */
|
|
135
|
+
export function latestSession(dir) {
|
|
136
|
+
const sdir = sessionDir(dir);
|
|
137
|
+
let bestId = null;
|
|
138
|
+
let bestMtime = -Infinity;
|
|
139
|
+
for (const id of listSessionIds(dir)) {
|
|
140
|
+
let mtime;
|
|
141
|
+
try {
|
|
142
|
+
mtime = statSync(join(sdir, `${id}.json`)).mtimeMs;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (mtime > bestMtime) {
|
|
148
|
+
bestMtime = mtime;
|
|
149
|
+
bestId = id;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (bestId === null)
|
|
153
|
+
return null;
|
|
154
|
+
return { id: bestId, ...readSession(dir, bestId) };
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* A one-line-per-fact readout of a session's usage mix — Cursor has no
|
|
158
|
+
* statusline, so this is how you see the numbers the Claude Code bar would show.
|
|
159
|
+
* Reads local JSON only; sends nothing.
|
|
160
|
+
*/
|
|
161
|
+
export function formatSessionStats(s) {
|
|
162
|
+
if (s === null) {
|
|
163
|
+
return 'graft stats: no session recorded yet — use graft in an agent session, then look again.';
|
|
164
|
+
}
|
|
165
|
+
const graft = s.graftReads ?? 0;
|
|
166
|
+
const source = s.sourceReads ?? 0;
|
|
167
|
+
const saved = s.savedTokens ?? 0;
|
|
168
|
+
const total = graft + source;
|
|
169
|
+
const mix = total === 0 ? 'no retrieval yet' : `${Math.round((graft / total) * 100)}% graft`;
|
|
170
|
+
const lines = [
|
|
171
|
+
`graft stats — session ${s.id}`,
|
|
172
|
+
` graft reads: ${graft}`,
|
|
173
|
+
` source reads: ${source} (Read / Grep / Glob)`,
|
|
174
|
+
` mix: ${mix}`,
|
|
175
|
+
` tokens saved: ~${saved.toLocaleString()}`,
|
|
176
|
+
];
|
|
177
|
+
if (s.lastQuery)
|
|
178
|
+
lines.push(` last query: ${s.lastQuery}`);
|
|
179
|
+
return lines.join('\n');
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=session-metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-metrics.js","sourceRoot":"","sources":["../../src/claude/session-metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
type Json = Record<string, any>;
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Is this allowlist entry one graft wrote?
|
|
4
|
+
*
|
|
5
|
+
* Scoped to the forms graft is actually invoked as — NOT any rule mentioning
|
|
6
|
+
* "graft". A user who allowlists their own `Bash(graft-mytool:*)` keeps it; only
|
|
7
|
+
* graft's own set is replaced, which is what lets a renamed entry disappear on
|
|
8
|
+
* upgrade instead of accumulating beside its replacement.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isGraftAllowEntry(entry: unknown): boolean;
|
|
11
|
+
/** Is this footer regex graft's? It points at the card tree, which is graft's alone. */
|
|
12
|
+
export declare function isGraftFooterRegex(re: unknown): boolean;
|
|
13
|
+
/** True when init should write (or refresh) Graft's Claude Code statusLine. */
|
|
14
|
+
export declare function statuslineWanted(opts?: {
|
|
15
|
+
statusline?: boolean;
|
|
16
|
+
}): boolean;
|
|
17
|
+
export declare function mergeGraftSettings(existing: Json, opts?: {
|
|
18
|
+
statusline?: boolean;
|
|
19
|
+
}): {
|
|
3
20
|
merged: Json;
|
|
4
21
|
warnings: string[];
|
|
5
22
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-merge.d.ts","sourceRoot":"","sources":["../../src/claude/settings-merge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"settings-merge.d.ts","sourceRoot":"","sources":["../../src/claude/settings-merge.ts"],"names":[],"mappings":"AAEA,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AA+ChC;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEzD;AAED,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAEvD;AAOD,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAE7E;AA6BD,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,IAAI,EACd,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAoCtC"}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { isGraftEntry } from '../hosts/config-write.js';
|
|
1
2
|
const SL_CMD = 'node "${CLAUDE_PROJECT_DIR:-.}/.claude/helpers/graft-statusline.cjs"';
|
|
3
|
+
/** Stable marker for "this statusLine is Graft's", not the full command string —
|
|
4
|
+
* an older shim path or a GRAFT_DIR wrapper still names this file. */
|
|
5
|
+
const GRAFT_STATUSLINE_HELPER = 'graft-statusline.cjs';
|
|
2
6
|
const FOOTER = 'graft/[\\w./-]+\\.md';
|
|
3
7
|
// Every form graft is actually invoked as. 'graft:*' covers a global install;
|
|
4
8
|
// the other two cover a repo working on graft itself (or any consumer running it
|
|
@@ -17,11 +21,14 @@ function graftBlocks() {
|
|
|
17
21
|
return {
|
|
18
22
|
PostToolUse: [
|
|
19
23
|
{ matcher: 'Write|Edit|MultiEdit', hooks: [{ type: 'command', command: hookCmd('post-edit'), timeout: 10000 }] },
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
|
|
24
|
+
// Score the usage mix and sum token savings. A graft retrieval (CLI `graft …`
|
|
25
|
+
// via Bash, or the `graft_*` MCP tools) prints a `[graft] tokens saved ≈ N`
|
|
26
|
+
// footer this hook sums into the session total; the same hook classifies
|
|
27
|
+
// Read/Grep/Glob as source reads vs graft as graft reads, which is what feeds
|
|
28
|
+
// `graft stats` and the `session_summary` graft-vs-grep ratio. Broad matcher,
|
|
29
|
+
// but the handler no-ops instantly unless there is something to record, so an
|
|
30
|
+
// unrelated Bash or a plain Read costs only a stdin read.
|
|
31
|
+
{ matcher: 'Bash|mcp__graft__|Read|Grep|Glob', hooks: [{ type: 'command', command: hookCmd('tool-savings'), timeout: 8000 }] },
|
|
25
32
|
],
|
|
26
33
|
// Longer budget than the other hooks: its `graft ask` is a real query, and a
|
|
27
34
|
// query now brings the graph up to date first (graph/refresh.ts) — usually
|
|
@@ -36,39 +43,75 @@ function graftBlocks() {
|
|
|
36
43
|
Stop: [{ hooks: [{ type: 'command', command: hookCmd('stop'), timeout: 8000 }] }],
|
|
37
44
|
};
|
|
38
45
|
}
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Is this allowlist entry one graft wrote?
|
|
48
|
+
*
|
|
49
|
+
* Scoped to the forms graft is actually invoked as — NOT any rule mentioning
|
|
50
|
+
* "graft". A user who allowlists their own `Bash(graft-mytool:*)` keeps it; only
|
|
51
|
+
* graft's own set is replaced, which is what lets a renamed entry disappear on
|
|
52
|
+
* upgrade instead of accumulating beside its replacement.
|
|
53
|
+
*/
|
|
54
|
+
export function isGraftAllowEntry(entry) {
|
|
55
|
+
return /^Bash\((?:graft|npx graft|graft-dev|node dist\/cli\.js)(?::|\))/.test(String(entry));
|
|
41
56
|
}
|
|
42
|
-
|
|
57
|
+
/** Is this footer regex graft's? It points at the card tree, which is graft's alone. */
|
|
58
|
+
export function isGraftFooterRegex(re) {
|
|
59
|
+
return String(re).includes('graft/');
|
|
60
|
+
}
|
|
61
|
+
function envNoStatusline() {
|
|
62
|
+
const v = process.env.GRAFT_NO_STATUSLINE;
|
|
63
|
+
return v !== undefined && v !== '' && v !== '0' && v !== 'false';
|
|
64
|
+
}
|
|
65
|
+
/** True when init should write (or refresh) Graft's Claude Code statusLine. */
|
|
66
|
+
export function statuslineWanted(opts = {}) {
|
|
67
|
+
return opts.statusline !== false && !envNoStatusline();
|
|
68
|
+
}
|
|
69
|
+
function isGraftStatusline(value) {
|
|
70
|
+
if (!value || typeof value !== 'object')
|
|
71
|
+
return false;
|
|
72
|
+
const command = value.command;
|
|
73
|
+
return typeof command === 'string' && command.includes(GRAFT_STATUSLINE_HELPER);
|
|
74
|
+
}
|
|
75
|
+
function applyStatusline(merged, key, warnings, wanted, foreignWarning) {
|
|
76
|
+
const current = merged[key];
|
|
77
|
+
const ours = isGraftStatusline(current);
|
|
78
|
+
if (!wanted) {
|
|
79
|
+
if (!current || ours)
|
|
80
|
+
delete merged[key];
|
|
81
|
+
else
|
|
82
|
+
warnings.push(foreignWarning);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (!current || ours) {
|
|
86
|
+
merged[key] = { type: 'command', command: SL_CMD };
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
warnings.push(foreignWarning);
|
|
90
|
+
}
|
|
91
|
+
export function mergeGraftSettings(existing, opts = {}) {
|
|
43
92
|
const merged = { ...(existing ?? {}) };
|
|
44
93
|
const warnings = [];
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
warnings.push('Existing statusLine left untouched (a session allows only one). To use Graft, point it at .claude/helpers/graft-statusline.cjs.');
|
|
49
|
-
if (!merged.subagentStatusLine)
|
|
50
|
-
merged.subagentStatusLine = { type: 'command', command: SL_CMD };
|
|
51
|
-
else if (merged.subagentStatusLine.command !== SL_CMD)
|
|
52
|
-
warnings.push('Existing subagentStatusLine left untouched.');
|
|
94
|
+
const wanted = statuslineWanted(opts);
|
|
95
|
+
applyStatusline(merged, 'statusLine', warnings, wanted, 'Existing statusLine left untouched (a session allows only one). To use Graft, point it at .claude/helpers/graft-statusline.cjs.');
|
|
96
|
+
applyStatusline(merged, 'subagentStatusLine', warnings, wanted, 'Existing subagentStatusLine left untouched.');
|
|
53
97
|
merged.hooks = { ...(merged.hooks ?? {}) };
|
|
54
98
|
for (const [event, blocks] of Object.entries(graftBlocks())) {
|
|
55
99
|
const prior = Array.isArray(merged.hooks[event]) ? merged.hooks[event] : [];
|
|
56
|
-
const foreign = prior.filter((e) => !
|
|
100
|
+
const foreign = prior.filter((e) => !isGraftEntry(e)); // drop old Graft entries → idempotent
|
|
57
101
|
merged.hooks[event] = [...foreign, ...blocks];
|
|
58
102
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
merged.footerLinksRegexes =
|
|
103
|
+
// Drop graft's own prior regex before re-adding, so a change to FOOTER replaces
|
|
104
|
+
// the old pattern instead of stacking beside it. The user's regexes are kept.
|
|
105
|
+
const priorFooter = Array.isArray(merged.footerLinksRegexes) ? merged.footerLinksRegexes : [];
|
|
106
|
+
merged.footerLinksRegexes = [...priorFooter.filter((r) => !isGraftFooterRegex(r)), FOOTER];
|
|
63
107
|
// headless/subagent runs hard-deny Bash by default; without an allowlist entry
|
|
64
108
|
// `graft ask`'s own Bash calls (and the skill it installs) can't run out-of-box.
|
|
109
|
+
// Same shape as the hooks merge above: drop graft's prior entries, then add the
|
|
110
|
+
// current set. Append-only left a renamed invocation form in the user's settings
|
|
111
|
+
// forever, with nothing able to remove it.
|
|
65
112
|
merged.permissions = { ...(merged.permissions ?? {}) };
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
if (!allow.includes(entry))
|
|
69
|
-
allow.push(entry);
|
|
70
|
-
}
|
|
71
|
-
merged.permissions.allow = allow;
|
|
113
|
+
const priorAllow = Array.isArray(merged.permissions.allow) ? merged.permissions.allow : [];
|
|
114
|
+
merged.permissions.allow = [...priorAllow.filter((e) => !isGraftAllowEntry(e)), ...ALLOW_ENTRIES];
|
|
72
115
|
return { merged, warnings };
|
|
73
116
|
}
|
|
74
117
|
//# sourceMappingURL=settings-merge.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-merge.js","sourceRoot":"","sources":["../../src/claude/settings-merge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"settings-merge.js","sourceRoot":"","sources":["../../src/claude/settings-merge.ts"],"names":[],"mappings":"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"}
|
package/dist/claude/state.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AgentHost } from '../telemetry/contract.js';
|
|
1
2
|
export { LOCK_STALE_MS, acquireLock, acquireLockIn, releaseLockIn, cacheDir, emptyStats, patchStats, readStats, releaseLock, resolveContextDir, writeJsonAtomic, writeStats, } from '../util/state.js';
|
|
2
3
|
export type { Stats } from '../util/state.js';
|
|
3
4
|
export interface SessionState {
|
|
@@ -14,12 +15,36 @@ export interface SessionState {
|
|
|
14
15
|
/** Weak-match nudges spent this session, capped so the line stays signal.
|
|
15
16
|
* Optional for the same backwards-compatibility reason as above. */
|
|
16
17
|
nudges?: number;
|
|
18
|
+
/** Turns this session in which the agent used a graft retrieval tool — the
|
|
19
|
+
* denominator for "did it tell the user what that saved". Counted at Stop,
|
|
20
|
+
* and only for turns whose reply we could actually read (see claude/tally.ts). */
|
|
21
|
+
graftTurns?: number;
|
|
22
|
+
/** Of those turns, the ones whose reply carried a "graft saved ~N tokens"
|
|
23
|
+
* tally. `graftTurns - reportedTurns` is silent value: saved, never said. */
|
|
24
|
+
reportedTurns?: number;
|
|
25
|
+
/** Set by the tool-savings hook when the current turn touched graft, cleared
|
|
26
|
+
* at Stop once the turn has been counted. Transient, not a total. */
|
|
27
|
+
turnUsedGraft?: boolean;
|
|
28
|
+
/** `uuid` of the last assistant reply already examined, so a Stop that fires
|
|
29
|
+
* without new prose (or fires twice) can't count one reply as two turns. */
|
|
30
|
+
lastTallyUuid?: string;
|
|
17
31
|
/** Set once this session has been rolled up into a `session_summary`
|
|
18
32
|
* telemetry event, so a resumed or long-lived session is counted once.
|
|
19
33
|
* A flag rather than deleting the file: the file still holds `lastQuery` and
|
|
20
34
|
* `injectedPointers`, which a resumed session needs. */
|
|
21
35
|
summarized?: boolean;
|
|
36
|
+
/** Which host recorded this session, stamped on the first tool use. Lets the
|
|
37
|
+
* `session_summary` be attributed correctly even when Claude Code's idle sweep
|
|
38
|
+
* is what finally flushes a Cursor session. Optional: files written before
|
|
39
|
+
* host-stamping (or an empty session never touched by a tool) fall back to the
|
|
40
|
+
* flushing host. */
|
|
41
|
+
host?: AgentHost;
|
|
22
42
|
}
|
|
43
|
+
/** The per-repo session directory holding one `<id>.json` per agent session. */
|
|
44
|
+
export declare function sessionDir(d: string): string;
|
|
45
|
+
/** Every session id with a file on disk, or `[]` when none exist (never throws).
|
|
46
|
+
* Shared by the telemetry rollup and `graft stats` so they agree on the set. */
|
|
47
|
+
export declare function listSessionIds(d: string): string[];
|
|
23
48
|
export declare function readSession(d: string, id: string): SessionState;
|
|
24
49
|
export declare function writeSession(d: string, id: string, s: SessionState): void;
|
|
25
50
|
//# sourceMappingURL=state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/claude/state.ts"],"names":[],"mappings":"AASA,OAAO,EACL,aAAa,EACb,WAAW,EACX,aAAa,EACb,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,UAAU,GACX,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IACxC,gFAAgF;IAChF,WAAW,EAAE,MAAM,CAAC;IACpB;;8DAE0D;IAC1D,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;wEACoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;4DAGwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/claude/state.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EACL,aAAa,EACb,WAAW,EACX,aAAa,EACb,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,UAAU,GACX,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IACxC,gFAAgF;IAChF,WAAW,EAAE,MAAM,CAAC;IACpB;;8DAE0D;IAC1D,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;wEACoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;sFAEkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;iFAC6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;yEACqE;IACrE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;gFAC4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;4DAGwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;wBAIoB;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAMD,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAyC;AAItF;iFACiF;AACjF,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAIlD;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,YAAY,CAE/D;AACD,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,GAAG,IAAI,CAEzE"}
|
package/dist/claude/state.js
CHANGED
|
@@ -4,13 +4,26 @@
|
|
|
4
4
|
* pre-query auto-refresh can take the same lock; they are re-exported here so
|
|
5
5
|
* every existing import path keeps working.
|
|
6
6
|
*/
|
|
7
|
+
import { readdirSync } from 'node:fs';
|
|
7
8
|
import { join } from 'node:path';
|
|
8
9
|
import { cacheDir, readJson, writeJsonAtomic } from '../util/state.js';
|
|
9
10
|
export { LOCK_STALE_MS, acquireLock, acquireLockIn, releaseLockIn, cacheDir, emptyStats, patchStats, readStats, releaseLock, resolveContextDir, writeJsonAtomic, writeStats, } from '../util/state.js';
|
|
10
11
|
function emptySession() {
|
|
11
12
|
return { lastQuery: null, perAgentQuery: {}, graftReads: 0, sourceReads: 0, savedTokens: 0, injectedPointers: [], nudges: 0 };
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
+
/** The per-repo session directory holding one `<id>.json` per agent session. */
|
|
15
|
+
export function sessionDir(d) { return join(cacheDir(d), 'session'); }
|
|
16
|
+
function sessionPath(d, id) { return join(sessionDir(d), `${id}.json`); }
|
|
17
|
+
/** Every session id with a file on disk, or `[]` when none exist (never throws).
|
|
18
|
+
* Shared by the telemetry rollup and `graft stats` so they agree on the set. */
|
|
19
|
+
export function listSessionIds(d) {
|
|
20
|
+
try {
|
|
21
|
+
return readdirSync(sessionDir(d)).filter((f) => f.endsWith('.json')).map((f) => f.slice(0, -'.json'.length));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
14
27
|
export function readSession(d, id) {
|
|
15
28
|
return readJson(sessionPath(d, id)) ?? emptySession();
|
|
16
29
|
}
|
package/dist/claude/state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/claude/state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/claude/state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EACL,aAAa,EACb,WAAW,EACX,aAAa,EACb,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,UAAU,GACX,MAAM,kBAAkB,CAAC;AA0C1B,SAAS,YAAY;IACnB,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAChI,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,UAAU,CAAC,CAAS,IAAY,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;AAEtF,SAAS,WAAW,CAAC,CAAS,EAAE,EAAU,IAAY,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;AAEjG;iFACiF;AACjF,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/G,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,EAAU;IAC/C,OAAO,QAAQ,CAAe,WAAW,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,YAAY,EAAE,CAAC;AACtE,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,CAAS,EAAE,EAAU,EAAE,CAAe;IACjE,eAAe,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -3,9 +3,11 @@ import { type Stats } from './state.js';
|
|
|
3
3
|
* The statusline's fast path is the hook-maintained cache (graft/.cache/stats.json).
|
|
4
4
|
* When it's absent — a fresh checkout, or a plain `graft build` that doesn't write the
|
|
5
5
|
* cache — fall back to reading the graph itself (wiring.json) so the bar reflects reality
|
|
6
|
-
* immediately instead of showing "not built".
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* immediately instead of showing "not built". An empty wiring.json is still a graph
|
|
7
|
+
* (docs-only repos legitimately have 0 nodes); "not built" is only when the artifact
|
|
8
|
+
* is missing. The graph carries no drift signal, so it reads as synced until the next
|
|
9
|
+
* edit repopulates the cache. Still a pure read (no subprocess): the cache is preferred
|
|
10
|
+
* because it carries live dirty/stale state.
|
|
9
11
|
*/
|
|
10
12
|
export declare function resolveStats(dir: string): Stats | null;
|
|
11
13
|
export declare function main(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statusline.d.ts","sourceRoot":"","sources":["../../src/claude/statusline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAG5E
|
|
1
|
+
{"version":3,"file":"statusline.d.ts","sourceRoot":"","sources":["../../src/claude/statusline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAG5E;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAMtD;AAED,wBAAgB,IAAI,IAAI,IAAI,CAW3B"}
|
|
@@ -6,9 +6,11 @@ import { readWiring, computeStats } from './stats.js';
|
|
|
6
6
|
* The statusline's fast path is the hook-maintained cache (graft/.cache/stats.json).
|
|
7
7
|
* When it's absent — a fresh checkout, or a plain `graft build` that doesn't write the
|
|
8
8
|
* cache — fall back to reading the graph itself (wiring.json) so the bar reflects reality
|
|
9
|
-
* immediately instead of showing "not built".
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* immediately instead of showing "not built". An empty wiring.json is still a graph
|
|
10
|
+
* (docs-only repos legitimately have 0 nodes); "not built" is only when the artifact
|
|
11
|
+
* is missing. The graph carries no drift signal, so it reads as synced until the next
|
|
12
|
+
* edit repopulates the cache. Still a pure read (no subprocess): the cache is preferred
|
|
13
|
+
* because it carries live dirty/stale state.
|
|
12
14
|
*/
|
|
13
15
|
export function resolveStats(dir) {
|
|
14
16
|
const cached = readStats(dir);
|
|
@@ -17,7 +19,7 @@ export function resolveStats(dir) {
|
|
|
17
19
|
const wiring = readWiring(dir);
|
|
18
20
|
if (wiring)
|
|
19
21
|
return { ...emptyStats(), ...computeStats(wiring) };
|
|
20
|
-
return
|
|
22
|
+
return null;
|
|
21
23
|
}
|
|
22
24
|
export function main() {
|
|
23
25
|
let input = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statusline.js","sourceRoot":"","sources":["../../src/claude/statusline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAc,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtD
|
|
1
|
+
{"version":3,"file":"statusline.js","sourceRoot":"","sources":["../../src/claude/statusline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAc,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAClD,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,MAAM;QAAE,OAAO,EAAE,GAAG,UAAU,EAAE,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;IAChE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,IAAI;IAClB,IAAI,KAAK,GAAQ,EAAE,CAAC;IACpB,IAAI,CAAC;QAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC;IACrF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzE,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,UAAU,IAAI,SAAS,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC;IACjC,IAAI,KAAK,EAAE,CAAC;QAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QAAC,OAAO;IAAC,CAAC;IAC5E,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,KAAK,EAAE,cAAc,EAAE,eAAe,CAAC;IACnD,MAAM,MAAM,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAChF,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Did this reply tell the user what graft saved? */
|
|
2
|
+
export declare function hasSavingsTally(text: string): boolean;
|
|
3
|
+
/** One turn's worth of assistant prose, plus the id of the entry it ended on. */
|
|
4
|
+
export interface AssistantTurn {
|
|
5
|
+
/** `uuid` of the last assistant entry, so the same reply is never counted twice. */
|
|
6
|
+
uuid: string;
|
|
7
|
+
/** Every text block the agent emitted since the last user prompt, joined. */
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The assistant prose of the turn the transcript ends on, or null when it can't
|
|
12
|
+
* be read (no path — a host whose Stop hook doesn't name one — unreadable file,
|
|
13
|
+
* or a tail with no assistant text in it).
|
|
14
|
+
*
|
|
15
|
+
* Null is meaningfully different from "no tally found": a turn we cannot observe
|
|
16
|
+
* is left out of BOTH the numerator and the denominator by the caller, so a host
|
|
17
|
+
* that exposes no transcript reports no ratio rather than a ratio of zero.
|
|
18
|
+
*
|
|
19
|
+
* Sidechain entries (subagents) are skipped throughout — a subagent's own prose
|
|
20
|
+
* is never what the user read.
|
|
21
|
+
*/
|
|
22
|
+
export declare function lastAssistantTurn(transcriptPath: unknown): AssistantTurn | null;
|
|
23
|
+
//# sourceMappingURL=tally.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tally.d.ts","sourceRoot":"","sources":["../../src/claude/tally.ts"],"names":[],"mappings":"AA0CA,qDAAqD;AACrD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,oFAAoF;IACpF,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;CACd;AAuCD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CA0B/E"}
|