@colbymchenry/codegraph-darwin-arm64 0.9.4 → 0.9.5
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/lib/dist/bin/codegraph.js +12 -0
- package/lib/dist/bin/codegraph.js.map +1 -1
- package/lib/dist/extraction/grammars.d.ts.map +1 -1
- package/lib/dist/extraction/grammars.js +14 -1
- package/lib/dist/extraction/grammars.js.map +1 -1
- package/lib/dist/extraction/index.d.ts +15 -2
- package/lib/dist/extraction/index.d.ts.map +1 -1
- package/lib/dist/extraction/index.js +170 -78
- package/lib/dist/extraction/index.js.map +1 -1
- package/lib/dist/extraction/languages/index.d.ts.map +1 -1
- package/lib/dist/extraction/languages/index.js +2 -0
- package/lib/dist/extraction/languages/index.js.map +1 -1
- package/lib/dist/extraction/languages/objc.d.ts +3 -0
- package/lib/dist/extraction/languages/objc.d.ts.map +1 -0
- package/lib/dist/extraction/languages/objc.js +133 -0
- package/lib/dist/extraction/languages/objc.js.map +1 -0
- package/lib/dist/extraction/tree-sitter-types.d.ts +4 -0
- package/lib/dist/extraction/tree-sitter-types.d.ts.map +1 -1
- package/lib/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/lib/dist/extraction/tree-sitter.js +82 -5
- package/lib/dist/extraction/tree-sitter.js.map +1 -1
- package/lib/dist/index.d.ts +21 -2
- package/lib/dist/index.d.ts.map +1 -1
- package/lib/dist/index.js +33 -0
- package/lib/dist/index.js.map +1 -1
- package/lib/dist/installer/instructions-template.d.ts +2 -2
- package/lib/dist/installer/instructions-template.d.ts.map +1 -1
- package/lib/dist/installer/instructions-template.js +1 -1
- package/lib/dist/mcp/daemon-paths.d.ts +46 -0
- package/lib/dist/mcp/daemon-paths.d.ts.map +1 -0
- package/lib/dist/mcp/daemon-paths.js +125 -0
- package/lib/dist/mcp/daemon-paths.js.map +1 -0
- package/lib/dist/mcp/daemon.d.ts +161 -0
- package/lib/dist/mcp/daemon.d.ts.map +1 -0
- package/lib/dist/mcp/daemon.js +403 -0
- package/lib/dist/mcp/daemon.js.map +1 -0
- package/lib/dist/mcp/engine.d.ts +100 -0
- package/lib/dist/mcp/engine.d.ts.map +1 -0
- package/lib/dist/mcp/engine.js +291 -0
- package/lib/dist/mcp/engine.js.map +1 -0
- package/lib/dist/mcp/index.d.ts +64 -53
- package/lib/dist/mcp/index.d.ts.map +1 -1
- package/lib/dist/mcp/index.js +307 -387
- package/lib/dist/mcp/index.js.map +1 -1
- package/lib/dist/mcp/proxy.d.ts +46 -0
- package/lib/dist/mcp/proxy.d.ts.map +1 -0
- package/lib/dist/mcp/proxy.js +276 -0
- package/lib/dist/mcp/proxy.js.map +1 -0
- package/lib/dist/mcp/server-instructions.d.ts +1 -1
- package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
- package/lib/dist/mcp/server-instructions.js +1 -1
- package/lib/dist/mcp/session.d.ts +67 -0
- package/lib/dist/mcp/session.d.ts.map +1 -0
- package/lib/dist/mcp/session.js +276 -0
- package/lib/dist/mcp/session.js.map +1 -0
- package/lib/dist/mcp/tools.d.ts +49 -0
- package/lib/dist/mcp/tools.d.ts.map +1 -1
- package/lib/dist/mcp/tools.js +239 -14
- package/lib/dist/mcp/tools.js.map +1 -1
- package/lib/dist/mcp/transport.d.ts +111 -29
- package/lib/dist/mcp/transport.d.ts.map +1 -1
- package/lib/dist/mcp/transport.js +181 -71
- package/lib/dist/mcp/transport.js.map +1 -1
- package/lib/dist/mcp/version.d.ts +19 -0
- package/lib/dist/mcp/version.d.ts.map +1 -0
- package/lib/dist/mcp/version.js +71 -0
- package/lib/dist/mcp/version.js.map +1 -0
- package/lib/dist/resolution/callback-synthesizer.d.ts +3 -2
- package/lib/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/lib/dist/resolution/callback-synthesizer.js +274 -3
- package/lib/dist/resolution/callback-synthesizer.js.map +1 -1
- package/lib/dist/resolution/frameworks/expo-modules.d.ts +3 -0
- package/lib/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/expo-modules.js +143 -0
- package/lib/dist/resolution/frameworks/expo-modules.js.map +1 -0
- package/lib/dist/resolution/frameworks/fabric.d.ts +3 -0
- package/lib/dist/resolution/frameworks/fabric.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/fabric.js +354 -0
- package/lib/dist/resolution/frameworks/fabric.js.map +1 -0
- package/lib/dist/resolution/frameworks/index.d.ts +4 -0
- package/lib/dist/resolution/frameworks/index.d.ts.map +1 -1
- package/lib/dist/resolution/frameworks/index.js +21 -1
- package/lib/dist/resolution/frameworks/index.js.map +1 -1
- package/lib/dist/resolution/frameworks/react-native.d.ts +3 -0
- package/lib/dist/resolution/frameworks/react-native.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/react-native.js +360 -0
- package/lib/dist/resolution/frameworks/react-native.js.map +1 -0
- package/lib/dist/resolution/frameworks/swift-objc.d.ts +37 -0
- package/lib/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/swift-objc.js +252 -0
- package/lib/dist/resolution/frameworks/swift-objc.js.map +1 -0
- package/lib/dist/resolution/import-resolver.d.ts.map +1 -1
- package/lib/dist/resolution/import-resolver.js +1 -0
- package/lib/dist/resolution/import-resolver.js.map +1 -1
- package/lib/dist/resolution/swift-objc-bridge.d.ts +134 -0
- package/lib/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
- package/lib/dist/resolution/swift-objc-bridge.js +256 -0
- package/lib/dist/resolution/swift-objc-bridge.js.map +1 -0
- package/lib/dist/sync/index.d.ts +3 -1
- package/lib/dist/sync/index.d.ts.map +1 -1
- package/lib/dist/sync/index.js +7 -1
- package/lib/dist/sync/index.js.map +1 -1
- package/lib/dist/sync/watcher.d.ts +109 -7
- package/lib/dist/sync/watcher.d.ts.map +1 -1
- package/lib/dist/sync/watcher.js +215 -33
- package/lib/dist/sync/watcher.js.map +1 -1
- package/lib/dist/sync/worktree.d.ts +54 -0
- package/lib/dist/sync/worktree.d.ts.map +1 -0
- package/lib/dist/sync/worktree.js +136 -0
- package/lib/dist/sync/worktree.js.map +1 -0
- package/lib/dist/types.d.ts +1 -1
- package/lib/dist/types.d.ts.map +1 -1
- package/lib/dist/types.js +1 -0
- package/lib/dist/types.js.map +1 -1
- package/lib/node_modules/.package-lock.json +29 -1
- package/lib/node_modules/chokidar/LICENSE +21 -0
- package/lib/node_modules/chokidar/README.md +305 -0
- package/lib/node_modules/chokidar/esm/handler.d.ts +90 -0
- package/lib/node_modules/chokidar/esm/handler.js +629 -0
- package/lib/node_modules/chokidar/esm/index.d.ts +215 -0
- package/lib/node_modules/chokidar/esm/index.js +798 -0
- package/lib/node_modules/chokidar/esm/package.json +1 -0
- package/lib/node_modules/chokidar/handler.d.ts +90 -0
- package/lib/node_modules/chokidar/handler.js +635 -0
- package/lib/node_modules/chokidar/index.d.ts +215 -0
- package/lib/node_modules/chokidar/index.js +804 -0
- package/lib/node_modules/chokidar/package.json +69 -0
- package/lib/node_modules/readdirp/LICENSE +21 -0
- package/lib/node_modules/readdirp/README.md +120 -0
- package/lib/node_modules/readdirp/esm/index.d.ts +108 -0
- package/lib/node_modules/readdirp/esm/index.js +257 -0
- package/lib/node_modules/readdirp/esm/package.json +1 -0
- package/lib/node_modules/readdirp/index.d.ts +108 -0
- package/lib/node_modules/readdirp/index.js +263 -0
- package/lib/node_modules/readdirp/package.json +70 -0
- package/lib/package.json +2 -1
- package/package.json +1 -1
package/lib/dist/mcp/tools.js
CHANGED
|
@@ -41,7 +41,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
41
41
|
exports.ToolHandler = exports.tools = void 0;
|
|
42
42
|
exports.getExploreBudget = getExploreBudget;
|
|
43
43
|
exports.getExploreOutputBudget = getExploreOutputBudget;
|
|
44
|
+
exports.formatStaleBanner = formatStaleBanner;
|
|
45
|
+
exports.formatStaleFooter = formatStaleFooter;
|
|
44
46
|
const index_1 = __importStar(require("../index"));
|
|
47
|
+
const worktree_1 = require("../sync/worktree");
|
|
45
48
|
const crypto_1 = require("crypto");
|
|
46
49
|
const fs_1 = require("fs");
|
|
47
50
|
const utils_1 = require("../utils");
|
|
@@ -241,6 +244,42 @@ function markSessionConsulted(sessionId) {
|
|
|
241
244
|
// to write rather than overwrite an attacker-chosen target.
|
|
242
245
|
}
|
|
243
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* Per-file staleness banner emitted at the top of a tool response when the
|
|
249
|
+
* file watcher has pending events for files referenced by the response.
|
|
250
|
+
* The agent uses this to fall back to Read for those specific files
|
|
251
|
+
* without waiting for the debounced sync (issue #403).
|
|
252
|
+
*/
|
|
253
|
+
function formatStaleBanner(stale) {
|
|
254
|
+
const now = Date.now();
|
|
255
|
+
const lines = stale.map((p) => {
|
|
256
|
+
const ageMs = Math.max(0, now - p.lastSeenMs);
|
|
257
|
+
const label = p.indexing ? 'indexing in progress' : 'pending sync';
|
|
258
|
+
return ` - ${p.path} (edited ${ageMs}ms ago, ${label})`;
|
|
259
|
+
});
|
|
260
|
+
return ('⚠️ Some files referenced below were edited since the last index sync — ' +
|
|
261
|
+
'their codegraph entries may be stale:\n' +
|
|
262
|
+
lines.join('\n') +
|
|
263
|
+
'\nFor accurate content of those specific files, Read them directly. ' +
|
|
264
|
+
'The rest of this response is fresh.');
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Compact footer listing pending files that are NOT referenced in this
|
|
268
|
+
* response. Gives the agent a complete project-wide freshness picture
|
|
269
|
+
* without bloating the main banner.
|
|
270
|
+
*/
|
|
271
|
+
function formatStaleFooter(stale) {
|
|
272
|
+
const MAX = 5;
|
|
273
|
+
const now = Date.now();
|
|
274
|
+
const shown = stale.slice(0, MAX);
|
|
275
|
+
const lines = shown.map((p) => {
|
|
276
|
+
const ageMs = Math.max(0, now - p.lastSeenMs);
|
|
277
|
+
return ` - ${p.path} (edited ${ageMs}ms ago)`;
|
|
278
|
+
});
|
|
279
|
+
const more = stale.length > MAX ? `\n - …and ${stale.length - MAX} more` : '';
|
|
280
|
+
return (`(Note: ${stale.length} file(s) elsewhere in this project are pending index ` +
|
|
281
|
+
`sync but were not referenced above:\n${lines.join('\n')}${more})`);
|
|
282
|
+
}
|
|
244
283
|
/**
|
|
245
284
|
* Common projectPath property for cross-project queries
|
|
246
285
|
*/
|
|
@@ -483,6 +522,12 @@ class ToolHandler {
|
|
|
483
522
|
// The directory the server last searched for a default project. Surfaced in
|
|
484
523
|
// the "not initialized" error so users can see why detection missed.
|
|
485
524
|
defaultProjectHint = null;
|
|
525
|
+
// Per-start-path cache of the git worktree/index mismatch (issue #155). The
|
|
526
|
+
// mismatch is a fixed property of (where the request came from → which
|
|
527
|
+
// .codegraph/ it resolves to), so the up-to-two `git rev-parse` spawns run
|
|
528
|
+
// once and every later tool call reuses the result — never shelling out to
|
|
529
|
+
// git on the hot path. `undefined` = not computed yet; `null` = no mismatch.
|
|
530
|
+
worktreeMismatchCache = new Map();
|
|
486
531
|
constructor(cg) {
|
|
487
532
|
this.cg = cg;
|
|
488
533
|
}
|
|
@@ -632,6 +677,7 @@ class ToolHandler {
|
|
|
632
677
|
cg.close();
|
|
633
678
|
}
|
|
634
679
|
this.projectCache.clear();
|
|
680
|
+
this.worktreeMismatchCache.clear();
|
|
635
681
|
}
|
|
636
682
|
/**
|
|
637
683
|
* Validate that a value is a non-empty string within length bounds.
|
|
@@ -665,6 +711,132 @@ class ToolHandler {
|
|
|
665
711
|
}
|
|
666
712
|
return value;
|
|
667
713
|
}
|
|
714
|
+
/**
|
|
715
|
+
* Cached git worktree/index mismatch for a tool call's effective project.
|
|
716
|
+
*
|
|
717
|
+
* The "effective project" is what the request targets: an explicit
|
|
718
|
+
* `projectPath` arg, else the directory the server resolved its default
|
|
719
|
+
* project from (`defaultProjectHint`), else cwd. Memoized per start path —
|
|
720
|
+
* see `worktreeMismatchCache`. Best-effort: if the project can't be resolved
|
|
721
|
+
* (e.g. nothing initialized yet), it reports "no mismatch" so a tool is never
|
|
722
|
+
* broken by this check.
|
|
723
|
+
*/
|
|
724
|
+
worktreeMismatchFor(projectPath) {
|
|
725
|
+
const startPath = projectPath ?? this.defaultProjectHint ?? process.cwd();
|
|
726
|
+
const cached = this.worktreeMismatchCache.get(startPath);
|
|
727
|
+
if (cached !== undefined)
|
|
728
|
+
return cached;
|
|
729
|
+
let mismatch = null;
|
|
730
|
+
try {
|
|
731
|
+
mismatch = (0, worktree_1.detectWorktreeIndexMismatch)(startPath, this.getCodeGraph(projectPath).getProjectRoot());
|
|
732
|
+
}
|
|
733
|
+
catch {
|
|
734
|
+
// No resolvable project (or any other resolution error) → nothing to warn.
|
|
735
|
+
mismatch = null;
|
|
736
|
+
}
|
|
737
|
+
this.worktreeMismatchCache.set(startPath, mismatch);
|
|
738
|
+
return mismatch;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Prefix a successful read-tool result with a compact worktree-mismatch
|
|
742
|
+
* notice when the resolved index belongs to a different git working tree than
|
|
743
|
+
* the caller's (issue #155). Without this, an agent in a nested worktree
|
|
744
|
+
* silently trusts main-branch results. No-op on error results and when there
|
|
745
|
+
* is no mismatch. `codegraph_status` is excluded — it embeds its own verbose
|
|
746
|
+
* warning — so it stays out of this path.
|
|
747
|
+
*/
|
|
748
|
+
withWorktreeNotice(result, projectPath) {
|
|
749
|
+
if (result.isError)
|
|
750
|
+
return result;
|
|
751
|
+
const mismatch = this.worktreeMismatchFor(projectPath);
|
|
752
|
+
if (!mismatch)
|
|
753
|
+
return result;
|
|
754
|
+
const notice = (0, worktree_1.worktreeMismatchNotice)(mismatch);
|
|
755
|
+
const [first, ...rest] = result.content;
|
|
756
|
+
if (first && first.type === 'text') {
|
|
757
|
+
return { ...result, content: [{ type: 'text', text: `${notice}\n\n${first.text}` }, ...rest] };
|
|
758
|
+
}
|
|
759
|
+
return result;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Annotate a successful read-tool result with per-file staleness — the
|
|
763
|
+
* non-blocking answer to issue #403. The file watcher tracks every event
|
|
764
|
+
* it sees per path; here we intersect "files referenced in this response"
|
|
765
|
+
* against that pending set and prepend a compact banner so the agent can
|
|
766
|
+
* fall back to Read for those *specific* files without waiting for the
|
|
767
|
+
* debounced sync to fire. Other pending files in the project (not
|
|
768
|
+
* referenced by this response) get a small footer so the agent has a
|
|
769
|
+
* complete picture without bloating the banner.
|
|
770
|
+
*
|
|
771
|
+
* Cost when nothing is pending — the common case — is one boolean check.
|
|
772
|
+
* No I/O, no parsing of markdown beyond a per-pending-file substring scan.
|
|
773
|
+
*/
|
|
774
|
+
withStalenessNotice(result, projectPath) {
|
|
775
|
+
if (result.isError)
|
|
776
|
+
return result;
|
|
777
|
+
let cg;
|
|
778
|
+
try {
|
|
779
|
+
cg = this.getCodeGraph(projectPath);
|
|
780
|
+
}
|
|
781
|
+
catch {
|
|
782
|
+
return result; // no default project — leave as is
|
|
783
|
+
}
|
|
784
|
+
// Cross-project `projectPath` calls open a cached CodeGraph WITHOUT a
|
|
785
|
+
// watcher (watchers are only attached to the default session project).
|
|
786
|
+
// When the cross-project path happens to be the same project as the
|
|
787
|
+
// default cg, the cached instance is the wrong one — its pendingFiles is
|
|
788
|
+
// permanently empty. Detect the equal-path case and prefer the default
|
|
789
|
+
// cg so the staleness signal still fires when an agent passes the
|
|
790
|
+
// explicit projectPath form of its own project.
|
|
791
|
+
if (this.cg && cg !== this.cg) {
|
|
792
|
+
try {
|
|
793
|
+
const sameProject = (0, path_1.resolve)(this.cg.getProjectRoot()) === (0, path_1.resolve)(cg.getProjectRoot());
|
|
794
|
+
if (sameProject)
|
|
795
|
+
cg = this.cg;
|
|
796
|
+
}
|
|
797
|
+
catch {
|
|
798
|
+
/* getProjectRoot may throw on a closed instance — leave cg as is */
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
// Defensive: some test fakes inject a partial CodeGraph stub without the
|
|
802
|
+
// newer pending-files API. Treat missing/throwing as "no pending files."
|
|
803
|
+
let pending = [];
|
|
804
|
+
try {
|
|
805
|
+
pending = cg.getPendingFiles?.() ?? [];
|
|
806
|
+
}
|
|
807
|
+
catch {
|
|
808
|
+
return result;
|
|
809
|
+
}
|
|
810
|
+
if (pending.length === 0)
|
|
811
|
+
return result;
|
|
812
|
+
const [first, ...rest] = result.content;
|
|
813
|
+
if (!first || first.type !== 'text')
|
|
814
|
+
return result;
|
|
815
|
+
const text = first.text;
|
|
816
|
+
const inResponse = [];
|
|
817
|
+
const elsewhere = [];
|
|
818
|
+
for (const p of pending) {
|
|
819
|
+
// Substring match against the project-relative POSIX path — that's
|
|
820
|
+
// exactly the format both the watcher and every codegraph response
|
|
821
|
+
// emit, so a plain includes() is sufficient and avoids regex pitfalls.
|
|
822
|
+
if (text.includes(p.path))
|
|
823
|
+
inResponse.push(p);
|
|
824
|
+
else
|
|
825
|
+
elsewhere.push(p);
|
|
826
|
+
}
|
|
827
|
+
let banner = '';
|
|
828
|
+
if (inResponse.length > 0) {
|
|
829
|
+
banner = formatStaleBanner(inResponse);
|
|
830
|
+
}
|
|
831
|
+
let footer = '';
|
|
832
|
+
if (elsewhere.length > 0) {
|
|
833
|
+
footer = formatStaleFooter(elsewhere);
|
|
834
|
+
}
|
|
835
|
+
if (!banner && !footer)
|
|
836
|
+
return result;
|
|
837
|
+
const composed = [banner, text, footer].filter(Boolean).join('\n\n');
|
|
838
|
+
return { ...result, content: [{ type: 'text', text: composed }, ...rest] };
|
|
839
|
+
}
|
|
668
840
|
/**
|
|
669
841
|
* Execute a tool by name
|
|
670
842
|
*/
|
|
@@ -695,30 +867,51 @@ class ToolHandler {
|
|
|
695
867
|
if (typeof check === 'object' && check !== undefined)
|
|
696
868
|
return check;
|
|
697
869
|
}
|
|
870
|
+
// Read tools resolve through a single result variable so cross-cutting
|
|
871
|
+
// notices — worktree-index mismatch (issue #155) and per-file
|
|
872
|
+
// staleness (issue #403) — can be applied in one place. status embeds
|
|
873
|
+
// its own verbose worktree warning but still flows through the
|
|
874
|
+
// staleness wrapper so its pending-files section stays consistent
|
|
875
|
+
// with what the read tools surface.
|
|
876
|
+
let result;
|
|
698
877
|
switch (toolName) {
|
|
699
878
|
case 'codegraph_search':
|
|
700
|
-
|
|
879
|
+
result = await this.handleSearch(args);
|
|
880
|
+
break;
|
|
701
881
|
case 'codegraph_context':
|
|
702
|
-
|
|
882
|
+
result = await this.handleContext(args);
|
|
883
|
+
break;
|
|
703
884
|
case 'codegraph_callers':
|
|
704
|
-
|
|
885
|
+
result = await this.handleCallers(args);
|
|
886
|
+
break;
|
|
705
887
|
case 'codegraph_callees':
|
|
706
|
-
|
|
888
|
+
result = await this.handleCallees(args);
|
|
889
|
+
break;
|
|
707
890
|
case 'codegraph_impact':
|
|
708
|
-
|
|
891
|
+
result = await this.handleImpact(args);
|
|
892
|
+
break;
|
|
709
893
|
case 'codegraph_explore':
|
|
710
|
-
|
|
894
|
+
result = await this.handleExplore(args);
|
|
895
|
+
break;
|
|
711
896
|
case 'codegraph_node':
|
|
712
|
-
|
|
897
|
+
result = await this.handleNode(args);
|
|
898
|
+
break;
|
|
713
899
|
case 'codegraph_status':
|
|
900
|
+
// status embeds the pending-files list as a first-class section
|
|
901
|
+
// (see handleStatus), so we skip the auto-banner wrapper here to
|
|
902
|
+
// avoid duplicating the same info at the top of the response.
|
|
714
903
|
return await this.handleStatus(args);
|
|
715
904
|
case 'codegraph_files':
|
|
716
|
-
|
|
905
|
+
result = await this.handleFiles(args);
|
|
906
|
+
break;
|
|
717
907
|
case 'codegraph_trace':
|
|
718
|
-
|
|
908
|
+
result = await this.handleTrace(args);
|
|
909
|
+
break;
|
|
719
910
|
default:
|
|
720
911
|
return this.errorResult(`Unknown tool: ${toolName}`);
|
|
721
912
|
}
|
|
913
|
+
const withWorktree = this.withWorktreeNotice(result, args.projectPath);
|
|
914
|
+
return this.withStalenessNotice(withWorktree, args.projectPath);
|
|
722
915
|
}
|
|
723
916
|
catch (err) {
|
|
724
917
|
return this.errorResult(`Tool execution failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -1870,16 +2063,34 @@ class ToolHandler {
|
|
|
1870
2063
|
* Handle codegraph_status
|
|
1871
2064
|
*/
|
|
1872
2065
|
async handleStatus(args) {
|
|
1873
|
-
|
|
2066
|
+
let cg = this.getCodeGraph(args.projectPath);
|
|
2067
|
+
// Same trick as withStalenessNotice — when an explicit projectPath
|
|
2068
|
+
// resolves to the same project as the default session cg, prefer the
|
|
2069
|
+
// default so getPendingFiles() (only populated by the default's watcher)
|
|
2070
|
+
// is non-empty when there are pending edits.
|
|
2071
|
+
if (this.cg && cg !== this.cg) {
|
|
2072
|
+
try {
|
|
2073
|
+
if ((0, path_1.resolve)(this.cg.getProjectRoot()) === (0, path_1.resolve)(cg.getProjectRoot())) {
|
|
2074
|
+
cg = this.cg;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
catch { /* closed instance — leave as is */ }
|
|
2078
|
+
}
|
|
1874
2079
|
const stats = cg.getStats();
|
|
2080
|
+
// Warn when this index actually belongs to a different git working tree
|
|
2081
|
+
// (e.g. the server resolved up from a nested worktree to the main checkout).
|
|
2082
|
+
// Queries then reflect that tree's branch, not the worktree being edited.
|
|
2083
|
+
// status shows the verbose, multi-line form; the read tools get the compact
|
|
2084
|
+
// one-liner via withWorktreeNotice. Both share the cached detection.
|
|
2085
|
+
const mismatch = this.worktreeMismatchFor(args.projectPath);
|
|
1875
2086
|
const lines = [
|
|
1876
2087
|
'## CodeGraph Status',
|
|
1877
2088
|
'',
|
|
1878
|
-
`**Files indexed:** ${stats.fileCount}`,
|
|
1879
|
-
`**Total nodes:** ${stats.nodeCount}`,
|
|
1880
|
-
`**Total edges:** ${stats.edgeCount}`,
|
|
1881
|
-
`**Database size:** ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`,
|
|
1882
2089
|
];
|
|
2090
|
+
if (mismatch) {
|
|
2091
|
+
lines.push(`> ⚠ ${(0, worktree_1.worktreeMismatchWarning)(mismatch).replace(/\n/g, '\n> ')}`, '');
|
|
2092
|
+
}
|
|
2093
|
+
lines.push(`**Files indexed:** ${stats.fileCount}`, `**Total nodes:** ${stats.nodeCount}`, `**Total edges:** ${stats.edgeCount}`, `**Database size:** ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`);
|
|
1883
2094
|
// Surface the active SQLite backend (node:sqlite, Node's built-in real
|
|
1884
2095
|
// SQLite — full WAL + FTS5, no native build).
|
|
1885
2096
|
lines.push(`**Backend:** node:sqlite (Node built-in) — full WAL + FTS5`);
|
|
@@ -1907,6 +2118,20 @@ class ToolHandler {
|
|
|
1907
2118
|
lines.push(`- ${lang}: ${count}`);
|
|
1908
2119
|
}
|
|
1909
2120
|
}
|
|
2121
|
+
// Per-file freshness — the inverse of the auto-prepended staleness banner
|
|
2122
|
+
// (issue #403). Surfacing it inside `status` gives the agent a single
|
|
2123
|
+
// place to ask "is the index caught up?" rather than inferring from
|
|
2124
|
+
// banners on other tool calls.
|
|
2125
|
+
const pending = cg.getPendingFiles();
|
|
2126
|
+
if (pending.length > 0) {
|
|
2127
|
+
lines.push('', '### Pending sync:');
|
|
2128
|
+
const now = Date.now();
|
|
2129
|
+
for (const p of pending) {
|
|
2130
|
+
const ageMs = Math.max(0, now - p.lastSeenMs);
|
|
2131
|
+
const label = p.indexing ? 'indexing in progress' : 'pending sync';
|
|
2132
|
+
lines.push(`- ${p.path} (edited ${ageMs}ms ago, ${label})`);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
1910
2135
|
return this.textResult(lines.join('\n'));
|
|
1911
2136
|
}
|
|
1912
2137
|
/**
|