@colbymchenry/codegraph 0.9.7 → 0.9.9

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 (120) hide show
  1. package/README.md +116 -32
  2. package/dist/bin/codegraph.d.ts +25 -0
  3. package/dist/bin/node-version-check.d.ts +37 -0
  4. package/dist/bin/uninstall.d.ts +14 -0
  5. package/dist/context/formatter.d.ts +30 -0
  6. package/dist/context/index.d.ts +119 -0
  7. package/dist/context/markers.d.ts +19 -0
  8. package/dist/db/index.d.ts +101 -0
  9. package/dist/db/migrations.d.ts +44 -0
  10. package/dist/db/queries.d.ts +344 -0
  11. package/dist/db/sqlite-adapter.d.ts +46 -0
  12. package/dist/directory.d.ts +57 -0
  13. package/dist/errors.d.ts +136 -0
  14. package/dist/extraction/dfm-extractor.d.ts +31 -0
  15. package/dist/extraction/generated-detection.d.ts +30 -0
  16. package/dist/extraction/grammars.d.ts +100 -0
  17. package/dist/extraction/index.d.ts +138 -0
  18. package/dist/extraction/languages/c-cpp.d.ts +4 -0
  19. package/dist/extraction/languages/csharp.d.ts +3 -0
  20. package/dist/extraction/languages/dart.d.ts +3 -0
  21. package/dist/extraction/languages/go.d.ts +3 -0
  22. package/dist/extraction/languages/index.d.ts +10 -0
  23. package/dist/extraction/languages/java.d.ts +3 -0
  24. package/dist/extraction/languages/javascript.d.ts +3 -0
  25. package/dist/extraction/languages/kotlin.d.ts +3 -0
  26. package/dist/extraction/languages/lua.d.ts +3 -0
  27. package/dist/extraction/languages/luau.d.ts +3 -0
  28. package/dist/extraction/languages/objc.d.ts +3 -0
  29. package/dist/extraction/languages/pascal.d.ts +3 -0
  30. package/dist/extraction/languages/php.d.ts +3 -0
  31. package/dist/extraction/languages/python.d.ts +3 -0
  32. package/dist/extraction/languages/ruby.d.ts +3 -0
  33. package/dist/extraction/languages/rust.d.ts +3 -0
  34. package/dist/extraction/languages/scala.d.ts +3 -0
  35. package/dist/extraction/languages/swift.d.ts +3 -0
  36. package/dist/extraction/languages/typescript.d.ts +3 -0
  37. package/dist/extraction/liquid-extractor.d.ts +52 -0
  38. package/dist/extraction/mybatis-extractor.d.ts +48 -0
  39. package/dist/extraction/parse-worker.d.ts +8 -0
  40. package/dist/extraction/svelte-extractor.d.ts +56 -0
  41. package/dist/extraction/tree-sitter-helpers.d.ts +28 -0
  42. package/dist/extraction/tree-sitter-types.d.ts +193 -0
  43. package/dist/extraction/tree-sitter.d.ts +317 -0
  44. package/dist/extraction/vue-extractor.d.ts +36 -0
  45. package/dist/extraction/wasm-runtime-flags.d.ts +38 -0
  46. package/dist/graph/index.d.ts +8 -0
  47. package/dist/graph/queries.d.ts +106 -0
  48. package/dist/graph/traversal.d.ts +127 -0
  49. package/dist/index.d.ts +498 -0
  50. package/dist/installer/config-writer.d.ts +28 -0
  51. package/dist/installer/index.d.ts +100 -0
  52. package/dist/installer/instructions-template.d.ts +18 -0
  53. package/dist/installer/targets/antigravity.d.ts +57 -0
  54. package/dist/installer/targets/claude.d.ts +56 -0
  55. package/dist/installer/targets/codex.d.ts +18 -0
  56. package/dist/installer/targets/cursor.d.ts +35 -0
  57. package/dist/installer/targets/gemini.d.ts +26 -0
  58. package/dist/installer/targets/hermes.d.ts +18 -0
  59. package/dist/installer/targets/kiro.d.ts +27 -0
  60. package/dist/installer/targets/opencode.d.ts +30 -0
  61. package/dist/installer/targets/registry.d.ts +35 -0
  62. package/dist/installer/targets/shared.d.ts +77 -0
  63. package/dist/installer/targets/toml.d.ts +52 -0
  64. package/dist/installer/targets/types.d.ts +101 -0
  65. package/dist/mcp/daemon-paths.d.ts +46 -0
  66. package/dist/mcp/daemon.d.ts +161 -0
  67. package/dist/mcp/engine.d.ts +105 -0
  68. package/dist/mcp/index.d.ts +112 -0
  69. package/dist/mcp/proxy.d.ts +81 -0
  70. package/dist/mcp/server-instructions.d.ts +19 -0
  71. package/dist/mcp/session.d.ts +77 -0
  72. package/dist/mcp/tools.d.ts +413 -0
  73. package/dist/mcp/transport.d.ts +188 -0
  74. package/dist/mcp/version.d.ts +19 -0
  75. package/dist/resolution/callback-synthesizer.d.ts +10 -0
  76. package/dist/resolution/frameworks/cargo-workspace.d.ts +18 -0
  77. package/dist/resolution/frameworks/csharp.d.ts +8 -0
  78. package/dist/resolution/frameworks/drupal.d.ts +51 -0
  79. package/dist/resolution/frameworks/expo-modules.d.ts +3 -0
  80. package/dist/resolution/frameworks/express.d.ts +8 -0
  81. package/dist/resolution/frameworks/fabric.d.ts +3 -0
  82. package/dist/resolution/frameworks/go.d.ts +8 -0
  83. package/dist/resolution/frameworks/index.d.ts +48 -0
  84. package/dist/resolution/frameworks/java.d.ts +8 -0
  85. package/dist/resolution/frameworks/laravel.d.ts +13 -0
  86. package/dist/resolution/frameworks/nestjs.d.ts +26 -0
  87. package/dist/resolution/frameworks/play.d.ts +19 -0
  88. package/dist/resolution/frameworks/python.d.ts +10 -0
  89. package/dist/resolution/frameworks/react-native.d.ts +3 -0
  90. package/dist/resolution/frameworks/react.d.ts +8 -0
  91. package/dist/resolution/frameworks/ruby.d.ts +8 -0
  92. package/dist/resolution/frameworks/rust.d.ts +8 -0
  93. package/dist/resolution/frameworks/svelte.d.ts +9 -0
  94. package/dist/resolution/frameworks/swift-objc.d.ts +37 -0
  95. package/dist/resolution/frameworks/swift.d.ts +10 -0
  96. package/dist/resolution/frameworks/vue.d.ts +9 -0
  97. package/dist/resolution/go-module.d.ts +26 -0
  98. package/dist/resolution/import-resolver.d.ts +68 -0
  99. package/dist/resolution/index.d.ts +116 -0
  100. package/dist/resolution/lru-cache.d.ts +24 -0
  101. package/dist/resolution/name-matcher.d.ts +32 -0
  102. package/dist/resolution/path-aliases.d.ts +68 -0
  103. package/dist/resolution/strip-comments.d.ts +27 -0
  104. package/dist/resolution/swift-objc-bridge.d.ts +134 -0
  105. package/dist/resolution/types.d.ts +209 -0
  106. package/dist/search/query-parser.d.ts +57 -0
  107. package/dist/search/query-utils.d.ts +71 -0
  108. package/dist/sync/git-hooks.d.ts +45 -0
  109. package/dist/sync/index.d.ts +19 -0
  110. package/dist/sync/watch-policy.d.ts +48 -0
  111. package/dist/sync/watcher.d.ts +191 -0
  112. package/dist/sync/worktree.d.ts +54 -0
  113. package/dist/types.d.ts +377 -0
  114. package/dist/ui/glyphs.d.ts +42 -0
  115. package/dist/ui/shimmer-progress.d.ts +11 -0
  116. package/dist/ui/shimmer-worker.d.ts +2 -0
  117. package/dist/ui/types.d.ts +17 -0
  118. package/dist/utils.d.ts +205 -0
  119. package/npm-sdk.js +75 -0
  120. package/package.json +18 -7
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Shared MCP daemon — issue #411.
3
+ *
4
+ * One detached `codegraph serve --mcp` daemon process per project root,
5
+ * accepting N concurrent MCP clients over a Unix-domain socket (or named pipe
6
+ * on Windows). Each incoming connection gets its own {@link MCPSession}; all
7
+ * sessions share a single {@link MCPEngine}, which means a single file watcher
8
+ * (one inotify set), a single SQLite connection (one WAL writer), and a single
9
+ * tree-sitter warm-up — paid once, amortized across every agent talking to the
10
+ * project.
11
+ *
12
+ * Lifecycle (see also `./index.ts` and `./proxy.ts`):
13
+ * - The daemon is spawned **detached** (its own session/process group, stdio
14
+ * decoupled) by the first launcher that finds no daemon running. It is NOT
15
+ * a child of any MCP host, so closing one terminal / Ctrl-C'ing one session
16
+ * can't take it down and sever the others. That's why this process has no
17
+ * PPID watchdog: it deliberately outlives every individual client.
18
+ * - Every MCP host talks to the daemon through a thin `proxy` process (the
19
+ * thing the host actually spawned). The proxy keeps the #277 PPID watchdog,
20
+ * so a SIGKILL'd host still reaps its proxy promptly; the proxy's socket
21
+ * close then decrements the daemon's refcount.
22
+ * - When the last client disconnects the daemon lingers for
23
+ * `CODEGRAPH_DAEMON_IDLE_TIMEOUT_MS` (default 300s) so back-to-back agent
24
+ * runs in the same project don't repay startup, then exits cleanly. This is
25
+ * what keeps a single-agent session from leaking a daemon forever (#277).
26
+ *
27
+ * What this file owns:
28
+ * - Listening on the daemon socket and spawning per-connection sessions.
29
+ * - The handshake "hello" line that lets a proxy verify it found a
30
+ * same-version daemon before piping any JSON-RPC through it.
31
+ * - The lockfile (`.codegraph/daemon.pid`) competing daemons arbitrate
32
+ * against — atomic `O_EXCL` create with the full record written in the same
33
+ * breath (no empty-file window) + cleanup on exit.
34
+ * - Reference counting + idle timeout.
35
+ * - Graceful shutdown on SIGTERM/SIGINT and idle exit.
36
+ *
37
+ * What this file does NOT own:
38
+ * - The proxy side (`./proxy.ts`).
39
+ * - The decision of *whether* to run as daemon at all — that's `MCPServer`.
40
+ * - The MCP protocol state machine — that's `./session.ts`.
41
+ */
42
+ import { DaemonLockInfo } from './daemon-paths';
43
+ /** Bytes/parse-window for an oversized hello line — bounded against a malicious peer. */
44
+ declare const MAX_HELLO_LINE_BYTES = 4096;
45
+ /**
46
+ * Wire format for the one-shot hello line the daemon emits on every new
47
+ * connection. Versioned with the package's own semver so a 0.9.x proxy never
48
+ * pipes through a 0.10.x daemon (or vice-versa) — the proxy falls back to
49
+ * direct mode on mismatch rather than risk subtle wire incompatibilities.
50
+ */
51
+ export interface DaemonHello {
52
+ codegraph: string;
53
+ pid: number;
54
+ socketPath: string;
55
+ protocol: 1;
56
+ }
57
+ export interface DaemonStartResult {
58
+ /** Always-non-null for a successfully-started daemon. */
59
+ socketPath: string;
60
+ /** Lockfile contents as written. */
61
+ lock: DaemonLockInfo;
62
+ }
63
+ /**
64
+ * Run as the shared daemon for `projectRoot`. Resolves once the socket is
65
+ * listening. The Daemon owns the socket, the engine, and the lockfile until
66
+ * `stop()` is called or it exits on idle/signal.
67
+ *
68
+ * Race-safe: callers must first call `tryAcquireDaemonLock(projectRoot)` and
69
+ * only construct a Daemon if they got the lock (`kind: 'acquired'`). The atomic
70
+ * `O_EXCL` create inside the acquire helper — which now also writes the full
71
+ * record before returning — is the only synchronization between competing
72
+ * daemons.
73
+ */
74
+ export declare class Daemon {
75
+ private projectRoot;
76
+ private server;
77
+ private clients;
78
+ private idleTimer;
79
+ private idleTimeoutMs;
80
+ private engine;
81
+ private stopping;
82
+ private socketPath;
83
+ private pidPath;
84
+ constructor(projectRoot: string, opts?: {
85
+ idleTimeoutMs?: number;
86
+ });
87
+ /**
88
+ * Bind the socket, kick off engine init, and register signal handlers. The
89
+ * lockfile body was already written atomically by `tryAcquireDaemonLock`, so
90
+ * there is nothing to write here. The promise resolves once the server is
91
+ * listening — the daemon then sticks around until idle/shutdown.
92
+ */
93
+ start(): Promise<DaemonStartResult>;
94
+ /** Currently-connected client count. Exposed for tests / status output. */
95
+ getClientCount(): number;
96
+ /** The socket path the daemon is (or will be) listening on. */
97
+ getSocketPath(): string;
98
+ /** Graceful shutdown: close all sessions, the engine, and clean up the lock. */
99
+ stop(reason?: string): Promise<void>;
100
+ private handleConnection;
101
+ private dropClient;
102
+ private armIdleTimer;
103
+ private disarmIdleTimer;
104
+ private cleanupLockfile;
105
+ }
106
+ /**
107
+ * Result of `tryAcquireDaemonLock`. Either we got the lockfile (caller becomes
108
+ * the daemon), or it already existed (caller should connect to the existing
109
+ * daemon as a proxy, or — if the holder is dead — clear it and retry).
110
+ */
111
+ export type AcquireResult = {
112
+ kind: 'acquired';
113
+ pidPath: string;
114
+ info: DaemonLockInfo;
115
+ } | {
116
+ kind: 'taken';
117
+ existing: DaemonLockInfo | null;
118
+ pidPath: string;
119
+ };
120
+ /**
121
+ * Atomically create the daemon pidfile with its full record already in place.
122
+ * Returns either an `acquired` result (the caller is the daemon-elect and may
123
+ * construct a {@link Daemon}) or a `taken` result.
124
+ *
125
+ * must-fix 1 (issue #411 review): the lockfile must appear in ONE atomic step,
126
+ * already complete — never empty, even momentarily. The first attempt at this
127
+ * (`O_EXCL` create then a separate `writeSync`) left a microsecond window where
128
+ * the file existed but was empty; under concurrent daemon startup a third
129
+ * candidate could read that empty file, decode it as `null`, and `unlink` the
130
+ * winner's lock → two daemons (two watchers, two writers). The window was
131
+ * normally too small to hit, but the chokidar watcher's extra startup time made
132
+ * concurrent daemons overlap enough to reproduce it reliably.
133
+ *
134
+ * The fix writes the complete record to a private temp file, then hard-links it
135
+ * into place: `link()` is atomic AND exclusive (EEXIST if the target exists), so
136
+ * the pidfile becomes visible in one step already containing a full record.
137
+ * Whoever links first wins; everyone else gets EEXIST and reads a complete file.
138
+ * There is no empty-file window at all.
139
+ */
140
+ export declare function tryAcquireDaemonLock(projectRoot: string): AcquireResult;
141
+ /**
142
+ * Remove a stale pidfile, but only if it still names a dead process. Re-reads
143
+ * the file immediately before unlinking so we never delete a lock that a live
144
+ * daemon (re)acquired in the meantime.
145
+ *
146
+ * must-fix 1 (issue #411 review): the original unconditionally `unlink`'d,
147
+ * which let a racing candidate delete a healthy daemon's lock. Passing
148
+ * `expectedDeadPid` (the pid the caller believed was dead) makes the clear a
149
+ * compare-and-delete: bail if the file now holds a different pid, or any live
150
+ * pid. Returns true when the stale lock is gone (or was already gone).
151
+ */
152
+ export declare function clearStaleDaemonLock(pidPath: string, expectedDeadPid?: number): boolean;
153
+ /**
154
+ * Probe whether `pid` is currently alive (signal-0). Treats EPERM as alive on
155
+ * every platform (the process exists, it's just not ours to signal) so we never
156
+ * mistake a live daemon for a dead one and clear its lock.
157
+ */
158
+ export declare function isProcessAlive(pid: number): boolean;
159
+ /** Exported for test stubs that need to bound the hello-line read. */
160
+ export { MAX_HELLO_LINE_BYTES };
161
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1,105 @@
1
+ /**
2
+ * MCP shared engine — the heavyweight, *shared* state for an MCP server:
3
+ * the project's {@link CodeGraph} instance, file watcher, and the
4
+ * {@link ToolHandler} cache for cross-project queries.
5
+ *
6
+ * One engine, many sessions:
7
+ * - direct mode (single stdio session) instantiates one engine + one session;
8
+ * - daemon mode instantiates one engine and a new session per socket
9
+ * connection. Every session reads from the same SQLite WAL and the same
10
+ * inotify watch set — that's the entire point of issue #411.
11
+ */
12
+ import { ToolHandler } from './tools';
13
+ export interface MCPEngineOptions {
14
+ /**
15
+ * Whether to start the file watcher when initializing. Daemon and direct
16
+ * modes both want this true; tests may set it false to keep the engine
17
+ * cheap. Honors {@link watchDisabledReason} regardless.
18
+ */
19
+ watch?: boolean;
20
+ }
21
+ /**
22
+ * Shared MCP engine. Thread-safe in the sense that multiple sessions can
23
+ * call its methods concurrently — internally it serializes initialization
24
+ * through a single promise so multiple sessions racing each other on first
25
+ * connect never double-open the SQLite file.
26
+ */
27
+ export declare class MCPEngine {
28
+ private cg;
29
+ private toolHandler;
30
+ private projectPath;
31
+ private initPromise;
32
+ private watcherStarted;
33
+ private opts;
34
+ private closed;
35
+ constructor(opts?: MCPEngineOptions);
36
+ /**
37
+ * Convenience for {@link MCPServer} compatibility: pre-seed an explicit
38
+ * project path (from the `--path` CLI flag) without yet opening it. This
39
+ * keeps the synchronous constructor cheap; the actual open happens on the
40
+ * first `ensureInitialized` call.
41
+ */
42
+ setProjectPathHint(projectPath: string): void;
43
+ /** Project root that the engine resolved on first init (null if none). */
44
+ getProjectPath(): string | null;
45
+ /** Shared ToolHandler — sessions delegate tool dispatch through this. */
46
+ getToolHandler(): ToolHandler;
47
+ /** Whether the default project's CodeGraph is open. */
48
+ hasDefaultCodeGraph(): boolean;
49
+ /**
50
+ * Walk up from `searchFrom` to find the nearest `.codegraph/` and open it.
51
+ * Idempotent: concurrent callers share one in-flight init; subsequent
52
+ * callers after success are no-ops.
53
+ *
54
+ * The original `MCPServer.tryInitializeDefault` carried the same retry-on-
55
+ * subsequent-tool-call semantics; we preserve them by NOT throwing when the
56
+ * search misses (just leaves `cg` null so the next call can retry).
57
+ */
58
+ ensureInitialized(searchFrom: string): Promise<void>;
59
+ /**
60
+ * Synchronous last-resort init used by the per-session retry loop when the
61
+ * background `ensureInitialized` already finished (or failed) and we need
62
+ * to pick up a project that appeared *after* the engine started.
63
+ */
64
+ retryInitializeSync(searchFrom: string): void;
65
+ /**
66
+ * Close everything. Used on graceful daemon shutdown (SIGTERM/idle timeout)
67
+ * and on direct-mode stop. Idempotent.
68
+ */
69
+ stop(): void;
70
+ private doInitialize;
71
+ /**
72
+ * Start file watching on the active CodeGraph instance. Idempotent — the
73
+ * watcher is per-engine, not per-session, which is why the daemon path
74
+ * collapses N inotify sets to one. The wording of the disabled-reason log
75
+ * exactly matches the prior in-tree implementation so log-driven dashboards
76
+ * keep working.
77
+ */
78
+ private startWatching;
79
+ /**
80
+ * Reconcile the index with the current filesystem once, right after open —
81
+ * catches edits, adds, deletes, and `git pull`/`checkout` changes made while
82
+ * no watcher was running. Runs in the background, but the returned promise
83
+ * is pushed into the ToolHandler as a one-shot gate so the *first* tool
84
+ * call awaits completion before serving (without this, a tool call that
85
+ * races past sync returns rows for files that no longer exist on disk —
86
+ * and the per-file staleness banner can't help because `getPendingFiles()`
87
+ * is populated by the watcher, not by catch-up).
88
+ */
89
+ private catchUpSync;
90
+ }
91
+ /**
92
+ * Parse and clamp the CODEGRAPH_WATCH_DEBOUNCE_MS env override.
93
+ *
94
+ * Issue #403: workspaces with bursty writes (formatter-on-save, multi-file
95
+ * refactors) sometimes want a longer quiet window before sync. Returns
96
+ * `undefined` for unset / empty / non-numeric / out-of-range values so the
97
+ * FileWatcher default (2000ms) takes over — never throws.
98
+ *
99
+ * Clamp range: 100ms (faster would mean a sync per keystroke) to 60s (longer
100
+ * and the watcher feels broken). Out-of-range values are treated as "ignore
101
+ * this misconfiguration" rather than capped, since silently capping a 0 or
102
+ * a typoed value would mask a real config bug.
103
+ */
104
+ export declare function parseDebounceEnv(raw: string | undefined): number | undefined;
105
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1,112 @@
1
+ /**
2
+ * CodeGraph MCP Server
3
+ *
4
+ * Model Context Protocol server that exposes CodeGraph functionality
5
+ * as tools for AI assistants like Claude.
6
+ *
7
+ * @module mcp
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { MCPServer } from 'codegraph';
12
+ *
13
+ * const server = new MCPServer('/path/to/project');
14
+ * await server.start();
15
+ * ```
16
+ *
17
+ * Runtime modes (decided in {@link MCPServer.start}):
18
+ *
19
+ * - **Direct** — one process serves one MCP client over stdio. The pre-#411
20
+ * behavior; used when the user opts out (`CODEGRAPH_NO_DAEMON=1`), no
21
+ * `.codegraph/` is reachable, or the daemon machinery fails for any reason.
22
+ * - **Proxy** — what an MCP host actually talks to when sharing is on: a thin
23
+ * stdio↔socket pipe to the shared daemon. The proxy carries the #277 PPID
24
+ * watchdog, so a SIGKILL'd host reaps its proxy promptly. See {@link ./proxy.ts}.
25
+ * - **Daemon** — a *detached* background process (its own session/process
26
+ * group) that serves N proxies over a Unix-domain socket / named pipe,
27
+ * sharing one CodeGraph + watcher + SQLite handle. Spawned on demand; never a
28
+ * child of any host, so it survives individual sessions and is reaped by
29
+ * client-refcount + idle timeout. See {@link ./daemon.ts} and issue #411.
30
+ *
31
+ * The detached-daemon + always-proxy split is the fix for the review finding
32
+ * that the original in-process daemon (a) was the first host's child, so closing
33
+ * that terminal severed every other client, and (b) disabled the PPID watchdog,
34
+ * regressing #277 (orphaned daemons on host SIGKILL).
35
+ */
36
+ /**
37
+ * MCP Server for CodeGraph
38
+ *
39
+ * Implements the Model Context Protocol to expose CodeGraph
40
+ * functionality as tools that can be called by AI assistants.
41
+ *
42
+ * Backwards-compatible constructor and `start()` signature with the
43
+ * pre-issue-#411 implementation: callers continue to do
44
+ * `new MCPServer(path).start()`. Internally we now pick from direct / proxy /
45
+ * daemon at start time.
46
+ */
47
+ export declare class MCPServer {
48
+ private projectPath;
49
+ private session;
50
+ private engine;
51
+ private daemon;
52
+ private ppidWatchdog;
53
+ private originalPpid;
54
+ private hostPpid;
55
+ private stopped;
56
+ private mode;
57
+ constructor(projectPath?: string);
58
+ /**
59
+ * Start the MCP server.
60
+ *
61
+ * Decision order:
62
+ * 1. `CODEGRAPH_NO_DAEMON=1` → direct mode (unchanged pre-#411 behavior).
63
+ * 2. `CODEGRAPH_DAEMON_INTERNAL=1` → we ARE the detached daemon; listen.
64
+ * 3. No `.codegraph/` reachable → direct mode (the daemon's lockfile and
65
+ * socket both live under `.codegraph/`).
66
+ * 4. Otherwise connect to (or spawn) the shared daemon and proxy to it.
67
+ *
68
+ * On any unexpected failure in step 4 we transparently fall back to direct
69
+ * mode — a misbehaving daemon must never block a session from starting.
70
+ */
71
+ start(): Promise<void>;
72
+ /**
73
+ * Stop the server. In daemon mode this triggers graceful shutdown of every
74
+ * connected session; in direct mode it mirrors the pre-#411 behavior (close
75
+ * cg, exit). Proxy mode never routes through here — the proxy exits itself.
76
+ */
77
+ stop(): void;
78
+ /** Single-process stdio MCP session — the pre-issue-#411 code path. */
79
+ private startDirect;
80
+ /**
81
+ * Run as the detached shared daemon (process spawned with
82
+ * `CODEGRAPH_DAEMON_INTERNAL=1`). Arbitrate the O_EXCL lock, then either
83
+ * become the daemon (bind the socket, serve forever) or — if a live daemon
84
+ * already holds the lock — exit so we don't leak a redundant process.
85
+ *
86
+ * No PPID watchdog and no stdin handlers: the daemon is detached on purpose
87
+ * and reaps itself via client-refcount + idle timeout (see {@link Daemon}).
88
+ */
89
+ private startDaemonProcess;
90
+ /**
91
+ * Proxy mode (the common case). Serve the MCP handshake LOCALLY for instant
92
+ * tool registration, forwarding tool calls to the shared daemon — which is
93
+ * connected in the background (probed, then spawned + polled if absent) so the
94
+ * handshake never waits ~600ms on it. Runs until the host disconnects; the
95
+ * proxy falls back to an in-process engine if the daemon never binds, so this
96
+ * never wedges a session.
97
+ */
98
+ private runProxyWithLocalHandshake;
99
+ /** Standard SIGINT/SIGTERM handlers that route to our `stop()` (direct mode). */
100
+ private installSignalHandlers;
101
+ /**
102
+ * PPID watchdog (#277) — direct mode only. Daemon mode is detached on purpose
103
+ * and reaps via idle timeout; proxy mode installs its own watchdog inside
104
+ * {@link runProxy}. So this only ever runs for an in-process direct session.
105
+ */
106
+ private installPpidWatchdog;
107
+ }
108
+ export { StdioTransport } from './transport';
109
+ export { tools, ToolHandler } from './tools';
110
+ export { Daemon } from './daemon';
111
+ export { CodeGraphPackageVersion } from './version';
112
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,81 @@
1
+ /**
2
+ * MCP proxy mode — issue #411.
3
+ *
4
+ * The proxy is a near-transparent stdio↔socket pipe. Once it has verified
5
+ * the daemon's hello line (same major.minor.patch as ours), it does no
6
+ * protocol parsing of its own: every byte the MCP host writes to the proxy's
7
+ * stdin goes straight to the daemon socket, and every byte the daemon emits
8
+ * goes straight to the host's stdout. Server-initiated JSON-RPC requests
9
+ * (e.g. `roots/list`) flow through the same pipe transparently.
10
+ *
11
+ * Lifecycle expectations:
12
+ * - The proxy exits when *either* stream closes (host stdin closed →
13
+ * daemon socket end, or daemon-side socket close → host stdout end).
14
+ * - Closing the socket on the proxy side is what tells the daemon to
15
+ * decrement its connected-clients refcount.
16
+ * - On a parent-process death we can't detect via stdin close (e.g. SIGKILL
17
+ * of the MCP host), the proxy's PPID watchdog catches it — same logic
18
+ * the direct-mode server uses; see issue #277.
19
+ */
20
+ import * as net from 'net';
21
+ import type { MCPEngine } from './engine';
22
+ export interface ProxyResult {
23
+ /**
24
+ * `proxied` — successfully attached to a same-version daemon and piped
25
+ * stdio. The proxy stays alive until either end closes.
26
+ * `fallback-needed` — the daemon rejected us (version mismatch / unreachable
27
+ * socket) and the caller should run the server in direct mode.
28
+ */
29
+ outcome: 'proxied' | 'fallback-needed';
30
+ reason?: string;
31
+ }
32
+ /**
33
+ * Attempt to connect to the daemon at `socketPath` and pipe stdio through it.
34
+ *
35
+ * Returns a promise that resolves when either:
36
+ * - the connection succeeded and one of stdin/socket has now closed
37
+ * (after which the process should exit), or
38
+ * - the connection failed early enough that the caller can still fall
39
+ * back to direct mode.
40
+ *
41
+ * The `expectedVersion` param defaults to the package's own version — daemon
42
+ * and proxy MUST match exactly. Mismatch resolves with
43
+ * `outcome: 'fallback-needed'` so the caller can transparently start its own
44
+ * server. (We accept the cost of two concurrent servers in this case as the
45
+ * price of never silently running a stale daemon against newer client code.)
46
+ */
47
+ export declare function runProxy(socketPath: string, expectedVersion?: string): Promise<ProxyResult>;
48
+ /**
49
+ * Connect to a daemon at `socketPath` and verify its hello (exact version match).
50
+ * Returns the live socket (hello already consumed) or null if unreachable / stale
51
+ * / version-mismatched. Unlike {@link runProxy} it does NOT pipe — the caller
52
+ * owns the socket. Used by the local-handshake proxy's background connect.
53
+ */
54
+ export declare function connectWithHello(socketPath: string, expectedVersion?: string): Promise<net.Socket | 'version-mismatch' | null>;
55
+ /** Dependencies the local-handshake proxy needs, injected by MCPServer (which
56
+ * owns the daemon-spawn machinery and the engine factory). */
57
+ export interface LocalHandshakeDeps {
58
+ /** Probe → spawn → retry → hello-verify; resolves a connected daemon socket,
59
+ * or null when the daemon path is genuinely unavailable (→ in-process fallback). */
60
+ getDaemonSocket(): Promise<net.Socket | null>;
61
+ /** Lazily create an in-process engine — used ONLY if the daemon never comes up,
62
+ * preserving the "a broken daemon never wedges a session" guarantee. */
63
+ makeEngine(): MCPEngine;
64
+ /** Project root for the fallback engine's lazy init. */
65
+ root: string;
66
+ }
67
+ /**
68
+ * Local-handshake proxy (the cold-start fix).
69
+ *
70
+ * Answers `initialize` + `tools/list` from STATIC constants the instant the
71
+ * client asks — tools register in ~process-startup time instead of waiting
72
+ * ~600ms for the daemon to spawn+bind, which is what produced the "No such tool
73
+ * available" race that made headless agents flail into grep/Read. Tool CALLS are
74
+ * forwarded to the shared daemon (connected in the background); the daemon's
75
+ * response to the forwarded `initialize` is suppressed (the client already got
76
+ * the local one). If the daemon never comes up (version mismatch / spawn fail),
77
+ * a lazily-created in-process engine serves the calls — so the handshake speedup
78
+ * never costs the old fall-back-to-direct robustness.
79
+ */
80
+ export declare function runLocalHandshakeProxy(deps: LocalHandshakeDeps): Promise<void>;
81
+ //# sourceMappingURL=proxy.d.ts.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Server-level instructions emitted in the MCP `initialize` response.
3
+ *
4
+ * MCP clients (Claude Code, Cursor, opencode, LangChain, OpenAI Agent
5
+ * SDK, …) surface this text in the agent's system prompt automatically,
6
+ * giving the agent a high-level playbook for the codegraph toolset
7
+ * before it sees individual tool descriptions.
8
+ *
9
+ * Goals when editing this:
10
+ * - Tool selection by intent (which tool for which question)
11
+ * - Common chains (refactor planning = X then Y)
12
+ * - Anti-patterns (don't grep when codegraph_search is faster)
13
+ *
14
+ * Keep it tight. The agent reads this every session — long instructions
15
+ * burn tokens. Reference only tools that exist on `main`; gate any
16
+ * conditional tools behind feature checks if/when they ship.
17
+ */
18
+ export declare const SERVER_INSTRUCTIONS = "# Codegraph \u2014 code intelligence over an indexed knowledge graph\n\nCodegraph is a SQLite knowledge graph of every symbol, edge, and file\nin the workspace. Reads are sub-millisecond; the index lags writes by\nabout a second through the file watcher. Consult it BEFORE writing or\nediting code, not during.\n\n## Answer directly \u2014 don't delegate exploration\n\nFor \"how does X work\", architecture, trace, or where-is-X questions,\nanswer DIRECTLY \u2014 usually with ONE `codegraph_explore` call.\n`codegraph_explore` takes either a natural-language question or a bag of\nsymbol/file names and returns the verbatim source of the relevant symbols\ngrouped by file, so it is Read-equivalent and most often the ONLY\ncodegraph call you need. Codegraph IS the pre-built search index \u2014 so\ndelegating the lookup to a separate file-reading sub-task/agent, or\nrunning your own grep + read loop, repeats work codegraph already did and\ncosts more for the same answer. Reach for raw Read/Grep only to confirm a\nspecific detail codegraph didn't cover. A direct codegraph answer is\ntypically one to a few calls; a grep/read exploration is dozens.\n\n## Tool selection by intent\n\n- **Almost any question \u2014 \"how does X work\", architecture, a bug, \"what/where is X\", or surveying an area** \u2192 `codegraph_explore` (PRIMARY \u2014 call FIRST; ONE capped call returns the verbatim source of the relevant symbols grouped by file; most often the ONLY call you need)\n- **\"How does X reach/become Y? / the flow / the path from X to Y\"** \u2192 `codegraph_explore`, naming the symbols that span the flow (e.g. `mutateElement renderScene`) \u2014 it surfaces the call path among them, including dynamic-dispatch hops (callbacks, React re-render, JSX children) grep can't follow\n- **\"What is the symbol named X?\" (just its location)** \u2192 `codegraph_search`\n- **\"What calls this?\" / \"What does this call?\" / \"What would changing this break?\"** \u2192 `codegraph_callers` / `codegraph_callees` / `codegraph_impact`\n- **One specific symbol's full source (esp. a body `codegraph_explore` trimmed), or an OVERLOADED name** \u2192 `codegraph_node` (with `includeCode`): for an ambiguous name it returns EVERY matching definition's body in one call, so you never Read a file to find the right overload\n- **\"What's in directory X?\"** \u2192 `codegraph_files`\n- **\"Is the index ready / what's its size?\"** \u2192 `codegraph_status`\n\n## Common chains\n\n- **Flow / \"how does X reach Y\"**: ONE `codegraph_explore` with the symbol names spanning the flow \u2014 it surfaces the call path among them (riding dynamic-dispatch hops) AND returns their source. No need to reconstruct the path with `codegraph_search` + `codegraph_callers`.\n- **Onboarding / understanding any area**: ONE `codegraph_explore` is usually the whole answer. Only follow up \u2014 `codegraph_node` for a specific symbol \u2014 if something is still unclear.\n- **Refactor planning**: `codegraph_search` \u2192 `codegraph_callers` \u2192 `codegraph_impact`. The blast-radius answer comes from impact, not from walking callers manually.\n- **Debugging a regression**: `codegraph_callers` of the suspected symbol; widen with `codegraph_impact` if an unexpected call appears.\n\n## Anti-patterns\n\n- **Trust codegraph's results \u2014 don't re-verify them with grep.** They come from a full AST parse; re-checking with grep is slower, less accurate, and wastes context.\n- **Don't grep first** when looking up a symbol by name \u2014 `codegraph_search` is faster and returns kind + location + signature.\n- **Don't chain `codegraph_search` + `codegraph_node`** to understand an area \u2014 ONE `codegraph_explore` returns the relevant symbols' source together in a single round-trip.\n- **Don't loop `codegraph_node` over many symbols** \u2014 one `codegraph_explore` call returns them all grouped by file, while each separate call re-reads the whole context and costs far more. Use `codegraph_node` for a single symbol.\n- **After editing, check the staleness banner.** When a tool response starts with \"\u26A0\uFE0F Some files referenced below were edited since the last index sync\u2026\", the listed files are pending re-index \u2014 Read those specific files for accurate content. Every file NOT in that banner is fresh, so still trust codegraph. `codegraph_status` also lists pending files under \"Pending sync\".\n\n## Limitations\n\n- If a tool reports the project isn't initialized, `.codegraph/` doesn't exist yet \u2014 offer to run `codegraph init -i` to build the index.\n- Index lags file writes by ~1 second.\n- Cross-file resolution is best-effort name matching; ambiguous calls may return multiple candidates.\n- No live correctness validation \u2014 that's still the TypeScript compiler / test suite / linter's job. Codegraph supplements those with structural context they don't have.\n";
19
+ //# sourceMappingURL=server-instructions.d.ts.map
@@ -0,0 +1,77 @@
1
+ /**
2
+ * MCP per-connection session — speaks the JSON-RPC protocol (initialize,
3
+ * tools/list, tools/call) over a single {@link JsonRpcTransport}. It owns
4
+ * per-client state only (which protocol version the client asked for, whether
5
+ * it advertised `roots`, the one-shot roots/list latch); the heavyweight
6
+ * resources (CodeGraph, watcher, ToolHandler) live in the shared
7
+ * {@link MCPEngine} so daemon mode can collapse N inotify sets / DB handles
8
+ * to one.
9
+ *
10
+ * The state-machine itself mirrors what `MCPServer` used to do inline before
11
+ * issue #411 split it out — the same regression tests in
12
+ * `__tests__/mcp-initialize.test.ts` still drive this code path.
13
+ */
14
+ import { JsonRpcTransport } from './transport';
15
+ import { MCPEngine } from './engine';
16
+ /**
17
+ * MCP Server Info — kept on the session because some clients log it. The
18
+ * version tracks the real package version (was a hard-coded '0.1.0').
19
+ */
20
+ export declare const SERVER_INFO: {
21
+ name: string;
22
+ version: string;
23
+ };
24
+ /** MCP Protocol Version (latest the server claims). */
25
+ export declare const PROTOCOL_VERSION = "2024-11-05";
26
+ export interface MCPSessionOptions {
27
+ /**
28
+ * Explicit project path from the `--path` CLI flag. When set, the session
29
+ * will not bother asking the client for `roots/list` — we already know
30
+ * where the project lives.
31
+ */
32
+ explicitProjectPath?: string | null;
33
+ }
34
+ /**
35
+ * One MCP client's view of the server. Created fresh per stdio launch
36
+ * (direct mode) or per socket connection (daemon mode).
37
+ */
38
+ export declare class MCPSession {
39
+ private transport;
40
+ private engine;
41
+ private clientSupportsRoots;
42
+ private rootsAttempted;
43
+ private resolvePromise;
44
+ private explicitProjectPath;
45
+ constructor(transport: JsonRpcTransport, engine: MCPEngine, opts?: MCPSessionOptions);
46
+ /**
47
+ * Start handling messages from the transport. Returns immediately — the
48
+ * session lives for as long as the transport is open.
49
+ */
50
+ start(): void;
51
+ /**
52
+ * Tear down the session. Does NOT touch the engine (the engine may serve
53
+ * other sessions) or call `process.exit` (the daemon decides when to exit).
54
+ */
55
+ stop(): void;
56
+ /** Underlying transport — exposed for daemon-side close hooks. */
57
+ getTransport(): JsonRpcTransport;
58
+ private handleMessage;
59
+ private handleInitialize;
60
+ private handleToolsList;
61
+ private handleToolsCall;
62
+ /**
63
+ * Lazy default-project resolution. Three layers:
64
+ * 1. await the in-flight init kicked off from `handleInitialize` (if any);
65
+ * 2. if still uninitialized and we never asked the client for its roots,
66
+ * do so now (one-shot); fall back to cwd if the client lacks roots;
67
+ * 3. last-resort: re-walk from the best candidate — picks up projects
68
+ * that were `codegraph init`'d *after* the server started.
69
+ */
70
+ private retryInitIfNeeded;
71
+ /**
72
+ * Ask the client for its workspace root via `roots/list` and open the
73
+ * first one. Falls back to `process.cwd()` on timeout or empty answer.
74
+ */
75
+ private initFromRoots;
76
+ }
77
+ //# sourceMappingURL=session.d.ts.map