@sdsrs/code-graph 0.137.0 → 0.138.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 CHANGED
@@ -590,6 +590,7 @@ plugins' entries. An installer that reads exit codes must not treat it as
590
590
  | Variable | Set by | Purpose |
591
591
  |---|---|---|
592
592
  | `CODE_GRAPH_STATUSLINE_CWD` | statusline-composite | Forwards Claude Code's authoritative cwd to each statusline provider. |
593
+ | `CODE_GRAPH_STATUSLINE_DEBUG` | test suite | Makes statusline-composite print why it dropped a provider instead of swallowing the error. Off by default so a broken third-party provider stays silent in a real statusline. |
593
594
  | `CODE_GRAPH_INSTALL_LOCK_HELD` | launcher | Tells a child that the parent already holds the install lock, so it does not deadlock. |
594
595
  | `CODE_GRAPH_AUTO_UPDATE_SILENT` | session-init | Runs the update check without console output. |
595
596
  | `CODE_GRAPH_NO_ANSWER_IN_DENY` | lifecycle | Keeps `cg-answer` out of the deny path. |
@@ -4,7 +4,7 @@
4
4
  "author": {
5
5
  "name": "sdsrs"
6
6
  },
7
- "version": "0.137.0",
7
+ "version": "0.138.0",
8
8
  "keywords": [
9
9
  "code-graph",
10
10
  "ast",
@@ -140,7 +140,65 @@ function runProvider(command, needsStdin, stdin, id) {
140
140
  })).toString().trim();
141
141
 
142
142
  return out || null;
143
- } catch { return null; }
143
+ } catch (err) {
144
+ // EPIPE is OUR failure to deliver, not the provider's failure to answer.
145
+ // `execFileSync` writes `input` into the child's stdin; a provider that
146
+ // exits without reading it (a static `echo`, an env-var-only line) makes
147
+ // that write fail — and by then the child has already produced its output
148
+ // in full. Discarding it drops the user's original statusline for a reason
149
+ // that has nothing to do with the provider. `_previous` is registered with
150
+ // needsStdin unconditionally true (lifecycle.js), so every such user is
151
+ // exposed. Measured: 10/10 EPIPE with a payload past the pipe buffer and
152
+ // the child's stdout complete in 10 of 10; on ordinary payloads the same
153
+ // discard happens as a timing race, which is what made
154
+ // lifecycle.e2e.test.js's "issue #24" intermittent for two releases.
155
+ // An empty stdout still falls through: nothing was produced, nothing to keep.
156
+ // Gated on a CLEAN exit. A provider that genuinely failed can EPIPE too, and
157
+ // its stdout is then partial by definition. Measured: a `set -e` script that
158
+ // prints one segment and exits 1 salvages as "SEGMENT-ONE |" — half a line,
159
+ // rendered as if whole, and rendering differently run to run depending on
160
+ // which side of the race it lands. `err.status` is 0 in the case this exists
161
+ // for (pre-ship delta review 2026-09-06).
162
+ //
163
+ // `status === 0`, not `!status`: a signal-killed child reports `status:
164
+ // null`, which `!status` would wave through — and that is a live path, not a
165
+ // defensive one. A provider that prints and then takes a signal while we
166
+ // still have stdin to write lands HERE, with `code: 'EPIPE'`, `status: null`
167
+ // and half a line; measured 6 runs of 6 across SIGTERM and SIGKILL. Only our
168
+ // OWN 3 s timeout misses this branch, surfacing as ETIMEDOUT instead.
169
+ // Measured set: clean → EPIPE / 0 / whole line; `exit 1` → EPIPE / 1 / half;
170
+ // self-signal → EPIPE / null / half; our timeout → ETIMEDOUT / null / half.
171
+ const salvaged =
172
+ err && err.code === 'EPIPE' && err.status === 0
173
+ ? (err.stdout || '').toString().trim()
174
+ : '';
175
+ // Swallowing is correct in production and stays the default: a third-party
176
+ // provider that throws must not take the user's whole statusline with it.
177
+ // But a swallowed error is also why `lifecycle.e2e.test.js`'s "issue #24"
178
+ // has gone intermittently red with nothing to go on — a dropped provider and
179
+ // a provider that printed nothing are the same observation from outside, so
180
+ // every diagnosis of it so far has been a guess. Opt-in, off unless the env
181
+ // var is set, so no shipped behavior changes.
182
+ if (process.env.CODE_GRAPH_STATUSLINE_DEBUG) {
183
+ // Its own try/catch. On Linux this is belt-and-braces rather than
184
+ // load-bearing: a write to a closed stderr does NOT throw synchronously —
185
+ // measured both shapes, `fs.closeSync(2)` is a silent no-op and a piped
186
+ // reader that exits surfaces one tick later as an uncaughtException, which
187
+ // no try/catch here can see (hook-fail-open.js already handles that path
188
+ // and skips its own stderr write on EPIPE). It earns its place on Windows,
189
+ // where piped stderr writes are synchronous, and it costs nothing: this
190
+ // sits inside the catch that keeps a bad provider from taking the whole
191
+ // statusline down, so a debug switch must not be the thing that does.
192
+ try {
193
+ const why = err && (err.code || err.message) ? (err.code || err.message) : String(err);
194
+ // Report BOTH outcomes. Returning early on a salvage would make the
195
+ // channel silent on the exact failure it was built to diagnose.
196
+ const verb = salvaged ? 'recovered after' : 'dropped';
197
+ process.stderr.write(`[statusline] provider '${id}' ${verb}: ${why}\n`);
198
+ } catch { /* diagnostics are best-effort; rendering is not */ }
199
+ }
200
+ return salvaged || null;
201
+ }
144
202
  }
145
203
 
146
204
  // Extract Claude Code's current working directory from the stdin JSON context.
@@ -35,7 +35,7 @@ jobs:
35
35
  node-version: '20'
36
36
  - name: Build snapshot
37
37
  run: |
38
- npx -y -p @sdsrs/code-graph@0.137.0 code-graph-mcp snapshot create --out snapshot.db
38
+ npx -y -p @sdsrs/code-graph@0.138.0 code-graph-mcp snapshot create --out snapshot.db
39
39
  zstd -9 snapshot.db -o snapshot.db.zst
40
40
  mv snapshot.db.zst "code-graph-snapshot-${GITHUB_SHA:0:7}.db.zst"
41
41
  - name: Upload to release
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdsrs/code-graph",
3
- "version": "0.137.0",
3
+ "version": "0.138.0",
4
4
  "description": "MCP server that indexes codebases into an AST knowledge graph with semantic search, call graph traversal, and HTTP route tracing",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -35,10 +35,10 @@
35
35
  "node": ">=16"
36
36
  },
37
37
  "optionalDependencies": {
38
- "@sdsrs/code-graph-linux-x64": "0.137.0",
39
- "@sdsrs/code-graph-linux-arm64": "0.137.0",
40
- "@sdsrs/code-graph-darwin-x64": "0.137.0",
41
- "@sdsrs/code-graph-darwin-arm64": "0.137.0",
42
- "@sdsrs/code-graph-win32-x64": "0.137.0"
38
+ "@sdsrs/code-graph-linux-x64": "0.138.0",
39
+ "@sdsrs/code-graph-linux-arm64": "0.138.0",
40
+ "@sdsrs/code-graph-darwin-x64": "0.138.0",
41
+ "@sdsrs/code-graph-darwin-arm64": "0.138.0",
42
+ "@sdsrs/code-graph-win32-x64": "0.138.0"
43
43
  }
44
44
  }