@colbymchenry/codegraph-darwin-x64 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.
Files changed (137) hide show
  1. package/lib/dist/bin/codegraph.js +12 -0
  2. package/lib/dist/bin/codegraph.js.map +1 -1
  3. package/lib/dist/extraction/grammars.d.ts.map +1 -1
  4. package/lib/dist/extraction/grammars.js +14 -1
  5. package/lib/dist/extraction/grammars.js.map +1 -1
  6. package/lib/dist/extraction/index.d.ts +15 -2
  7. package/lib/dist/extraction/index.d.ts.map +1 -1
  8. package/lib/dist/extraction/index.js +170 -78
  9. package/lib/dist/extraction/index.js.map +1 -1
  10. package/lib/dist/extraction/languages/index.d.ts.map +1 -1
  11. package/lib/dist/extraction/languages/index.js +2 -0
  12. package/lib/dist/extraction/languages/index.js.map +1 -1
  13. package/lib/dist/extraction/languages/objc.d.ts +3 -0
  14. package/lib/dist/extraction/languages/objc.d.ts.map +1 -0
  15. package/lib/dist/extraction/languages/objc.js +133 -0
  16. package/lib/dist/extraction/languages/objc.js.map +1 -0
  17. package/lib/dist/extraction/tree-sitter-types.d.ts +4 -0
  18. package/lib/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  19. package/lib/dist/extraction/tree-sitter.d.ts.map +1 -1
  20. package/lib/dist/extraction/tree-sitter.js +82 -5
  21. package/lib/dist/extraction/tree-sitter.js.map +1 -1
  22. package/lib/dist/index.d.ts +21 -2
  23. package/lib/dist/index.d.ts.map +1 -1
  24. package/lib/dist/index.js +33 -0
  25. package/lib/dist/index.js.map +1 -1
  26. package/lib/dist/installer/instructions-template.d.ts +2 -2
  27. package/lib/dist/installer/instructions-template.d.ts.map +1 -1
  28. package/lib/dist/installer/instructions-template.js +1 -1
  29. package/lib/dist/mcp/daemon-paths.d.ts +46 -0
  30. package/lib/dist/mcp/daemon-paths.d.ts.map +1 -0
  31. package/lib/dist/mcp/daemon-paths.js +125 -0
  32. package/lib/dist/mcp/daemon-paths.js.map +1 -0
  33. package/lib/dist/mcp/daemon.d.ts +161 -0
  34. package/lib/dist/mcp/daemon.d.ts.map +1 -0
  35. package/lib/dist/mcp/daemon.js +403 -0
  36. package/lib/dist/mcp/daemon.js.map +1 -0
  37. package/lib/dist/mcp/engine.d.ts +100 -0
  38. package/lib/dist/mcp/engine.d.ts.map +1 -0
  39. package/lib/dist/mcp/engine.js +291 -0
  40. package/lib/dist/mcp/engine.js.map +1 -0
  41. package/lib/dist/mcp/index.d.ts +64 -53
  42. package/lib/dist/mcp/index.d.ts.map +1 -1
  43. package/lib/dist/mcp/index.js +307 -387
  44. package/lib/dist/mcp/index.js.map +1 -1
  45. package/lib/dist/mcp/proxy.d.ts +46 -0
  46. package/lib/dist/mcp/proxy.d.ts.map +1 -0
  47. package/lib/dist/mcp/proxy.js +276 -0
  48. package/lib/dist/mcp/proxy.js.map +1 -0
  49. package/lib/dist/mcp/server-instructions.d.ts +1 -1
  50. package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
  51. package/lib/dist/mcp/server-instructions.js +1 -1
  52. package/lib/dist/mcp/session.d.ts +67 -0
  53. package/lib/dist/mcp/session.d.ts.map +1 -0
  54. package/lib/dist/mcp/session.js +276 -0
  55. package/lib/dist/mcp/session.js.map +1 -0
  56. package/lib/dist/mcp/tools.d.ts +49 -0
  57. package/lib/dist/mcp/tools.d.ts.map +1 -1
  58. package/lib/dist/mcp/tools.js +239 -14
  59. package/lib/dist/mcp/tools.js.map +1 -1
  60. package/lib/dist/mcp/transport.d.ts +111 -29
  61. package/lib/dist/mcp/transport.d.ts.map +1 -1
  62. package/lib/dist/mcp/transport.js +181 -71
  63. package/lib/dist/mcp/transport.js.map +1 -1
  64. package/lib/dist/mcp/version.d.ts +19 -0
  65. package/lib/dist/mcp/version.d.ts.map +1 -0
  66. package/lib/dist/mcp/version.js +71 -0
  67. package/lib/dist/mcp/version.js.map +1 -0
  68. package/lib/dist/resolution/callback-synthesizer.d.ts +3 -2
  69. package/lib/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  70. package/lib/dist/resolution/callback-synthesizer.js +274 -3
  71. package/lib/dist/resolution/callback-synthesizer.js.map +1 -1
  72. package/lib/dist/resolution/frameworks/expo-modules.d.ts +3 -0
  73. package/lib/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
  74. package/lib/dist/resolution/frameworks/expo-modules.js +143 -0
  75. package/lib/dist/resolution/frameworks/expo-modules.js.map +1 -0
  76. package/lib/dist/resolution/frameworks/fabric.d.ts +3 -0
  77. package/lib/dist/resolution/frameworks/fabric.d.ts.map +1 -0
  78. package/lib/dist/resolution/frameworks/fabric.js +354 -0
  79. package/lib/dist/resolution/frameworks/fabric.js.map +1 -0
  80. package/lib/dist/resolution/frameworks/index.d.ts +4 -0
  81. package/lib/dist/resolution/frameworks/index.d.ts.map +1 -1
  82. package/lib/dist/resolution/frameworks/index.js +21 -1
  83. package/lib/dist/resolution/frameworks/index.js.map +1 -1
  84. package/lib/dist/resolution/frameworks/react-native.d.ts +3 -0
  85. package/lib/dist/resolution/frameworks/react-native.d.ts.map +1 -0
  86. package/lib/dist/resolution/frameworks/react-native.js +360 -0
  87. package/lib/dist/resolution/frameworks/react-native.js.map +1 -0
  88. package/lib/dist/resolution/frameworks/swift-objc.d.ts +37 -0
  89. package/lib/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
  90. package/lib/dist/resolution/frameworks/swift-objc.js +252 -0
  91. package/lib/dist/resolution/frameworks/swift-objc.js.map +1 -0
  92. package/lib/dist/resolution/import-resolver.d.ts.map +1 -1
  93. package/lib/dist/resolution/import-resolver.js +1 -0
  94. package/lib/dist/resolution/import-resolver.js.map +1 -1
  95. package/lib/dist/resolution/swift-objc-bridge.d.ts +134 -0
  96. package/lib/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
  97. package/lib/dist/resolution/swift-objc-bridge.js +256 -0
  98. package/lib/dist/resolution/swift-objc-bridge.js.map +1 -0
  99. package/lib/dist/sync/index.d.ts +3 -1
  100. package/lib/dist/sync/index.d.ts.map +1 -1
  101. package/lib/dist/sync/index.js +7 -1
  102. package/lib/dist/sync/index.js.map +1 -1
  103. package/lib/dist/sync/watcher.d.ts +109 -7
  104. package/lib/dist/sync/watcher.d.ts.map +1 -1
  105. package/lib/dist/sync/watcher.js +215 -33
  106. package/lib/dist/sync/watcher.js.map +1 -1
  107. package/lib/dist/sync/worktree.d.ts +54 -0
  108. package/lib/dist/sync/worktree.d.ts.map +1 -0
  109. package/lib/dist/sync/worktree.js +136 -0
  110. package/lib/dist/sync/worktree.js.map +1 -0
  111. package/lib/dist/types.d.ts +1 -1
  112. package/lib/dist/types.d.ts.map +1 -1
  113. package/lib/dist/types.js +1 -0
  114. package/lib/dist/types.js.map +1 -1
  115. package/lib/node_modules/.package-lock.json +29 -1
  116. package/lib/node_modules/chokidar/LICENSE +21 -0
  117. package/lib/node_modules/chokidar/README.md +305 -0
  118. package/lib/node_modules/chokidar/esm/handler.d.ts +90 -0
  119. package/lib/node_modules/chokidar/esm/handler.js +629 -0
  120. package/lib/node_modules/chokidar/esm/index.d.ts +215 -0
  121. package/lib/node_modules/chokidar/esm/index.js +798 -0
  122. package/lib/node_modules/chokidar/esm/package.json +1 -0
  123. package/lib/node_modules/chokidar/handler.d.ts +90 -0
  124. package/lib/node_modules/chokidar/handler.js +635 -0
  125. package/lib/node_modules/chokidar/index.d.ts +215 -0
  126. package/lib/node_modules/chokidar/index.js +804 -0
  127. package/lib/node_modules/chokidar/package.json +69 -0
  128. package/lib/node_modules/readdirp/LICENSE +21 -0
  129. package/lib/node_modules/readdirp/README.md +120 -0
  130. package/lib/node_modules/readdirp/esm/index.d.ts +108 -0
  131. package/lib/node_modules/readdirp/esm/index.js +257 -0
  132. package/lib/node_modules/readdirp/esm/package.json +1 -0
  133. package/lib/node_modules/readdirp/index.d.ts +108 -0
  134. package/lib/node_modules/readdirp/index.js +263 -0
  135. package/lib/node_modules/readdirp/package.json +70 -0
  136. package/lib/package.json +2 -1
  137. package/package.json +1 -1
