@sdsrs/code-graph
Advanced tools
@@ -7,3 +7,3 @@ { | ||
| }, | ||
| "version": "0.25.1", | ||
| "version": "0.26.0", | ||
| "keywords": [ | ||
@@ -10,0 +10,0 @@ "code-graph", |
@@ -121,3 +121,8 @@ #!/usr/bin/env node | ||
| // --- Only inject if high-impact (2+ production callers) --- | ||
| // --- Inject when the symbol has any caller (1+) --- | ||
| // Earlier gate was 2+ direct callers; reality is that editing a function with | ||
| // even one production caller benefits from a one-line impact summary, and the | ||
| // per-symbol 2-minute cooldown caps frequency. The 2+ floor was a remnant of | ||
| // the v0.21 "agent picks tools without push" assumption — same bias mem #8234 | ||
| // records as bounded leverage at the bench level. | ||
| const directCallers = jsonResult.direct_callers || 0; | ||
@@ -128,3 +133,3 @@ const totalCallers = jsonResult.total_callers || 0; | ||
| if (directCallers < 2) process.exit(0); | ||
| if (directCallers < 1) process.exit(0); | ||
@@ -131,0 +136,0 @@ // Mark cooldown |
@@ -36,19 +36,26 @@ #!/usr/bin/env node | ||
| // v0.21 — flipped to opt-in default. Routing-bench backend P@1=100% (v0.20.0) | ||
| // proves Sonnet 4.5 picks tools correctly without push injection; per-prompt | ||
| // CLI exec was costing 200-500 tokens/turn across N turns to repeat what the | ||
| // agent would have called itself. Mirrors session-init.js computeQuietHooks | ||
| // priority chain so a single env knob covers both hooks. | ||
| // Default ON. The v0.21 opt-in flip relied on routing-bench P@1=100% to argue | ||
| // "Sonnet 4.5 picks tools without push injection." That bench measures *triage | ||
| // accuracy once the agent has decided to query a tool*; it does not measure | ||
| // the prior question — *whether the agent reaches for a tool at all*. | ||
| // pre-grep-guide.js sees the real-world counter-evidence: a 15-day baseline of | ||
| // 429 raw `grep` vs 191 functional CLI calls on the same indexed source tree | ||
| // — a 13× pre-training bias toward grep. Push injection on the relevant turns | ||
| // is the corrective; cooldowns (impact 30s / overview 5min / callgraph 60s / | ||
| // search 60s) already cap per-type frequency. SessionStart `project_map` stays | ||
| // default OFF (lifecycle.js / session-init.js) — that one is a static dump | ||
| // duplicated by MEMORY.md; this hook is reactive and trigger-shaped, so the | ||
| // two defaults diverge intentionally. | ||
| // | ||
| // Priority (high → low): | ||
| // 1. CODE_GRAPH_QUIET_HOOKS=0 → forced noisy (legacy back-compat) | ||
| // 2. CODE_GRAPH_QUIET_HOOKS=1 → forced quiet (legacy back-compat) | ||
| // 3. CODE_GRAPH_VERBOSE_HOOKS=1 → opt-in noisy (new, recommended) | ||
| // 4. default → quiet | ||
| // 1. CODE_GRAPH_QUIET_HOOKS=1 → forced quiet (escape hatch) | ||
| // 2. CODE_GRAPH_QUIET_HOOKS=0 → forced noisy (legacy back-compat, redundant with default) | ||
| // 3. CODE_GRAPH_VERBOSE_HOOKS=1 → noisy (legacy back-compat, redundant with default) | ||
| // 4. default → noisy | ||
| function computeQuietHooks(env = process.env) { | ||
| const envQuiet = env.CODE_GRAPH_QUIET_HOOKS; | ||
| if (envQuiet === '1') return true; | ||
| if (envQuiet === '0') return false; | ||
| if (envQuiet === '1') return true; | ||
| if (env.CODE_GRAPH_VERBOSE_HOOKS === '1') return false; | ||
| return true; | ||
| return false; | ||
| } | ||
@@ -55,0 +62,0 @@ |
@@ -514,60 +514,46 @@ 'use strict'; | ||
| test('CODE_GRAPH_QUIET_HOOKS=1 short-circuits silently on stdout, stderr, exit 0', () => { | ||
| const { spawnSync } = require('node:child_process'); | ||
| const script = path.join(__dirname, 'user-prompt-context.js'); | ||
| const proc = spawnSync(process.execPath, [script], { | ||
| input: JSON.stringify({ message: 'impact analysis for fn_that_would_trigger_search' }), | ||
| env: { ...process.env, CODE_GRAPH_QUIET_HOOKS: '1' }, | ||
| encoding: 'utf8', | ||
| timeout: 2000, | ||
| }); | ||
| // Quiet mode must be fully silent — any stderr leaks into Claude's display. | ||
| assert.equal(proc.stdout, '', 'stdout must be empty'); | ||
| assert.equal(proc.stderr, '', 'stderr must be empty'); | ||
| assert.equal(proc.status, 0, 'must exit 0'); | ||
| }); | ||
| // ── computeQuietHooks priority chain (default-noisy flip) ──────── | ||
| // ── computeQuietHooks priority chain (v0.21 opt-in flip) ──────── | ||
| test('computeQuietHooks: default (no env) is QUIET', () => { | ||
| // v0.21: flipped from opt-out to opt-in. Routing-bench P@1=100% earned | ||
| // the right to stop pushing context the agent would have requested. | ||
| assert.equal(computeQuietHooks({}), true); | ||
| test('computeQuietHooks: default (no env) is NOISY', () => { | ||
| // Default flipped back to push-on. The v0.21 opt-in default relied on | ||
| // routing-bench P@1=100% but that measures triage accuracy, not whether | ||
| // the agent reaches for a tool at all. pre-grep-guide.js sees 13× raw-grep | ||
| // bias on the same source tree — push is the corrective. | ||
| assert.equal(computeQuietHooks({}), false); | ||
| }); | ||
| test('computeQuietHooks: CODE_GRAPH_VERBOSE_HOOKS=1 enables push (opt-in)', () => { | ||
| assert.equal(computeQuietHooks({ CODE_GRAPH_VERBOSE_HOOKS: '1' }), false); | ||
| test('computeQuietHooks: CODE_GRAPH_QUIET_HOOKS=1 forces quiet (escape hatch)', () => { | ||
| assert.equal(computeQuietHooks({ CODE_GRAPH_QUIET_HOOKS: '1' }), true); | ||
| }); | ||
| test('computeQuietHooks: legacy CODE_GRAPH_QUIET_HOOKS=0 forces noisy (back-compat)', () => { | ||
| test('computeQuietHooks: CODE_GRAPH_QUIET_HOOKS=0 stays noisy (back-compat, same as default)', () => { | ||
| assert.equal(computeQuietHooks({ CODE_GRAPH_QUIET_HOOKS: '0' }), false); | ||
| }); | ||
| test('computeQuietHooks: legacy CODE_GRAPH_QUIET_HOOKS=1 forces quiet (back-compat)', () => { | ||
| assert.equal(computeQuietHooks({ CODE_GRAPH_QUIET_HOOKS: '1' }), true); | ||
| test('computeQuietHooks: CODE_GRAPH_VERBOSE_HOOKS=1 stays noisy (back-compat, same as default)', () => { | ||
| assert.equal(computeQuietHooks({ CODE_GRAPH_VERBOSE_HOOKS: '1' }), false); | ||
| }); | ||
| test('computeQuietHooks: legacy QUIET_HOOKS=0 wins over VERBOSE_HOOKS=1 (priority chain)', () => { | ||
| // Priority order: CODE_GRAPH_QUIET_HOOKS=0/1 > CODE_GRAPH_VERBOSE_HOOKS > default. | ||
| test('computeQuietHooks: QUIET_HOOKS=1 wins over VERBOSE_HOOKS=1 (priority chain)', () => { | ||
| // Priority: CODE_GRAPH_QUIET_HOOKS=1 (escape) > QUIET_HOOKS=0 / VERBOSE_HOOKS=1 > default. | ||
| assert.equal(computeQuietHooks({ CODE_GRAPH_QUIET_HOOKS: '1', CODE_GRAPH_VERBOSE_HOOKS: '1' }), true); | ||
| assert.equal(computeQuietHooks({ CODE_GRAPH_QUIET_HOOKS: '0', CODE_GRAPH_VERBOSE_HOOKS: '0' }), false); | ||
| assert.equal(computeQuietHooks({ CODE_GRAPH_QUIET_HOOKS: '1', CODE_GRAPH_VERBOSE_HOOKS: '1' }), true); | ||
| }); | ||
| test('default env (no flags) short-circuits silently — opt-in flip', () => { | ||
| // End-to-end check: with the opt-in flip, a default-env spawn produces | ||
| // no stdout/stderr even on a message that previously would have injected. | ||
| test('CODE_GRAPH_QUIET_HOOKS=1 short-circuits silently on stdout, stderr, exit 0 (escape hatch verified end-to-end)', () => { | ||
| // End-to-end: the escape hatch must produce zero stdout/stderr noise | ||
| // (any leak would land in Claude's display). Was the only e2e check before | ||
| // the default-noisy flip — kept under the new default to guarantee that | ||
| // setting the env still fully silences the hook. | ||
| const { spawnSync } = require('node:child_process'); | ||
| const script = path.join(__dirname, 'user-prompt-context.js'); | ||
| const cleanEnv = { ...process.env }; | ||
| delete cleanEnv.CODE_GRAPH_QUIET_HOOKS; | ||
| delete cleanEnv.CODE_GRAPH_VERBOSE_HOOKS; | ||
| const proc = spawnSync(process.execPath, [script], { | ||
| input: JSON.stringify({ message: 'impact of refactoring parse_code function' }), | ||
| env: cleanEnv, | ||
| env: { ...process.env, CODE_GRAPH_QUIET_HOOKS: '1' }, | ||
| encoding: 'utf8', | ||
| timeout: 2000, | ||
| }); | ||
| assert.equal(proc.stdout, '', 'default must be silent on stdout'); | ||
| assert.equal(proc.stderr, '', 'default must be silent on stderr'); | ||
| assert.equal(proc.status, 0, 'default must exit 0'); | ||
| assert.equal(proc.stdout, '', 'quiet must be silent on stdout'); | ||
| assert.equal(proc.stderr, '', 'quiet must be silent on stderr'); | ||
| assert.equal(proc.status, 0, 'quiet must exit 0'); | ||
| }); |
@@ -18,8 +18,12 @@ --- | ||
| > | ||
| > **v0.17.0 起**:SessionStart `project_map` 注入 **默认 OFF**(不再随 adoption | ||
| > 切换)。本文件 + 7 个工具描述已经覆盖路由所需的全部决策信息,每次会话再 | ||
| > dump ≈2.3 KB 的项目地图是冗余的常驻上下文成本。 | ||
| > 显式启用:`CODE_GRAPH_VERBOSE_HOOKS=1` —— Bash 调 `code-graph-mcp map --compact` | ||
| > 也是等价的按需替代。 | ||
| > 向后兼容:`CODE_GRAPH_QUIET_HOOKS=0` 强制 noisy / `=1` 强制 quiet(优先级最高)。 | ||
| > **Hook 默认值(两个 hook,默认不同 —— 故意的)**: | ||
| > - **SessionStart `project_map` 注入:默认 OFF**(v0.17.0 起)。本文件 + 7 个 | ||
| > 工具描述已经覆盖路由所需决策信息,每次会话再 dump ≈2.3 KB 项目地图是冗余的 | ||
| > 常驻上下文。显式启用:`CODE_GRAPH_VERBOSE_HOOKS=1`;或按需 `code-graph-mcp map --compact`。 | ||
| > - **UserPromptSubmit context push:默认 ON**。基于用户消息 intent 推 impact / | ||
| > overview / callgraph / search 结果(per-type cooldown 30s–5min)。routing-bench | ||
| > P@1=100% 测的是分诊准确率(已决定查工具时选哪个),不等于触发率(是否 | ||
| > 决定查工具)—— 真实 baseline 是 raw-grep ≈13× 偏向于内置 Grep。Push 是 | ||
| > pre-training bias 的矫正。Escape hatch:`CODE_GRAPH_QUIET_HOOKS=1`。 | ||
| > - 优先级:`CODE_GRAPH_QUIET_HOOKS=1` (escape) > 其他 env > 默认。 | ||
| > | ||
@@ -126,2 +130,3 @@ > **v0.18.4 起**:原"进阶 5"(impact / similar / deps / dead-code / trace)已折叠 | ||
| - `CODE_GRAPH_VERBOSE_HOOKS=1`(v0.17.0+) — opt in 到 SessionStart `project_map` 注入(默认 OFF)。 | ||
| - `CODE_GRAPH_QUIET_HOOKS=0` — 强制恢复 `project_map` 注入;优先级高于 VERBOSE_HOOKS(向后兼容路径)。 | ||
| - `CODE_GRAPH_QUIET_HOOKS=1` — UserPromptSubmit context push 的 escape hatch(默认 ON);同时强制 SessionStart `project_map` quiet。 | ||
| - `CODE_GRAPH_QUIET_HOOKS=0` — 强制恢复 SessionStart `project_map` 注入(向后兼容路径)。 |
+6
-6
| { | ||
| "name": "@sdsrs/code-graph", | ||
| "version": "0.25.1", | ||
| "version": "0.26.0", | ||
| "description": "MCP server that indexes codebases into an AST knowledge graph with semantic search, call graph traversal, and HTTP route tracing", | ||
@@ -38,8 +38,8 @@ "license": "MIT", | ||
| "optionalDependencies": { | ||
| "@sdsrs/code-graph-linux-x64": "0.25.1", | ||
| "@sdsrs/code-graph-linux-arm64": "0.25.1", | ||
| "@sdsrs/code-graph-darwin-x64": "0.25.1", | ||
| "@sdsrs/code-graph-darwin-arm64": "0.25.1", | ||
| "@sdsrs/code-graph-win32-x64": "0.25.1" | ||
| "@sdsrs/code-graph-linux-x64": "0.26.0", | ||
| "@sdsrs/code-graph-linux-arm64": "0.26.0", | ||
| "@sdsrs/code-graph-darwin-x64": "0.26.0", | ||
| "@sdsrs/code-graph-darwin-arm64": "0.26.0", | ||
| "@sdsrs/code-graph-win32-x64": "0.26.0" | ||
| } | ||
| } |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
307519
0.34%77
-1.28%6171
-0.02%