@sdsrs/code-graph 0.73.0 → 0.73.1

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.
@@ -4,7 +4,7 @@
4
4
  "author": {
5
5
  "name": "sdsrs"
6
6
  },
7
- "version": "0.73.0",
7
+ "version": "0.73.1",
8
8
  "keywords": [
9
9
  "code-graph",
10
10
  "ast",
@@ -330,6 +330,32 @@ function relicRepairGuard({ log = console.log, relic = undefined } = {}) {
330
330
  return true;
331
331
  }
332
332
 
333
+ // A dev-mode rebuild must PRESERVE the existing binary's feature set. This repair
334
+ // used to hardcode `--no-default-features`, which silently downgraded a hybrid
335
+ // (embed-model) dev binary to FTS5-only and ping-ponged against a manual
336
+ // `cargo build --release --features embed-model`. Probe the binary's COMPILED
337
+ // feature via `health-check --json` → `model_available` (= cfg!(feature =
338
+ // "embed-model"), reported even with no index) and rebuild to match. Returns
339
+ // true/false, or null when the binary can't be probed (missing/broken) — the
340
+ // caller then defaults to FTS5 + an explicit note, never a silent downgrade.
341
+ // End users never reach this path (binary-stale → auto-update; binary-missing
342
+ // non-dev → install instructions); it is purely the source-repo dev convenience.
343
+ function detectEmbedModel(binary, run = execFileSync) {
344
+ if (!binary) return null;
345
+ try {
346
+ const out = run(binary, ['health-check', '--json'], {
347
+ timeout: 10000, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
348
+ });
349
+ return JSON.parse(out).model_available === true;
350
+ } catch { return null; }
351
+ }
352
+
353
+ function devBuildCommand(embed) {
354
+ return embed
355
+ ? 'cargo build --release --features embed-model'
356
+ : 'cargo build --release --no-default-features';
357
+ }
358
+
333
359
  function runRepairs(results) {
334
360
  const fixable = results.filter(r => r.fixId);
335
361
  if (fixable.length === 0) return 0;
@@ -353,14 +379,23 @@ function runRepairs(results) {
353
379
  }
354
380
  break;
355
381
  }
382
+ // Preserve the current binary's feature set \u2014 never silently downgrade
383
+ // a hybrid (embed-model) dev binary to FTS5-only (which also ping-pongs
384
+ // against a manual `--features embed-model` build).
385
+ const embed = detectEmbedModel(findBinary());
386
+ const buildCmd = devBuildCommand(embed === true);
356
387
  console.log('\n Building binary...');
357
- console.log(' \u2192 cargo build --release --no-default-features');
388
+ if (embed === null) {
389
+ console.log(' (could not detect current feature set \u2014 building FTS5-only;');
390
+ console.log(' for semantic search rebuild with `cargo build --release --features embed-model`)');
391
+ }
392
+ console.log(` \u2192 ${buildCmd}`);
358
393
  try {
359
394
  const projectRoot = path.resolve(__dirname, '..', '..');
360
- execSync('cargo build --release --no-default-features', {
395
+ execSync(buildCmd, {
361
396
  cwd: projectRoot,
362
397
  stdio: 'inherit',
363
- timeout: 300000,
398
+ timeout: 600000, // embed-model (Candle) builds exceed the old 5min
364
399
  });
365
400
  clearBinaryCache();
366
401
  console.log(' \u2705 Build complete');
@@ -374,13 +409,16 @@ function runRepairs(results) {
374
409
  case 'binary-missing': {
375
410
  console.log('\n Installing binary...');
376
411
  if (isDevMode()) {
412
+ // No binary to probe \u2014 build the fast FTS5 binary, but point at the
413
+ // hybrid option so FTS5 isn't silently presented as the only choice.
377
414
  console.log(' \u2192 cargo build --release --no-default-features');
415
+ console.log(' (for semantic search: cargo build --release --features embed-model)');
378
416
  try {
379
417
  const projectRoot = path.resolve(__dirname, '..', '..');
380
418
  execSync('cargo build --release --no-default-features', {
381
419
  cwd: projectRoot,
382
420
  stdio: 'inherit',
383
- timeout: 300000,
421
+ timeout: 600000,
384
422
  });
385
423
  clearBinaryCache();
386
424
  console.log(' \u2705 Build complete');
@@ -500,7 +538,7 @@ function runDoctor(opts = {}) {
500
538
  return { results, issueCount: issues.length };
501
539
  }
502
540
 
503
- module.exports = { runDiagnostics, formatReport, runRepairs, runDoctor, surveyHookCoverage, relicRepairGuard, classifyEmbeddings };
541
+ module.exports = { runDiagnostics, formatReport, runRepairs, runDoctor, surveyHookCoverage, relicRepairGuard, classifyEmbeddings, detectEmbedModel, devBuildCommand };
504
542
 
505
543
  if (require.main === module) {
506
544
  const args = process.argv.slice(2);
@@ -122,3 +122,30 @@ test('classifyEmbeddings OK for hybrid (partial + complete) and no-embeddable',
122
122
  assert.equal(none.status, 'ok');
123
123
  assert.match(none.detail, /no embeddable nodes/);
124
124
  });
125
+
126
+ // ── dev-rebuild feature preservation (no silent hybrid→FTS5 downgrade / ping-pong) ──
127
+ test('devBuildCommand preserves feature set: hybrid → --features embed-model, fts → --no-default-features', () => {
128
+ const { devBuildCommand } = require('./doctor');
129
+ assert.match(devBuildCommand(true), /--features embed-model/);
130
+ assert.doesNotMatch(devBuildCommand(true), /--no-default-features/);
131
+ assert.match(devBuildCommand(false), /--no-default-features/);
132
+ assert.doesNotMatch(devBuildCommand(false), /--features embed-model/);
133
+ });
134
+
135
+ test('detectEmbedModel reads model_available from `health-check --json`; probe failure → null (never a false downgrade signal)', () => {
136
+ const { detectEmbedModel } = require('./doctor');
137
+ // hybrid binary
138
+ const hybridStub = (_bin, args) => {
139
+ assert.deepEqual(args, ['health-check', '--json']);
140
+ return JSON.stringify({ model_available: true });
141
+ };
142
+ assert.equal(detectEmbedModel('/bin/cg', hybridStub), true);
143
+ // FTS5-only binary
144
+ assert.equal(detectEmbedModel('/bin/cg', () => JSON.stringify({ model_available: false })), false);
145
+ // probe throws (binary broken) → null (caller defaults to FTS5 + note, not a downgrade claim)
146
+ assert.equal(detectEmbedModel('/bin/cg', () => { throw new Error('boom'); }), null);
147
+ // unparseable output → null
148
+ assert.equal(detectEmbedModel('/bin/cg', () => 'not json'), null);
149
+ // no binary → null
150
+ assert.equal(detectEmbedModel(null), null);
151
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdsrs/code-graph",
3
- "version": "0.73.0",
3
+ "version": "0.73.1",
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.73.0",
39
- "@sdsrs/code-graph-linux-arm64": "0.73.0",
40
- "@sdsrs/code-graph-darwin-x64": "0.73.0",
41
- "@sdsrs/code-graph-darwin-arm64": "0.73.0",
42
- "@sdsrs/code-graph-win32-x64": "0.73.0"
38
+ "@sdsrs/code-graph-linux-x64": "0.73.1",
39
+ "@sdsrs/code-graph-linux-arm64": "0.73.1",
40
+ "@sdsrs/code-graph-darwin-x64": "0.73.1",
41
+ "@sdsrs/code-graph-darwin-arm64": "0.73.1",
42
+ "@sdsrs/code-graph-win32-x64": "0.73.1"
43
43
  }
44
44
  }