@@ -1,11 +1,17 @@
1
1
  /**
2
2
  * File Watcher
3
3
  *
4
- * Watches the project directory for file changes and triggers
5
- * debounced sync operations to keep the code graph up-to-date.
4
+ * Watches the project directory for file changes and triggers debounced sync
5
+ * operations to keep the code graph up-to-date.
6
6
  *
7
- * Uses Node.js native fs.watch with recursive mode (macOS FSEvents,
8
- * Windows ReadDirectoryChangesW, Linux inotify on Node 19+).
7
+ * Uses chokidar, whose `ignored` callback filters directories BEFORE they are
8
+ * watched so we never register inotify watches on excluded trees like
9
+ * node_modules/, dist/, .git/ (fixes #276: recursive fs.watch exhausted the
10
+ * kernel watch budget on large repos). The ignore decision reuses the indexer's
11
+ * `buildDefaultIgnore` (built-in default-ignore dirs + the project's .gitignore)
12
+ * so the watcher watches exactly the set the indexer indexes — in particular,
13
+ * node_modules/build/cache dirs are excluded even when the repo has no
14
+ * .gitignore (#407), which a .gitignore-only filter would miss.
9
15
  */
10
16
  /**
11
17
  * Options for the file watcher
@@ -29,22 +35,73 @@ export interface WatchOptions {
29
35
  */
30
36
  onSyncError?: (error: Error) => void;
