@aria-cli/tools 1.0.2 → 1.0.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 (157) hide show
  1. package/dist/.aria-build-stamp.json +1 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/definitions/code-intelligence.d.ts +9 -0
  4. package/dist/definitions/code-intelligence.d.ts.map +1 -0
  5. package/dist/definitions/code-intelligence.js +471 -0
  6. package/dist/definitions/code-intelligence.js.map +1 -0
  7. package/dist/definitions/core.d.ts +3 -0
  8. package/dist/definitions/core.d.ts.map +1 -1
  9. package/dist/definitions/core.js +13 -1
  10. package/dist/definitions/core.js.map +1 -1
  11. package/dist/definitions/filesystem.d.ts +3 -2
  12. package/dist/definitions/filesystem.d.ts.map +1 -1
  13. package/dist/definitions/filesystem.js +4 -38
  14. package/dist/definitions/filesystem.js.map +1 -1
  15. package/dist/definitions/frg.d.ts +4 -0
  16. package/dist/definitions/frg.d.ts.map +1 -0
  17. package/dist/definitions/frg.js +64 -0
  18. package/dist/definitions/frg.js.map +1 -0
  19. package/dist/definitions/index.d.ts +3 -0
  20. package/dist/definitions/index.d.ts.map +1 -1
  21. package/dist/definitions/index.js +3 -0
  22. package/dist/definitions/index.js.map +1 -1
  23. package/dist/definitions/search.d.ts +10 -0
  24. package/dist/definitions/search.d.ts.map +1 -0
  25. package/dist/definitions/search.js +61 -0
  26. package/dist/definitions/search.js.map +1 -0
  27. package/dist/executors/apply-patch.d.ts.map +1 -1
  28. package/dist/executors/apply-patch.js +18 -0
  29. package/dist/executors/apply-patch.js.map +1 -1
  30. package/dist/executors/code-intelligence.d.ts +139 -0
  31. package/dist/executors/code-intelligence.d.ts.map +1 -0
  32. package/dist/executors/code-intelligence.js +883 -0
  33. package/dist/executors/code-intelligence.js.map +1 -0
  34. package/dist/executors/filesystem.d.ts.map +1 -1
  35. package/dist/executors/filesystem.js +14 -8
  36. package/dist/executors/filesystem.js.map +1 -1
  37. package/dist/executors/frg-freshness.d.ts +94 -0
  38. package/dist/executors/frg-freshness.d.ts.map +1 -0
  39. package/dist/executors/frg-freshness.js +577 -0
  40. package/dist/executors/frg-freshness.js.map +1 -0
  41. package/dist/executors/frg.d.ts +28 -0
  42. package/dist/executors/frg.d.ts.map +1 -0
  43. package/dist/executors/frg.js +299 -0
  44. package/dist/executors/frg.js.map +1 -0
  45. package/dist/executors/index.d.ts +6 -0
  46. package/dist/executors/index.d.ts.map +1 -1
  47. package/dist/executors/index.js +5 -0
  48. package/dist/executors/index.js.map +1 -1
  49. package/dist/executors/lsp-client.d.ts +39 -0
  50. package/dist/executors/lsp-client.d.ts.map +1 -0
  51. package/dist/executors/lsp-client.js +297 -0
  52. package/dist/executors/lsp-client.js.map +1 -0
  53. package/dist/executors/restart.d.ts +4 -9
  54. package/dist/executors/restart.d.ts.map +1 -1
  55. package/dist/executors/restart.js +20 -51
  56. package/dist/executors/restart.js.map +1 -1
  57. package/dist/executors/search-freshness.d.ts +51 -0
  58. package/dist/executors/search-freshness.d.ts.map +1 -0
  59. package/dist/executors/search-freshness.js +196 -0
  60. package/dist/executors/search-freshness.js.map +1 -0
  61. package/dist/executors/search.d.ts +12 -0
  62. package/dist/executors/search.d.ts.map +1 -0
  63. package/dist/executors/search.js +67 -0
  64. package/dist/executors/search.js.map +1 -0
  65. package/dist/headless-control-contract.d.ts +4 -0
  66. package/dist/headless-control-contract.d.ts.map +1 -1
  67. package/dist/index.d.ts +2 -2
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +1 -1
  70. package/dist/index.js.map +1 -1
  71. package/dist/network-runtime/local-control-contract.d.ts +2 -0
  72. package/dist/network-runtime/local-control-contract.d.ts.map +1 -1
  73. package/dist/network-runtime/local-control-contract.js +2 -0
  74. package/dist/network-runtime/local-control-contract.js.map +1 -1
  75. package/dist-cjs/.tsbuildinfo +1 -1
  76. package/dist-cjs/definitions/code-intelligence.d.ts +8 -0
  77. package/dist-cjs/definitions/code-intelligence.js +474 -0
  78. package/dist-cjs/definitions/code-intelligence.js.map +1 -0
  79. package/dist-cjs/definitions/core.d.ts +3 -0
  80. package/dist-cjs/definitions/core.js +17 -2
  81. package/dist-cjs/definitions/core.js.map +1 -1
  82. package/dist-cjs/definitions/filesystem.d.ts +3 -2
  83. package/dist-cjs/definitions/filesystem.js +3 -37
  84. package/dist-cjs/definitions/filesystem.js.map +1 -1
  85. package/dist-cjs/definitions/frg.d.ts +3 -0
  86. package/dist-cjs/definitions/frg.js +67 -0
  87. package/dist-cjs/definitions/frg.js.map +1 -0
  88. package/dist-cjs/definitions/index.d.ts +3 -0
  89. package/dist-cjs/definitions/index.js +7 -1
  90. package/dist-cjs/definitions/index.js.map +1 -1
  91. package/dist-cjs/definitions/search.d.ts +9 -0
  92. package/dist-cjs/definitions/search.js +64 -0
  93. package/dist-cjs/definitions/search.js.map +1 -0
  94. package/dist-cjs/executors/apply-patch.js +18 -0
  95. package/dist-cjs/executors/apply-patch.js.map +1 -1
  96. package/dist-cjs/executors/code-intelligence.d.ts +138 -0
  97. package/dist-cjs/executors/code-intelligence.js +926 -0
  98. package/dist-cjs/executors/code-intelligence.js.map +1 -0
  99. package/dist-cjs/executors/filesystem.js +17 -8
  100. package/dist-cjs/executors/filesystem.js.map +1 -1
  101. package/dist-cjs/executors/frg-freshness.d.ts +93 -0
  102. package/dist-cjs/executors/frg-freshness.js +628 -0
  103. package/dist-cjs/executors/frg-freshness.js.map +1 -0
  104. package/dist-cjs/executors/frg.d.ts +27 -0
  105. package/dist-cjs/executors/frg.js +335 -0
  106. package/dist-cjs/executors/frg.js.map +1 -0
  107. package/dist-cjs/executors/index.d.ts +6 -0
  108. package/dist-cjs/executors/index.js +34 -2
  109. package/dist-cjs/executors/index.js.map +1 -1
  110. package/dist-cjs/executors/lsp-client.d.ts +38 -0
  111. package/dist-cjs/executors/lsp-client.js +311 -0
  112. package/dist-cjs/executors/lsp-client.js.map +1 -0
  113. package/dist-cjs/executors/restart.d.ts +4 -9
  114. package/dist-cjs/executors/restart.js +19 -50
  115. package/dist-cjs/executors/restart.js.map +1 -1
  116. package/dist-cjs/executors/search-freshness.d.ts +50 -0
  117. package/dist-cjs/executors/search-freshness.js +235 -0
  118. package/dist-cjs/executors/search-freshness.js.map +1 -0
  119. package/dist-cjs/executors/search.d.ts +11 -0
  120. package/dist-cjs/executors/search.js +103 -0
  121. package/dist-cjs/executors/search.js.map +1 -0
  122. package/dist-cjs/headless-control-contract.d.ts +15 -11
  123. package/dist-cjs/index.d.ts +2 -2
  124. package/dist-cjs/index.js +22 -2
  125. package/dist-cjs/index.js.map +1 -1
  126. package/dist-cjs/network-runtime/local-control-contract.d.ts +2 -0
  127. package/dist-cjs/network-runtime/local-control-contract.js +2 -0
  128. package/dist-cjs/network-runtime/local-control-contract.js.map +1 -1
  129. package/package.json +9 -5
  130. package/src/definitions/code-intelligence.ts +526 -0
  131. package/src/definitions/core.ts +13 -1
  132. package/src/definitions/filesystem.ts +3 -39
  133. package/src/definitions/frg.ts +67 -0
  134. package/src/definitions/index.ts +3 -0
  135. package/src/definitions/search.ts +67 -0
  136. package/src/executors/apply-patch.ts +20 -0
  137. package/src/executors/code-intelligence.ts +1179 -0
  138. package/src/executors/filesystem.ts +15 -8
  139. package/src/executors/frg-freshness.ts +743 -0
  140. package/src/executors/frg.ts +394 -0
  141. package/src/executors/index.ts +58 -0
  142. package/src/executors/lsp-client.ts +355 -0
  143. package/src/executors/restart.ts +21 -56
  144. package/src/executors/search-freshness.ts +249 -0
  145. package/src/executors/search.ts +89 -0
  146. package/src/index.ts +25 -0
  147. package/src/network-runtime/local-control-contract.ts +2 -0
  148. package/tests/definitions/tool-inventory.test.ts +17 -6
  149. package/tests/executors/frg-freshness.test.ts +136 -0
  150. package/tests/executors/frg-merge.test.ts +70 -0
  151. package/tests/executors/frg-session-content.test.ts +40 -0
  152. package/tests/executors/frg.test.ts +56 -0
  153. package/tests/integration/headless-control-contract.integration.test.ts +2 -0
  154. package/tests/loading-tier.test.ts +6 -6
  155. package/tests/test-lane-manifest.ts +4 -0
  156. package/tsconfig.cjs.json +9 -1
  157. package/tsconfig.json +1 -1
