@aroman22/codegraph-vba 1.3.3

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 (153) hide show
  1. package/README.md +855 -0
  2. package/dist/bin/codegraph.d.ts +26 -0
  3. package/dist/bin/command-supervision.d.ts +12 -0
  4. package/dist/bin/fatal-handler.d.ts +20 -0
  5. package/dist/bin/node-version-check.d.ts +37 -0
  6. package/dist/bin/uninstall.d.ts +14 -0
  7. package/dist/context/formatter.d.ts +30 -0
  8. package/dist/context/index.d.ts +119 -0
  9. package/dist/context/markers.d.ts +19 -0
  10. package/dist/db/index.d.ts +122 -0
  11. package/dist/db/migrations.d.ts +44 -0
  12. package/dist/db/queries.d.ts +402 -0
  13. package/dist/db/sqlite-adapter.d.ts +53 -0
  14. package/dist/directory.d.ts +179 -0
  15. package/dist/errors.d.ts +136 -0
  16. package/dist/extraction/astro-extractor.d.ts +79 -0
  17. package/dist/extraction/dfm-extractor.d.ts +31 -0
  18. package/dist/extraction/extraction-version.d.ts +25 -0
  19. package/dist/extraction/function-ref.d.ts +118 -0
  20. package/dist/extraction/generated-detection.d.ts +30 -0
  21. package/dist/extraction/grammars.d.ts +128 -0
  22. package/dist/extraction/index.d.ts +187 -0
  23. package/dist/extraction/languages/c-cpp.d.ts +12 -0
  24. package/dist/extraction/languages/csharp.d.ts +25 -0
  25. package/dist/extraction/languages/dart.d.ts +3 -0
  26. package/dist/extraction/languages/go.d.ts +3 -0
  27. package/dist/extraction/languages/index.d.ts +10 -0
  28. package/dist/extraction/languages/java.d.ts +3 -0
  29. package/dist/extraction/languages/javascript.d.ts +3 -0
  30. package/dist/extraction/languages/kotlin.d.ts +3 -0
  31. package/dist/extraction/languages/lua.d.ts +3 -0
  32. package/dist/extraction/languages/luau.d.ts +3 -0
  33. package/dist/extraction/languages/objc.d.ts +3 -0
  34. package/dist/extraction/languages/pascal.d.ts +3 -0
  35. package/dist/extraction/languages/php.d.ts +3 -0
  36. package/dist/extraction/languages/python.d.ts +3 -0
  37. package/dist/extraction/languages/r.d.ts +3 -0
  38. package/dist/extraction/languages/ruby.d.ts +3 -0
  39. package/dist/extraction/languages/rust.d.ts +3 -0
  40. package/dist/extraction/languages/scala.d.ts +3 -0
  41. package/dist/extraction/languages/swift.d.ts +3 -0
  42. package/dist/extraction/languages/typescript.d.ts +16 -0
  43. package/dist/extraction/liquid-extractor.d.ts +59 -0
  44. package/dist/extraction/mybatis-extractor.d.ts +48 -0
  45. package/dist/extraction/parse-pool.d.ts +126 -0
  46. package/dist/extraction/parse-worker.d.ts +8 -0
  47. package/dist/extraction/razor-extractor.d.ts +42 -0
  48. package/dist/extraction/sql-query-extractor.d.ts +25 -0
  49. package/dist/extraction/svelte-extractor.d.ts +56 -0
  50. package/dist/extraction/tree-sitter-helpers.d.ts +28 -0
  51. package/dist/extraction/tree-sitter-types.d.ts +239 -0
  52. package/dist/extraction/tree-sitter.d.ts +647 -0
  53. package/dist/extraction/vba-extractor.d.ts +394 -0
  54. package/dist/extraction/vba-form-extractor.d.ts +89 -0
  55. package/dist/extraction/vba-preprocess.d.ts +81 -0
  56. package/dist/extraction/vue-extractor.d.ts +51 -0
  57. package/dist/extraction/wasm-runtime-flags.d.ts +38 -0
  58. package/dist/graph/index.d.ts +8 -0
  59. package/dist/graph/queries.d.ts +106 -0
  60. package/dist/graph/traversal.d.ts +127 -0
  61. package/dist/index.d.ts +563 -0
  62. package/dist/installer/config-writer.d.ts +28 -0
  63. package/dist/installer/index.d.ts +100 -0
  64. package/dist/installer/instructions-template.d.ts +41 -0
  65. package/dist/installer/targets/antigravity.d.ts +57 -0
  66. package/dist/installer/targets/claude.d.ts +62 -0
  67. package/dist/installer/targets/codex.d.ts +18 -0
  68. package/dist/installer/targets/cursor.d.ts +35 -0
  69. package/dist/installer/targets/gemini.d.ts +26 -0
  70. package/dist/installer/targets/hermes.d.ts +18 -0
  71. package/dist/installer/targets/kiro.d.ts +27 -0
  72. package/dist/installer/targets/opencode.d.ts +38 -0
  73. package/dist/installer/targets/registry.d.ts +35 -0
  74. package/dist/installer/targets/shared.d.ts +101 -0
  75. package/dist/installer/targets/toml.d.ts +52 -0
  76. package/dist/installer/targets/types.d.ts +108 -0
  77. package/dist/mcp/daemon-manager.d.ts +42 -0
  78. package/dist/mcp/daemon-paths.d.ts +73 -0
  79. package/dist/mcp/daemon-registry.d.ts +47 -0
  80. package/dist/mcp/daemon.d.ts +258 -0
  81. package/dist/mcp/dynamic-boundaries.d.ts +41 -0
  82. package/dist/mcp/engine.d.ts +122 -0
  83. package/dist/mcp/index.d.ts +113 -0
  84. package/dist/mcp/liveness-watchdog.d.ts +18 -0
  85. package/dist/mcp/ppid-watchdog.d.ts +62 -0
  86. package/dist/mcp/proxy.d.ts +87 -0
  87. package/dist/mcp/query-pool.d.ts +94 -0
  88. package/dist/mcp/query-worker.d.ts +24 -0
  89. package/dist/mcp/server-instructions.d.ts +34 -0
  90. package/dist/mcp/session.d.ts +79 -0
  91. package/dist/mcp/stdin-teardown.d.ts +27 -0
  92. package/dist/mcp/tools.d.ts +591 -0
  93. package/dist/mcp/transport.d.ts +188 -0
  94. package/dist/mcp/version.d.ts +19 -0
  95. package/dist/project-config.d.ts +56 -0
  96. package/dist/reasoning/config.d.ts +45 -0
  97. package/dist/reasoning/credentials.d.ts +5 -0
  98. package/dist/reasoning/login.d.ts +21 -0
  99. package/dist/reasoning/reasoner.d.ts +43 -0
  100. package/dist/resolution/c-fnptr-synthesizer.d.ts +5 -0
  101. package/dist/resolution/callback-synthesizer.d.ts +15 -0
  102. package/dist/resolution/frameworks/astro.d.ts +9 -0
  103. package/dist/resolution/frameworks/cargo-workspace.d.ts +18 -0
  104. package/dist/resolution/frameworks/csharp.d.ts +8 -0
  105. package/dist/resolution/frameworks/drupal.d.ts +51 -0
  106. package/dist/resolution/frameworks/expo-modules.d.ts +3 -0
  107. package/dist/resolution/frameworks/express.d.ts +8 -0
  108. package/dist/resolution/frameworks/fabric.d.ts +3 -0
  109. package/dist/resolution/frameworks/go.d.ts +8 -0
  110. package/dist/resolution/frameworks/goframe.d.ts +41 -0
  111. package/dist/resolution/frameworks/index.d.ts +50 -0
  112. package/dist/resolution/frameworks/java.d.ts +8 -0
  113. package/dist/resolution/frameworks/laravel.d.ts +13 -0
  114. package/dist/resolution/frameworks/nestjs.d.ts +26 -0
  115. package/dist/resolution/frameworks/play.d.ts +19 -0
  116. package/dist/resolution/frameworks/python.d.ts +10 -0
  117. package/dist/resolution/frameworks/react-native.d.ts +3 -0
  118. package/dist/resolution/frameworks/react.d.ts +8 -0
  119. package/dist/resolution/frameworks/ruby.d.ts +8 -0
  120. package/dist/resolution/frameworks/rust.d.ts +8 -0
  121. package/dist/resolution/frameworks/svelte.d.ts +9 -0
  122. package/dist/resolution/frameworks/swift-objc.d.ts +37 -0
  123. package/dist/resolution/frameworks/swift.d.ts +10 -0
  124. package/dist/resolution/frameworks/vue.d.ts +9 -0
  125. package/dist/resolution/go-module.d.ts +26 -0
  126. package/dist/resolution/goframe-synthesizer.d.ts +28 -0
  127. package/dist/resolution/import-resolver.d.ts +78 -0
  128. package/dist/resolution/index.d.ts +196 -0
  129. package/dist/resolution/lru-cache.d.ts +24 -0
  130. package/dist/resolution/name-matcher.d.ts +93 -0
  131. package/dist/resolution/path-aliases.d.ts +68 -0
  132. package/dist/resolution/strip-comments.d.ts +27 -0
  133. package/dist/resolution/swift-objc-bridge.d.ts +134 -0
  134. package/dist/resolution/types.d.ts +233 -0
  135. package/dist/resolution/workspace-packages.d.ts +48 -0
  136. package/dist/search/query-parser.d.ts +57 -0
  137. package/dist/search/query-utils.d.ts +87 -0
  138. package/dist/sync/git-hooks.d.ts +45 -0
  139. package/dist/sync/index.d.ts +19 -0
  140. package/dist/sync/watch-policy.d.ts +48 -0
  141. package/dist/sync/watcher.d.ts +358 -0
  142. package/dist/sync/worktree.d.ts +54 -0
  143. package/dist/telemetry/index.d.ts +143 -0
  144. package/dist/types.d.ts +409 -0
  145. package/dist/ui/glyphs.d.ts +42 -0
  146. package/dist/ui/shimmer-progress.d.ts +11 -0
  147. package/dist/ui/shimmer-worker.d.ts +2 -0
  148. package/dist/ui/types.d.ts +17 -0
  149. package/dist/upgrade/index.d.ts +132 -0
  150. package/dist/utils.d.ts +224 -0
  151. package/npm-sdk.js +75 -0
  152. package/npm-shim.js +246 -0
  153. package/package.json +32 -0
