@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,355 @@
1
+ /**
2
+ * Native LSP Client — uses vscode-jsonrpc for proper message framing.
3
+ *
4
+ * Same approach as Claude Code: createMessageConnection over stdio.
5
+ * Same configuration as Claude Code's LSP plugins.
6
+ * Supports: TypeScript, Python, Go, Rust, C/C++, Swift, Java, Kotlin, Lua, PHP, Ruby, C#.
7
+ */
8
+
9
+ import { spawn, type ChildProcess } from "node:child_process";
10
+ import { readFileSync } from "node:fs";
11
+ import { resolve } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { createRequire } from "node:module";
14
+
15
+ // vscode-jsonrpc: same library Claude Code uses for LSP message framing.
16
+ // Direct import via createRequire (package lacks ESM exports field).
17
+ const require_ = createRequire(fileURLToPath(import.meta.url));
18
+ const { createMessageConnection, StreamMessageReader, StreamMessageWriter } = require_(
19
+ "vscode-jsonrpc/node",
20
+ ) as {
21
+ createMessageConnection: (reader: unknown, writer: unknown) => VscodeConnection;
22
+ StreamMessageReader: new (stream: NodeJS.ReadableStream) => unknown;
23
+ StreamMessageWriter: new (stream: NodeJS.WritableStream) => unknown;
24
+ };
25
+
26
+ interface VscodeConnection {
27
+ sendRequest(method: string, params?: unknown): Promise<unknown>;
28
+ sendNotification(method: string, params?: unknown): void;
29
+ onNotification(method: string, handler: (params: unknown) => void): void;
30
+ listen(): void;
31
+ dispose(): void;
32
+ }
33
+
34
+ // ---------------------------------------------------------------------------
35
+ // Language server registry — same config as Claude Code LSP plugins
36
+ // ---------------------------------------------------------------------------
37
+
38
+ export interface LspServerConfig {
39
+ command: string;
40
+ args: string[];
41
+ extensionToLanguage: Record<string, string>;
42
+ startupTimeout: number;
43
+ }
44
+
45
+ export const LSP_SERVERS: Record<string, LspServerConfig> = {
46
+ typescript: {
47
+ command: "typescript-language-server",
48
+ args: ["--stdio"],
49
+ extensionToLanguage: {
50
+ ".ts": "typescript",
51
+ ".tsx": "typescriptreact",
52
+ ".js": "javascript",
53
+ ".jsx": "javascriptreact",
54
+ ".mts": "typescript",
55
+ ".cts": "typescript",
56
+ ".mjs": "javascript",
57
+ ".cjs": "javascript",
58
+ },
59
+ startupTimeout: 30_000,
60
+ },
61
+ python: {
62
+ command: "pyright-langserver",
63
+ args: ["--stdio"],
64
+ extensionToLanguage: { ".py": "python", ".pyi": "python" },
65
+ startupTimeout: 60_000,
66
+ },
67
+ go: {
68
+ command: "gopls",
69
+ args: [],
70
+ extensionToLanguage: { ".go": "go" },
71
+ startupTimeout: 30_000,
72
+ },
73
+ rust: {
74
+ command: "rust-analyzer",
75
+ args: [],
76
+ extensionToLanguage: { ".rs": "rust" },
77
+ startupTimeout: 60_000,
78
+ },
79
+ "c/cpp": {
80
+ command: "clangd",
81
+ args: ["--background-index"],
82
+ extensionToLanguage: {
83
+ ".c": "c",
84
+ ".h": "c",
85
+ ".cpp": "cpp",
86
+ ".cc": "cpp",
87
+ ".cxx": "cpp",
88
+ ".hpp": "cpp",
89
+ ".hxx": "cpp",
90
+ },
91
+ startupTimeout: 30_000,
92
+ },
93
+ swift: {
94
+ command: "sourcekit-lsp",
95
+ args: [],
96
+ extensionToLanguage: { ".swift": "swift" },
97
+ startupTimeout: 30_000,
98
+ },
99
+ java: {
100
+ command: "jdtls",
101
+ args: [],
102
+ extensionToLanguage: { ".java": "java" },
103
+ startupTimeout: 120_000,
104
+ },
105
+ kotlin: {
106
+ command: "kotlin-language-server",
107
+ args: ["--stdio"],
108
+ extensionToLanguage: { ".kt": "kotlin", ".kts": "kotlin" },
109
+ startupTimeout: 120_000,
110
+ },
111
+ lua: {
112
+ command: "lua-language-server",
113
+ args: [],
114
+ extensionToLanguage: { ".lua": "lua" },
115
+ startupTimeout: 30_000,
116
+ },
117
+ php: {
118
+ command: "intelephense",
119
+ args: ["--stdio"],
120
+ extensionToLanguage: { ".php": "php" },
121
+ startupTimeout: 30_000,
122
+ },
123
+ ruby: {
124
+ command: "ruby-lsp",
125
+ args: [],
126
+ extensionToLanguage: { ".rb": "ruby", ".rake": "ruby", ".gemspec": "ruby" },
127
+ startupTimeout: 30_000,
128
+ },
129
+ csharp: {
130
+ command: "csharp-ls",
131
+ args: [],
132
+ extensionToLanguage: { ".cs": "csharp" },
133
+ startupTimeout: 30_000,
134
+ },
135
+ };
136
+
137
+ // Build reverse lookup: file extension → server name
138
+ const EXT_TO_SERVER = new Map<string, string>();
139
+ for (const [serverName, config] of Object.entries(LSP_SERVERS)) {
140
+ for (const ext of Object.keys(config.extensionToLanguage)) {
141
+ EXT_TO_SERVER.set(ext, serverName);
142
+ }
143
+ }
144
+
145
+ export function getServerForFile(filePath: string): string | undefined {
146
+ const ext = filePath.slice(filePath.lastIndexOf("."));
147
+ return EXT_TO_SERVER.get(ext);
148
+ }
149
+
150
+ export function getLanguageId(filePath: string): string | undefined {
151
+ const ext = filePath.slice(filePath.lastIndexOf("."));
152
+ const serverName = EXT_TO_SERVER.get(ext);
153
+ if (!serverName) return undefined;
154
+ return LSP_SERVERS[serverName]?.extensionToLanguage[ext];
155
+ }
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // LSP Server Instance — uses vscode-jsonrpc createMessageConnection
159
+ // ---------------------------------------------------------------------------
160
+
161
+ export class LspServer {
162
+ private process: ChildProcess | null = null;
163
+ private connection: VscodeConnection | null = null;
164
+ private initialized = false;
165
+ private openFiles = new Set<string>();
166
+ private diagnosticsReceived = new Set<string>();
167
+ readonly serverName: string;
168
+ readonly config: LspServerConfig;
169
+ private rootUri: string;
170
+
171
+ constructor(serverName: string, rootPath: string) {
172
+ this.serverName = serverName;
173
+ const cfg = LSP_SERVERS[serverName];
174
+ if (!cfg) throw new Error(`Unknown LSP server: ${serverName}`);
175
+ this.config = cfg;
176
+ this.rootUri = `file://${resolve(rootPath)}`;
177
+ }
178
+
179
+ async start(): Promise<void> {
180
+ if (this.connection) return;
181
+
182
+ this.process = spawn(this.config.command, this.config.args, {
183
+ stdio: ["pipe", "pipe", "pipe"],
184
+ });
185
+
186
+ this.process.stderr?.on("data", () => {
187
+ // Swallow stderr — language servers are noisy
188
+ });
189
+
190
+ this.process.on("exit", () => {
191
+ this.connection?.dispose();
192
+ this.connection = null;
193
+ this.process = null;
194
+ this.initialized = false;
195
+ this.openFiles.clear();
196
+ this.diagnosticsReceived.clear();
197
+ });
198
+
199
+ // Create vscode-jsonrpc connection — handles Content-Length framing,
200
+ // request/response matching, notification routing. Same as Claude Code.
201
+ this.connection = createMessageConnection(
202
+ new StreamMessageReader(this.process.stdout!),
203
+ new StreamMessageWriter(this.process.stdin!),
204
+ );
205
+
206
+ // Listen for diagnostics notifications (signals file analysis complete)
207
+ this.connection.onNotification("textDocument/publishDiagnostics", (params: unknown) => {
208
+ const p = params as { uri?: string };
209
+ if (p.uri) this.diagnosticsReceived.add(p.uri);
210
+ });
211
+
212
+ this.connection.listen();
213
+
214
+ // Initialize handshake
215
+ await this.connection.sendRequest("initialize", {
216
+ processId: globalThis.process.pid,
217
+ rootUri: this.rootUri,
218
+ capabilities: {
219
+ textDocument: {
220
+ definition: { dynamicRegistration: false },
221
+ references: { dynamicRegistration: false },
222
+ hover: { dynamicRegistration: false, contentFormat: ["plaintext", "markdown"] },
223
+ documentSymbol: { dynamicRegistration: false },
224
+ rename: { dynamicRegistration: false, prepareSupport: false },
225
+ publishDiagnostics: { relatedInformation: true },
226
+ },
227
+ workspace: {
228
+ workspaceFolders: true,
229
+ },
230
+ },
231
+ workspaceFolders: [{ uri: this.rootUri, name: "root" }],
232
+ });
233
+
234
+ this.connection.sendNotification("initialized", {});
235
+ this.initialized = true;
236
+ }
237
+
238
+ async stop(): Promise<void> {
239
+ if (!this.connection) return;
240
+ try {
241
+ await this.connection.sendRequest("shutdown");
242
+ this.connection.sendNotification("exit");
243
+ } catch {
244
+ // Force kill if graceful shutdown fails
245
+ }
246
+ this.connection.dispose();
247
+ this.connection = null;
248
+ this.process?.kill("SIGTERM");
249
+ this.process = null;
250
+ this.initialized = false;
251
+ }
252
+
253
+ // Ensure a file is opened in the language server
254
+ async openFile(filePath: string): Promise<void> {
255
+ const uri = `file://${resolve(filePath)}`;
256
+ if (this.openFiles.has(uri)) return;
257
+
258
+ const languageId = getLanguageId(filePath);
259
+ if (!languageId) return;
260
+
261
+ let text: string;
262
+ try {
263
+ text = readFileSync(resolve(filePath), "utf-8");
264
+ } catch {
265
+ return;
266
+ }
267
+
268
+ this.connection?.sendNotification("textDocument/didOpen", {
269
+ textDocument: { uri, languageId, version: 1, text },
270
+ });
271
+ this.openFiles.add(uri);
272
+
273
+ // No wait needed. LSP servers queue requests after didOpen and respond
274
+ // when ready. Tested: TypeScript responds to hover in 239ms with zero
275
+ // wait (before diagnostics arrive). Pyright responds after its analysis.
276
+ // The sendRequest in the caller will block until the server responds.
277
+ }
278
+
279
+ // --- LSP Operations ---
280
+
281
+ async definition(filePath: string, line: number, character: number): Promise<unknown> {
282
+ await this.ensureReady(filePath);
283
+ return this.connection!.sendRequest("textDocument/definition", {
284
+ textDocument: { uri: `file://${resolve(filePath)}` },
285
+ position: { line, character },
286
+ });
287
+ }
288
+
289
+ async references(filePath: string, line: number, character: number): Promise<unknown> {
290
+ await this.ensureReady(filePath);
291
+ return this.connection!.sendRequest("textDocument/references", {
292
+ textDocument: { uri: `file://${resolve(filePath)}` },
293
+ position: { line, character },
294
+ context: { includeDeclaration: true },
295
+ });
296
+ }
297
+
298
+ async hover(filePath: string, line: number, character: number): Promise<unknown> {
299
+ await this.ensureReady(filePath);
300
+ return this.connection!.sendRequest("textDocument/hover", {
301
+ textDocument: { uri: `file://${resolve(filePath)}` },
302
+ position: { line, character },
303
+ });
304
+ }
305
+
306
+ async documentSymbols(filePath: string): Promise<unknown> {
307
+ await this.ensureReady(filePath);
308
+ return this.connection!.sendRequest("textDocument/documentSymbol", {
309
+ textDocument: { uri: `file://${resolve(filePath)}` },
310
+ });
311
+ }
312
+
313
+ async rename(
314
+ filePath: string,
315
+ line: number,
316
+ character: number,
317
+ newName: string,
318
+ ): Promise<unknown> {
319
+ await this.ensureReady(filePath);
320
+ return this.connection!.sendRequest("textDocument/rename", {
321
+ textDocument: { uri: `file://${resolve(filePath)}` },
322
+ position: { line, character },
323
+ newName,
324
+ });
325
+ }
326
+
327
+ // --- Internals ---
328
+
329
+ private async ensureReady(filePath: string): Promise<void> {
330
+ if (!this.initialized) await this.start();
331
+ await this.openFile(filePath);
332
+ }
333
+ }
334
+
335
+ // ---------------------------------------------------------------------------
336
+ // Server Pool — one server per language, shared across queries
337
+ // ---------------------------------------------------------------------------
338
+
339
+ const serverPool = new Map<string, LspServer>();
340
+
341
+ export function getOrCreateServer(serverName: string, rootPath: string): LspServer {
342
+ const key = `${serverName}:${rootPath}`;
343
+ let server = serverPool.get(key);
344
+ if (!server) {
345
+ server = new LspServer(serverName, rootPath);
346
+ serverPool.set(key, server);
347
+ }
348
+ return server;
349
+ }
350
+
351
+ export async function shutdownAll(): Promise<void> {
352
+ const stops = [...serverPool.values()].map((s) => s.stop());
353
+ await Promise.allSettled(stops);
354
+ serverPool.clear();
355
+ }
@@ -1,15 +1,10 @@
1
1
  /**
2
2
  * @aria/tools - Restart executor
3
3
  *
4
- * Two restart paths:
5
- *
6
- * 1. Supervised (TUI via relaunch.ts): ARIA_NO_RELAUNCH=true is set by the
7
- * parent supervisor. Child exits with RELAUNCH_EXIT_CODE (199), parent
8
- * respawns automatically.
9
- *
10
- * 2. Daemon (daemon-launcher): No supervisor loop. The executor spawns a
11
- * replacement daemon on the process 'exit' event (after the singleton lock
12
- * is released), then sends SIGINT to trigger graceful shutdown.
4
+ * Single restart path for both TUI and daemon:
5
+ * Register an 'exit' handler to spawn a replacement process, then SIGINT
6
+ * the current process for graceful shutdown. The replacement starts fresh
7
+ * and goes through normal startup (heap check, createCliContext, REPL/daemon).
13
8
  */