@@ -0,0 +1,394 @@
1
+ import * as fsSync from "node:fs";
2
+ import * as nodePath from "node:path";
3
+ import { frg, type FrgToolInput, type FrgToolOutput } from "@aria-cli/fastripgrep";
4
+ import { getErrorMessage, fail, isPathWithinBase, success } from "./utils.js";
5
+ import type { ToolContext, ToolResult } from "../types.js";
6
+ import { executeSpawn } from "./shell.js";
7
+ import {
8
+ clearPendingFrgMutations,
9
+ ensureFrgRepoStateLoaded,
10
+ flushPendingFrgMutations,
11
+ getFrgFreshnessSnapshot,
12
+ getPendingFrgMutations,
13
+ maybeBuildFrgIndexForRepo,
14
+ reconcileGitDirtyMutations,
15
+ scheduleBackgroundFrgFlush,
16
+ shouldForceSynchronousFrgFlush,
17
+ } from "./frg-freshness.js";
18
+
19
+ function resolveSearchRoot(inputPath: string | undefined, ctx: ToolContext): string {
20
+ const requested = inputPath ?? ".";
21
+ return nodePath.isAbsolute(requested)
22
+ ? nodePath.resolve(requested)
23
+ : nodePath.resolve(ctx.workingDir, requested);
24
+ }
25
+
26
+ function assertAllowedPath(resolvedPath: string, ctx: ToolContext): void {
27
+ let realBase = nodePath.resolve(ctx.workingDir);
28
+ try {
29
+ realBase = fsSync.realpathSync(ctx.workingDir);
30
+ } catch {
31
+ // ignore
32
+ }
33
+
34
+ let candidate = resolvedPath;
35
+ try {
36
+ candidate = fsSync.realpathSync(resolvedPath);
37
+ } catch {
38
+ candidate = nodePath.resolve(resolvedPath);
39
+ }
40
+
41
+ if (isPathWithinBase(candidate, realBase)) {
42
+ return;
43
+ }
44
+
45
+ const home = process.env.HOME;
46
+ if (home) {
47
+ try {
48
+ const realHome = fsSync.realpathSync(home);
49
+ if (isPathWithinBase(candidate, realHome)) {
50
+ return;
51
+ }
52
+ } catch {
53
+ // ignore
54
+ }
55
+ }
56
+
57
+ throw new Error(
58
+ "Path traversal not allowed: resolved path is outside working directory and home directory",
59
+ );
60
+ }
61
+
62
+ export interface FrgInput {
63
+ command:
64
+ | "index"
65
+ | "search"
66
+ | "update"
67
+ | "status"
68
+ | "replace"
69
+ | "init"
70
+ | "watch"
71
+ | "man"
72
+ | "completions"
73
+ | "upgrade";
74
+ pattern?: string;
75
+ replacement?: string;
76
+ path?: string;
77
+ noIndex?: boolean;
78
+ literal?: boolean;
79
+ caseInsensitive?: boolean;
80
+ smartCase?: boolean;
81
+ filesOnly?: boolean;
82
+ count?: boolean;
83
+ maxCount?: number;
84
+ quiet?: boolean;
85
+ context?: number;
86
+ json?: boolean;
87
+ glob?: string;
88
+ fileType?: string;
89
+ follow?: boolean;
90
+ extraPatterns?: string[];
91
+ maxFilesize?: number;
92
+ force?: boolean;
93
+ write?: boolean;
94
+ hook?: boolean;
95
+ shell?: "bash" | "elvish" | "fish" | "powershell" | "zsh";
96
+ }
97
+
98
+ function toNativeInput(input: FrgInput, root: string): FrgToolInput {
99
+ return {
100
+ command: input.command,
101
+ pattern: input.pattern,
102
+ replacement: input.replacement,
103
+ path: root,
104
+ noIndex: input.noIndex,
105
+ literal: input.literal,
106
+ caseInsensitive: input.caseInsensitive,
107
+ smartCase: input.smartCase,
108
+ filesOnly: input.filesOnly,
109
+ count: input.count,
110
+ maxCount: input.maxCount,
111
+ quiet: input.quiet,
112
+ context: input.context,
113
+ json: input.json,
114
+ glob: input.glob,
115
+ fileType: input.fileType,
116
+ follow: input.follow,
117
+ extraPatterns: input.extraPatterns,
118
+ maxFilesize: input.maxFilesize,
119
+ force: input.force,
120
+ write: input.write,
121
+ hook: input.hook,
122
+ shell: input.shell,
123
+ };
124
+ }
125
+
126
+ function getNativeSessionOverlay(root: string): {
127
+ sessionWrites: Array<{ path: string; content: string }>;
128
+ sessionDeletes: string[];
129
+ } {
130
+ const sessionWrites: Array<{ path: string; content: string }> = [];
131
+ const sessionDeletes: string[] = [];
132
+ for (const mutation of getPendingFrgMutations(root)) {
133
+ if (mutation.operation === "delete") {
134
+ sessionDeletes.push(nodePath.resolve(mutation.path));
135
+ continue;
136
+ }
137
+ // Read content from disk if not cached. This closes the gap where
138
+ // reconcileGitDirtyMutations detects git-dirty files but sets
139
+ // content=undefined (no cache). Without this read, the session overlay
140
+ // would skip these mutations entirely — making agent-written files
141
+ // invisible until the 750ms background flush completes.
142
+ let content = mutation.content;
143
+ if (typeof content !== "string") {
144
+ try {
145
+ const resolved = nodePath.resolve(mutation.path);
146
+ const buf = fsSync.readFileSync(resolved);
147
+ // Skip binary files
148
+ if (!buf.subarray(0, Math.min(buf.length, 8192)).includes(0)) {
149
+ content = buf.toString("utf8");
150
+ }
151
+ } catch {
152
+ // File unreadable (deleted, permission, etc.) — skip
153
+ }
154
+ }
155
+ if (typeof content === "string") {
156
+ sessionWrites.push({
157
+ path: nodePath.resolve(mutation.path),
158
+ content,
159
+ });
160
+ }
161
+ }
162
+ return { sessionWrites, sessionDeletes };
163
+ }
164
+
165
+ async function executeFrgWatch(
166
+ _input: FrgInput,
167
+ ctx: ToolContext,
168
+ root: string,
169
+ ): Promise<ToolResult> {
170
+ const resolvedWorkingDir = nodePath.resolve(ctx.workingDir);
171
+ const repoRoot = resolvedWorkingDir.includes(`${nodePath.sep}packages${nodePath.sep}`)
172
+ ? (resolvedWorkingDir.split(`${nodePath.sep}packages${nodePath.sep}`)[0] ?? process.cwd())
173
+ : process.cwd();
174
+ const binaryPath = nodePath.join(
175
+ repoRoot,
176
+ "vendor",
177
+ "fastripgrep",
178
+ "target",
179
+ "release",
180
+ process.platform === "win32" ? "frg.exe" : "frg",
181
+ );
182
+
183
+ if (!fsSync.existsSync(binaryPath)) {
184
+ return fail(
185
+ `frg watch failed: binary not found at ${binaryPath}. Build @aria-cli/fastripgrep first.`,
186
+ );
187
+ }
188
+
189
+ const spawnResult = await executeSpawn(
190
+ {
191
+ program: binaryPath,
192
+ args: ["watch", root],
193
+ cwd: root,
194
+ },
195
+ ctx,
196
+ );
197
+
198
+ if (!spawnResult.success) {
199
+ return spawnResult;
200
+ }
201
+
202
+ const data = (spawnResult.data ?? {}) as { pid?: number };
203
+ return success(`Started frg watch process for ${root}`, {
204
+ command: "watch",
205
+ path: root,
206
+ watched: true,
207
+ pid: data.pid,
208
+ });
209
+ }
210
+
211
+ function maybeReindexOnHeadChange(root: string, currentOutput: FrgToolOutput): FrgToolOutput {
212
+ if (currentOutput.command !== "status") {
213
+ return currentOutput;
214
+ }
215
+
216
+ try {
217
+ const indexedTree = currentOutput.status?.tree_hash ?? null;
218
+ const indexedHead = currentOutput.status?.commit_hash ?? null;
219
+ const freshness = getFrgFreshnessSnapshot(root, currentOutput.status);
220
+
221
+ if (freshness.headTree && indexedTree && freshness.headTree !== indexedTree) {
222
+ if (
223
+ freshness.sessionPendingCount === 0 &&
224
+ freshness.dirtyTrackedCount === 0 &&
225
+ freshness.dirtyUntrackedCount === 0
226
+ ) {
227
+ const updated = frg({ command: "update", path: root });
228
+ return {
229
+ ...updated,
230
+ content: `HEAD tree changed from indexed ${indexedTree} to ${freshness.headTree}; triggered incremental update`,
231
+ };
232
+ }
233
+
234
+ return {
235
+ ...currentOutput,
236
+ content: `HEAD tree differs from indexed tree (${indexedTree} -> ${freshness.headTree}); local dirty state deferred update`,
237
+ };
238
+ }
239
+
240
+ if (indexedHead && freshness.headCommit && indexedHead !== freshness.headCommit) {
241
+ return {
242
+ ...currentOutput,
243
+ content: `HEAD commit differs from indexed commit (${indexedHead} -> ${freshness.headCommit})`,
244
+ };
245
+ }
246
+ } catch {
247
+ // ignore and keep status output stable
248
+ }
249
+
250
+ return currentOutput;
251
+ }
252
+
253
+ function enrichStatusOutput(root: string, output: FrgToolOutput): FrgToolOutput {
254
+ if (output.command !== "status") {
255
+ return output;
256
+ }
257
+
258
+ const freshness = getFrgFreshnessSnapshot(root, output.status);
259
+ return {
260
+ ...output,
261
+ status: output.status
262
+ ? {
263
+ ...output.status,
264
+ head_commit: freshness.headCommit,
265
+ head_tree: freshness.headTree,
266
+ tree_match:
267
+ freshness.headTree && output.status.tree_hash
268
+ ? freshness.headTree === output.status.tree_hash
269
+ : undefined,
270
+ dirty_tracked_count: freshness.dirtyTrackedCount,
271
+ dirty_untracked_count: freshness.dirtyUntrackedCount,
272
+ session_pending_count: freshness.sessionPendingCount,
273
+ session_pending_bytes: freshness.sessionPendingBytes,
274
+ background_flush_scheduled: freshness.flush.scheduled,
275
+ flush_in_flight: freshness.flush.inFlight,
276
+ last_flush_at: freshness.flush.lastFlushAt,
277
+ last_flush_error: freshness.flush.lastError,
278
+ freshness_state: freshness.freshnessState,
279
+ }
280
+ : output.status,
281
+ };
282
+ }
283
+
284
+ function messageFor(output: FrgToolOutput, input: FrgInput): string {
285
+ if (output.command === "search") {
286
+ if (output.mode === "quiet") {
287
+ return output.matched
288
+ ? `Match found for ${input.pattern}`
289
+ : `No matches found for ${input.pattern}`;
290
+ }
291
+ if (output.mode === "files") {
292
+ return `Found ${(output.files ?? []).length} files for ${input.pattern}`;
293
+ }
294
+ if (output.mode === "counts") {
295
+ return `Counted matches in ${(output.counts ?? []).length} files for ${input.pattern}`;
296
+ }
297
+ return `Found ${(output.matches ?? []).length} matches for ${input.pattern}`;
298
+ }
299
+ if (output.command === "replace") {
300
+ return input.write
301
+ ? `Applied ${output.replacements ?? 0} replacements in ${output.files_changed ?? 0} files`
302
+ : `Previewed ${output.replacements ?? 0} replacements in ${output.files_changed ?? 0} files`;
303
+ }
304
+ if (output.command === "init") {
305
+ return output.hook_installed
306
+ ? `Initialized frg for ${output.path} and installed git hook`
307
+ : `Initialized frg for ${output.path}`;
308
+ }
309
+ if (output.command === "completions") {
310
+ return `Generated ${output.shell ?? input.shell ?? "shell"} completions for frg`;
311
+ }
312
+ if (output.command === "man") {
313
+ return "Generated frg man page content";
314
+ }
315
+ if (output.command === "upgrade") {
316
+ return output.content ?? "Provided frg upgrade metadata";
317
+ }
318
+ return `frg ${output.command} completed for ${output.path}`;
319
+ }
320
+
321
+ export async function executeFrg(input: unknown, ctx: ToolContext): Promise<ToolResult> {
322
+ try {
323
+ const args = input as FrgInput;
324
+ const root = resolveSearchRoot(args.path, ctx);
325
+ assertAllowedPath(root, ctx);
326
+
327
+ if (args.command === "watch") {
328
+ return executeFrgWatch(args, ctx, root);
329
+ }
330
+
331
+ ensureFrgRepoStateLoaded(root);
332
+ reconcileGitDirtyMutations(root);
333
+
334
+ if (args.command === "search") {
335
+ maybeBuildFrgIndexForRepo(root);
336
+
337
+ // Detect HEAD tree change (e.g., after a commit or branch switch).
338
+ // Without this, the index stays on the old tree until `frg status` is
339
+ // explicitly called. This is the same gap that aria-search had —
340
+ // the index was "ready" because .frg/ exists, but stale because
341
+ // the tree changed. Run incremental update to pick up the new tree.
342
+ try {
343
+ const statusOutput = frg({ command: "status", path: root });
344
+ if (statusOutput.status?.tree_hash && statusOutput.status?.commit_hash) {
345
+ const snapshot = getFrgFreshnessSnapshot(root, statusOutput.status);
346
+ if (
347
+ snapshot.headTree &&
348
+ statusOutput.status.tree_hash !== snapshot.headTree &&
349
+ snapshot.sessionPendingCount === 0 &&
350
+ snapshot.dirtyTrackedCount === 0 &&
351
+ snapshot.dirtyUntrackedCount === 0
352
+ ) {
353
+ // Clean working tree but HEAD moved — incremental update
354
+ frg({ command: "update", path: root });
355
+ clearPendingFrgMutations(root);
356
+ }
357
+ }
358
+ } catch {
359
+ // Best effort — search will still work with session overlay
360
+ }
361
+
362
+ const pending = getPendingFrgMutations(root);
363
+ if (pending.length > 0) {
364
+ if (!args.noIndex) {
365
+ if (shouldForceSynchronousFrgFlush(root)) {
366
+ flushPendingFrgMutations(root);
367
+ } else {
368
+ scheduleBackgroundFrgFlush(root);
369
+ }
370
+ }
371
+ }
372
+ }
373
+
374
+ const nativeInput = toNativeInput(args, root);
375
+ if (args.command === "search") {
376
+ const sessionOverlay = getNativeSessionOverlay(root);
377
+ nativeInput.sessionWrites = sessionOverlay.sessionWrites;
378
+ nativeInput.sessionDeletes = sessionOverlay.sessionDeletes;
379
+ }
380
+
381
+ let output = frg(nativeInput);
382
+ if (args.command === "status") {
383
+ output = maybeReindexOnHeadChange(root, output);
384
+ output = enrichStatusOutput(root, output);
385
+ if (output.content?.includes("triggered incremental update")) {
386
+ clearPendingFrgMutations(root);
387
+ }
388
+ }
389
+
390
+ return success(messageFor(output, args), output);
391
+ } catch (error) {
392
+ return fail(`frg failed: ${getErrorMessage(error)}`);
393
+ }
394
+ }
@@ -29,6 +29,39 @@ export type {
29
29
  GrepMatch,
30
30
  } from "./filesystem.js";