31
37
  }
38
+ /**
39
+ * Per-file pending entry — tracks a source file the watcher saw an event for
40
+ * but hasn't yet synced into the index. Exposed via {@link FileWatcher.getPendingFiles}
41
+ * so MCP tool responses can mark stale results without forcing a wait.
42
+ */
43
+ export interface PendingFile {
44
+ /** Project-relative POSIX path (e.g. "src/foo.ts"). */
45
+ path: string;
46
+ /** Wall-clock ms at the first event we saw for this path since the last sync. */
47
+ firstSeenMs: number;
48
+ /** Wall-clock ms at the most recent event we saw for this path. */
49
+ lastSeenMs: number;
50
+ /**
51
+ * True when a sync is currently in flight that began AFTER this file's most
52
+ * recent event — i.e. the next successful sync will pick it up. False when
53
+ * the file is still in the debounce window (no sync running yet).
54
+ */
55
+ indexing: boolean;
56
+ }
32
57
  /**
33
58
  * FileWatcher monitors a project directory for changes and triggers
34
59
  * debounced sync operations via a provided callback.
35
60
  *
36
61
  * Design goals:
37
- * - Minimal resource usage (native OS file events, no polling)
62
+ * - Minimal resource usage (chokidar filters excluded directories before
63
+ * registering an inotify watch — see module docs / #276)
38
64
  * - Debounced to avoid thrashing on rapid saves
39
65
  * - Filters to supported source files by extension
40
- * - Ignores .codegraph/ directory changes
66
+ * - Ignores .codegraph/ and .git/ regardless of .gitignore
67
+ * - Tracks per-file pending state so MCP tools can flag stale results
68
+ * without blocking on a sync (issue #403)
41
69
  */
