homegraph 1.1.2 → 1.2.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/LICENSE +21 -21
- package/README.md +268 -243
- package/dist/arkts/ohos-api-index.d.ts +15 -0
- package/dist/arkts/ohos-api-index.d.ts.map +1 -0
- package/dist/arkts/ohos-api-index.js +190 -0
- package/dist/arkts/ohos-api-index.js.map +1 -0
- package/dist/arkts/ohos-sdk-input.d.ts +36 -0
- package/dist/arkts/ohos-sdk-input.d.ts.map +1 -0
- package/dist/arkts/ohos-sdk-input.js +214 -0
- package/dist/arkts/ohos-sdk-input.js.map +1 -0
- package/dist/bin/command-supervision.d.ts +12 -0
- package/dist/bin/command-supervision.d.ts.map +1 -0
- package/dist/bin/command-supervision.js +86 -0
- package/dist/bin/command-supervision.js.map +1 -0
- package/dist/bin/homegraph.d.ts +1 -1
- package/dist/bin/homegraph.js +971 -71
- package/dist/bin/homegraph.js.map +1 -1
- package/dist/db/index.d.ts +20 -0
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +39 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +58 -19
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/queries.d.ts +14 -0
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +314 -143
- package/dist/db/queries.js.map +1 -1
- package/dist/db/schema.sql +172 -152
- package/dist/directory.d.ts +32 -0
- package/dist/directory.d.ts.map +1 -1
- package/dist/directory.js +88 -5
- package/dist/directory.js.map +1 -1
- package/dist/extraction/arkts-batch-worker.d.ts +2 -0
- package/dist/extraction/arkts-batch-worker.d.ts.map +1 -0
- package/dist/extraction/arkts-batch-worker.js +28 -0
- package/dist/extraction/arkts-batch-worker.js.map +1 -0
- package/dist/extraction/context.d.ts +11 -0
- package/dist/extraction/context.d.ts.map +1 -1
- package/dist/extraction/context.js +20 -0
- package/dist/extraction/context.js.map +1 -1
- package/dist/extraction/index.d.ts +16 -2
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +619 -380
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/arkts-state-decorators.d.ts +13 -0
- package/dist/extraction/languages/arkts-state-decorators.d.ts.map +1 -0
- package/dist/extraction/languages/arkts-state-decorators.js +26 -0
- package/dist/extraction/languages/arkts-state-decorators.js.map +1 -0
- package/dist/extraction/languages/arkts-viewtree.d.ts +4 -2
- package/dist/extraction/languages/arkts-viewtree.d.ts.map +1 -1
- package/dist/extraction/languages/arkts-viewtree.js +21 -6
- package/dist/extraction/languages/arkts-viewtree.js.map +1 -1
- package/dist/extraction/languages/arkts.d.ts +98 -2
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +1290 -61
- package/dist/extraction/languages/arkts.js.map +1 -1
- package/dist/extraction/languages/c-cpp.d.ts +56 -0
- package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
- package/dist/extraction/languages/c-cpp.js +198 -1
- package/dist/extraction/languages/c-cpp.js.map +1 -1
- package/dist/extraction/languages/ohos-api-consumer.d.ts +34 -0
- package/dist/extraction/languages/ohos-api-consumer.d.ts.map +1 -0
- package/dist/extraction/languages/ohos-api-consumer.js +283 -0
- package/dist/extraction/languages/ohos-api-consumer.js.map +1 -0
- package/dist/extraction/parse-pool.d.ts +126 -0
- package/dist/extraction/parse-pool.d.ts.map +1 -0
- package/dist/extraction/parse-pool.js +319 -0
- package/dist/extraction/parse-pool.js.map +1 -0
- package/dist/extraction/tree-sitter-types.d.ts +17 -0
- package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.d.ts +21 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.js +198 -27
- package/dist/extraction/tree-sitter.js.map +1 -1
- package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/dist/graph/traversal.d.ts.map +1 -1
- package/dist/graph/traversal.js +76 -17
- package/dist/graph/traversal.js.map +1 -1
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +78 -4
- package/dist/index.js.map +1 -1
- package/dist/installer/instructions-template.js +9 -9
- package/dist/installer/targets/shared.d.ts +5 -6
- package/dist/installer/targets/shared.d.ts.map +1 -1
- package/dist/installer/targets/shared.js +5 -6
- package/dist/installer/targets/shared.js.map +1 -1
- package/dist/mcp/daemon-paths.d.ts +30 -3
- package/dist/mcp/daemon-paths.d.ts.map +1 -1
- package/dist/mcp/daemon-paths.js +50 -10
- package/dist/mcp/daemon-paths.js.map +1 -1
- package/dist/mcp/daemon-registry.d.ts.map +1 -1
- package/dist/mcp/daemon-registry.js +7 -3
- package/dist/mcp/daemon-registry.js.map +1 -1
- package/dist/mcp/daemon.d.ts +48 -0
- package/dist/mcp/daemon.d.ts.map +1 -1
- package/dist/mcp/daemon.js +196 -32
- package/dist/mcp/daemon.js.map +1 -1
- package/dist/mcp/engine.d.ts +17 -0
- package/dist/mcp/engine.d.ts.map +1 -1
- package/dist/mcp/engine.js +73 -1
- package/dist/mcp/engine.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +25 -43
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/liveness-watchdog.js +16 -16
- package/dist/mcp/ppid-watchdog.d.ts +18 -0
- package/dist/mcp/ppid-watchdog.d.ts.map +1 -1
- package/dist/mcp/ppid-watchdog.js +37 -0
- package/dist/mcp/ppid-watchdog.js.map +1 -1
- package/dist/mcp/query-cache.d.ts +25 -0
- package/dist/mcp/query-cache.d.ts.map +1 -0
- package/dist/mcp/query-cache.js +191 -0
- package/dist/mcp/query-cache.js.map +1 -0
- package/dist/mcp/query-pool.d.ts +94 -0
- package/dist/mcp/query-pool.d.ts.map +1 -0
- package/dist/mcp/query-pool.js +297 -0
- package/dist/mcp/query-pool.js.map +1 -0
- package/dist/mcp/query-worker.d.ts +24 -0
- package/dist/mcp/query-worker.d.ts.map +1 -0
- package/dist/mcp/query-worker.js +87 -0
- package/dist/mcp/query-worker.js.map +1 -0
- package/dist/mcp/server-instructions.d.ts +5 -7
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +72 -74
- package/dist/mcp/server-instructions.js.map +1 -1
- package/dist/mcp/tools.d.ts +94 -4
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +877 -71
- package/dist/mcp/tools.js.map +1 -1
- package/dist/project-config.d.ts +20 -0
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +42 -2
- package/dist/project-config.js.map +1 -1
- package/dist/reasoning/login.js +1 -1
- package/dist/reasoning/login.js.map +1 -1
- package/dist/reasoning/reasoner.js +32 -32
- package/dist/resolution/c-fnptr-synthesizer.d.ts +0 -28
- package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
- package/dist/resolution/c-fnptr-synthesizer.js +765 -79
- package/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
- package/dist/resolution/callback-synthesizer.d.ts +1 -1
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/dist/resolution/callback-synthesizer.js +72 -11
- package/dist/resolution/callback-synthesizer.js.map +1 -1
- package/dist/resolution/cooperative-yield.d.ts +32 -0
- package/dist/resolution/cooperative-yield.d.ts.map +1 -0
- package/dist/resolution/cooperative-yield.js +42 -0
- package/dist/resolution/cooperative-yield.js.map +1 -0
- package/dist/resolution/index.d.ts +11 -2
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +72 -4
- package/dist/resolution/index.js.map +1 -1
- package/dist/resolution/name-matcher.d.ts +22 -0
- package/dist/resolution/name-matcher.d.ts.map +1 -1
- package/dist/resolution/name-matcher.js +317 -20
- package/dist/resolution/name-matcher.js.map +1 -1
- package/dist/spec/config.d.ts +39 -0
- package/dist/spec/config.d.ts.map +1 -0
- package/dist/spec/config.js +304 -0
- package/dist/spec/config.js.map +1 -0
- package/dist/spec/db/commit-node.d.ts +23 -0
- package/dist/spec/db/commit-node.d.ts.map +1 -0
- package/dist/spec/db/commit-node.js +62 -0
- package/dist/spec/db/commit-node.js.map +1 -0
- package/dist/spec/db/fragment-node.d.ts +24 -0
- package/dist/spec/db/fragment-node.d.ts.map +1 -0
- package/dist/spec/db/fragment-node.js +128 -0
- package/dist/spec/db/fragment-node.js.map +1 -0
- package/dist/spec/db/fts.d.ts +74 -0
- package/dist/spec/db/fts.d.ts.map +1 -0
- package/dist/spec/db/fts.js +324 -0
- package/dist/spec/db/fts.js.map +1 -0
- package/dist/spec/db/index.d.ts +13 -0
- package/dist/spec/db/index.d.ts.map +1 -0
- package/dist/spec/db/index.js +50 -0
- package/dist/spec/db/index.js.map +1 -0
- package/dist/spec/db/relations.d.ts +55 -0
- package/dist/spec/db/relations.d.ts.map +1 -0
- package/dist/spec/db/relations.js +158 -0
- package/dist/spec/db/relations.js.map +1 -0
- package/dist/spec/db/schema.d.ts +33 -0
- package/dist/spec/db/schema.d.ts.map +1 -0
- package/dist/spec/db/schema.js +119 -0
- package/dist/spec/db/schema.js.map +1 -0
- package/dist/spec/db/schema.sql +117 -0
- package/dist/spec/db/spec-node.d.ts +41 -0
- package/dist/spec/db/spec-node.d.ts.map +1 -0
- package/dist/spec/db/spec-node.js +114 -0
- package/dist/spec/db/spec-node.js.map +1 -0
- package/dist/spec/evolve/impact-locator.d.ts +13 -0
- package/dist/spec/evolve/impact-locator.d.ts.map +1 -0
- package/dist/spec/evolve/impact-locator.js +25 -0
- package/dist/spec/evolve/impact-locator.js.map +1 -0
- package/dist/spec/evolve/llm-client.d.ts +50 -0
- package/dist/spec/evolve/llm-client.d.ts.map +1 -0
- package/dist/spec/evolve/llm-client.js +176 -0
- package/dist/spec/evolve/llm-client.js.map +1 -0
- package/dist/spec/evolve/logic-checker.d.ts +12 -0
- package/dist/spec/evolve/logic-checker.d.ts.map +1 -0
- package/dist/spec/evolve/logic-checker.js +24 -0
- package/dist/spec/evolve/logic-checker.js.map +1 -0
- package/dist/spec/evolve/pipeline.d.ts +42 -0
- package/dist/spec/evolve/pipeline.d.ts.map +1 -0
- package/dist/spec/evolve/pipeline.js +567 -0
- package/dist/spec/evolve/pipeline.js.map +1 -0
- package/dist/spec/evolve/spec-rewriter.d.ts +42 -0
- package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -0
- package/dist/spec/evolve/spec-rewriter.js +230 -0
- package/dist/spec/evolve/spec-rewriter.js.map +1 -0
- package/dist/spec/graph/queries.d.ts +155 -0
- package/dist/spec/graph/queries.d.ts.map +1 -0
- package/dist/spec/graph/queries.js +440 -0
- package/dist/spec/graph/queries.js.map +1 -0
- package/dist/spec/llm/client.d.ts +29 -0
- package/dist/spec/llm/client.d.ts.map +1 -0
- package/dist/spec/llm/client.js +123 -0
- package/dist/spec/llm/client.js.map +1 -0
- package/dist/spec/llm/index.d.ts +3 -0
- package/dist/spec/llm/index.d.ts.map +1 -0
- package/dist/spec/llm/index.js +11 -0
- package/dist/spec/llm/index.js.map +1 -0
- package/dist/spec/llm/prompts.d.ts +13 -0
- package/dist/spec/llm/prompts.d.ts.map +1 -0
- package/dist/spec/llm/prompts.js +75 -0
- package/dist/spec/llm/prompts.js.map +1 -0
- package/dist/spec/mining/diff-parser.d.ts +33 -0
- package/dist/spec/mining/diff-parser.d.ts.map +1 -0
- package/dist/spec/mining/diff-parser.js +166 -0
- package/dist/spec/mining/diff-parser.js.map +1 -0
- package/dist/spec/mining/git-scanner.d.ts +103 -0
- package/dist/spec/mining/git-scanner.d.ts.map +1 -0
- package/dist/spec/mining/git-scanner.js +307 -0
- package/dist/spec/mining/git-scanner.js.map +1 -0
- package/dist/spec/mining/pipeline.d.ts +53 -0
- package/dist/spec/mining/pipeline.d.ts.map +1 -0
- package/dist/spec/mining/pipeline.js +178 -0
- package/dist/spec/mining/pipeline.js.map +1 -0
- package/dist/spec/mining/scope-resolver.d.ts +45 -0
- package/dist/spec/mining/scope-resolver.d.ts.map +1 -0
- package/dist/spec/mining/scope-resolver.js +103 -0
- package/dist/spec/mining/scope-resolver.js.map +1 -0
- package/dist/spec/mining/spec-extractor.d.ts +69 -0
- package/dist/spec/mining/spec-extractor.d.ts.map +1 -0
- package/dist/spec/mining/spec-extractor.js +369 -0
- package/dist/spec/mining/spec-extractor.js.map +1 -0
- package/dist/spec/types.d.ts +149 -0
- package/dist/spec/types.d.ts.map +1 -0
- package/dist/spec/types.js +15 -0
- package/dist/spec/types.js.map +1 -0
- package/dist/spec/utils.d.ts +167 -0
- package/dist/spec/utils.d.ts.map +1 -0
- package/dist/spec/utils.js +463 -0
- package/dist/spec/utils.js.map +1 -0
- package/dist/sync/worktree.d.ts +9 -0
- package/dist/sync/worktree.d.ts.map +1 -1
- package/dist/sync/worktree.js +40 -0
- package/dist/sync/worktree.js.map +1 -1
- package/dist/types.d.ts +6 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/shimmer-progress.d.ts +2 -0
- package/dist/ui/shimmer-progress.d.ts.map +1 -1
- package/dist/ui/shimmer-progress.js +19 -2
- package/dist/ui/shimmer-progress.js.map +1 -1
- package/dist/upgrade/index.js +1 -1
- package/dist/upgrade/index.js.map +1 -1
- package/package.json +58 -57
- package/scripts/add-lang/bench.sh +60 -60
- package/scripts/add-lang/check-grammar.mjs +75 -75
- package/scripts/add-lang/dump-ast.mjs +103 -103
- package/scripts/add-lang/verify-extraction.mjs +70 -70
- package/scripts/agent-eval/ab-adoption.sh +91 -91
- package/scripts/agent-eval/ab-hook.sh +86 -86
- package/scripts/agent-eval/ab-impl.sh +78 -78
- package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -102
- package/scripts/agent-eval/ab-sufficiency.sh +78 -78
- package/scripts/agent-eval/arms-F.sh +21 -21
- package/scripts/agent-eval/arms-matrix.sh +37 -37
- package/scripts/agent-eval/audit.sh +68 -68
- package/scripts/agent-eval/bench-readme.sh +28 -28
- package/scripts/agent-eval/bench-why-repo.sh +22 -22
- package/scripts/agent-eval/block-read-hook.sh +19 -19
- package/scripts/agent-eval/hook-settings.json +15 -15
- package/scripts/agent-eval/itrun.sh +120 -120
- package/scripts/agent-eval/offload-eval-3arm.sh +72 -72
- package/scripts/agent-eval/offload-eval-cost.mjs +133 -133
- package/scripts/agent-eval/offload-eval-effort.mjs +108 -108
- package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -25
- package/scripts/agent-eval/offload-eval-frontload.sh +47 -47
- package/scripts/agent-eval/offload-eval-ground-truth.json +18 -18
- package/scripts/agent-eval/offload-eval-hook.mjs +84 -84
- package/scripts/agent-eval/offload-eval-judge.mjs +103 -103
- package/scripts/agent-eval/offload-eval-matrix.sh +20 -20
- package/scripts/agent-eval/offload-eval-metrics.mjs +94 -94
- package/scripts/agent-eval/offload-eval-refs1.sh +50 -50
- package/scripts/agent-eval/offload-eval-setup.sh +24 -24
- package/scripts/agent-eval/offload-eval-styles.sh +71 -71
- package/scripts/agent-eval/offload-eval-summarize.mjs +68 -68
- package/scripts/agent-eval/offload-eval.md +76 -76
- package/scripts/agent-eval/parse-arms.mjs +116 -116
- package/scripts/agent-eval/parse-bench-readme.mjs +84 -84
- package/scripts/agent-eval/parse-run.mjs +45 -45
- package/scripts/agent-eval/parse-session.mjs +93 -93
- package/scripts/agent-eval/probe-context.mjs +21 -21
- package/scripts/agent-eval/probe-explore.mjs +40 -40
- package/scripts/agent-eval/probe-node.mjs +20 -20
- package/scripts/agent-eval/probe-sweep.mjs +119 -119
- package/scripts/agent-eval/probe-trace.mjs +20 -20
- package/scripts/agent-eval/redirect-read-hook.sh +38 -38
- package/scripts/agent-eval/repro-concurrent-explore.mjs +119 -0
- package/scripts/agent-eval/repro-daemon-clients.mjs +125 -0
- package/scripts/agent-eval/run-agent.sh +34 -34
- package/scripts/agent-eval/run-all.sh +69 -69
- package/scripts/agent-eval/run-arms.sh +56 -56
- package/scripts/agent-eval/seq-matrix.mjs +137 -137
- package/scripts/build-bundle.sh +118 -118
- package/scripts/exp_boundary_eval/README.md +247 -0
- package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/deveco_arm.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/setup.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/_test_mcp_chain.py +78 -0
- package/scripts/exp_boundary_eval/_test_stdin.py +8 -0
- package/scripts/exp_boundary_eval/_utils.py +1116 -0
- package/scripts/exp_boundary_eval/analyze.py +1313 -0
- package/scripts/exp_boundary_eval/data/agents.json +109 -0
- package/scripts/exp_boundary_eval/data/experiments.json +140 -0
- package/scripts/exp_boundary_eval/deveco_arm.py +519 -0
- package/scripts/exp_boundary_eval/run_all.py +378 -0
- package/scripts/exp_boundary_eval/run_one.py +165 -0
- package/scripts/exp_boundary_eval/run_session.py +158 -0
- package/scripts/exp_boundary_eval/setup.py +120 -0
- package/scripts/exp_boundary_eval/win_mcp_launcher.py +73 -0
- package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +36 -0
- package/scripts/exp_boundary_eval/win_node_launcher.py +24 -0
- package/scripts/extract-release-notes.mjs +130 -130
- package/scripts/local-install.sh +41 -41
- package/scripts/npm-sdk.js +75 -75
- package/scripts/npm-shim.js +268 -246
- package/scripts/ohos-sdk-publish.mjs +133 -0
- package/scripts/pack-npm.sh +119 -119
- package/scripts/prepare-release.mjs +270 -270
- package/scripts/qa_eval/README.md +407 -404
- package/scripts/qa_eval/_test_deveco_probe.py +41 -41
- package/scripts/qa_eval/agent_runner.py +526 -526
- package/scripts/qa_eval/data/.gitignore +4 -4
- package/scripts/qa_eval/data/test-set.jsonl +2 -22
- package/scripts/qa_eval/eval_metrics.py +274 -233
- package/scripts/qa_eval/external_agent.py +976 -671
- package/scripts/qa_eval/llm_config.py +92 -92
- package/scripts/qa_eval/memory_monitor.py +132 -132
- package/scripts/qa_eval/my_answer_accuracy.py +187 -187
- package/scripts/qa_eval/requirements.txt +2 -2
- package/scripts/qa_eval/run_pipeline.py +804 -711
- package/scripts/qa_eval/stats_efficiency.py +279 -279
- package/scripts/qa_eval/stats_scores.py +207 -207
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Aggregate the README A/B (bench-readme.sh output): per repo, median of N runs
|
|
3
|
-
// per arm → time, tool calls, tokens, cost, and % saved. Plus an average row.
|
|
4
|
-
//
|
|
5
|
-
// Tokens = SUM of per-turn assistant `usage` (input + output + cache read +
|
|
6
|
-
// cache creation) — the cumulative "total tokens processed". NOTE: `result.usage`
|
|
7
|
-
// is last-turn-only in current Claude Code, so it under-counts badly; don't use it.
|
|
8
|
-
// `total_cost_usd` and `duration_ms` are already cumulative.
|
|
9
|
-
//
|
|
10
|
-
// Usage: node parse-bench-readme.mjs [/tmp/ab-readme]
|
|
11
|
-
import { readFileSync, existsSync, readdirSync } from 'fs';
|
|
12
|
-
import { join } from 'path';
|
|
13
|
-
const ROOT = process.argv[2] || '/tmp/ab-readme';
|
|
14
|
-
const REPOS = ['vscode', 'excalidraw', 'django', 'tokio', 'okhttp', 'gin', 'alamofire'];
|
|
15
|
-
|
|
16
|
-
function parse(file) {
|
|
17
|
-
if (!existsSync(file)) return null;
|
|
18
|
-
const L = readFileSync(file, 'utf8').split('\n').filter(Boolean);
|
|
19
|
-
let tools = 0, reads = 0, grep = 0, cg = 0, tokens = 0, r = null, raced = false;
|
|
20
|
-
for (const l of L) { let e; try { e = JSON.parse(l); } catch { continue; }
|
|
21
|
-
if (e.type === 'assistant') {
|
|
22
|
-
const u = e.message?.usage;
|
|
23
|
-
if (u) tokens += (u.input_tokens || 0) + (u.output_tokens || 0) + (u.cache_read_input_tokens || 0) + (u.cache_creation_input_tokens || 0);
|
|
24
|
-
for (const b of (e.message?.content || [])) if (b.type === 'tool_use') {
|
|
25
|
-
const n = b.name;
|
|
26
|
-
if (n === 'ToolSearch') continue;
|
|
27
|
-
tools++;
|
|
28
|
-
if (n === 'Read') reads++;
|
|
29
|
-
else if (n === 'Grep' || n === 'Glob') grep++;
|
|
30
|
-
else if (/homegraph/.test(n)) cg++;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
// MCP cold-start race: the headless agent fired before `homegraph serve --mcp`
|
|
34
|
-
// finished registering its tools, so early calls returned "No such tool
|
|
35
|
-
// available" and the agent floundered into grep/Read. That measures HomeGraph's
|
|
36
|
-
// startup latency, NOT its steady-state value — flag the run so the aggregate
|
|
37
|
-
// can exclude it (an artifact of headless first-turn timing, not the tool).
|
|
38
|
-
if (e.type === 'user') for (const b of (Array.isArray(e.message?.content) ? e.message.content : [])) {
|
|
39
|
-
if (b.type === 'tool_result') {
|
|
40
|
-
const t = Array.isArray(b.content) ? b.content.map(c => c.text || '').join('') : (b.content || '');
|
|
41
|
-
if (/No such tool available/.test(t)) raced = true;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (e.type === 'result') r = e;
|
|
45
|
-
}
|
|
46
|
-
if (!r || r.subtype !== 'success') return null;
|
|
47
|
-
return { dur: r.duration_ms / 1000, tools, reads, grep, cg, tokens, cost: r.total_cost_usd || 0, raced };
|
|
48
|
-
}
|
|
49
|
-
const median = (arr) => { const v = [...arr].sort((a, b) => a - b); const n = v.length; return n === 0 ? 0 : n % 2 ? v[(n - 1) / 2] : (v[n / 2 - 1] + v[n / 2]) / 2; };
|
|
50
|
-
const fmtTime = (s) => s >= 60 ? `${Math.floor(s / 60)}m ${Math.round(s % 60)}s` : `${Math.round(s)}s`;
|
|
51
|
-
const fmtTok = (t) => t >= 1e6 ? `${(t / 1e6).toFixed(1)}M` : `${Math.round(t / 1000)}k`;
|
|
52
|
-
const pct = (w, wo) => wo > 0 ? Math.round((1 - w / wo) * 100) : 0;
|
|
53
|
-
|
|
54
|
-
console.log('repo n(w/wo) time WITH→WITHOUT tools W→WO tokens W→WO (saved) cost W→WO (saved)');
|
|
55
|
-
const savings = { cost: [], tokens: [], time: [], tools: [] };
|
|
56
|
-
for (const repo of REPOS) {
|
|
57
|
-
const dir = join(ROOT, repo);
|
|
58
|
-
const runDirs = existsSync(dir) ? readdirSync(dir).filter(d => /^run\d+$/.test(d)) : [];
|
|
59
|
-
// Exclude MCP-cold-start-raced WITH runs by default — they measure a startup
|
|
60
|
-
// race, not steady-state value. `CG_INCLUDE_RACED=1` keeps them (to see the raw
|
|
61
|
-
// distribution). The WITHOUT arm has no MCP, so it's never raced.
|
|
62
|
-
const includeRaced = process.env.CG_INCLUDE_RACED === '1';
|
|
63
|
-
const W = [], WO = []; let racedExcluded = 0;
|
|
64
|
-
for (const rd of runDirs) {
|
|
65
|
-
const w = parse(join(dir, rd, 'run-headless-with.jsonl'));
|
|
66
|
-
if (w) { if (w.raced && !includeRaced) racedExcluded++; else W.push(w); }
|
|
67
|
-
const wo = parse(join(dir, rd, 'run-headless-without.jsonl')); if (wo) WO.push(wo);
|
|
68
|
-
}
|
|
69
|
-
if (!W.length || !WO.length) { console.log(`${repo.padEnd(11)} (incomplete: w=${W.length} wo=${WO.length})`); continue; }
|
|
70
|
-
const m = (arr, k) => median(arr.map(x => x[k]));
|
|
71
|
-
const wT = m(W, 'dur'), woT = m(WO, 'dur'), wTok = m(W, 'tokens'), woTok = m(WO, 'tokens');
|
|
72
|
-
const wC = m(W, 'cost'), woC = m(WO, 'cost'), wTl = m(W, 'tools'), woTl = m(WO, 'tools');
|
|
73
|
-
savings.time.push(pct(wT, woT)); savings.tokens.push(pct(wTok, woTok)); savings.cost.push(pct(wC, woC)); savings.tools.push(pct(wTl, woTl));
|
|
74
|
-
console.log(
|
|
75
|
-
`${repo.padEnd(11)} ${W.length}/${WO.length} ` +
|
|
76
|
-
`${(fmtTime(wT) + '→' + fmtTime(woT)).padEnd(22)}` +
|
|
77
|
-
`${(Math.round(wTl) + '→' + Math.round(woTl)).padEnd(12)}` +
|
|
78
|
-
`${(fmtTok(wTok) + '→' + fmtTok(woTok) + ' (' + pct(wTok, woTok) + '%)').padEnd(24)}` +
|
|
79
|
-
`$${wC.toFixed(2)}→$${woC.toFixed(2)} (${pct(wC, woC)}%)` +
|
|
80
|
-
(racedExcluded ? ` [${racedExcluded} raced run${racedExcluded === 1 ? '' : 's'} excluded]` : '')
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
const avg = (a) => a.length ? Math.round(a.reduce((s, x) => s + x, 0) / a.length) : 0;
|
|
84
|
-
console.log(`\nAVERAGE saved: cost ${avg(savings.cost)}% · tokens ${avg(savings.tokens)}% · time ${avg(savings.time)}% · tool calls ${avg(savings.tools)}%`);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Aggregate the README A/B (bench-readme.sh output): per repo, median of N runs
|
|
3
|
+
// per arm → time, tool calls, tokens, cost, and % saved. Plus an average row.
|
|
4
|
+
//
|
|
5
|
+
// Tokens = SUM of per-turn assistant `usage` (input + output + cache read +
|
|
6
|
+
// cache creation) — the cumulative "total tokens processed". NOTE: `result.usage`
|
|
7
|
+
// is last-turn-only in current Claude Code, so it under-counts badly; don't use it.
|
|
8
|
+
// `total_cost_usd` and `duration_ms` are already cumulative.
|
|
9
|
+
//
|
|
10
|
+
// Usage: node parse-bench-readme.mjs [/tmp/ab-readme]
|
|
11
|
+
import { readFileSync, existsSync, readdirSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
const ROOT = process.argv[2] || '/tmp/ab-readme';
|
|
14
|
+
const REPOS = ['vscode', 'excalidraw', 'django', 'tokio', 'okhttp', 'gin', 'alamofire'];
|
|
15
|
+
|
|
16
|
+
function parse(file) {
|
|
17
|
+
if (!existsSync(file)) return null;
|
|
18
|
+
const L = readFileSync(file, 'utf8').split('\n').filter(Boolean);
|
|
19
|
+
let tools = 0, reads = 0, grep = 0, cg = 0, tokens = 0, r = null, raced = false;
|
|
20
|
+
for (const l of L) { let e; try { e = JSON.parse(l); } catch { continue; }
|
|
21
|
+
if (e.type === 'assistant') {
|
|
22
|
+
const u = e.message?.usage;
|
|
23
|
+
if (u) tokens += (u.input_tokens || 0) + (u.output_tokens || 0) + (u.cache_read_input_tokens || 0) + (u.cache_creation_input_tokens || 0);
|
|
24
|
+
for (const b of (e.message?.content || [])) if (b.type === 'tool_use') {
|
|
25
|
+
const n = b.name;
|
|
26
|
+
if (n === 'ToolSearch') continue;
|
|
27
|
+
tools++;
|
|
28
|
+
if (n === 'Read') reads++;
|
|
29
|
+
else if (n === 'Grep' || n === 'Glob') grep++;
|
|
30
|
+
else if (/homegraph/.test(n)) cg++;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// MCP cold-start race: the headless agent fired before `homegraph serve --mcp`
|
|
34
|
+
// finished registering its tools, so early calls returned "No such tool
|
|
35
|
+
// available" and the agent floundered into grep/Read. That measures HomeGraph's
|
|
36
|
+
// startup latency, NOT its steady-state value — flag the run so the aggregate
|
|
37
|
+
// can exclude it (an artifact of headless first-turn timing, not the tool).
|
|
38
|
+
if (e.type === 'user') for (const b of (Array.isArray(e.message?.content) ? e.message.content : [])) {
|
|
39
|
+
if (b.type === 'tool_result') {
|
|
40
|
+
const t = Array.isArray(b.content) ? b.content.map(c => c.text || '').join('') : (b.content || '');
|
|
41
|
+
if (/No such tool available/.test(t)) raced = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (e.type === 'result') r = e;
|
|
45
|
+
}
|
|
46
|
+
if (!r || r.subtype !== 'success') return null;
|
|
47
|
+
return { dur: r.duration_ms / 1000, tools, reads, grep, cg, tokens, cost: r.total_cost_usd || 0, raced };
|
|
48
|
+
}
|
|
49
|
+
const median = (arr) => { const v = [...arr].sort((a, b) => a - b); const n = v.length; return n === 0 ? 0 : n % 2 ? v[(n - 1) / 2] : (v[n / 2 - 1] + v[n / 2]) / 2; };
|
|
50
|
+
const fmtTime = (s) => s >= 60 ? `${Math.floor(s / 60)}m ${Math.round(s % 60)}s` : `${Math.round(s)}s`;
|
|
51
|
+
const fmtTok = (t) => t >= 1e6 ? `${(t / 1e6).toFixed(1)}M` : `${Math.round(t / 1000)}k`;
|
|
52
|
+
const pct = (w, wo) => wo > 0 ? Math.round((1 - w / wo) * 100) : 0;
|
|
53
|
+
|
|
54
|
+
console.log('repo n(w/wo) time WITH→WITHOUT tools W→WO tokens W→WO (saved) cost W→WO (saved)');
|
|
55
|
+
const savings = { cost: [], tokens: [], time: [], tools: [] };
|
|
56
|
+
for (const repo of REPOS) {
|
|
57
|
+
const dir = join(ROOT, repo);
|
|
58
|
+
const runDirs = existsSync(dir) ? readdirSync(dir).filter(d => /^run\d+$/.test(d)) : [];
|
|
59
|
+
// Exclude MCP-cold-start-raced WITH runs by default — they measure a startup
|
|
60
|
+
// race, not steady-state value. `CG_INCLUDE_RACED=1` keeps them (to see the raw
|
|
61
|
+
// distribution). The WITHOUT arm has no MCP, so it's never raced.
|
|
62
|
+
const includeRaced = process.env.CG_INCLUDE_RACED === '1';
|
|
63
|
+
const W = [], WO = []; let racedExcluded = 0;
|
|
64
|
+
for (const rd of runDirs) {
|
|
65
|
+
const w = parse(join(dir, rd, 'run-headless-with.jsonl'));
|
|
66
|
+
if (w) { if (w.raced && !includeRaced) racedExcluded++; else W.push(w); }
|
|
67
|
+
const wo = parse(join(dir, rd, 'run-headless-without.jsonl')); if (wo) WO.push(wo);
|
|
68
|
+
}
|
|
69
|
+
if (!W.length || !WO.length) { console.log(`${repo.padEnd(11)} (incomplete: w=${W.length} wo=${WO.length})`); continue; }
|
|
70
|
+
const m = (arr, k) => median(arr.map(x => x[k]));
|
|
71
|
+
const wT = m(W, 'dur'), woT = m(WO, 'dur'), wTok = m(W, 'tokens'), woTok = m(WO, 'tokens');
|
|
72
|
+
const wC = m(W, 'cost'), woC = m(WO, 'cost'), wTl = m(W, 'tools'), woTl = m(WO, 'tools');
|
|
73
|
+
savings.time.push(pct(wT, woT)); savings.tokens.push(pct(wTok, woTok)); savings.cost.push(pct(wC, woC)); savings.tools.push(pct(wTl, woTl));
|
|
74
|
+
console.log(
|
|
75
|
+
`${repo.padEnd(11)} ${W.length}/${WO.length} ` +
|
|
76
|
+
`${(fmtTime(wT) + '→' + fmtTime(woT)).padEnd(22)}` +
|
|
77
|
+
`${(Math.round(wTl) + '→' + Math.round(woTl)).padEnd(12)}` +
|
|
78
|
+
`${(fmtTok(wTok) + '→' + fmtTok(woTok) + ' (' + pct(wTok, woTok) + '%)').padEnd(24)}` +
|
|
79
|
+
`$${wC.toFixed(2)}→$${woC.toFixed(2)} (${pct(wC, woC)}%)` +
|
|
80
|
+
(racedExcluded ? ` [${racedExcluded} raced run${racedExcluded === 1 ? '' : 's'} excluded]` : '')
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const avg = (a) => a.length ? Math.round(a.reduce((s, x) => s + x, 0) / a.length) : 0;
|
|
84
|
+
console.log(`\nAVERAGE saved: cost ${avg(savings.cost)}% · tokens ${avg(savings.tokens)}% · time ${avg(savings.time)}% · tool calls ${avg(savings.tools)}%`);
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Parse a Claude Code stream-json run log: tool-call sequence + token usage.
|
|
3
|
-
import { readFileSync } from 'fs';
|
|
4
|
-
const file = process.argv[2];
|
|
5
|
-
const lines = readFileSync(file, 'utf8').split('\n').filter(Boolean);
|
|
6
|
-
|
|
7
|
-
const toolCalls = [];
|
|
8
|
-
let result = null;
|
|
9
|
-
let initTools = null;
|
|
10
|
-
|
|
11
|
-
for (const line of lines) {
|
|
12
|
-
let ev;
|
|
13
|
-
try { ev = JSON.parse(line); } catch { continue; }
|
|
14
|
-
if (ev.type === 'system' && ev.subtype === 'init') {
|
|
15
|
-
initTools = (ev.tools || []).filter(t => /homegraph/.test(t));
|
|
16
|
-
}
|
|
17
|
-
if (ev.type === 'assistant' && ev.message?.content) {
|
|
18
|
-
for (const block of ev.message.content) {
|
|
19
|
-
if (block.type === 'tool_use') {
|
|
20
|
-
let detail = '';
|
|
21
|
-
if (block.name === 'Task') detail = ` [subagent_type=${block.input?.subagent_type ?? '?'}] ${(block.input?.description ?? '').slice(0,40)}`;
|
|
22
|
-
else if (/homegraph/.test(block.name)) detail = ` ${JSON.stringify(block.input?.query ?? block.input?.task ?? block.input?.symbol ?? '').slice(0,60)}`;
|
|
23
|
-
else if (block.name === 'Bash') detail = ` ${(block.input?.command ?? '').slice(0,50)}`;
|
|
24
|
-
else if (block.name === 'Read') detail = ` ${(block.input?.file_path ?? '').split('/').slice(-1)[0]}`;
|
|
25
|
-
toolCalls.push(`${block.name}${detail}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if (ev.type === 'result') result = ev;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
console.log(`\n=== ${file.split('/').pop()} ===`);
|
|
33
|
-
console.log(`homegraph tools exposed: ${initTools ? initTools.length : '?'}`);
|
|
34
|
-
console.log(`\nTool calls (${toolCalls.length}):`);
|
|
35
|
-
const counts = {};
|
|
36
|
-
for (const tc of toolCalls) { const n = tc.split(' ')[0]; counts[n] = (counts[n]||0)+1; }
|
|
37
|
-
console.log(' by type:', JSON.stringify(counts));
|
|
38
|
-
toolCalls.forEach((tc, i) => console.log(` ${i+1}. ${tc}`));
|
|
39
|
-
|
|
40
|
-
if (result) {
|
|
41
|
-
const u = result.usage || {};
|
|
42
|
-
const totalIn = (u.input_tokens||0) + (u.cache_read_input_tokens||0) + (u.cache_creation_input_tokens||0);
|
|
43
|
-
console.log(`\nResult: ${result.subtype} | duration ${(result.duration_ms/1000).toFixed(0)}s | turns ${result.num_turns}`);
|
|
44
|
-
console.log(` tokens: in=${totalIn} out=${u.output_tokens||0} | cost $${(result.total_cost_usd||0).toFixed(3)}`);
|
|
45
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Parse a Claude Code stream-json run log: tool-call sequence + token usage.
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
const file = process.argv[2];
|
|
5
|
+
const lines = readFileSync(file, 'utf8').split('\n').filter(Boolean);
|
|
6
|
+
|
|
7
|
+
const toolCalls = [];
|
|
8
|
+
let result = null;
|
|
9
|
+
let initTools = null;
|
|
10
|
+
|
|
11
|
+
for (const line of lines) {
|
|
12
|
+
let ev;
|
|
13
|
+
try { ev = JSON.parse(line); } catch { continue; }
|
|
14
|
+
if (ev.type === 'system' && ev.subtype === 'init') {
|
|
15
|
+
initTools = (ev.tools || []).filter(t => /homegraph/.test(t));
|
|
16
|
+
}
|
|
17
|
+
if (ev.type === 'assistant' && ev.message?.content) {
|
|
18
|
+
for (const block of ev.message.content) {
|
|
19
|
+
if (block.type === 'tool_use') {
|
|
20
|
+
let detail = '';
|
|
21
|
+
if (block.name === 'Task') detail = ` [subagent_type=${block.input?.subagent_type ?? '?'}] ${(block.input?.description ?? '').slice(0,40)}`;
|
|
22
|
+
else if (/homegraph/.test(block.name)) detail = ` ${JSON.stringify(block.input?.query ?? block.input?.task ?? block.input?.symbol ?? '').slice(0,60)}`;
|
|
23
|
+
else if (block.name === 'Bash') detail = ` ${(block.input?.command ?? '').slice(0,50)}`;
|
|
24
|
+
else if (block.name === 'Read') detail = ` ${(block.input?.file_path ?? '').split('/').slice(-1)[0]}`;
|
|
25
|
+
toolCalls.push(`${block.name}${detail}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (ev.type === 'result') result = ev;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
console.log(`\n=== ${file.split('/').pop()} ===`);
|
|
33
|
+
console.log(`homegraph tools exposed: ${initTools ? initTools.length : '?'}`);
|
|
34
|
+
console.log(`\nTool calls (${toolCalls.length}):`);
|
|
35
|
+
const counts = {};
|
|
36
|
+
for (const tc of toolCalls) { const n = tc.split(' ')[0]; counts[n] = (counts[n]||0)+1; }
|
|
37
|
+
console.log(' by type:', JSON.stringify(counts));
|
|
38
|
+
toolCalls.forEach((tc, i) => console.log(` ${i+1}. ${tc}`));
|
|
39
|
+
|
|
40
|
+
if (result) {
|
|
41
|
+
const u = result.usage || {};
|
|
42
|
+
const totalIn = (u.input_tokens||0) + (u.cache_read_input_tokens||0) + (u.cache_creation_input_tokens||0);
|
|
43
|
+
console.log(`\nResult: ${result.subtype} | duration ${(result.duration_ms/1000).toFixed(0)}s | turns ${result.num_turns}`);
|
|
44
|
+
console.log(` tokens: in=${totalIn} out=${u.output_tokens||0} | cost $${(result.total_cost_usd||0).toFixed(3)}`);
|
|
45
|
+
}
|
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Parse the newest Claude Code session log for a project + its subagent logs,
|
|
3
|
-
// and report the tool-call breakdown (main + subagents). Works for interactive
|
|
4
|
-
// runs (driven via itrun.sh) — Claude Code writes full transcripts to
|
|
5
|
-
// ~/.claude/projects/<escaped-cwd>/<session>.jsonl with subagents/ alongside.
|
|
6
|
-
import { readFileSync, readdirSync, statSync, existsSync, realpathSync } from 'fs';
|
|
7
|
-
import { join } from 'path';
|
|
8
|
-
import { homedir } from 'os';
|
|
9
|
-
|
|
10
|
-
const projectArg = process.argv[2];
|
|
11
|
-
if (!projectArg) { console.error('usage: parse-session.mjs <project-dir>'); process.exit(1); }
|
|
12
|
-
|
|
13
|
-
// Claude Code escapes the (real) cwd by replacing every "/" with "-".
|
|
14
|
-
const real = realpathSync(projectArg);
|
|
15
|
-
const escaped = real.replace(/\//g, '-');
|
|
16
|
-
const projDir = join(homedir(), '.claude', 'projects', escaped);
|
|
17
|
-
if (!existsSync(projDir)) { console.error('no session logs at', projDir); process.exit(1); }
|
|
18
|
-
|
|
19
|
-
// Newest top-level session .jsonl
|
|
20
|
-
const sessions = readdirSync(projDir)
|
|
21
|
-
.filter(f => f.endsWith('.jsonl'))
|
|
22
|
-
.map(f => ({ f, m: statSync(join(projDir, f)).mtimeMs }))
|
|
23
|
-
.sort((a, b) => b.m - a.m);
|
|
24
|
-
if (sessions.length === 0) { console.error('no .jsonl sessions in', projDir); process.exit(1); }
|
|
25
|
-
const sessionId = sessions[0].f.replace('.jsonl', '');
|
|
26
|
-
|
|
27
|
-
function tally(file) {
|
|
28
|
-
const counts = {};
|
|
29
|
-
for (const line of readFileSync(file, 'utf8').split('\n')) {
|
|
30
|
-
if (!line) continue;
|
|
31
|
-
let ev; try { ev = JSON.parse(line); } catch { continue; }
|
|
32
|
-
const content = ev.message?.content;
|
|
33
|
-
if (!Array.isArray(content)) continue;
|
|
34
|
-
for (const b of content) {
|
|
35
|
-
if (b.type === 'tool_use') counts[b.name] = (counts[b.name] || 0) + 1;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return counts;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Sum token usage from a transcript. The TUI's "Done (…Xk tokens…)" line only
|
|
42
|
-
// covers a subagent's throughput; this works for main-thread runs too and is
|
|
43
|
-
// consistent across both paths. `gen` = output, `fresh` = uncached input
|
|
44
|
-
// (input + cache_creation), `cached` = cache reads (≈free), `total` = all.
|
|
45
|
-
function sumTokens(file) {
|
|
46
|
-
const t = { gen: 0, fresh: 0, cached: 0 };
|
|
47
|
-
for (const line of readFileSync(file, 'utf8').split('\n')) {
|
|
48
|
-
if (!line) continue;
|
|
49
|
-
let ev; try { ev = JSON.parse(line); } catch { continue; }
|
|
50
|
-
const u = ev.message?.usage;
|
|
51
|
-
if (!u) continue;
|
|
52
|
-
t.gen += u.output_tokens || 0;
|
|
53
|
-
t.fresh += (u.input_tokens || 0) + (u.cache_creation_input_tokens || 0);
|
|
54
|
-
t.cached += u.cache_read_input_tokens || 0;
|
|
55
|
-
}
|
|
56
|
-
return t;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const mainCounts = tally(join(projDir, sessionId + '.jsonl'));
|
|
60
|
-
|
|
61
|
-
// Subagent transcripts live under <session>/subagents/*.jsonl
|
|
62
|
-
const subDir = join(projDir, sessionId, 'subagents');
|
|
63
|
-
const subCounts = {};
|
|
64
|
-
let subAgentFiles = 0;
|
|
65
|
-
if (existsSync(subDir)) {
|
|
66
|
-
for (const f of readdirSync(subDir).filter(f => f.endsWith('.jsonl'))) {
|
|
67
|
-
subAgentFiles++;
|
|
68
|
-
const c = tally(join(subDir, f));
|
|
69
|
-
for (const [k, v] of Object.entries(c)) subCounts[k] = (subCounts[k] || 0) + v;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const fmt = (counts) => Object.entries(counts).sort((a, b) => b[1] - a[1])
|
|
74
|
-
.map(([k, v]) => ` ${String(v).padStart(3)} ${k}`).join('\n') || ' (none)';
|
|
75
|
-
|
|
76
|
-
console.log(`session: ${sessionId}`);
|
|
77
|
-
console.log(`\nMAIN thread tools:\n${fmt(mainCounts)}`);
|
|
78
|
-
console.log(`\nSUBAGENT tools (${subAgentFiles} subagent transcript${subAgentFiles === 1 ? '' : 's'}):\n${fmt(subCounts)}`);
|
|
79
|
-
|
|
80
|
-
const explore = subCounts['mcp__homegraph__homegraph_explore'] || mainCounts['mcp__homegraph__homegraph_explore'] || 0;
|
|
81
|
-
const reads = (subCounts['Read'] || 0) + (mainCounts['Read'] || 0);
|
|
82
|
-
const greps = (subCounts['Grep'] || 0) + (mainCounts['Grep'] || 0) + (subCounts['Bash'] || 0) + (mainCounts['Bash'] || 0);
|
|
83
|
-
console.log(`\nVERDICT: homegraph_explore used ${explore}x | Read ${reads} | Grep/Bash ${greps}`);
|
|
84
|
-
|
|
85
|
-
// Token totals (main + subagents), consistent across main-thread and subagent runs.
|
|
86
|
-
const tok = { gen: 0, fresh: 0, cached: 0 };
|
|
87
|
-
const addTok = (t) => { tok.gen += t.gen; tok.fresh += t.fresh; tok.cached += t.cached; };
|
|
88
|
-
addTok(sumTokens(join(projDir, sessionId + '.jsonl')));
|
|
89
|
-
if (existsSync(subDir)) {
|
|
90
|
-
for (const f of readdirSync(subDir).filter(f => f.endsWith('.jsonl'))) addTok(sumTokens(join(subDir, f)));
|
|
91
|
-
}
|
|
92
|
-
const k = (n) => (n / 1000).toFixed(1) + 'k';
|
|
93
|
-
console.log(`TOKENS: gen ${k(tok.gen)} | fresh-in ${k(tok.fresh)} | cached-in ${k(tok.cached)} | billable≈ ${k(tok.gen + tok.fresh)}`);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Parse the newest Claude Code session log for a project + its subagent logs,
|
|
3
|
+
// and report the tool-call breakdown (main + subagents). Works for interactive
|
|
4
|
+
// runs (driven via itrun.sh) — Claude Code writes full transcripts to
|
|
5
|
+
// ~/.claude/projects/<escaped-cwd>/<session>.jsonl with subagents/ alongside.
|
|
6
|
+
import { readFileSync, readdirSync, statSync, existsSync, realpathSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
|
|
10
|
+
const projectArg = process.argv[2];
|
|
11
|
+
if (!projectArg) { console.error('usage: parse-session.mjs <project-dir>'); process.exit(1); }
|
|
12
|
+
|
|
13
|
+
// Claude Code escapes the (real) cwd by replacing every "/" with "-".
|
|
14
|
+
const real = realpathSync(projectArg);
|
|
15
|
+
const escaped = real.replace(/\//g, '-');
|
|
16
|
+
const projDir = join(homedir(), '.claude', 'projects', escaped);
|
|
17
|
+
if (!existsSync(projDir)) { console.error('no session logs at', projDir); process.exit(1); }
|
|
18
|
+
|
|
19
|
+
// Newest top-level session .jsonl
|
|
20
|
+
const sessions = readdirSync(projDir)
|
|
21
|
+
.filter(f => f.endsWith('.jsonl'))
|
|
22
|
+
.map(f => ({ f, m: statSync(join(projDir, f)).mtimeMs }))
|
|
23
|
+
.sort((a, b) => b.m - a.m);
|
|
24
|
+
if (sessions.length === 0) { console.error('no .jsonl sessions in', projDir); process.exit(1); }
|
|
25
|
+
const sessionId = sessions[0].f.replace('.jsonl', '');
|
|
26
|
+
|
|
27
|
+
function tally(file) {
|
|
28
|
+
const counts = {};
|
|
29
|
+
for (const line of readFileSync(file, 'utf8').split('\n')) {
|
|
30
|
+
if (!line) continue;
|
|
31
|
+
let ev; try { ev = JSON.parse(line); } catch { continue; }
|
|
32
|
+
const content = ev.message?.content;
|
|
33
|
+
if (!Array.isArray(content)) continue;
|
|
34
|
+
for (const b of content) {
|
|
35
|
+
if (b.type === 'tool_use') counts[b.name] = (counts[b.name] || 0) + 1;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return counts;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Sum token usage from a transcript. The TUI's "Done (…Xk tokens…)" line only
|
|
42
|
+
// covers a subagent's throughput; this works for main-thread runs too and is
|
|
43
|
+
// consistent across both paths. `gen` = output, `fresh` = uncached input
|
|
44
|
+
// (input + cache_creation), `cached` = cache reads (≈free), `total` = all.
|
|
45
|
+
function sumTokens(file) {
|
|
46
|
+
const t = { gen: 0, fresh: 0, cached: 0 };
|
|
47
|
+
for (const line of readFileSync(file, 'utf8').split('\n')) {
|
|
48
|
+
if (!line) continue;
|
|
49
|
+
let ev; try { ev = JSON.parse(line); } catch { continue; }
|
|
50
|
+
const u = ev.message?.usage;
|
|
51
|
+
if (!u) continue;
|
|
52
|
+
t.gen += u.output_tokens || 0;
|
|
53
|
+
t.fresh += (u.input_tokens || 0) + (u.cache_creation_input_tokens || 0);
|
|
54
|
+
t.cached += u.cache_read_input_tokens || 0;
|
|
55
|
+
}
|
|
56
|
+
return t;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const mainCounts = tally(join(projDir, sessionId + '.jsonl'));
|
|
60
|
+
|
|
61
|
+
// Subagent transcripts live under <session>/subagents/*.jsonl
|
|
62
|
+
const subDir = join(projDir, sessionId, 'subagents');
|
|
63
|
+
const subCounts = {};
|
|
64
|
+
let subAgentFiles = 0;
|
|
65
|
+
if (existsSync(subDir)) {
|
|
66
|
+
for (const f of readdirSync(subDir).filter(f => f.endsWith('.jsonl'))) {
|
|
67
|
+
subAgentFiles++;
|
|
68
|
+
const c = tally(join(subDir, f));
|
|
69
|
+
for (const [k, v] of Object.entries(c)) subCounts[k] = (subCounts[k] || 0) + v;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const fmt = (counts) => Object.entries(counts).sort((a, b) => b[1] - a[1])
|
|
74
|
+
.map(([k, v]) => ` ${String(v).padStart(3)} ${k}`).join('\n') || ' (none)';
|
|
75
|
+
|
|
76
|
+
console.log(`session: ${sessionId}`);
|
|
77
|
+
console.log(`\nMAIN thread tools:\n${fmt(mainCounts)}`);
|
|
78
|
+
console.log(`\nSUBAGENT tools (${subAgentFiles} subagent transcript${subAgentFiles === 1 ? '' : 's'}):\n${fmt(subCounts)}`);
|
|
79
|
+
|
|
80
|
+
const explore = subCounts['mcp__homegraph__homegraph_explore'] || mainCounts['mcp__homegraph__homegraph_explore'] || 0;
|
|
81
|
+
const reads = (subCounts['Read'] || 0) + (mainCounts['Read'] || 0);
|
|
82
|
+
const greps = (subCounts['Grep'] || 0) + (mainCounts['Grep'] || 0) + (subCounts['Bash'] || 0) + (mainCounts['Bash'] || 0);
|
|
83
|
+
console.log(`\nVERDICT: homegraph_explore used ${explore}x | Read ${reads} | Grep/Bash ${greps}`);
|
|
84
|
+
|
|
85
|
+
// Token totals (main + subagents), consistent across main-thread and subagent runs.
|
|
86
|
+
const tok = { gen: 0, fresh: 0, cached: 0 };
|
|
87
|
+
const addTok = (t) => { tok.gen += t.gen; tok.fresh += t.fresh; tok.cached += t.cached; };
|
|
88
|
+
addTok(sumTokens(join(projDir, sessionId + '.jsonl')));
|
|
89
|
+
if (existsSync(subDir)) {
|
|
90
|
+
for (const f of readdirSync(subDir).filter(f => f.endsWith('.jsonl'))) addTok(sumTokens(join(subDir, f)));
|
|
91
|
+
}
|
|
92
|
+
const k = (n) => (n / 1000).toFixed(1) + 'k';
|
|
93
|
+
console.log(`TOKENS: gen ${k(tok.gen)} | fresh-in ${k(tok.fresh)} | cached-in ${k(tok.cached)} | billable≈ ${k(tok.gen + tok.fresh)}`);
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Probe homegraph_context (with call-paths) against an index using the built dist.
|
|
3
|
-
// Usage: node probe-context.mjs <repo-with-.homegraph> <task words...>
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import { resolve } from 'node:path';
|
|
6
|
-
|
|
7
|
-
const [, , repo, ...taskParts] = process.argv;
|
|
8
|
-
const task = taskParts.join(' ');
|
|
9
|
-
if (!repo || !task) { console.error('usage: probe-context.mjs <repo> <task...>'); process.exit(1); }
|
|
10
|
-
|
|
11
|
-
const load = async (rel) => import(pathToFileURL(resolve(rel)).href);
|
|
12
|
-
const idx = await load('dist/index.js');
|
|
13
|
-
const tools = await load('dist/mcp/tools.js');
|
|
14
|
-
const HomeGraph = idx.default?.default ?? idx.default ?? idx.HomeGraph;
|
|
15
|
-
const ToolHandler = tools.ToolHandler ?? tools.default?.ToolHandler;
|
|
16
|
-
|
|
17
|
-
const cg = HomeGraph.openSync(repo);
|
|
18
|
-
const h = new ToolHandler(cg);
|
|
19
|
-
const res = await h.execute('homegraph_context', { task });
|
|
20
|
-
console.log(res.content?.[0]?.text ?? '(no text)');
|
|
21
|
-
try { cg.close?.(); } catch {}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Probe homegraph_context (with call-paths) against an index using the built dist.
|
|
3
|
+
// Usage: node probe-context.mjs <repo-with-.homegraph> <task words...>
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
|
|
7
|
+
const [, , repo, ...taskParts] = process.argv;
|
|
8
|
+
const task = taskParts.join(' ');
|
|
9
|
+
if (!repo || !task) { console.error('usage: probe-context.mjs <repo> <task...>'); process.exit(1); }
|
|
10
|
+
|
|
11
|
+
const load = async (rel) => import(pathToFileURL(resolve(rel)).href);
|
|
12
|
+
const idx = await load('dist/index.js');
|
|
13
|
+
const tools = await load('dist/mcp/tools.js');
|
|
14
|
+
const HomeGraph = idx.default?.default ?? idx.default ?? idx.HomeGraph;
|
|
15
|
+
const ToolHandler = tools.ToolHandler ?? tools.default?.ToolHandler;
|
|
16
|
+
|
|
17
|
+
const cg = HomeGraph.openSync(repo);
|
|
18
|
+
const h = new ToolHandler(cg);
|
|
19
|
+
const res = await h.execute('homegraph_context', { task });
|
|
20
|
+
console.log(res.content?.[0]?.text ?? '(no text)');
|
|
21
|
+
try { cg.close?.(); } catch {}
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// One-shot probe: run handleExplore against an existing index using the built
|
|
3
|
-
// dist, print the output + a few stats. Lets us verify explore's coverage fix
|
|
4
|
-
// without a full agent run. Usage: node probe-explore.mjs <repo-with-.homegraph> "<query>"
|
|
5
|
-
import { pathToFileURL } from 'node:url';
|
|
6
|
-
import { resolve } from 'node:path';
|
|
7
|
-
|
|
8
|
-
const [, , repo, query] = process.argv;
|
|
9
|
-
if (!repo || !query) {
|
|
10
|
-
console.error('usage: probe-explore.mjs <repo> "<query>"');
|
|
11
|
-
process.exit(1);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const load = async (rel) => import(pathToFileURL(resolve(rel)).href);
|
|
15
|
-
const idx = await load('dist/index.js');
|
|
16
|
-
const tools = await load('dist/mcp/tools.js');
|
|
17
|
-
|
|
18
|
-
// esModuleInterop: dynamic import of CJS yields { default: module.exports, ...named }
|
|
19
|
-
const HomeGraph = idx.default?.default ?? idx.default ?? idx.HomeGraph;
|
|
20
|
-
const ToolHandler = tools.ToolHandler ?? tools.default?.ToolHandler;
|
|
21
|
-
|
|
22
|
-
if (typeof HomeGraph?.openSync !== 'function') {
|
|
23
|
-
console.error('could not resolve HomeGraph.openSync; index keys:', Object.keys(idx), 'default keys:', idx.default && Object.keys(idx.default));
|
|
24
|
-
process.exit(2);
|
|
25
|
-
}
|
|
26
|
-
if (typeof ToolHandler !== 'function') {
|
|
27
|
-
console.error('could not resolve ToolHandler; tools keys:', Object.keys(tools));
|
|
28
|
-
process.exit(2);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const cg = HomeGraph.openSync(repo);
|
|
32
|
-
const h = new ToolHandler(cg);
|
|
33
|
-
const res = await h.execute('homegraph_explore', { query });
|
|
34
|
-
const text = res.content?.[0]?.text ?? '(no text)';
|
|
35
|
-
console.log(text);
|
|
36
|
-
console.error('\n--- PROBE STATS ---');
|
|
37
|
-
console.error('output chars:', text.length);
|
|
38
|
-
console.error('triggerRender body present (-> setState({})):', /triggerRender[\s\S]{0,400}setState\(\{\}\)/.test(text));
|
|
39
|
-
console.error('App.tsx in source section:', /\*\*`.*App\.tsx`\*\* —/.test(text));
|
|
40
|
-
try { cg.close?.(); } catch {}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// One-shot probe: run handleExplore against an existing index using the built
|
|
3
|
+
// dist, print the output + a few stats. Lets us verify explore's coverage fix
|
|
4
|
+
// without a full agent run. Usage: node probe-explore.mjs <repo-with-.homegraph> "<query>"
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
|
|
8
|
+
const [, , repo, query] = process.argv;
|
|
9
|
+
if (!repo || !query) {
|
|
10
|
+
console.error('usage: probe-explore.mjs <repo> "<query>"');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const load = async (rel) => import(pathToFileURL(resolve(rel)).href);
|
|
15
|
+
const idx = await load('dist/index.js');
|
|
16
|
+
const tools = await load('dist/mcp/tools.js');
|
|
17
|
+
|
|
18
|
+
// esModuleInterop: dynamic import of CJS yields { default: module.exports, ...named }
|
|
19
|
+
const HomeGraph = idx.default?.default ?? idx.default ?? idx.HomeGraph;
|
|
20
|
+
const ToolHandler = tools.ToolHandler ?? tools.default?.ToolHandler;
|
|
21
|
+
|
|
22
|
+
if (typeof HomeGraph?.openSync !== 'function') {
|
|
23
|
+
console.error('could not resolve HomeGraph.openSync; index keys:', Object.keys(idx), 'default keys:', idx.default && Object.keys(idx.default));
|
|
24
|
+
process.exit(2);
|
|
25
|
+
}
|
|
26
|
+
if (typeof ToolHandler !== 'function') {
|
|
27
|
+
console.error('could not resolve ToolHandler; tools keys:', Object.keys(tools));
|
|
28
|
+
process.exit(2);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const cg = HomeGraph.openSync(repo);
|
|
32
|
+
const h = new ToolHandler(cg);
|
|
33
|
+
const res = await h.execute('homegraph_explore', { query });
|
|
34
|
+
const text = res.content?.[0]?.text ?? '(no text)';
|
|
35
|
+
console.log(text);
|
|
36
|
+
console.error('\n--- PROBE STATS ---');
|
|
37
|
+
console.error('output chars:', text.length);
|
|
38
|
+
console.error('triggerRender body present (-> setState({})):', /triggerRender[\s\S]{0,400}setState\(\{\}\)/.test(text));
|
|
39
|
+
console.error('App.tsx in source section:', /\*\*`.*App\.tsx`\*\* —/.test(text));
|
|
40
|
+
try { cg.close?.(); } catch {}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Probe homegraph_node (with trail) against an index using the built dist.
|
|
3
|
-
// Usage: node probe-node.mjs <repo-with-.homegraph> <symbol> [code]
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import { resolve } from 'node:path';
|
|
6
|
-
|
|
7
|
-
const [, , repo, symbol, code] = process.argv;
|
|
8
|
-
if (!repo || !symbol) { console.error('usage: probe-node.mjs <repo> <symbol> [code]'); process.exit(1); }
|
|
9
|
-
|
|
10
|
-
const load = async (rel) => import(pathToFileURL(resolve(rel)).href);
|
|
11
|
-
const idx = await load('dist/index.js');
|
|
12
|
-
const tools = await load('dist/mcp/tools.js');
|
|
13
|
-
const HomeGraph = idx.default?.default ?? idx.default ?? idx.HomeGraph;
|
|
14
|
-
const ToolHandler = tools.ToolHandler ?? tools.default?.ToolHandler;
|
|
15
|
-
|
|
16
|
-
const cg = HomeGraph.openSync(repo);
|
|
17
|
-
const h = new ToolHandler(cg);
|
|
18
|
-
const res = await h.execute('homegraph_node', { symbol, includeCode: code === 'code' });
|
|
19
|
-
console.log(res.content?.[0]?.text ?? '(no text)');
|
|
20
|
-
try { cg.close?.(); } catch {}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Probe homegraph_node (with trail) against an index using the built dist.
|
|
3
|
+
// Usage: node probe-node.mjs <repo-with-.homegraph> <symbol> [code]
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
|
|
7
|
+
const [, , repo, symbol, code] = process.argv;
|
|
8
|
+
if (!repo || !symbol) { console.error('usage: probe-node.mjs <repo> <symbol> [code]'); process.exit(1); }
|
|
9
|
+
|
|
10
|
+
const load = async (rel) => import(pathToFileURL(resolve(rel)).href);
|
|
11
|
+
const idx = await load('dist/index.js');
|
|
12
|
+
const tools = await load('dist/mcp/tools.js');
|
|
13
|
+
const HomeGraph = idx.default?.default ?? idx.default ?? idx.HomeGraph;
|
|
14
|
+
const ToolHandler = tools.ToolHandler ?? tools.default?.ToolHandler;
|
|
15
|
+
|
|
16
|
+
const cg = HomeGraph.openSync(repo);
|
|
17
|
+
const h = new ToolHandler(cg);
|
|
18
|
+
const res = await h.execute('homegraph_node', { symbol, includeCode: code === 'code' });
|
|
19
|
+
console.log(res.content?.[0]?.text ?? '(no text)');
|
|
20
|
+
try { cg.close?.(); } catch {}
|