31
31
 
32
+ // fastripgrep executors
33
+ export { executeFrg } from "./frg.js";
34
+ export {
35
+ recordFrgMutation,
36
+ getPendingFrgMutations,
37
+ getPendingFrgMutationBytes,
38
+ clearPendingFrgMutations,
39
+ flushPendingFrgMutations,
40
+ maybeBuildFrgIndexForRepo,
41
+ ensureFrgRepoStateLoaded,
42
+ reconcileGitDirtyMutations,
43
+ getGitDirtyStatus,
44
+ getFrgFlushStatus,
45
+ getFrgFreshnessSnapshot,
46
+ scheduleBackgroundFrgFlush,
47
+ shouldForceSynchronousFrgFlush,
48
+ DEFAULT_BACKGROUND_FRG_FLUSH_DELAY_MS,
49
+ BACKGROUND_FRG_MAX_FLUSH_DELAY_MS,
50
+ MAX_PENDING_MUTATIONS_BEFORE_SYNC_FLUSH,
51
+ MAX_PENDING_MUTATION_BYTES_BEFORE_SYNC_FLUSH,
52
+ MAX_CACHED_MUTATION_CONTENT_BYTES,
53
+ } from "./frg-freshness.js";
54
+
55
+ // fastripgrep executor types
56
+ export type { FrgInput } from "./frg.js";
57
+ export type {
58
+ FrgMutationOperation,
59
+ FrgMutationRecord,
60
+ FrgGitDirtyStatus,
61
+ FrgFlushStatus,
62
+ FrgFreshnessSnapshot,
63
+ } from "./frg-freshness.js";
64
+
32
65
  // Apply Patch executor
33
66
  export { executeApplyPatch } from "./apply-patch.js";
34
67
 
@@ -218,5 +251,30 @@ export type { RestartInput } from "./restart.js";
218
251
  export { executeDeploy } from "./deploy.js";
219
252
  export type { DeployInput, DeployOutput } from "./deploy.js";
220
253
 
254
+ // Code Intelligence executors
255
+ export {
256
+ executeRg,
257
+ executeUg,
258
+ executeProbe,
259
+ executeSg,
260
+ executeCbm,
261
+ executeLsp,
262
+ executeSerena,
263
+ executeFff,
264
+ } from "./code-intelligence.js";
265
+
266
+ // Code Intelligence executor types
267
+ export type {
268
+ RgInput,
269
+ UgInput,
270
+ ProbeInput,
271
+ SgInput,
272
+ CbmInput,
273
+ LspInput,
274
+ SerenaInput,
275
+ FffInput,
276
+ RgMatch,
277
+ } from "./code-intelligence.js";
278
+
221
279
  // Shared executor utilities
222
280
  export { success, fail, getErrorMessage } from "./utils.js";