42
70
  export declare class FileWatcher {
43
71
  private watcher;
44
72
  private debounceTimer;
45
- private hasChanges;
73
+ /**
74
+ * Files seen by the watcher since the last successful sync — populated on
75
+ * every chokidar event, cleared at the start of a sync, and re-populated by
76
+ * events that arrive mid-sync (or restored on sync failure). Keyed by the
77
+ * same project-relative POSIX path the rest of the codebase uses, so a
78
+ * caller can intersect tool-response file paths against this map cheaply.
79
+ */
80
+ private pendingFiles;
81
+ /**
82
+ * Wall-clock ms at which the in-flight sync began. Combined with
83
+ * {@link pendingFiles}'s `lastSeenMs`, this distinguishes "still in the
84
+ * debounce window" (lastSeen > syncStarted, sync hasn't started yet for
85
+ * this edit) from "currently being indexed" (lastSeen <= syncStarted).
86
+ */
87
+ private syncStartedMs;
46
88
  private syncing;
47
89
  private stopped;
90
+ /**
91
+ * False until chokidar fires its `ready` event. Gates `pendingFiles`
92
+ * insertion so the initial crawl's `add` events (one per pre-existing
93
+ * source file) don't pollute the per-file staleness signal. The events
94
+ * still flow into `scheduleSync()` to preserve the previous "initial
95
+ * scan triggers a reconciling sync" behavior.
96
+ */
97
+ private chokidarReady;
98
+ /**
99
+ * Callbacks that resolve when chokidar fires `ready`. Used by tests (and
100
+ * any production caller that cares about a clean baseline) to deterministically
101
+ * gate on the end of the initial scan instead of guessing at a sleep duration.
102
+ */
103
+ private readyWaiters;
104
+ private ignoreMatcher;
48
105
  private readonly projectRoot;
49
106
  private readonly debounceMs;
50
107
  private readonly syncFn;
@@ -59,6 +116,14 @@ export declare class FileWatcher {
59
116
  * Returns true if watching started successfully, false otherwise.
60
117
  */
61
118
  start(): boolean;
119
+ /** Our own dirs are always ignored, regardless of .gitignore. */
120
+ private isAlwaysIgnored;
121
+ /**
122
+ * chokidar `ignored` predicate — true for any path that should NOT be watched.
123
+ * Uses chokidar's provided `stats` to decide directory-vs-file so a dir-only
124
+ * rule like `build/` matches, without an extra `statSync` per path.
125
+ */
126
+ private shouldIgnore;
62
127
  /**
63
128
  * Stop watching for file changes.
64
129
  */
@@ -67,13 +132,50 @@ export declare class FileWatcher {
67
132
  * Whether the watcher is currently active.
68
133
  */
69
134
  isActive(): boolean;
135
+ /**
136
+ * Resolves once chokidar has fired its `ready` event (or immediately if
137
+ * it has already done so). Useful for tests that need a deterministic
138
+ * boundary before asserting on `pendingFiles` — guessing a sleep duration
139
+ * is flaky under load because chokidar can take longer than expected to
140
+ * finish its initial crawl on slow filesystems / parallel test runs.
141
+ *
142
+ * Production callers don't need this: `pendingFiles` is read continuously,
143
+ * the staleness banner is always correct (empty or populated), and the
144
+ * initial-scan window is a small one-time startup cost.
145
+ */
146
+ waitUntilReady(timeoutMs?: number): Promise<void>;
70
147
  /**
71
148
  * Schedule a debounced sync.
72
149
  */
73
150
  private scheduleSync;
74
151
  /**
75
152
  * Flush pending changes by running sync.
153
+ *
154
+ * pendingFiles is NOT cleared at the start of sync — entries are removed
155
+ * only after sync commits successfully, and only for entries whose
156
+ * lastSeenMs <= syncStartedMs. That way, a query that arrives mid-sync
157
+ * still sees the affected files marked stale (the DB hasn't been updated
158
+ * yet), and an event that lands mid-sync persists into the follow-up.
159
+ *
160
+ * On sync failure pendingFiles is left untouched — every edit is still
161
+ * unindexed, and the rescheduled sync will absorb the same set next time.
76
162
  */
77
163
  private flush;
164
+ /**
165
+ * Snapshot of files seen by the watcher since the last successful sync.
166
+ *
167
+ * Used by MCP tool responses to mark stale results without blocking on a
168
+ * sync: a tool that returns a hit in `src/foo.ts` while `src/foo.ts` is in
169
+ * this list tells the agent "Read this file directly, the index lags."
170
+ *
171
+ * `indexing` is true when a sync is currently in flight whose start time is
172
+ * AFTER this file's most recent event — i.e. that sync will absorb the
173
+ * edit. False means the file is still inside the debounce window and no
174
+ * sync has started yet (a follow-up call a few hundred ms later may show
175
+ * `indexing: true` or the file may have left the list entirely).
176
+ *
177
+ * Cheap: O(pendingFiles.size), no I/O, no locks.
178
+ */
179
+ getPendingFiles(): PendingFile[];
78
180
  }
79
181
  //# sourceMappingURL=watcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/sync/watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAEhF;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACtC;AAED;;;;;;;;;GASG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,aAAa,CAA8C;IACnE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8D;IACrF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAiC;IACjE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAA8B;gBAGzD,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,EACnE,OAAO,GAAE,YAAiB;IAS5B;;;OAGG;IACH,KAAK,IAAI,OAAO;IA2DhB;;OAEG;IACH,IAAI,IAAI,IAAI;IAiBZ;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,OAAO,CAAC,YAAY;IAUpB;;OAEG;YACW,KAAK;CAuBpB"}
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/sync/watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAWH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAEhF;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACtC;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,aAAa,CAA8C;IACnE;;;;;;OAMG;IACH,OAAO,CAAC,YAAY,CAAkE;IACtF;;;;;OAKG;IACH,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa,CAAS;IAC9B;;;;OAIG;IACH,OAAO,CAAC,YAAY,CAAyB;IAI7C,OAAO,CAAC,aAAa,CAAuB;IAE5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8D;IACrF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAiC;IACjE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAA8B;gBAGzD,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,EACnE,OAAO,GAAE,YAAiB;IAS5B;;;OAGG;IACH,KAAK,IAAI,OAAO;IAuFhB,iEAAiE;IACjE,OAAO,CAAC,eAAe;IAOvB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAYpB;;OAEG;IACH,IAAI,IAAI,IAAI;IAmBZ;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,SAAS,SAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAahD;;OAEG;IACH,OAAO,CAAC,YAAY;IAUpB;;;;;;;;;;;OAWG;YACW,KAAK;IAuCnB;;;;;;;;;;;;;;OAcG;IACH,eAAe,IAAI,WAAW,EAAE;CAYjC"}
@@ -2,11 +2,17 @@
2
2
  /**
3
3
  * File Watcher
4
4
  *
5
- * Watches the project directory for file changes and triggers
6
- * debounced sync operations to keep the code graph up-to-date.
5
+ * Watches the project directory for file changes and triggers debounced sync
6
+ * operations to keep the code graph up-to-date.
7
7
  *
8
- * Uses Node.js native fs.watch with recursive mode (macOS FSEvents,
9
- * Windows ReadDirectoryChangesW, Linux inotify on Node 19+).
8
+ * Uses chokidar, whose `ignored` callback filters directories BEFORE they are
9
+ * watched so we never register inotify watches on excluded trees like
10
+ * node_modules/, dist/, .git/ (fixes #276: recursive fs.watch exhausted the
11
+ * kernel watch budget on large repos). The ignore decision reuses the indexer's
12
+ * `buildDefaultIgnore` (built-in default-ignore dirs + the project's .gitignore)
13
+ * so the watcher watches exactly the set the indexer indexes — in particular,
14
+ * node_modules/build/cache dirs are excluded even when the repo has no
15
+ * .gitignore (#407), which a .gitignore-only filter would miss.
10
16
  */
11
17
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
18
  if (k2 === undefined) k2 = k;
@@ -41,9 +47,13 @@ var __importStar = (this && this.__importStar) || (function () {
41
47
  return result;
42
48
  };
43
49
  })();
50
+ var __importDefault = (this && this.__importDefault) || function (mod) {
51
+ return (mod && mod.__esModule) ? mod : { "default": mod };
52
+ };
44
53
  Object.defineProperty(exports, "__esModule", { value: true });
45
54
  exports.FileWatcher = void 0;
46
- const fs = __importStar(require("fs"));
55
+ const path = __importStar(require("path"));
56
+ const chokidar_1 = __importDefault(require("chokidar"));
47
57
  const extraction_1 = require("../extraction");
48
58
  const errors_1 = require("../errors");
49
59
  const utils_1 = require("../utils");
@@ -53,17 +63,52 @@ const watch_policy_1 = require("./watch-policy");
53
63
  * debounced sync operations via a provided callback.
54
64
  *
55
65
  * Design goals:
56
- * - Minimal resource usage (native OS file events, no polling)
66
+ * - Minimal resource usage (chokidar filters excluded directories before
67
+ * registering an inotify watch — see module docs / #276)
57
68
  * - Debounced to avoid thrashing on rapid saves
58
69
  * - Filters to supported source files by extension
59
- * - Ignores .codegraph/ directory changes
70
+ * - Ignores .codegraph/ and .git/ regardless of .gitignore
71
+ * - Tracks per-file pending state so MCP tools can flag stale results
72
+ * without blocking on a sync (issue #403)
60
73
  */
61
74
  class FileWatcher {
62
75
  watcher = null;
63
76
  debounceTimer = null;
64
- hasChanges = false;
77
+ /**
78
+ * Files seen by the watcher since the last successful sync — populated on
79
+ * every chokidar event, cleared at the start of a sync, and re-populated by
80
+ * events that arrive mid-sync (or restored on sync failure). Keyed by the
81
+ * same project-relative POSIX path the rest of the codebase uses, so a
82
+ * caller can intersect tool-response file paths against this map cheaply.
83
+ */
84
+ pendingFiles = new Map();
85
+ /**
86
+ * Wall-clock ms at which the in-flight sync began. Combined with
87
+ * {@link pendingFiles}'s `lastSeenMs`, this distinguishes "still in the
88
+ * debounce window" (lastSeen > syncStarted, sync hasn't started yet for
89
+ * this edit) from "currently being indexed" (lastSeen <= syncStarted).
90
+ */
91
+ syncStartedMs = 0;
65
92
  syncing = false;
66
93
  stopped = false;
94
+ /**
95
+ * False until chokidar fires its `ready` event. Gates `pendingFiles`
96
+ * insertion so the initial crawl's `add` events (one per pre-existing
97
+ * source file) don't pollute the per-file staleness signal. The events
98
+ * still flow into `scheduleSync()` to preserve the previous "initial
99
+ * scan triggers a reconciling sync" behavior.
100
+ */
101
+ chokidarReady = false;
102
+ /**
103
+ * Callbacks that resolve when chokidar fires `ready`. Used by tests (and
104
+ * any production caller that cares about a clean baseline) to deterministically
105
+ * gate on the end of the initial scan instead of guessing at a sleep duration.
106
+ */
107
+ readyWaiters = [];
108
+ // The shared ignore matcher (built-in defaults + project .gitignore), built
109
+ // once at start(). Same source of truth the indexer uses, so watcher scope
110
+ // can never diverge from index scope.
111
+ ignoreMatcher = null;
67
112
  projectRoot;
68
113
  debounceMs;
69
114
  syncFn;
@@ -84,49 +129,108 @@ class FileWatcher {
84
129
  if (this.watcher)
85
130
  return true; // Already watching
86
131
  this.stopped = false;
87
- // Some environments make recursive fs.watch unusable — most notably WSL2
88
- // /mnt/ drives, where setup blocks long enough to break MCP startup
89
- // handshakes (issue #199). Skip watching there; callers fall back to
90
- // manual `codegraph sync` or the git sync hooks.
132
+ // Some environments make filesystem watching unusable — most notably
133
+ // WSL2 /mnt/ drives, where the underlying fs.watch calls block long
134
+ // enough to break MCP startup handshakes (issue #199). Skip watching
135
+ // there; callers fall back to manual `codegraph sync` or git sync hooks.
91
136
  const disabledReason = (0, watch_policy_1.watchDisabledReason)(this.projectRoot);
92
137
  if (disabledReason) {
93
138
  (0, errors_1.logDebug)('File watcher disabled', { reason: disabledReason, projectRoot: this.projectRoot });
94
139
  return false;
95
140
  }
141
+ // Reuse the indexer's ignore set so the watcher and indexer agree on scope.
142
+ // chokidar only registers an inotify watch on directories that pass this
143
+ // filter — that's the #276 fix.
144
+ this.ignoreMatcher = (0, extraction_1.buildDefaultIgnore)(this.projectRoot);
96
145
  try {
97
- this.watcher = fs.watch(this.projectRoot, { recursive: true }, (_eventType, filename) => {
98
- if (!filename || this.stopped)
146
+ this.watcher = chokidar_1.default.watch(this.projectRoot, {
147
+ // chokidar calls this for every path it encounters and only watches
148
+ // those that pass — so excluded trees (node_modules/, dist/, .git/, …)
149
+ // never get an inotify watch in the first place.
150
+ ignored: (testPath, stats) => this.shouldIgnore(testPath, stats),
151
+ });
152
+ // Chokidar emits `add` for every pre-existing source file during its
153
+ // initial scan. Those events should still trigger the post-startup
154
+ // reconciling sync (preserving prior behavior), but they must NOT land
155
+ // in pendingFiles — otherwise every file in the project shows up as
156
+ // "edited but not indexed" on startup, which is the opposite of the
157
+ // signal #403 is supposed to provide. Flip the flag on chokidar's
158
+ // `ready` event; from then on, real edits populate pendingFiles.
159
+ //
160
+ // We also clear `pendingFiles` here as defense-in-depth: chokidar can
161
+ // emit late initial-scan `add` events via setImmediate AFTER the
162
+ // `ready` callback runs (observed under test-parallelism load).
163
+ // Clearing once at ready guarantees a clean baseline; real subsequent
164
+ // edits repopulate the set normally.
165
+ this.watcher.on('ready', () => {
166
+ this.chokidarReady = true;
167
+ this.pendingFiles.clear();
168
+ for (const cb of this.readyWaiters)
169
+ cb();
170
+ this.readyWaiters.length = 0;
171
+ });
172
+ // chokidar emits 'all' for every event type; we only sync source files.
173
+ this.watcher.on('all', (_event, filePath) => {
174
+ if (this.stopped)
99
175
  return;
100
- // Normalize path separators
101
- const normalized = (0, utils_1.normalizePath)(filename);
102
- // Ignore .codegraph/ directory changes (our own DB writes)
103
- if (normalized === '.codegraph' ||
104
- normalized.startsWith('.codegraph/') ||
105
- normalized.startsWith('.codegraph\\')) {
176
+ const normalized = (0, utils_1.normalizePath)(path.relative(this.projectRoot, filePath));
177
+ // Defense in depth: `ignored` should already keep these out, but events
178
+ // can still arrive during setup or via symlink traversal.
179
+ if (this.isAlwaysIgnored(normalized))
106
180
  return;
107
- }
108
- // Only sync changes to files we can actually parse.
109
- if (!(0, extraction_1.isSourceFile)(normalized)) {
181
+ if (!(0, extraction_1.isSourceFile)(normalized))
110
182
  return;
111
- }
112
183
  (0, errors_1.logDebug)('File change detected', { file: normalized });
113
- this.hasChanges = true;
184
+ // Only track events from after chokidar's initial scan as pending
185
+ // edits — pre-existing files on disk are already represented by
186
+ // (or about to be reconciled by) the index, not a user edit.
187
+ if (this.chokidarReady) {
188
+ const now = Date.now();
189
+ const existing = this.pendingFiles.get(normalized);
190
+ this.pendingFiles.set(normalized, {
191
+ firstSeenMs: existing?.firstSeenMs ?? now,
192
+ lastSeenMs: now,
193
+ });
194
+ }
114
195
  this.scheduleSync();
115
196
  });
116
- // Handle watcher errors gracefully
197
+ // Handle watcher errors gracefully — don't crash, the user can restart.
117
198
  this.watcher.on('error', (err) => {
118
199
  (0, errors_1.logWarn)('File watcher error', { error: String(err) });
119
- // Don't crash — watcher may recover or user can restart
120
200
  });
121
201
  (0, errors_1.logDebug)('File watcher started', { projectRoot: this.projectRoot, debounceMs: this.debounceMs });
122
202
  return true;
123
203
  }
124
204
  catch (err) {
125
- // Recursive watch not supported (e.g., Linux < Node 19)
126
- (0, errors_1.logWarn)('Could not start file watcher — recursive fs.watch not supported on this platform', { error: String(err) });
205
+ // Watcher setup failed (e.g., permission denied, missing directory).
206
+ (0, errors_1.logWarn)('Could not start file watcher', { error: String(err) });
127
207
  return false;
128
208
  }
129
209
  }
210
+ /** Our own dirs are always ignored, regardless of .gitignore. */
211
+ isAlwaysIgnored(rel) {
212
+ return (rel === '.codegraph' || rel.startsWith('.codegraph/') ||
213
+ rel === '.git' || rel.startsWith('.git/'));
214
+ }
215
+ /**
216
+ * chokidar `ignored` predicate — true for any path that should NOT be watched.
217
+ * Uses chokidar's provided `stats` to decide directory-vs-file so a dir-only
218
+ * rule like `build/` matches, without an extra `statSync` per path.
219
+ */
220
+ shouldIgnore(testPath, stats) {
221
+ const rel = (0, utils_1.normalizePath)(path.relative(this.projectRoot, testPath));
222
+ if (!rel || rel === '.' || rel.startsWith('..'))
223
+ return false; // root / outside
224
+ if (this.isAlwaysIgnored(rel))
225
+ return true;
226
+ if (!this.ignoreMatcher)
227
+ return false;
228
+ if (stats) {
229
+ return this.ignoreMatcher.ignores(stats.isDirectory() ? rel + '/' : rel);
230
+ }
231
+ // Stats unknown: test both forms so a directory match isn't missed.
232
+ return this.ignoreMatcher.ignores(rel) || this.ignoreMatcher.ignores(rel + '/');
233
+ }
130
234
  /**
131
235
  * Stop watching for file changes.
132
236
  */
@@ -140,7 +244,9 @@ class FileWatcher {
140
244
  this.watcher.close();
141
245
  this.watcher = null;
142
246
  }
143
- this.hasChanges = false;
247
+ this.pendingFiles.clear();
248
+ this.chokidarReady = false;
249
+ this.ignoreMatcher = null;
144
250
  (0, errors_1.logDebug)('File watcher stopped');
145
251
  }
146
252
  /**
@@ -149,6 +255,31 @@ class FileWatcher {
149
255
  isActive() {
150
256
  return this.watcher !== null && !this.stopped;
151
257
  }
258
+ /**
259
+ * Resolves once chokidar has fired its `ready` event (or immediately if
260
+ * it has already done so). Useful for tests that need a deterministic
261
+ * boundary before asserting on `pendingFiles` — guessing a sleep duration
262
+ * is flaky under load because chokidar can take longer than expected to
263
+ * finish its initial crawl on slow filesystems / parallel test runs.
264
+ *
265
+ * Production callers don't need this: `pendingFiles` is read continuously,
266
+ * the staleness banner is always correct (empty or populated), and the
267
+ * initial-scan window is a small one-time startup cost.
268
+ */
269
+ waitUntilReady(timeoutMs = 10000) {
270
+ if (this.chokidarReady)
271
+ return Promise.resolve();
272
+ return new Promise((resolve, reject) => {
273
+ const t = setTimeout(() => {
274
+ const idx = this.readyWaiters.indexOf(handler);
275
+ if (idx >= 0)
276
+ this.readyWaiters.splice(idx, 1);
277
+ reject(new Error(`FileWatcher.waitUntilReady timed out after ${timeoutMs}ms`));
278
+ }, timeoutMs);
279
+ const handler = () => { clearTimeout(t); resolve(); };
280
+ this.readyWaiters.push(handler);
281
+ });
282
+ }
152
283
  /**
153
284
  * Schedule a debounced sync.
154
285
  */
@@ -163,30 +294,81 @@ class FileWatcher {
163
294
  }
164
295
  /**
165
296
  * Flush pending changes by running sync.
297
+ *
298
+ * pendingFiles is NOT cleared at the start of sync — entries are removed
299
+ * only after sync commits successfully, and only for entries whose
300
+ * lastSeenMs <= syncStartedMs. That way, a query that arrives mid-sync
301
+ * still sees the affected files marked stale (the DB hasn't been updated
302
+ * yet), and an event that lands mid-sync persists into the follow-up.
303
+ *
304
+ * On sync failure pendingFiles is left untouched — every edit is still
305
+ * unindexed, and the rescheduled sync will absorb the same set next time.
166
306
  */
167
307
  async flush() {
168
308
  // If already syncing, the post-sync check will re-trigger
169
309
  if (this.syncing || this.stopped)
170
310
  return;
171
- this.hasChanges = false;
311
+ this.syncStartedMs = Date.now();
172
312
  this.syncing = true;
173
313
  try {
174
314
  const result = await this.syncFn();
315
+ // Remove entries whose most recent event predates this sync — those
316
+ // edits are now in the DB. Entries with lastSeenMs > syncStartedMs
317
+ // arrived mid-sync; whether the in-flight sync captured them depends
318
+ // on when sync read that file, so we keep them as pending and let
319
+ // the follow-up sync handle them. We prefer false positives ("shown
320
+ // stale, actually fresh" → at worst one extra Read) over false
321
+ // negatives ("shown fresh, actually stale" → misleads the agent).
322
+ for (const [filePath, info] of this.pendingFiles) {
323
+ if (info.lastSeenMs <= this.syncStartedMs) {
324
+ this.pendingFiles.delete(filePath);
325
+ }
326
+ }
175
327
  this.onSyncComplete?.(result);
176
328
  }
177
329
  catch (err) {
178
330
  const error = err instanceof Error ? err : new Error(String(err));
179
331
  (0, errors_1.logWarn)('Watch sync failed', { error: error.message });
332
+ // Failure: leave pendingFiles untouched. Every edit it tracks is
333
+ // still unindexed; the rescheduled sync sees the same set.
180
334
  this.onSyncError?.(error);
181
335
  }
182
336
  finally {
183
337
  this.syncing = false;
184
- // If new changes arrived during sync, schedule another
185
- if (this.hasChanges && !this.stopped) {
338
+ // If pending files remain (mid-sync events, or this sync failed),
339
+ // schedule another pass.
340
+ if (this.pendingFiles.size > 0 && !this.stopped) {
186
341
  this.scheduleSync();
187
342
  }
188
343
  }
189
344
  }
345
+ /**
346
+ * Snapshot of files seen by the watcher since the last successful sync.
347
+ *
348
+ * Used by MCP tool responses to mark stale results without blocking on a
349
+ * sync: a tool that returns a hit in `src/foo.ts` while `src/foo.ts` is in
350
+ * this list tells the agent "Read this file directly, the index lags."
351
+ *
352
+ * `indexing` is true when a sync is currently in flight whose start time is
353
+ * AFTER this file's most recent event — i.e. that sync will absorb the
354
+ * edit. False means the file is still inside the debounce window and no
355
+ * sync has started yet (a follow-up call a few hundred ms later may show
356
+ * `indexing: true` or the file may have left the list entirely).
357
+ *
358
+ * Cheap: O(pendingFiles.size), no I/O, no locks.
359
+ */
360
+ getPendingFiles() {
361
+ const result = [];
362
+ for (const [filePath, info] of this.pendingFiles) {
363
+ result.push({
364
+ path: filePath,
365
+ firstSeenMs: info.firstSeenMs,
366
+ lastSeenMs: info.lastSeenMs,
367
+ indexing: this.syncing && this.syncStartedMs >= info.lastSeenMs,
368
+ });
369
+ }
370
+ return result;
371
+ }
190
372
  }
191
373
  exports.FileWatcher = FileWatcher;
192
374
  //# sourceMappingURL=watcher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/sync/watcher.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,uCAAyB;AACzB,8CAA6C;AAC7C,sCAA8C;AAC9C,oCAAyC;AACzC,iDAAqD;AAwBrD;;;;;;;;;GASG;AACH,MAAa,WAAW;IACd,OAAO,GAAwB,IAAI,CAAC;IACpC,aAAa,GAAyC,IAAI,CAAC;IAC3D,UAAU,GAAG,KAAK,CAAC;IACnB,OAAO,GAAG,KAAK,CAAC;IAChB,OAAO,GAAG,KAAK,CAAC;IAEP,WAAW,CAAS;IACpB,UAAU,CAAS;IACnB,MAAM,CAA8D;IACpE,cAAc,CAAkC;IAChD,WAAW,CAA+B;IAE3D,YACE,WAAmB,EACnB,MAAmE,EACnE,UAAwB,EAAE;QAE1B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;QAC7C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;QAC7C,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,CAAC,mBAAmB;QAClD,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QAErB,yEAAyE;QACzE,oEAAoE;QACpE,qEAAqE;QACrE,iDAAiD;QACjD,MAAM,cAAc,GAAG,IAAA,kCAAmB,EAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7D,IAAI,cAAc,EAAE,CAAC;YACnB,IAAA,iBAAQ,EAAC,uBAAuB,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YAC7F,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,KAAK,CACrB,IAAI,CAAC,WAAW,EAChB,EAAE,SAAS,EAAE,IAAI,EAAE,EACnB,CAAC,UAAU,EAAE,QAAQ,EAAE,EAAE;gBACvB,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO;oBAAE,OAAO;gBAEtC,4BAA4B;gBAC5B,MAAM,UAAU,GAAG,IAAA,qBAAa,EAAC,QAAQ,CAAC,CAAC;gBAE3C,2DAA2D;gBAC3D,IACE,UAAU,KAAK,YAAY;oBAC3B,UAAU,CAAC,UAAU,CAAC,aAAa,CAAC;oBACpC,UAAU,CAAC,UAAU,CAAC,cAAc,CAAC,EACrC,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,oDAAoD;gBACpD,IAAI,CAAC,IAAA,yBAAY,EAAC,UAAU,CAAC,EAAE,CAAC;oBAC9B,OAAO;gBACT,CAAC;gBAED,IAAA,iBAAQ,EAAC,sBAAsB,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;gBACvD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;gBACvB,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,CAAC,CACF,CAAC;YAEF,mCAAmC;YACnC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC/B,IAAA,gBAAO,EAAC,oBAAoB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACtD,wDAAwD;YAC1D,CAAC,CAAC,CAAC;YAEH,IAAA,iBAAQ,EAAC,sBAAsB,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YACjG,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,wDAAwD;YACxD,IAAA,gBAAO,EAAC,kFAAkF,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACpH,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAEpB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACjC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;QAED,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAA,iBAAQ,EAAC,sBAAsB,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;IAChD,CAAC;IAED;;OAEG;IACK,YAAY;QAClB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC1B,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACtB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,KAAK;QACjB,0DAA0D;QAC1D,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAEzC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAEpB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACnC,IAAI,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAClE,IAAA,gBAAO,EAAC,mBAAmB,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACvD,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YAErB,uDAAuD;YACvD,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACrC,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;CACF;AA1JD,kCA0JC"}
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/sync/watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Git Worktree Awareness
3
+ *
4
+ * A CodeGraph index lives in a `.codegraph/` directory and is resolved by
5
+ * walking up parent directories to the nearest one (see
6
+ * `findNearestCodeGraphRoot`). That walk is unaware of git worktrees: when a
7
+ * worktree is created *inside* the main checkout (e.g. some tools place them
8
+ * under `.gitignore`d paths like `.claude/worktrees/<name>/`), a command run
9
+ * from the worktree walks up and silently resolves the MAIN checkout's index.
10
+ *
11
+ * Every query then returns results from the main tree's code — usually a
12
+ * different branch — rather than the worktree the user is actually editing.
13
+ * Symbols added or changed only in the worktree are invisible. This module
14
+ * detects that "borrowed index" situation so callers can warn about it.
15
+ *
16
+ * Detection is best-effort: when git is unavailable or the path isn't a repo,
17
+ * it reports "no mismatch" and callers carry on unchanged.
18
+ */
19
+ /**
20
+ * Absolute, symlink-resolved toplevel of the git working tree that `dir`
21
+ * belongs to, or null when `dir` isn't inside a git repo (or git is missing).
22
+ *
23
+ * `git rev-parse --show-toplevel` returns the per-worktree root: the main
24
+ * checkout and each linked worktree report their own distinct directory, which
25
+ * is exactly the distinction this module relies on.
26
+ */
27
+ export declare function gitWorktreeRoot(dir: string): string | null;
28
+ export interface WorktreeIndexMismatch {
29
+ /** The git working tree the command was run from. */
30
+ worktreeRoot: string;
31
+ /** The (different) working tree whose `.codegraph` index is being used. */
32
+ indexRoot: string;
33
+ }
34
+ /**
35
+ * Detect when `startPath` lives in one git working tree but the resolved
36
+ * CodeGraph index (`indexRoot`) belongs to a *different* working tree.
37
+ *
38
+ * Returns null — meaning "nothing to warn about" — when:
39
+ * - `startPath` isn't in a git repo (or git is unavailable),
40
+ * - the index already lives in `startPath`'s own working tree, or
41
+ * - `indexRoot` isn't itself a working-tree root (an unrelated parent dir
42
+ * that merely happens to contain a `.codegraph/`), which keeps non-git
43
+ * and monorepo-subdir layouts from producing false warnings.
44
+ */
45
+ export declare function detectWorktreeIndexMismatch(startPath: string, indexRoot: string): WorktreeIndexMismatch | null;
46
+ /** One-line-per-fact warning describing a detected mismatch. */
47
+ export declare function worktreeMismatchWarning(m: WorktreeIndexMismatch): string;
48
+ /**
49
+ * Compact, single-line variant for prefixing a tool's result. Read tools
50
+ * return their answer inline, so the heads-up has to ride on the same payload
51
+ * the agent is already reading — a multi-line block would bury the result.
52
+ */
53
+ export declare function worktreeMismatchNotice(m: WorktreeIndexMismatch): string;
54
+ //# sourceMappingURL=worktree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../../src/sync/worktree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW1D;AAED,MAAM,WAAW,qBAAqB;IACpC,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,qBAAqB,GAAG,IAAI,CAa9B;AAED,gEAAgE;AAChE,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,qBAAqB,GAAG,MAAM,CASxE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAOvE"}