@colbymchenry/codegraph 0.9.9 → 1.0.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.
- package/README.md +120 -24
- package/dist/bin/codegraph.d.ts +1 -0
- package/dist/bin/fatal-handler.d.ts +20 -0
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/queries.d.ts +43 -0
- package/dist/db/sqlite-adapter.d.ts +7 -0
- package/dist/directory.d.ts +49 -2
- package/dist/extraction/astro-extractor.d.ts +79 -0
- package/dist/extraction/extraction-version.d.ts +25 -0
- package/dist/extraction/function-ref.d.ts +118 -0
- package/dist/extraction/grammars.d.ts +7 -1
- package/dist/extraction/index.d.ts +34 -0
- package/dist/extraction/languages/c-cpp.d.ts +8 -0
- package/dist/extraction/languages/csharp.d.ts +22 -0
- package/dist/extraction/languages/r.d.ts +3 -0
- package/dist/extraction/languages/typescript.d.ts +13 -0
- package/dist/extraction/liquid-extractor.d.ts +7 -0
- package/dist/extraction/razor-extractor.d.ts +42 -0
- package/dist/extraction/tree-sitter-types.d.ts +33 -0
- package/dist/extraction/tree-sitter.d.ts +211 -0
- package/dist/extraction/vue-extractor.d.ts +15 -0
- package/dist/index.d.ts +34 -2
- package/dist/installer/instructions-template.d.ts +34 -11
- package/dist/installer/targets/opencode.d.ts +9 -1
- package/dist/installer/targets/shared.d.ts +14 -0
- package/dist/mcp/daemon-manager.d.ts +42 -0
- package/dist/mcp/daemon-registry.d.ts +47 -0
- package/dist/mcp/daemon.d.ts +60 -1
- package/dist/mcp/dynamic-boundaries.d.ts +41 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/liveness-watchdog.d.ts +18 -0
- package/dist/mcp/ppid-watchdog.d.ts +44 -0
- package/dist/mcp/proxy.d.ts +6 -0
- package/dist/mcp/server-instructions.d.ts +12 -1
- package/dist/mcp/session.d.ts +2 -0
- package/dist/mcp/stdin-teardown.d.ts +27 -0
- package/dist/mcp/tools.d.ts +71 -0
- package/dist/resolution/callback-synthesizer.d.ts +3 -3
- package/dist/resolution/frameworks/astro.d.ts +9 -0
- package/dist/resolution/frameworks/index.d.ts +1 -0
- package/dist/resolution/import-resolver.d.ts +10 -0
- package/dist/resolution/index.d.ts +80 -0
- package/dist/resolution/name-matcher.d.ts +61 -0
- package/dist/resolution/types.d.ts +27 -3
- package/dist/resolution/workspace-packages.d.ts +48 -0
- package/dist/search/query-utils.d.ts +17 -1
- package/dist/sync/watcher.d.ts +124 -32
- package/dist/telemetry/index.d.ts +146 -0
- package/dist/types.d.ts +17 -2
- package/dist/upgrade/index.d.ts +132 -0
- package/dist/utils.d.ts +30 -24
- package/package.json +7 -7
package/dist/sync/watcher.d.ts
CHANGED
|
@@ -4,14 +4,31 @@
|
|
|
4
4
|
* Watches the project directory for file changes and triggers debounced sync
|
|
5
5
|
* operations to keep the code graph up-to-date.
|
|
6
6
|
*
|
|
7
|
-
* Uses
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* .
|
|
7
|
+
* Uses Node's built-in `fs.watch` directly (no third-party watcher, no native
|
|
8
|
+
* addon) with a per-platform strategy chosen to keep the open-descriptor /
|
|
9
|
+
* kernel-watch cost BOUNDED rather than growing with the number of files:
|
|
10
|
+
*
|
|
11
|
+
* - macOS / Windows: a SINGLE recursive `fs.watch(root, {recursive:true})`.
|
|
12
|
+
* libuv maps this to one FSEvents stream (macOS) / one
|
|
13
|
+
* ReadDirectoryChangesW handle (Windows), so it costs O(1) descriptors no
|
|
14
|
+
* matter how large the tree. This is the fix for the macOS file-table
|
|
15
|
+
* exhaustion (#644 / #496 / #555 / #628): the previous watcher held one
|
|
16
|
+
* open fd PER WATCHED FILE on macOS (tens of thousands of REG fds), which
|
|
17
|
+
* exhausted `kern.maxfiles` and crashed unrelated processes system-wide.
|
|
18
|
+
*
|
|
19
|
+
* - Linux: recursive `fs.watch` is unsupported, so we watch each (non-ignored)
|
|
20
|
+
* DIRECTORY with one inotify watch — O(directories), NOT O(files). New
|
|
21
|
+
* directories are picked up dynamically and an overall watch cap bounds
|
|
22
|
+
* inotify usage on pathological monorepos (#579). A single inotify watch on
|
|
23
|
+
* a directory already reports create/modify/delete for its children, so
|
|
24
|
+
* per-file watches are never needed.
|
|
25
|
+
*
|
|
26
|
+
* Excluded trees (node_modules/, dist/, .git/, …) are filtered via the
|
|
27
|
+
* indexer's `buildScopeIgnore` (built-in default-ignore dirs + the project's
|
|
28
|
+
* .gitignore) — on Linux they're never descended into (so they cost no watch),
|
|
29
|
+
* and on macOS/Windows the single recursive stream still covers them but their
|
|
30
|
+
* events are dropped before any sync is scheduled. Either way the watcher's
|
|
31
|
+
* scope matches the indexer's (#276 / #407).
|
|
15
32
|
*/
|
|
16
33
|
/**
|
|
17
34
|
* Options for the file watcher
|
|
@@ -34,6 +51,15 @@ export interface WatchOptions {
|
|
|
34
51
|
* Callback when a sync errors (for logging/diagnostics).
|
|
35
52
|
*/
|
|
36
53
|
onSyncError?: (error: Error) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Test-only. When true, `start()` installs NO OS-level fs.watch — the
|
|
56
|
+
* watcher is "inert" and only the {@link __emitWatchEventForTests} /
|
|
57
|
+
* {@link FileWatcher.ingestEventForTests} seam drives its pipeline. This
|
|
58
|
+
* restores the deterministic, OS-free behavior the unit tests need (real
|
|
59
|
+
* FSEvents/inotify delivery races under parallel vitest). Production never
|
|
60
|
+
* sets it.
|
|
61
|
+
*/
|
|
62
|
+
inertForTests?: boolean;
|
|
37
63
|
}
|
|
38
64
|
/**
|
|
39
65
|
* Thrown by a `syncFn` to signal that the underlying sync couldn't acquire
|
|
@@ -69,8 +95,9 @@ export interface PendingFile {
|
|
|
69
95
|
* debounced sync operations via a provided callback.
|
|
70
96
|
*
|
|
71
97
|
* Design goals:
|
|
72
|
-
* -
|
|
73
|
-
*
|
|
98
|
+
* - Bounded resource usage: O(1) descriptors on macOS/Windows (one recursive
|
|
99
|
+
* watch), O(directories) inotify watches on Linux — never O(files), which
|
|
100
|
+
* was the system-crashing fd leak on macOS (#644/#496/#555/#628).
|
|
74
101
|
* - Debounced to avoid thrashing on rapid saves
|
|
75
102
|
* - Filters to supported source files by extension
|
|
76
103
|
* - Ignores .codegraph/ and .git/ regardless of .gitignore
|
|
@@ -78,11 +105,18 @@ export interface PendingFile {
|
|
|
78
105
|
* without blocking on a sync (issue #403)
|
|
79
106
|
*/
|
|
80
107
|
export declare class FileWatcher {
|
|
81
|
-
|
|
108
|
+
/** macOS/Windows: the single recursive watcher. Null on Linux. */
|
|
109
|
+
private recursiveWatcher;
|
|
110
|
+
/** Linux: one watcher per watched directory (keyed by absolute path). */
|
|
111
|
+
private dirWatchers;
|
|
112
|
+
/** Set once the per-directory watch cap is hit, so we log only once. */
|
|
113
|
+
private dirCapWarned;
|
|
114
|
+
/** Test-only inert mode: started, but with no OS watcher installed. */
|
|
115
|
+
private inert;
|
|
82
116
|
private debounceTimer;
|
|
83
117
|
/**
|
|
84
118
|
* Files seen by the watcher since the last successful sync — populated on
|
|
85
|
-
* every
|
|
119
|
+
* every change event, cleared at the start of a sync, and re-populated by
|
|
86
120
|
* events that arrive mid-sync (or restored on sync failure). Keyed by the
|
|
87
121
|
* same project-relative POSIX path the rest of the codebase uses, so a
|
|
88
122
|
* caller can intersect tool-response file paths against this map cheaply.
|
|
@@ -98,17 +132,18 @@ export declare class FileWatcher {
|
|
|
98
132
|
private syncing;
|
|
99
133
|
private stopped;
|
|
100
134
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
135
|
+
* True once the initial watch set is established. Unlike the previous
|
|
136
|
+
* chokidar implementation there is no asynchronous initial "crawl" emitting
|
|
137
|
+
* an `add` per existing file — `fs.watch` only reports changes from the
|
|
138
|
+
* moment it's installed — so this flips to true synchronously at the end of
|
|
139
|
+
* `start()`. The startup reconcile against on-disk state is handled
|
|
140
|
+
* separately by the engine's catch-up sync, not by the watcher.
|
|
106
141
|
*/
|
|
107
|
-
private
|
|
142
|
+
private ready;
|
|
108
143
|
/**
|
|
109
|
-
* Callbacks that resolve when
|
|
110
|
-
* any production caller that cares about a clean baseline) to
|
|
111
|
-
* gate on
|
|
144
|
+
* Callbacks that resolve when the watch set is established. Used by tests
|
|
145
|
+
* (and any production caller that cares about a clean baseline) to
|
|
146
|
+
* deterministically gate on watcher readiness.
|
|
112
147
|
*/
|
|
113
148
|
private readyWaiters;
|
|
114
149
|
private ignoreMatcher;
|
|
@@ -117,6 +152,7 @@ export declare class FileWatcher {
|
|
|
117
152
|
private readonly syncFn;
|
|
118
153
|
private readonly onSyncComplete?;
|
|
119
154
|
private readonly onSyncError?;
|
|
155
|
+
private readonly inertForTests;
|
|
120
156
|
constructor(projectRoot: string, syncFn: () => Promise<{
|
|
121
157
|
filesChanged: number;
|
|
122
158
|
durationMs: number;
|
|
@@ -126,32 +162,79 @@ export declare class FileWatcher {
|
|
|
126
162
|
* Returns true if watching started successfully, false otherwise.
|
|
127
163
|
*/
|
|
128
164
|
start(): boolean;
|
|
165
|
+
/**
|
|
166
|
+
* macOS/Windows: one recursive watcher for the whole tree. O(1) descriptors.
|
|
167
|
+
* `filename` arrives relative to the project root (with subdirectories), so
|
|
168
|
+
* it maps straight to a project-relative path.
|
|
169
|
+
*/
|
|
170
|
+
private startRecursive;
|
|
171
|
+
/**
|
|
172
|
+
* Linux: walk the (non-ignored) tree and watch each directory. One inotify
|
|
173
|
+
* watch per directory reports create/modify/delete for that directory's
|
|
174
|
+
* direct children, so we never watch individual files.
|
|
175
|
+
*/
|
|
176
|
+
private startPerDirectory;
|
|
177
|
+
/**
|
|
178
|
+
* Add an inotify watch for `dir` and recurse into its non-ignored
|
|
179
|
+
* subdirectories. When `markExisting` is true (a directory that appeared
|
|
180
|
+
* AFTER startup), the source files already inside it are recorded as pending
|
|
181
|
+
* — this closes the `mkdir + write` race where files created before the new
|
|
182
|
+
* directory's watch is installed would otherwise be missed until the next
|
|
183
|
+
* full sync. The initial startup walk passes false (the engine's catch-up
|
|
184
|
+
* sync owns the baseline).
|
|
185
|
+
*/
|
|
186
|
+
private watchTree;
|
|
187
|
+
/**
|
|
188
|
+
* Linux per-directory event handler. `filename` is relative to `dir`. A new
|
|
189
|
+
* sub-directory is picked up by extending the watch tree; everything else is
|
|
190
|
+
* routed through the shared change handler.
|
|
191
|
+
*/
|
|
192
|
+
private handleDirEvent;
|
|
193
|
+
/**
|
|
194
|
+
* Shared change handler for both watch strategies. `rel` is a
|
|
195
|
+
* project-relative POSIX path. Applies the ignore + source-file filters and,
|
|
196
|
+
* for a real source change, records it as pending (#403) and schedules a
|
|
197
|
+
* debounced sync.
|
|
198
|
+
*
|
|
199
|
+
* The recursive (macOS/Windows) watcher reports events for ignored trees too
|
|
200
|
+
* (one stream covers the whole repo), so the ignore check here is load-bearing
|
|
201
|
+
* — it drops node_modules/dist/.git churn before any sync is scheduled.
|
|
202
|
+
*/
|
|
203
|
+
private handleChange;
|
|
204
|
+
/** Close and forget the watch for a directory that errored/was removed. */
|
|
205
|
+
private unwatchDir;
|
|
129
206
|
/** Our own dirs are always ignored, regardless of .gitignore. */
|
|
130
207
|
private isAlwaysIgnored;
|
|
131
208
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* rule like `build/` matches
|
|
209
|
+
* True for any directory that should NOT be watched (used while building the
|
|
210
|
+
* Linux per-directory watch tree). Tests the directory form of the path so a
|
|
211
|
+
* dir-only ignore rule like `build/` matches.
|
|
135
212
|
*/
|
|
136
|
-
private
|
|
213
|
+
private shouldIgnoreDir;
|
|
137
214
|
/**
|
|
138
215
|
* Stop watching for file changes.
|
|
139
216
|
*/
|
|
140
217
|
stop(): void;
|
|
218
|
+
/**
|
|
219
|
+
* @internal Test-only: feed a synthetic project-relative change through the
|
|
220
|
+
* same filter → pendingFiles → debounced-sync path a real fs.watch event
|
|
221
|
+
* takes. Lets the watcher / staleness-banner suites stay deterministic
|
|
222
|
+
* instead of racing on OS watch-delivery latency. See
|
|
223
|
+
* {@link __emitWatchEventForTests}.
|
|
224
|
+
*/
|
|
225
|
+
ingestEventForTests(relPath: string): void;
|
|
141
226
|
/**
|
|
142
227
|
* Whether the watcher is currently active.
|
|
143
228
|
*/
|
|
144
229
|
isActive(): boolean;
|
|
145
230
|
/**
|
|
146
|
-
* Resolves once
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* is flaky under load because chokidar can take longer than expected to
|
|
150
|
-
* finish its initial crawl on slow filesystems / parallel test runs.
|
|
231
|
+
* Resolves once the watch set has been installed (or immediately if it
|
|
232
|
+
* already has). Useful for tests that need a deterministic boundary before
|
|
233
|
+
* asserting on `pendingFiles`.
|
|
151
234
|
*
|
|
152
235
|
* Production callers don't need this: `pendingFiles` is read continuously,
|
|
153
|
-
* the staleness banner is always correct (empty or populated), and
|
|
154
|
-
* initial-scan window
|
|
236
|
+
* the staleness banner is always correct (empty or populated), and there is
|
|
237
|
+
* no asynchronous initial-scan window with `fs.watch`.
|
|
155
238
|
*/
|
|
156
239
|
waitUntilReady(timeoutMs?: number): Promise<void>;
|
|
157
240
|
/**
|
|
@@ -188,4 +271,13 @@ export declare class FileWatcher {
|
|
|
188
271
|
*/
|
|
189
272
|
getPendingFiles(): PendingFile[];
|
|
190
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* Test-only: synthesize a source-file change for the live watcher running at
|
|
276
|
+
* `projectRoot`, exercising the real filter → pendingFiles → debounced-sync
|
|
277
|
+
* logic without depending on fs.watch delivery timing (which races under
|
|
278
|
+
* parallel vitest). `relPath` is project-relative POSIX (e.g. "src/foo.ts").
|
|
279
|
+
* Returns false if no live watcher is registered for that root (e.g. outside a
|
|
280
|
+
* test runtime, where the registry is intentionally not populated).
|
|
281
|
+
*/
|
|
282
|
+
export declare function __emitWatchEventForTests(projectRoot: string, relPath: string): boolean;
|
|
191
283
|
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anonymous usage telemetry — client side.
|
|
3
|
+
*
|
|
4
|
+
* The contract for what may be collected lives in docs/design/telemetry.md
|
|
5
|
+
* (and user-facing TELEMETRY.md); the ingest endpoint that enforces it is
|
|
6
|
+
* public at telemetry-worker/. This module honors four invariants:
|
|
7
|
+
*
|
|
8
|
+
* 1. Zero hot-path cost: recording is an in-memory increment. Disk writes are
|
|
9
|
+
* a tiny synchronous append at process exit (works under `process.exit()`,
|
|
10
|
+
* where `beforeExit` never fires); network sends happen opportunistically
|
|
11
|
+
* (startup of long-running commands, daemon interval, bounded await at the
|
|
12
|
+
* end of install/init) and are fire-and-forget everywhere else.
|
|
13
|
+
* 2. Zero stdout: stdio is the MCP protocol channel. Notices and debug output
|
|
14
|
+
* go to stderr only.
|
|
15
|
+
* 3. Off is off: when disabled, nothing is recorded, nothing is sent, and no
|
|
16
|
+
* socket is opened — there is no "opted out" ping. Turning telemetry off
|
|
17
|
+
* also deletes any buffered, unsent data.
|
|
18
|
+
* 4. Fail silent: offline, endpoint down, disk full — every failure mode is
|
|
19
|
+
* silence, never a retry loop, never an error surfaced to the user/agent.
|
|
20
|
+
*
|
|
21
|
+
* Usage counts aggregate locally into per-day rollups; only *completed* (UTC)
|
|
22
|
+
* days are sent, so volume scales with active machines, not with tool calls.
|
|
23
|
+
*/
|
|
24
|
+
export declare const TELEMETRY_ENDPOINT = "https://telemetry.getcodegraph.com/v1/events";
|
|
25
|
+
export declare const TELEMETRY_DOCS = "https://github.com/colbymchenry/codegraph/blob/main/TELEMETRY.md";
|
|
26
|
+
export type UsageKind = 'mcp_tool' | 'cli_command';
|
|
27
|
+
export type LifecycleEvent = 'install' | 'index' | 'uninstall';
|
|
28
|
+
/** Coarse buckets — exact counts are deliberately not collected. */
|
|
29
|
+
export declare function bucketFileCount(n: number): '<100' | '100-1k' | '1k-10k' | '10k+';
|
|
30
|
+
export declare function bucketDuration(ms: number): '<10s' | '10-60s' | '1-5m' | '5m+';
|
|
31
|
+
/** Collapse a backend identifier (e.g. `node-sqlite`) to the schema's enum. */
|
|
32
|
+
export declare function backendKind(backend: string): 'native' | 'wasm';
|
|
33
|
+
/**
|
|
34
|
+
* Shared "a full index completed" event (CLI init/index + installer local
|
|
35
|
+
* init): language names and coarse buckets only — never paths, file names,
|
|
36
|
+
* or exact counts. Structurally typed so callers don't need engine imports.
|
|
37
|
+
*/
|
|
38
|
+
export declare function recordIndexEvent(cg: {
|
|
39
|
+
getStats(): {
|
|
40
|
+
filesByLanguage: Record<string, number>;
|
|
41
|
+
};
|
|
42
|
+
getBackend(): string;
|
|
43
|
+
}, result: {
|
|
44
|
+
filesIndexed: number;
|
|
45
|
+
durationMs: number;
|
|
46
|
+
}): void;
|
|
47
|
+
export interface ClientInfo {
|
|
48
|
+
name?: string;
|
|
49
|
+
version?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface TelemetryStatus {
|
|
52
|
+
enabled: boolean;
|
|
53
|
+
/** What decided the current state — mirrors the precedence order. */
|
|
54
|
+
decidedBy: 'DO_NOT_TRACK' | 'CODEGRAPH_TELEMETRY' | 'config' | 'default';
|
|
55
|
+
machineId: string | null;
|
|
56
|
+
configPath: string;
|
|
57
|
+
}
|
|
58
|
+
export interface TelemetryOptions {
|
|
59
|
+
/** Global state dir; defaults to ~/.codegraph. Tests inject a temp dir. */
|
|
60
|
+
dir?: string;
|
|
61
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
62
|
+
now?: () => Date;
|
|
63
|
+
env?: NodeJS.ProcessEnv;
|
|
64
|
+
stderr?: (line: string) => void;
|
|
65
|
+
/** Tests opt out so short-lived instances don't pile onto process 'exit'. */
|
|
66
|
+
installExitHook?: boolean;
|
|
67
|
+
}
|
|
68
|
+
export declare class Telemetry {
|
|
69
|
+
private readonly dir;
|
|
70
|
+
private readonly fetchImpl;
|
|
71
|
+
private readonly now;
|
|
72
|
+
private readonly env;
|
|
73
|
+
private readonly writeStderr;
|
|
74
|
+
private counts;
|
|
75
|
+
private events;
|
|
76
|
+
private readonly installExitHook;
|
|
77
|
+
private exitHookInstalled;
|
|
78
|
+
private configCache;
|
|
79
|
+
private intervalHandle;
|
|
80
|
+
constructor(opts?: TelemetryOptions);
|
|
81
|
+
get configPath(): string;
|
|
82
|
+
get queuePath(): string;
|
|
83
|
+
/**
|
|
84
|
+
* Resolution order (first match wins) — keep in sync with TELEMETRY.md:
|
|
85
|
+
* DO_NOT_TRACK=1 > CODEGRAPH_TELEMETRY=0|1 > stored config > default on.
|
|
86
|
+
*/
|
|
87
|
+
getStatus(): TelemetryStatus;
|
|
88
|
+
isEnabled(): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Persist an explicit user choice (installer toggle or `codegraph
|
|
91
|
+
* telemetry on|off`). Turning telemetry off also deletes any buffered,
|
|
92
|
+
* unsent data — off means off.
|
|
93
|
+
*/
|
|
94
|
+
setEnabled(enabled: boolean, source: 'installer' | 'cli'): void;
|
|
95
|
+
/** True once any consent decision (or the first-run notice) is on disk. */
|
|
96
|
+
hasStoredChoice(): boolean;
|
|
97
|
+
/** In-memory increment — safe on the MCP tool-call hot path. */
|
|
98
|
+
recordUsage(kind: UsageKind, name: string, ok: boolean, client?: ClientInfo): void;
|
|
99
|
+
/** install / index / uninstall — buffered like everything else. */
|
|
100
|
+
recordLifecycle(event: LifecycleEvent, props: Record<string, unknown>): void;
|
|
101
|
+
/**
|
|
102
|
+
* Fire-and-forget send of everything sendable. Never throws, never logs
|
|
103
|
+
* above debug. Safe to call at startup of long-running commands.
|
|
104
|
+
*/
|
|
105
|
+
maybeFlush(): void;
|
|
106
|
+
/**
|
|
107
|
+
* Drain in-memory state to the buffer, then send completed-day rollups and
|
|
108
|
+
* lifecycle events. Bounded by `timeoutMs`; leftovers stay buffered for the
|
|
109
|
+
* next process. Awaited only where latency is invisible (install/init).
|
|
110
|
+
*/
|
|
111
|
+
flushNow(timeoutMs?: number): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Periodic flush for long-lived processes (MCP daemon / serve). Unref'd so
|
|
114
|
+
* it never keeps the process alive.
|
|
115
|
+
*/
|
|
116
|
+
startInterval(everyMs?: number): void;
|
|
117
|
+
stopInterval(): void;
|
|
118
|
+
private utcDay;
|
|
119
|
+
private readConfig;
|
|
120
|
+
private writeConfig;
|
|
121
|
+
/**
|
|
122
|
+
* Default-on consent is gated by a one-time stderr notice (interactive
|
|
123
|
+
* installs record their choice explicitly and never reach this).
|
|
124
|
+
*/
|
|
125
|
+
private firstRunNotice;
|
|
126
|
+
/**
|
|
127
|
+
* Synchronous, tiny, exit-safe: drain in-memory deltas to the JSONL queue.
|
|
128
|
+
* Runs on `process.on('exit')`, so it must never be async or slow.
|
|
129
|
+
*/
|
|
130
|
+
persistSync(): void;
|
|
131
|
+
private appendLines;
|
|
132
|
+
/**
|
|
133
|
+
* Atomically claim the queue for sending (rename). Concurrent processes
|
|
134
|
+
* can't double-send; a crash mid-send leaves a claim file that
|
|
135
|
+
* `recoverStaleClaims` merges back after an hour.
|
|
136
|
+
*/
|
|
137
|
+
private claimQueue;
|
|
138
|
+
private recoverStaleClaims;
|
|
139
|
+
/** Returns the lines that did NOT make it out (to be re-queued). */
|
|
140
|
+
private send;
|
|
141
|
+
private packageVersion;
|
|
142
|
+
private ensureExitHook;
|
|
143
|
+
private debug;
|
|
144
|
+
}
|
|
145
|
+
export declare function getTelemetry(): Telemetry;
|
|
146
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/types.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type EdgeKind = 'contains' | 'calls' | 'imports' | 'exports' | 'extends'
|
|
|
20
20
|
* Supported programming languages. See NODE_KINDS for why this is a
|
|
21
21
|
* runtime-iterable const array.
|
|
22
22
|
*/
|
|
23
|
-
export declare const LANGUAGES: readonly ["typescript", "javascript", "tsx", "jsx", "python", "go", "rust", "java", "c", "cpp", "csharp", "php", "ruby", "swift", "kotlin", "dart", "svelte", "vue", "liquid", "pascal", "scala", "lua", "luau", "objc", "yaml", "twig", "xml", "properties", "unknown"];
|
|
23
|
+
export declare const LANGUAGES: readonly ["typescript", "javascript", "tsx", "jsx", "python", "go", "rust", "java", "c", "cpp", "csharp", "razor", "php", "ruby", "swift", "kotlin", "dart", "svelte", "vue", "astro", "liquid", "pascal", "scala", "lua", "luau", "objc", "r", "yaml", "twig", "xml", "properties", "unknown"];
|
|
24
24
|
export type Language = (typeof LANGUAGES)[number];
|
|
25
25
|
/**
|
|
26
26
|
* A node in the knowledge graph representing a code symbol
|
|
@@ -64,6 +64,14 @@ export interface Node {
|
|
|
64
64
|
decorators?: string[];
|
|
65
65
|
/** Generic type parameters */
|
|
66
66
|
typeParameters?: string[];
|
|
67
|
+
/**
|
|
68
|
+
* Normalized return/result type name for a function/method (the bare class
|
|
69
|
+
* name, smart-pointer pointee unwrapped). Captured for C/C++ so resolution
|
|
70
|
+
* can infer a chained receiver's type from what the inner call returns —
|
|
71
|
+
* `Foo::instance().bar()` resolves `bar` on `Foo` (issue #645). Undefined for
|
|
72
|
+
* languages/symbols where it isn't captured.
|
|
73
|
+
*/
|
|
74
|
+
returnType?: string;
|
|
67
75
|
/** When the node was last updated */
|
|
68
76
|
updatedAt: number;
|
|
69
77
|
}
|
|
@@ -139,6 +147,13 @@ export interface ExtractionError {
|
|
|
139
147
|
/** Error code for categorization */
|
|
140
148
|
code?: string;
|
|
141
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Kinds an unresolved reference can carry. `function_ref` is internal-only —
|
|
152
|
+
* a function name used as a VALUE (callback registration, #756). It never
|
|
153
|
+
* becomes an edge kind: resolution maps it to a `references` edge targeting
|
|
154
|
+
* function/method nodes only (see `matchFunctionRef`).
|
|
155
|
+
*/
|
|
156
|
+
export type ReferenceKind = EdgeKind | 'function_ref';
|
|
142
157
|
/**
|
|
143
158
|
* A reference that couldn't be resolved during extraction
|
|
144
159
|
*/
|
|
@@ -148,7 +163,7 @@ export interface UnresolvedReference {
|
|
|
148
163
|
/** Name being referenced */
|
|
149
164
|
referenceName: string;
|
|
150
165
|
/** Type of reference (call, type, import, etc.) */
|
|
151
|
-
referenceKind:
|
|
166
|
+
referenceKind: ReferenceKind;
|
|
152
167
|
/** Location of the reference */
|
|
153
168
|
line: number;
|
|
154
169
|
column: number;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `codegraph upgrade`
|
|
3
|
+
*
|
|
4
|
+
* Self-update for the CLI, whatever way it was installed:
|
|
5
|
+
*
|
|
6
|
+
* - **bundle** — the self-contained runtime+app installed by `install.sh`
|
|
7
|
+
* (Linux/macOS) or `install.ps1` (Windows). Upgrading re-runs the SAME
|
|
8
|
+
* canonical installer script (single source of truth) so the download /
|
|
9
|
+
* version-resolution / PATH logic never drifts between first-install and
|
|
10
|
+
* upgrade.
|
|
11
|
+
* - **npm** — installed via `npm i -g @colbymchenry/codegraph`. Upgrading
|
|
12
|
+
* shells out to npm.
|
|
13
|
+
* - **npx** — ephemeral; nothing to upgrade (next `npx` fetches latest).
|
|
14
|
+
* - **source** — a git checkout running its own `dist/`; `git pull` + rebuild.
|
|
15
|
+
*
|
|
16
|
+
* Detection is structural (see `detectInstallMethod`): a bundle carries a
|
|
17
|
+
* vendored `node` binary and a `bin/codegraph` launcher next to its `lib/`, so
|
|
18
|
+
* we can recognize it from the running file's path without a marker file.
|
|
19
|
+
*
|
|
20
|
+
* Windows wrinkle: a running `node.exe` is locked and can't be deleted, so the
|
|
21
|
+
* bundle's `current\` dir can't be overwritten in place by the process doing
|
|
22
|
+
* the upgrade. We therefore spawn a DETACHED helper that waits for this
|
|
23
|
+
* process to exit (releasing the lock), then runs `install.ps1`. This is the
|
|
24
|
+
* conventional Windows self-update dance (rustup/nvm-windows do the same).
|
|
25
|
+
*/
|
|
26
|
+
export declare const REPO = "colbymchenry/codegraph";
|
|
27
|
+
export declare const NPM_PACKAGE = "@colbymchenry/codegraph";
|
|
28
|
+
export declare const INSTALL_SH_URL = "https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh";
|
|
29
|
+
export type InstallMethod = {
|
|
30
|
+
kind: 'bundle';
|
|
31
|
+
os: 'unix' | 'windows';
|
|
32
|
+
bundleRoot: string;
|
|
33
|
+
installDir: string | null;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'npm';
|
|
36
|
+
scope: 'global' | 'local';
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'npx';
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'source';
|
|
41
|
+
root: string;
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'unknown';
|
|
44
|
+
reason: string;
|
|
45
|
+
};
|
|
46
|
+
export interface DetectInput {
|
|
47
|
+
/** `__filename` of the running CLI module — `<…>/dist/bin/codegraph.js`. */
|
|
48
|
+
filename: string;
|
|
49
|
+
platform: NodeJS.Platform;
|
|
50
|
+
cwd: string;
|
|
51
|
+
/** Injectable existence probe (defaults to fs.existsSync) — for tests. */
|
|
52
|
+
exists?: (p: string) => boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Where the bundle installer keeps its install root, derived from the bundle
|
|
56
|
+
* dir so an upgrade reuses a custom `CODEGRAPH_INSTALL_DIR`. Returns null when
|
|
57
|
+
* the layout isn't the one the installer creates (then the installer falls
|
|
58
|
+
* back to its own default).
|
|
59
|
+
*
|
|
60
|
+
* unix: <installDir>/versions/<vX.Y.Z> (bundleRoot) → <installDir>
|
|
61
|
+
* windows: <installDir>\current (bundleRoot) → <installDir>
|
|
62
|
+
*/
|
|
63
|
+
export declare function deriveInstallDir(bundleRoot: string, os: 'unix' | 'windows', exists: (p: string) => boolean): string | null;
|
|
64
|
+
export declare function detectInstallMethod(input: DetectInput): InstallMethod;
|
|
65
|
+
export interface Semver {
|
|
66
|
+
major: number;
|
|
67
|
+
minor: number;
|
|
68
|
+
patch: number;
|
|
69
|
+
pre: string | null;
|
|
70
|
+
}
|
|
71
|
+
export declare function parseSemver(version: string): Semver | null;
|
|
72
|
+
/** Returns >0 if a>b, <0 if a<b, 0 if equal. Throws on unparseable input. */
|
|
73
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
74
|
+
export declare function isUpdateAvailable(current: string, latest: string): boolean;
|
|
75
|
+
/** `0.9.9` / `v0.9.9` → `v0.9.9` (release tags are v-prefixed). */
|
|
76
|
+
export declare function normalizeVersion(v: string): string;
|
|
77
|
+
/** Strip a leading `v`: `v0.9.9` → `0.9.9`. */
|
|
78
|
+
export declare function stripV(v: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* Parse the release tag out of the `Location` header GitHub returns for
|
|
81
|
+
* `/releases/latest` → `…/releases/tag/v0.9.9`. Pure so it's unit-tested.
|
|
82
|
+
*/
|
|
83
|
+
export declare function parseLatestTagFromLocation(location: string | undefined): string | null;
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the latest release tag (e.g. `v0.9.9`).
|
|
86
|
+
*
|
|
87
|
+
* Primary: read the redirect `Location` from `github.com/<repo>/releases/latest`
|
|
88
|
+
* — same trick install.sh uses, because the unauthenticated GitHub API is
|
|
89
|
+
* rate-limited to 60 req/h/IP and 403s on shared/cloud hosts (issue #325). The
|
|
90
|
+
* redirect has no such limit. Fall back to the API only if the redirect can't
|
|
91
|
+
* be read.
|
|
92
|
+
*/
|
|
93
|
+
export declare function resolveLatestVersion(repo?: string, timeoutMs?: number): Promise<string>;
|
|
94
|
+
export interface UpgradeOptions {
|
|
95
|
+
/** Pin a specific version (positional arg or CODEGRAPH_VERSION). */
|
|
96
|
+
version?: string;
|
|
97
|
+
/** Report current vs latest, don't change anything. */
|
|
98
|
+
check?: boolean;
|
|
99
|
+
/** Reinstall even if already on the resolved version. */
|
|
100
|
+
force?: boolean;
|
|
101
|
+
}
|
|
102
|
+
/** Injectable side-effects so the orchestrator stays unit-testable. */
|
|
103
|
+
export interface UpgradeDeps {
|
|
104
|
+
currentVersion: string;
|
|
105
|
+
method: InstallMethod;
|
|
106
|
+
resolveLatest: (pin?: string) => Promise<string>;
|
|
107
|
+
/** Run a command inheriting stdio; returns its exit code (-1 = spawn failed). */
|
|
108
|
+
run: (cmd: string, args: string[], env?: NodeJS.ProcessEnv) => number;
|
|
109
|
+
hasCommand: (cmd: string) => boolean;
|
|
110
|
+
log: (msg: string) => void;
|
|
111
|
+
warn: (msg: string) => void;
|
|
112
|
+
error: (msg: string) => void;
|
|
113
|
+
platform: NodeJS.Platform;
|
|
114
|
+
}
|
|
115
|
+
/** The honest, additive re-index reminder shown after a successful upgrade. */
|
|
116
|
+
export declare function reindexAdvisory(): string;
|
|
117
|
+
/**
|
|
118
|
+
* Returns the process exit code (0 = success / nothing to do, 1 = failure).
|
|
119
|
+
*/
|
|
120
|
+
export declare function runUpgrade(opts: UpgradeOptions, deps: UpgradeDeps): Promise<number>;
|
|
121
|
+
/** Build the in-place Windows upgrade script (exported for unit-testing). */
|
|
122
|
+
export declare function buildWindowsUpgradeScript(bundleRoot: string, version: string, arch: string): string;
|
|
123
|
+
/**
|
|
124
|
+
* True if `cmd` resolves to an executable on PATH. A pure-Node PATH scan — NOT
|
|
125
|
+
* a spawned `command -v`/`which`: `command` is a shell builtin (no standalone
|
|
126
|
+
* binary on Debian, though macOS ships one), and `which` isn't guaranteed
|
|
127
|
+
* present on minimal images, so spawning either is unreliable. Scanning PATH
|
|
128
|
+
* ourselves behaves identically on every platform.
|
|
129
|
+
*/
|
|
130
|
+
export declare function hasCommand(cmd: string): boolean;
|
|
131
|
+
export declare function defaultRun(cmd: string, args: string[], env?: NodeJS.ProcessEnv): number;
|
|
132
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/utils.d.ts
CHANGED
|
@@ -29,12 +29,38 @@
|
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
32
|
+
* Config "languages" whose nodes are pure key/value DATA lifted from a config
|
|
33
|
+
* file (e.g. Spring `application.{yml,properties}`), not source code.
|
|
34
|
+
*/
|
|
35
|
+
export declare const CONFIG_LEAF_LANGUAGES: ReadonlySet<string>;
|
|
36
|
+
/**
|
|
37
|
+
* A config-leaf node is a single key lifted out of a pure config/data file —
|
|
38
|
+
* `kind: 'constant'` in a {@link CONFIG_LEAF_LANGUAGES} language. Its on-disk
|
|
39
|
+
* line is `key = <value>`, and that value is routinely a secret (DB password,
|
|
40
|
+
* API key, JDBC URL with embedded creds). CodeGraph must surface the KEY only
|
|
41
|
+
* and never read/return the value, or it pushes secrets into agent context
|
|
42
|
+
* unbidden — the value isn't needed for resolution, and an agent that genuinely
|
|
43
|
+
* needs it can read the file directly. (#383)
|
|
44
|
+
*/
|
|
45
|
+
export declare function isConfigLeafNode(node: {
|
|
46
|
+
kind: string;
|
|
47
|
+
language?: string;
|
|
48
|
+
}): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Validate that a file path stays within the project root, resolving symlinks.
|
|
51
|
+
*
|
|
52
|
+
* Two layers: a cheap lexical check that catches `../` traversal, then a
|
|
53
|
+
* realpath check that catches symlink escapes — an in-repo symlink whose
|
|
54
|
+
* logical path is inside the root but whose real target points outside it
|
|
55
|
+
* (issue #527). A symlink that stays within the root is still allowed, so
|
|
56
|
+
* legitimate in-tree symlinks keep working. Both content-serving read sinks
|
|
57
|
+
* (codegraph_node `includeCode`, codegraph_explore source) go through here, so
|
|
58
|
+
* this is the chokepoint that keeps out-of-root file contents from leaking.
|
|
34
59
|
*
|
|
35
60
|
* @param projectRoot - The project root directory
|
|
36
|
-
* @param filePath - The relative file path to validate
|
|
37
|
-
* @returns The resolved absolute path, or null if it
|
|
61
|
+
* @param filePath - The (relative or absolute) file path to validate
|
|
62
|
+
* @returns The resolved absolute path (realpath when it exists), or null if it
|
|
63
|
+
* escapes the root
|
|
38
64
|
*/
|
|
39
65
|
export declare function validatePathWithinRoot(projectRoot: string, filePath: string): string | null;
|
|
40
66
|
/**
|
|
@@ -48,26 +74,6 @@ export declare function validatePathWithinRoot(projectRoot: string, filePath: st
|
|
|
48
74
|
* @returns An error message if invalid, or null if valid
|
|
49
75
|
*/
|
|
50
76
|
export declare function validateProjectPath(dirPath: string): string | null;
|
|
51
|
-
/**
|
|
52
|
-
* Check if a file path resolves to a location within the given root directory.
|
|
53
|
-
*
|
|
54
|
-
* Prevents path traversal attacks by ensuring the resolved absolute path
|
|
55
|
-
* starts with the resolved root path. Handles '..' sequences, symlink-like
|
|
56
|
-
* relative paths, and platform-specific separators.
|
|
57
|
-
*
|
|
58
|
-
* @param filePath - The path to check (can be relative or absolute)
|
|
59
|
-
* @param rootDir - The root directory that filePath must stay within
|
|
60
|
-
* @returns true if filePath resolves to a location within rootDir
|
|
61
|
-
*/
|
|
62
|
-
export declare function isPathWithinRoot(filePath: string, rootDir: string): boolean;
|
|
63
|
-
/**
|
|
64
|
-
* Like isPathWithinRoot but also resolves symlinks via fs.realpathSync.
|
|
65
|
-
*
|
|
66
|
-
* This catches symlink escapes where the logical path appears to be within
|
|
67
|
-
* root but the real path on disk points elsewhere. Falls back to logical
|
|
68
|
-
* path checking if realpath resolution fails (e.g. broken symlink).
|
|
69
|
-
*/
|
|
70
|
-
export declare function isPathWithinRootReal(filePath: string, rootDir: string): boolean;
|
|
71
77
|
/**
|
|
72
78
|
* Safely parse JSON with a fallback value.
|
|
73
79
|
* Prevents crashes from corrupted database metadata.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colbymchenry/codegraph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Local-first code intelligence for AI agents (MCP). Self-contained — bundles its own runtime.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codegraph": "npm-shim.js"
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"./package.json": "./package.json"
|
|
16
16
|
},
|
|
17
17
|
"optionalDependencies": {
|
|
18
|
-
"@colbymchenry/codegraph-darwin-arm64": "0.
|
|
19
|
-
"@colbymchenry/codegraph-darwin-x64": "0.
|
|
20
|
-
"@colbymchenry/codegraph-linux-arm64": "0.
|
|
21
|
-
"@colbymchenry/codegraph-linux-x64": "0.
|
|
22
|
-
"@colbymchenry/codegraph-win32-arm64": "0.
|
|
23
|
-
"@colbymchenry/codegraph-win32-x64": "0.
|
|
18
|
+
"@colbymchenry/codegraph-darwin-arm64": "1.0.1",
|
|
19
|
+
"@colbymchenry/codegraph-darwin-x64": "1.0.1",
|
|
20
|
+
"@colbymchenry/codegraph-linux-arm64": "1.0.1",
|
|
21
|
+
"@colbymchenry/codegraph-linux-x64": "1.0.1",
|
|
22
|
+
"@colbymchenry/codegraph-win32-arm64": "1.0.1",
|
|
23
|
+
"@colbymchenry/codegraph-win32-x64": "1.0.1"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"npm-shim.js",
|