14
9
 
15
10
  import type { ToolContext, ToolResult } from "../types.js";
@@ -18,13 +13,7 @@ import { spawn } from "node:child_process";
18
13
  import { existsSync, readdirSync, readFileSync } from "node:fs";
19
14
  import { dirname, join, resolve } from "node:path";
20
15
  import * as os from "node:os";
21
- import {
22
- NO_RELAUNCH_ENV,
23
- RELAUNCH_EXIT_CODE,
24
- RESUME_ARION_ENV,
25
- RESUME_SESSION_ENV,
26
- writeRelaunchMarker,
27
- } from "@aria-cli/types";
16
+ import { RESUME_ARION_ENV, RESUME_SESSION_ENV, writeRelaunchMarker } from "@aria-cli/types";
28
17
 
29
18
  export interface RestartInput {
30
19
  reason?: string;
@@ -94,7 +83,6 @@ function findDaemonPid(): number | null {
94
83
  export async function executeRestart(input: RestartInput, ctx: ToolContext): Promise<ToolResult> {
95
84
  if (ctx.abortSignal?.aborted) return fail("Operation cancelled");
96
85
 
97
- const supervised = process.env[NO_RELAUNCH_ENV] === "true";
98
86
  const reason = (input.reason ?? "").trim() || "Restart requested";
99
87
 
100
88
  // Note: tool definition has requiresConfirmation: true, so the framework
@@ -115,7 +103,7 @@ export async function executeRestart(input: RestartInput, ctx: ToolContext): Pro
115
103
  const sessionId = process.env[RESUME_SESSION_ENV] ?? null;
116
104
  const arionName = process.env[RESUME_ARION_ENV] || "ARIA";
117
105
 
118
- // Write disk-backed marker as durable fallback in case IPC is lost.
106
+ // Write disk-backed marker so the replacement process can resume the session.
119
107
  writeRelaunchMarker({
120
108
  sessionId,
121
109
  arionName,
@@ -123,46 +111,23 @@ export async function executeRestart(input: RestartInput, ctx: ToolContext): Pro
123
111
  timestamp: new Date().toISOString(),
124
112
  });
125
113
 
126
- if (supervised) {
127
- // Path 1: Supervised by parent-child relaunch loop (TUI mode).
128
- // Pass session forward via IPC, then exit with magic code for parent to respawn.
129
- try {
130
- const send = (process as NodeJS.Process & { send?: (msg: unknown) => void }).send;
131
- if (typeof send === "function") {
132
- send({
133
- type: "aria-relaunch",
134
- resumeSessionId: sessionId,
135
- arionName,
136
- });
137
- }
138
- } catch {
139
- // non-fatal — disk marker is the fallback
140
- }
141
- process.exit(RELAUNCH_EXIT_CODE);
142
- } else {
143
- // Path 2: Daemon mode — no supervisor loop.
144
- // Register an 'exit' handler to spawn the replacement AFTER the singleton
145
- // lock is released (beforeExit releases it). Then SIGINT ourselves to
146
- // trigger graceful daemon shutdown. The SIGINT handler fires synchronously
147
- // (just sets controller.abort()), so the tool can still return success.
148
- const daemonPid = findDaemonPid() ?? process.pid;
149
-
150
- process.once("exit", () => {
151
- spawn(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {
152
- detached: true,
153
- stdio: "ignore",
154
- }).unref();
155
- });
114
+ // Spawn replacement on exit, then SIGINT for graceful shutdown.
115
+ // Works for both TUI and daemon no supervisor needed.
116
+ const targetPid = findDaemonPid() ?? process.pid;
156
117
 
157
- process.stderr.write(`[aria] Sending SIGINT to daemon pid ${daemonPid} for graceful restart\n`);
158
- try {
159
- process.kill(daemonPid, "SIGINT");
160
- } catch {
161
- // PID already dead or not our process — fall back to self
162
- process.kill(process.pid, "SIGINT");
163
- }
118
+ process.once("exit", () => {
119
+ spawn(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {
120
+ detached: true,
121
+ stdio: "ignore",
122
+ }).unref();
123
+ });
124
+
125
+ process.stderr.write(`[aria] Sending SIGINT to pid ${targetPid} for graceful restart\n`);
126
+ try {
127
+ process.kill(targetPid, "SIGINT");
128
+ } catch {
129
+ process.kill(process.pid, "SIGINT");
164
130
  }
165
131
 
166
- // Unreachable in supervised path (process.exit above), but returned in daemon path
167
132
  return success("Restarting...");
168
133
  }