@@ -0,0 +1,42 @@
1
+ import type { DaemonRecord, StopResult } from './daemon-registry';
2
+ /** Sentinel option values (not real roots, so they can't collide with a project path). */
3
+ export declare const STOP_ALL = "__stop_all__";
4
+ export declare const CANCEL = "__cancel__";
5
+ export interface PickItem {
6
+ value: string;
7
+ label: string;
8
+ hint?: string;
9
+ }
10
+ /** Compact uptime: `45s`, `12m`, `3h 5m`. */
11
+ export declare function formatUptime(ms: number): string;
12
+ /**
13
+ * Build the ordered, UI-ready option list: the current project's daemon first
14
+ * (so it's the auto-selected default), the rest newest-first, then "Stop all"
15
+ * (only when there's more than one) and "Cancel".
16
+ */
17
+ export declare function buildPickItems(daemons: DaemonRecord[], cwdRoot: string | null, now: number): PickItem[];
18
+ export interface PickerDeps {
19
+ list: () => DaemonRecord[];
20
+ stop: (root: string) => Promise<StopResult>;
21
+ stopAll: () => Promise<StopResult[]>;
22
+ /** Realpath'd root of the current project's daemon, or null. */
23
+ cwdRoot: string | null;
24
+ now: () => number;
25
+ /** Render the picker; returns the chosen value or a cancel sentinel. */
26
+ select: (opts: {
27
+ message: string;
28
+ options: PickItem[];
29
+ initialValue: string;
30
+ }) => Promise<unknown>;
31
+ isCancel: (v: unknown) => boolean;
32
+ /** Per-action note (e.g. "Stopped daemon …"). */
33
+ note: (msg: string) => void;
34
+ /** Final line + teardown (clack outro). */
35
+ done: (msg: string) => void;
36
+ }
37
+ /**
38
+ * Pick a daemon → stop it → re-prompt with what's left, until the user cancels
39
+ * (Esc / Ctrl-C / "Cancel"), picks "Stop all", or nothing remains.
40
+ */
41
+ export declare function runDaemonPicker(deps: PickerDeps): Promise<void>;
42
+ //# sourceMappingURL=daemon-manager.d.ts.map
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Daemon socket + lockfile path helpers — issue #411.
3
+ *
4
+ * One shared `codegraph serve --mcp` daemon per project root means we need a
5
+ * stable, project-keyed rendezvous between cooperating processes. The IPC
6
+ * surface area is just two file paths:
7
+ *
8
+ * - `daemon.sock` — Unix domain socket / named pipe the daemon listens on.
9
+ * - `daemon.pid` — atomic-create lockfile holding the daemon's pid + version.
10
+ *
11
+ * Both live under `.codegraph/` so the project-scoped uninstall (`codegraph
12
+ * uninit`) sweeps them up for free.
13
+ *
14
+ * Special-case: Unix domain socket paths have a hard length limit (~104 on
15
+ * macOS, ~108 on Linux); when the in-project path exceeds it we fall back to
16
+ * an absolute-path hash under `os.tmpdir()`. The pidfile always stays in the
17
+ * project (it doesn't have a length limit) — and acts as the authoritative
18
+ * pointer to the socket path the daemon chose.
19
+ *
20
+ * Second special-case (#997, #974): some filesystems can't host an AF_UNIX node
21
+ * AT ALL — ExFAT/FAT external volumes, certain network mounts, WSL2 DrvFs — so
22
+ * `listen()` throws ENOTSUP/EACCES regardless of path length. We can't cheaply
23
+ * tell those apart from a normal volume up front, so instead of guessing we
24
+ * expose an ORDERED candidate list (`getDaemonSocketCandidates`): the in-project
25
+ * path first, the deterministic tmpdir path as the fallback of last resort. The
26
+ * daemon binds the first that works (relocating past a capability error); the
27
+ * proxy connects the first that answers. Both walk the SAME list, so they still
28
+ * converge on whichever the daemon bound with zero coordination.
29
+ */
30
+ /**
31
+ * Ordered socket / named-pipe path candidates the daemon should try to bind (and
32
+ * the proxy should try to connect) for `projectRoot`, most-preferred first.
33
+ * Deterministic given a project root, so independent processes converge without
34
+ * coordination — even when the preferred candidate is unusable and both fall
35
+ * through to the same fallback.
36
+ *
37
+ * - Windows: a single named pipe (lives in the kernel pipe namespace, not on
38
+ * the project FS, so neither the length nor the ExFAT hazard applies).
39
+ * - Short in-project path: `[ .codegraph/daemon.sock , <tmpdir> ]` — try the
40
+ * project first, fall back to tmpdir if its FS can't host a socket (#997).
41
+ * - Long in-project path (deep monorepos, Bazel out dirs): `[ <tmpdir> ]` only
42
+ * — bind would throw ENAMETOOLONG, so we skip straight to tmpdir.
43
+ */
44
+ export declare function getDaemonSocketCandidates(projectRoot: string): string[];
45
+ /**
46
+ * The PREFERRED (primary) socket path — candidate 0. Use this only where a
47
+ * single representative path is wanted (the lockfile's informational
48
+ * `socketPath` field, status display). For binding/connecting, walk the full
49
+ * {@link getDaemonSocketCandidates} list — the daemon may bind a fallback when
50
+ * candidate 0 is unusable.
51
+ */
52
+ export declare function getDaemonSocketPath(projectRoot: string): string;
53
+ /** Absolute path to the daemon pid lockfile for `projectRoot`. */
54
+ export declare function getDaemonPidPath(projectRoot: string): string;
55
+ /** Structured contents of the pid lockfile. */
56
+ export interface DaemonLockInfo {
57
+ pid: number;
58
+ version: string;
59
+ socketPath: string;
60
+ startedAt: number;
61
+ }
62
+ /**
63
+ * Serialize a {@link DaemonLockInfo} for writing to the pidfile. JSON for
64
+ * human readability — operators occasionally `cat` this when debugging.
65
+ */
66
+ export declare function encodeLockInfo(info: DaemonLockInfo): string;
67
+ /**
68
+ * Parse a pidfile body. Tolerant of old-format pidfiles (plain decimal pid) so
69
+ * a 0.10.x daemon doesn't trip over a 0.9.x lockfile if that ever happens —
70
+ * we treat such a lockfile as "process is unknown version, refuse to share."
71
+ */
72
+ export declare function decodeLockInfo(raw: string): DaemonLockInfo | null;
73
+ //# sourceMappingURL=daemon-paths.d.ts.map
@@ -0,0 +1,47 @@
1
+ export interface DaemonRecord {
2
+ /** Realpath'd project root the daemon serves. */
3
+ root: string;
4
+ pid: number;
5
+ version: string;
6
+ socketPath: string;
7
+ /** Epoch ms when the daemon bound its socket. */
8
+ startedAt: number;
9
+ }
10
+ /**
11
+ * `~/.codegraph/daemons` — GLOBAL, keyed off the home install dir. (The
12
+ * `CODEGRAPH_DIR` env var only renames the per-project index dir, not this.)
13
+ */
14
+ export declare function getRegistryDir(): string;
15
+ /**
16
+ * Is `pid` a live process? `kill(pid, 0)` sends no signal — it just probes:
17
+ * ESRCH ⇒ dead, EPERM ⇒ alive but not ours (still alive). Same liveness check
18
+ * the PPID watchdog (#277) and daemon lock arbitration use.
19
+ */
20
+ export declare function isProcessAlive(pid: number): boolean;
21
+ /** Best-effort: record this daemon so `list`/`stop --all` can find it. */
22
+ export declare function registerDaemon(rec: DaemonRecord): void;
23
+ /** Best-effort: drop this daemon's record on graceful shutdown. */
24
+ export declare function deregisterDaemon(root: string): void;
25
+ /**
26
+ * All registered daemons whose process is still alive, newest first. Dead/garbage
27
+ * records are deleted as a side effect (self-healing) unless `prune` is false.
28
+ */
29
+ export declare function listDaemons(opts?: {
30
+ prune?: boolean;
31
+ }): DaemonRecord[];
32
+ export interface StopResult {
33
+ root: string;
34
+ pid: number | null;
35
+ /** 'term' graceful, 'kill' force, 'not-running' stale lock, 'no-daemon' none found. */
36
+ outcome: 'term' | 'kill' | 'not-running' | 'no-daemon';
37
+ }
38
+ /**
39
+ * Stop the daemon serving `root`: SIGTERM, wait, then SIGKILL if it won't go,
40
+ * then sweep its artifacts. `root` must be realpath'd (match how the daemon
41
+ * keys its socket/lockfile). Resolves the pid from the authoritative lockfile,
42
+ * falling back to the registry.
43
+ */
44
+ export declare function stopDaemonAt(root: string): Promise<StopResult>;
45
+ /** Stop every registered, live daemon. */
46
+ export declare function stopAllDaemons(): Promise<StopResult[]>;
47
+ //# sourceMappingURL=daemon-registry.d.ts.map
@@ -0,0 +1,258 @@
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 * as net from 'net';
43
+ import { DaemonLockInfo } from './daemon-paths';
44
+ /** Bytes/parse-window for an oversized hello line — bounded against a malicious peer. */
45
+ declare const MAX_HELLO_LINE_BYTES = 4096;
46
+ /**
47
+ * Wire format for the one-shot hello line the daemon emits on every new
48
+ * connection. Versioned with the package's own semver so a 0.9.x proxy never
49
+ * pipes through a 0.10.x daemon (or vice-versa) — the proxy falls back to
50
+ * direct mode on mismatch rather than risk subtle wire incompatibilities.
51
+ */
52
+ export interface DaemonHello {
53
+ codegraph: string;
54
+ pid: number;
55
+ socketPath: string;
56
+ protocol: 1;
57
+ }
58
+ /**
59
+ * Optional reverse-handshake line a proxy sends right after it verifies the
60
+ * daemon hello, carrying its own pids so the daemon can reap the client if its
61
+ * process dies WITHOUT the socket ever signalling close (the Windows named-pipe
62
+ * hazard behind #692). Entirely optional and fail-safe: a connection that never
63
+ * sends it (a legacy/direct client) just falls back to the socket-close
64
+ * lifecycle. The `codegraph_client` marker is what tells it apart from the
65
+ * client's first JSON-RPC message.
66
+ */
67
+ export interface DaemonClientHello {
68
+ codegraph_client: 1;
69
+ pid: number;
70
+ hostPid: number | null;
71
+ }
72
+ export interface DaemonStartResult {
73
+ /** Always-non-null for a successfully-started daemon. */
74
+ socketPath: string;
75
+ /** Lockfile contents as written. */
76
+ lock: DaemonLockInfo;
77
+ }
78
+ /**
79
+ * Run as the shared daemon for `projectRoot`. Resolves once the socket is
80
+ * listening. The Daemon owns the socket, the engine, and the lockfile until
81
+ * `stop()` is called or it exits on idle/signal.
82
+ *
83
+ * Race-safe: callers must first call `tryAcquireDaemonLock(projectRoot)` and
84
+ * only construct a Daemon if they got the lock (`kind: 'acquired'`). The atomic
85
+ * `O_EXCL` create inside the acquire helper — which now also writes the full
86
+ * record before returning — is the only synchronization between competing
87
+ * daemons.
88
+ */
89
+ export declare class Daemon {
90
+ private projectRoot;
91
+ private server;
92
+ private clients;
93
+ /** Per-client peer pids from the optional client-hello, for the liveness sweep. */
94
+ private clientPeers;
95
+ private idleTimer;
96
+ private idleTimeoutMs;
97
+ private maxIdleMs;
98
+ private lastActivityAt;
99
+ private maxIdleTimer;
100
+ private clientSweepTimer;
101
+ private engine;
102
+ private stopping;
103
+ private socketPath;
104
+ private pidPath;
105
+ constructor(projectRoot: string, opts?: {
106
+ idleTimeoutMs?: number;
107
+ maxIdleMs?: number;
108
+ });
109
+ /**
110
+ * Bind the socket, kick off engine init, and register signal handlers. The
111
+ * lockfile body was already written atomically by `tryAcquireDaemonLock`, so
112
+ * there is nothing to write here. The promise resolves once the server is
113
+ * listening — the daemon then sticks around until idle/shutdown.
114
+ */
115
+ start(): Promise<DaemonStartResult>;
116
+ /** Currently-connected client count. Exposed for tests / status output. */
117
+ getClientCount(): number;
118
+ /** The socket path the daemon is (or will be) listening on. */
119
+ getSocketPath(): string;
120
+ /** Graceful shutdown: close all sessions, the engine, and clean up the lock. */
121
+ stop(reason?: string): Promise<void>;
122
+ private handleConnection;
123
+ private dropClient;
124
+ private armIdleTimer;
125
+ private disarmIdleTimer;
126
+ /**
127
+ * Defense-in-depth against a daemon that outlives its clients (#692), for the
128
+ * cases the refcount + idle timer miss because a socket close never arrives:
129
+ * - **Inactivity backstop:** exit if no inbound traffic for `maxIdleMs` while
130
+ * clients are still (nominally) connected. A phantom client sends nothing,
131
+ * so it can't pin the daemon past this window.
132
+ * - **Liveness sweep:** drop any client whose peer process has died (per the
133
+ * client-hello pids), which re-arms the idle timer once the last real
134
+ * client is gone. Catches a dead peer within one sweep instead of waiting
135
+ * out the whole backstop.
136
+ * Both timers are unref'd — the listening server keeps the loop alive, and
137
+ * neither should hold it open on its own.
138
+ */
139
+ private startLivenessTimers;
140
+ /**
141
+ * Drop every connected client whose peer process is gone. Returns the count
142
+ * reaped. `isAlive` is injected for testing. Clients with unknown pids (no
143
+ * client-hello) are skipped — they rely on the socket-close path.
144
+ */
145
+ reapDeadClients(isAlive: (pid: number) => boolean): number;
146
+ private cleanupLockfile;
147
+ }
148
+ /**
149
+ * Result of `tryAcquireDaemonLock`. Either we got the lockfile (caller becomes
150
+ * the daemon), or it already existed (caller should connect to the existing
151
+ * daemon as a proxy, or — if the holder is dead — clear it and retry).
152
+ */
153
+ export type AcquireResult = {
154
+ kind: 'acquired';
155
+ pidPath: string;
156
+ info: DaemonLockInfo;
157
+ } | {
158
+ kind: 'taken';
159
+ existing: DaemonLockInfo | null;
160
+ pidPath: string;
161
+ };
162
+ /**
163
+ * Atomically create the daemon pidfile with its full record already in place.
164
+ * Returns either an `acquired` result (the caller is the daemon-elect and may
165
+ * construct a {@link Daemon}) or a `taken` result.
166
+ *
167
+ * must-fix 1 (issue #411 review): the lockfile must appear in ONE atomic step,
168
+ * already complete — never empty, even momentarily. The first attempt at this
169
+ * (`O_EXCL` create then a separate `writeSync`) left a microsecond window where
170
+ * the file existed but was empty; under concurrent daemon startup a third
171
+ * candidate could read that empty file, decode it as `null`, and `unlink` the
172
+ * winner's lock → two daemons (two watchers, two writers). The window was
173
+ * normally too small to hit, but the file watcher's extra startup time made
174
+ * concurrent daemons overlap enough to reproduce it reliably.
175
+ *
176
+ * The fix writes the complete record to a private temp file, then hard-links it
177
+ * into place: `link()` is atomic AND exclusive (EEXIST if the target exists), so
178
+ * the pidfile becomes visible in one step already containing a full record.
179
+ * Whoever links first wins; everyone else gets EEXIST and reads a complete file.
180
+ * There is no empty-file window at all.
181
+ *
182
+ * Filesystems without hard links (#997): ExFAT/FAT external volumes and some
183
+ * network mounts can't `link()` at all — it throws ENOTSUP/EPERM, which would
184
+ * otherwise kill the daemon before it ever reaches the socket bind. There we
185
+ * fall back to an O_EXCL create (`acquireLockViaExclusiveOpen`): still exclusive
186
+ * ("first writer wins"), but the full record is written through the fd in a
187
+ * second step, so the empty-file window the link approach removed is reopened —
188
+ * only on these filesystems, only for the microseconds between create and write
189
+ * (far narrower than the original bug, which the file watcher's startup latency
190
+ * widened). The race's worst case is two daemons briefly; on a single external
191
+ * drive that's strictly better than the daemon never starting at all.
192
+ */
193
+ export declare function tryAcquireDaemonLock(projectRoot: string): AcquireResult;
194
+ /**
195
+ * Exclusive-create the pidfile (O_CREAT|O_EXCL via the `wx` flag) and write the
196
+ * full record through the same fd — the hard-link-free fallback used by
197
+ * {@link tryAcquireDaemonLock} on filesystems without `link()`. Returns true if
198
+ * we created it (acquired the lock), false on EEXIST (another candidate holds
199
+ * it). Any other error propagates. Still exclusive, so "first writer wins" holds
200
+ * exactly as the link path does; the only difference is the brief empty-file
201
+ * window between create and write. Exported for testing.
202
+ */
203
+ export declare function acquireLockViaExclusiveOpen(pidPath: string, info: DaemonLockInfo): boolean;
204
+ /**
205
+ * Remove a stale pidfile, but only if it still names a dead process. Re-reads
206
+ * the file immediately before unlinking so we never delete a lock that a live
207
+ * daemon (re)acquired in the meantime.
208
+ *
209
+ * must-fix 1 (issue #411 review): the original unconditionally `unlink`'d,
210
+ * which let a racing candidate delete a healthy daemon's lock. Passing
211
+ * `expectedDeadPid` (the pid the caller believed was dead) makes the clear a
212
+ * compare-and-delete: bail if the file now holds a different pid, or any live
213
+ * pid. Returns true when the stale lock is gone (or was already gone).
214
+ */
215
+ export declare function clearStaleDaemonLock(pidPath: string, expectedDeadPid?: number): boolean;
216
+ /**
217
+ * Probe whether `pid` is currently alive (signal-0). Treats EPERM as alive on
218
+ * every platform (the process exists, it's just not ours to signal) so we never
219
+ * mistake a live daemon for a dead one and clear its lock.
220
+ */
221
+ export declare function isProcessAlive(pid: number): boolean;
222
+ /**
223
+ * Bind the first usable socket from an ordered candidate list, relocating past
224
+ * any path that fails to bind for a non-conflict reason (see {@link
225
+ * SOCKET_BIND_CONFLICT_CODE}). The injected `listen` does the real
226
+ * `net.Server.listen` (and stale-socket clear); abstracted so the relocation
227
+ * policy is unit-testable without a real unsupported filesystem. Returns the
228
+ * server plus the path actually bound. An EADDRINUSE, or any error on the LAST
229
+ * candidate, propagates — the caller releases the lockfile and falls back to
230
+ * direct mode (#974). Exported for testing.
231
+ */
232
+ export declare function bindFirstUsableSocket(candidates: string[], listen: (socketPath: string) => Promise<net.Server>, opts?: {
233
+ onRelocate?: (from: string, to: string, code: string) => void;
234
+ }): Promise<{
235
+ server: net.Server;
236
+ socketPath: string;
237
+ }>;
238
+ /**
239
+ * Parse one client-hello line. Returns the peer pids if `line` is a well-formed
240
+ * client-hello (carries the `codegraph_client` marker), or null otherwise — in
241
+ * which case the caller treats the bytes as ordinary JSON-RPC.
242
+ */
243
+ export declare function parseClientHelloLine(line: string): {
244
+ pid: number;
245
+ hostPid: number | null;
246
+ } | null;
247
+ /**
248
+ * A client's peer is dead when its proxy process is gone, or when its known
249
+ * host process is gone. Unknown pid (no client-hello) is never "dead" on this
250
+ * basis — those clients rely on the socket-close path. Exported for testing.
251
+ */
252
+ export declare function peerIsDead(peers: {
253
+ pid: number | null;
254
+ hostPid: number | null;
255
+ }, isAlive: (pid: number) => boolean): boolean;
256
+ /** Exported for test stubs that need to bound the hello-line read. */
257
+ export { MAX_HELLO_LINE_BYTES };
258
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1,41 @@
1
+ export interface BoundaryMatch {
2
+ /** Stable form id, e.g. 'computed-call' — used for per-form dedupe. */
3
+ form: string;
4
+ /** Human label for the dispatch form, e.g. 'computed member call'. */
5
+ label: string;
6
+ /** One-line source snippet of the site (from the original, untrimmed text). */
7
+ snippet: string;
8
+ /** 1-based line within the scanned body's FILE (absolute, ready to print). */
9
+ line: number;
10
+ /**
11
+ * Statically-visible dispatch key, when one exists: the string literal in
12
+ * `handlers['save']`, the `:symbol` in ruby `send`, the type name in
13
+ * `Send(new CreateCmd(...))`. Drives candidate lookup. Undefined when the
14
+ * key is a runtime value (variable, computed expression).
15
+ */
16
+ key?: string;
17
+ /** For typed-bus matches the key is a TYPE name (candidates ~ `${key}Handler`). */
18
+ keyIsType?: boolean;
19
+ /** Additional sites of the same form+key in this body beyond the reported one. */
20
+ moreSites?: number;
21
+ }
22
+ /**
23
+ * Blank the CONTENTS of string literals (quotes preserved, offsets preserved)
24
+ * so dispatch-shaped prose — docs, error messages, template text — can't fire
25
+ * a matcher. Run AFTER comment stripping (comments are already spaces).
26
+ * Backslash escapes are honored; `'`/`"` strings end at a newline (treated as
27
+ * unterminated, matching the comment stripper); backticks span lines, and
28
+ * `${...}` interpolations inside them are blanked too — missing a dispatch
29
+ * inside a template literal is acceptable, false-firing on prose is not.
30
+ */
31
+ export declare function blankStringContents(text: string): string;
32
+ /**
33
+ * Scan one symbol's body for dynamic-dispatch sites.
34
+ *
35
+ * @param body the symbol's source text (sliced from the file)
36
+ * @param language Node.language of the symbol
37
+ * @param fileStartLine 1-based line where `body` starts in its file — returned
38
+ * line numbers are absolute file lines.
39
+ */
40
+ export declare function scanDynamicDispatch(body: string, language: string, fileStartLine: number): BoundaryMatch[];
41
+ //# sourceMappingURL=dynamic-boundaries.d.ts.map
@@ -0,0 +1,122 @@
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
+ * Whether to off-load read-tool dispatch to a worker-thread pool. Only the
22
+ * SHARED daemon wants this — it serves many concurrent clients on one event
23
+ * loop, so without a pool concurrent explores serialize and starve the MCP
24
+ * transport. Direct mode (one stdio client, no concurrency) leaves it off so a
25
+ * single call never pays a worker round-trip. `CODEGRAPH_QUERY_POOL_SIZE=0`
26
+ * disables it even in daemon mode.
27
+ */
28
+ queryPool?: boolean;
29
+ }
30
+ /**
31
+ * Shared MCP engine. Thread-safe in the sense that multiple sessions can
32
+ * call its methods concurrently — internally it serializes initialization
33
+ * through a single promise so multiple sessions racing each other on first
34
+ * connect never double-open the SQLite file.
35
+ */
36
+ export declare class MCPEngine {
37
+ private cg;
38
+ private toolHandler;
39
+ private projectPath;
40
+ private initPromise;
41
+ private watcherStarted;
42
+ private opts;
43
+ private closed;
44
+ private queryPool;
45
+ constructor(opts?: MCPEngineOptions);
46
+ /**
47
+ * Start the worker-thread query pool once a default project is open (daemon
48
+ * mode only; honors `CODEGRAPH_QUERY_POOL_SIZE`). Idempotent and best-effort:
49
+ * if workers can't spawn on this platform the ToolHandler keeps serving reads
50
+ * in-process, so the pool can only help, never break, tool calls.
51
+ */
52
+ private maybeStartPool;
53
+ /**
54
+ * Convenience for {@link MCPServer} compatibility: pre-seed an explicit
55
+ * project path (from the `--path` CLI flag) without yet opening it. This
56
+ * keeps the synchronous constructor cheap; the actual open happens on the
57
+ * first `ensureInitialized` call.
58
+ */
59
+ setProjectPathHint(projectPath: string): void;
60
+ /** Project root that the engine resolved on first init (null if none). */
61
+ getProjectPath(): string | null;
62
+ /** Shared ToolHandler — sessions delegate tool dispatch through this. */
63
+ getToolHandler(): ToolHandler;
64
+ /** Whether the default project's CodeGraph is open. */
65
+ hasDefaultCodeGraph(): boolean;
66
+ /**
67
+ * Walk up from `searchFrom` to find the nearest `.codegraph/` and open it.
68
+ * Idempotent: concurrent callers share one in-flight init; subsequent
69
+ * callers after success are no-ops.
70
+ *
71
+ * The original `MCPServer.tryInitializeDefault` carried the same retry-on-
72
+ * subsequent-tool-call semantics; we preserve them by NOT throwing when the
73
+ * search misses (just leaves `cg` null so the next call can retry).
74
+ */
75
+ ensureInitialized(searchFrom: string): Promise<void>;
76
+ /**
77
+ * Synchronous last-resort init used by the per-session retry loop when the
78
+ * background `ensureInitialized` already finished (or failed) and we need
79
+ * to pick up a project that appeared *after* the engine started.
80
+ */
81
+ retryInitializeSync(searchFrom: string): void;
82
+ /**
83
+ * Close everything. Used on graceful daemon shutdown (SIGTERM/idle timeout)
84
+ * and on direct-mode stop. Idempotent.
85
+ */
86
+ stop(): void;
87
+ private doInitialize;
88
+ /**
89
+ * Start file watching on the active CodeGraph instance. Idempotent — the
90
+ * watcher is per-engine, not per-session, which is why the daemon path
91
+ * collapses N inotify sets to one. The wording of the disabled-reason log
92
+ * exactly matches the prior in-tree implementation so log-driven dashboards
93
+ * keep working.
94
+ */
95
+ private startWatching;
96
+ /**
97
+ * Reconcile the index with the current filesystem once, right after open —
98
+ * catches edits, adds, deletes, and `git pull`/`checkout` changes made while
99
+ * no watcher was running. Runs in the background, but the returned promise
100
+ * is pushed into the ToolHandler as a one-shot gate so the *first* tool
101
+ * call awaits completion before serving (without this, a tool call that
102
+ * races past sync returns rows for files that no longer exist on disk —
103
+ * and the per-file staleness banner can't help because `getPendingFiles()`
104
+ * is populated by the watcher, not by catch-up).
105
+ */
106
+ private catchUpSync;
107
+ }
108
+ /**
109
+ * Parse and clamp the CODEGRAPH_WATCH_DEBOUNCE_MS env override.
110
+ *
111
+ * Issue #403: workspaces with bursty writes (formatter-on-save, multi-file
112
+ * refactors) sometimes want a longer quiet window before sync. Returns
113
+ * `undefined` for unset / empty / non-numeric / out-of-range values so the
114
+ * FileWatcher default (2000ms) takes over — never throws.
115
+ *
116
+ * Clamp range: 100ms (faster would mean a sync per keystroke) to 60s (longer
117
+ * and the watcher feels broken). Out-of-range values are treated as "ignore
118
+ * this misconfiguration" rather than capped, since silently capping a 0 or
119
+ * a typoed value would mask a real config bug.
120
+ */
121
+ export declare function parseDebounceEnv(raw: string | undefined): number | undefined;
122
+ //# sourceMappingURL=engine.d.ts.map