@polterware/polter 0.4.2 → 0.5.0

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 (45) hide show
  1. package/README.md +0 -70
  2. package/dist/api.js +62 -30
  3. package/dist/app-HGIGWI7F.js +393 -0
  4. package/dist/appPanel-EZOHLTBX.js +1365 -0
  5. package/dist/applier-OEXIUYYO.js +10 -0
  6. package/dist/chunk-3RG5ZIWI.js +10 -0
  7. package/dist/chunk-45CQFZU7.js +262 -0
  8. package/dist/chunk-57CZSEY5.js +5398 -0
  9. package/dist/chunk-6IBRTRLX.js +257 -0
  10. package/dist/chunk-AK3NTS3Y.js +220 -0
  11. package/dist/chunk-BGT5TT2A.js +32 -0
  12. package/dist/chunk-BIN7BDA2.js +77 -0
  13. package/dist/chunk-E2B5FFBU.js +81 -0
  14. package/dist/chunk-EAMHFQKU.js +222 -0
  15. package/dist/chunk-ELSIHPJL.js +455 -0
  16. package/dist/{chunk-XCCKD3RZ.js → chunk-GCS7JEYU.js} +7 -3
  17. package/dist/chunk-GKROVUDG.js +15 -0
  18. package/dist/chunk-GVIKF6UI.js +738 -0
  19. package/dist/chunk-JQB2A3CA.js +72 -0
  20. package/dist/chunk-KEGROLGX.js +50 -0
  21. package/dist/chunk-OKHPN6X7.js +49 -0
  22. package/dist/chunk-RVMOIUSL.js +22 -0
  23. package/dist/chunk-TD6YNU6L.js +22 -0
  24. package/dist/chunk-U64WZOJ3.js +101 -0
  25. package/dist/chunk-U6725U7K.js +138 -0
  26. package/dist/chunk-XNRIN3VM.js +125 -0
  27. package/dist/chunk-ZU5VZHYD.js +28 -0
  28. package/dist/commands-BIIWGCVS.js +15 -0
  29. package/dist/editor-AUFJZ4PE.js +11 -0
  30. package/dist/engine-EZQ26HDJ.js +11 -0
  31. package/dist/globalConf-AGMMIKSL.js +7 -0
  32. package/dist/index.js +49 -7601
  33. package/dist/ipcServer-HXOPKNBP.js +10 -0
  34. package/dist/mcp.js +182 -13892
  35. package/dist/mcpInstaller-J2AGFNWR.js +19 -0
  36. package/dist/parser-4ZBGSI2U.js +10 -0
  37. package/dist/planner-ZVBA66V6.js +9 -0
  38. package/dist/processManager-6T5DBURV.js +37 -0
  39. package/dist/projectConfig-TRCJS3VI.js +21 -0
  40. package/dist/skillSetup-ZQEHJ5ZG.js +14 -0
  41. package/dist/status-QMRCV4XJ.js +8 -0
  42. package/dist/storage-C3D7TLJW.js +17 -0
  43. package/dist/toolResolver-A2BUT3NK.js +17 -0
  44. package/package.json +20 -2
  45. package/dist/chunk-CWBIXRZP.js +0 -2607
@@ -0,0 +1,222 @@
1
+ import {
2
+ findProcessesByCwd,
3
+ findRunningByCommand,
4
+ generateProcessId,
5
+ getProcessOutput,
6
+ listProcesses,
7
+ removeProcess,
8
+ startProcess,
9
+ stopProcess
10
+ } from "./chunk-6IBRTRLX.js";
11
+ import {
12
+ existsSync,
13
+ mkdirSync,
14
+ unlinkSync
15
+ } from "./chunk-TD6YNU6L.js";
16
+
17
+ // src/lib/ipcServer.ts
18
+ import net from "net";
19
+ import { dirname } from "path";
20
+ import { z } from "zod";
21
+
22
+ // src/lib/ipcProtocol.ts
23
+ var DELIMITER = "\n";
24
+ function serializeMessage(msg) {
25
+ return JSON.stringify(msg) + DELIMITER;
26
+ }
27
+ function parseMessages(buffer) {
28
+ const messages = [];
29
+ let remainder = buffer;
30
+ let idx;
31
+ while ((idx = remainder.indexOf(DELIMITER)) !== -1) {
32
+ const line = remainder.slice(0, idx);
33
+ remainder = remainder.slice(idx + 1);
34
+ if (line.length === 0) continue;
35
+ try {
36
+ messages.push(JSON.parse(line));
37
+ } catch {
38
+ }
39
+ }
40
+ return { messages, remainder };
41
+ }
42
+
43
+ // src/lib/ipcServer.ts
44
+ var PsStartParams = z.object({
45
+ command: z.string(),
46
+ args: z.array(z.string()).default([]),
47
+ cwd: z.string().default(process.cwd()),
48
+ id: z.string().optional()
49
+ });
50
+ var PsIdParams = z.object({
51
+ id: z.string()
52
+ });
53
+ var PsLogsParams = z.object({
54
+ id: z.string(),
55
+ tail: z.number().optional(),
56
+ stream: z.enum(["stdout", "stderr", "both"]).optional()
57
+ });
58
+ var PsFindByCwdParams = z.object({
59
+ cwd: z.string(),
60
+ filter: z.string().optional()
61
+ });
62
+ var PsFindRunningParams = z.object({
63
+ cwd: z.string(),
64
+ command: z.string(),
65
+ args: z.array(z.string())
66
+ });
67
+ var PsGenerateIdParams = z.object({
68
+ command: z.string(),
69
+ args: z.array(z.string())
70
+ });
71
+ var handlers = {
72
+ "ps.list": () => listProcesses(),
73
+ "ps.start": (params) => {
74
+ const { command, args, cwd, id } = PsStartParams.parse(params);
75
+ return startProcess(id ?? generateProcessId(command, args), command, args, cwd);
76
+ },
77
+ "ps.stop": async (params) => {
78
+ const { id } = PsIdParams.parse(params);
79
+ return stopProcess(id);
80
+ },
81
+ "ps.logs": (params) => {
82
+ const { id, tail, stream } = PsLogsParams.parse(params);
83
+ return getProcessOutput(id, tail, stream);
84
+ },
85
+ "ps.remove": (params) => {
86
+ const { id } = PsIdParams.parse(params);
87
+ removeProcess(id);
88
+ return null;
89
+ },
90
+ "ps.find_by_cwd": (params) => {
91
+ const { cwd, filter } = PsFindByCwdParams.parse(params);
92
+ let processes = findProcessesByCwd(cwd);
93
+ if (filter) {
94
+ const f = filter.toLowerCase();
95
+ processes = processes.filter(
96
+ (p) => (p.command + " " + p.args.join(" ")).toLowerCase().includes(f)
97
+ );
98
+ }
99
+ return processes;
100
+ },
101
+ "ps.find_running": (params) => {
102
+ const { cwd, command, args } = PsFindRunningParams.parse(params);
103
+ return findRunningByCommand(cwd, command, args) ?? null;
104
+ },
105
+ "ps.generate_id": (params) => {
106
+ const { command, args } = PsGenerateIdParams.parse(params);
107
+ return generateProcessId(command, args);
108
+ },
109
+ status: () => ({ tui: true, pid: process.pid })
110
+ };
111
+ async function handleRequest(req) {
112
+ const handler = handlers[req.method];
113
+ if (!handler) {
114
+ return {
115
+ jsonrpc: "2.0",
116
+ id: req.id,
117
+ error: { code: -32601, message: `Method not found: ${req.method}` }
118
+ };
119
+ }
120
+ try {
121
+ const result = await handler(req.params ?? {});
122
+ return { jsonrpc: "2.0", id: req.id, result };
123
+ } catch (err) {
124
+ return {
125
+ jsonrpc: "2.0",
126
+ id: req.id,
127
+ error: { code: -32e3, message: err.message }
128
+ };
129
+ }
130
+ }
131
+ function createIpcServer(socketPath) {
132
+ let server = null;
133
+ const connections = /* @__PURE__ */ new Set();
134
+ function handleConnection(socket) {
135
+ connections.add(socket);
136
+ let buffer = "";
137
+ socket.on("data", async (data) => {
138
+ buffer += data.toString();
139
+ const { messages, remainder } = parseMessages(buffer);
140
+ buffer = remainder;
141
+ for (const msg of messages) {
142
+ if ("method" in msg) {
143
+ const response = await handleRequest(msg);
144
+ try {
145
+ socket.write(serializeMessage(response));
146
+ } catch {
147
+ }
148
+ }
149
+ }
150
+ });
151
+ socket.on("close", () => {
152
+ connections.delete(socket);
153
+ });
154
+ socket.on("error", () => {
155
+ connections.delete(socket);
156
+ });
157
+ }
158
+ async function cleanStaleSocket() {
159
+ if (!existsSync(socketPath)) return;
160
+ return new Promise((resolve) => {
161
+ const probe = net.createConnection(socketPath);
162
+ probe.on("connect", () => {
163
+ probe.destroy();
164
+ resolve();
165
+ });
166
+ probe.on("error", () => {
167
+ try {
168
+ unlinkSync(socketPath);
169
+ } catch {
170
+ }
171
+ resolve();
172
+ });
173
+ });
174
+ }
175
+ return {
176
+ async start() {
177
+ mkdirSync(dirname(socketPath), { recursive: true });
178
+ await cleanStaleSocket();
179
+ return new Promise((resolve, reject) => {
180
+ server = net.createServer(handleConnection);
181
+ server.on("error", (err) => {
182
+ if (err.code === "EADDRINUSE") {
183
+ try {
184
+ unlinkSync(socketPath);
185
+ } catch {
186
+ }
187
+ server.listen(socketPath, () => resolve());
188
+ } else {
189
+ reject(err);
190
+ }
191
+ });
192
+ server.listen(socketPath, () => resolve());
193
+ });
194
+ },
195
+ async stop() {
196
+ for (const conn of connections) {
197
+ conn.destroy();
198
+ }
199
+ connections.clear();
200
+ return new Promise((resolve) => {
201
+ if (!server) {
202
+ resolve();
203
+ return;
204
+ }
205
+ server.close(() => {
206
+ try {
207
+ unlinkSync(socketPath);
208
+ } catch {
209
+ }
210
+ server = null;
211
+ resolve();
212
+ });
213
+ });
214
+ }
215
+ };
216
+ }
217
+
218
+ export {
219
+ serializeMessage,
220
+ parseMessages,
221
+ createIpcServer
222
+ };
@@ -0,0 +1,455 @@
1
+ import {
2
+ commandExists,
3
+ execCapture
4
+ } from "./chunk-RVMOIUSL.js";
5
+ import {
6
+ existsSync,
7
+ readFileSync
8
+ } from "./chunk-TD6YNU6L.js";
9
+
10
+ // src/lib/toolResolver.ts
11
+ import { join as join3 } from "path";
12
+
13
+ // src/lib/runner.ts
14
+ import { execa, execaSync } from "execa";
15
+ import { delimiter, dirname, join, resolve } from "path";
16
+ import pRetry from "p-retry";
17
+ function getSupabaseBinaryCandidates() {
18
+ if (process.platform === "win32") {
19
+ return ["supabase.cmd", "supabase.exe", "supabase"];
20
+ }
21
+ return ["supabase"];
22
+ }
23
+ function hasLocalSupabaseBinary(binDir) {
24
+ return getSupabaseBinaryCandidates().some(
25
+ (candidate) => existsSync(join(binDir, candidate))
26
+ );
27
+ }
28
+ function getPathEnvKey(env) {
29
+ return Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "PATH";
30
+ }
31
+ function findLocalSupabaseBinDir(startDir = process.cwd()) {
32
+ let currentDir = resolve(startDir);
33
+ while (true) {
34
+ const binDir = join(currentDir, "node_modules", ".bin");
35
+ if (hasLocalSupabaseBinary(binDir)) {
36
+ return binDir;
37
+ }
38
+ const parentDir = dirname(currentDir);
39
+ if (parentDir === currentDir) {
40
+ return void 0;
41
+ }
42
+ currentDir = parentDir;
43
+ }
44
+ }
45
+ function resolveSupabaseCommand(startDir = process.cwd(), env = process.env) {
46
+ const localBinDir = findLocalSupabaseBinDir(startDir);
47
+ if (!localBinDir) {
48
+ return {
49
+ command: "supabase",
50
+ env: { ...env },
51
+ source: "path"
52
+ };
53
+ }
54
+ const pathKey = getPathEnvKey(env);
55
+ const currentPath = env[pathKey];
56
+ return {
57
+ command: "supabase",
58
+ env: {
59
+ ...env,
60
+ [pathKey]: currentPath ? `${localBinDir}${delimiter}${currentPath}` : localBinDir
61
+ },
62
+ source: "repository",
63
+ localBinDir
64
+ };
65
+ }
66
+ function runCommand(execution, args, cwd = process.cwd(), options) {
67
+ let stdoutBuf = "";
68
+ let stderrBuf = "";
69
+ const resolvedExecution = typeof execution === "string" ? { command: execution } : execution;
70
+ const subprocess = execa(resolvedExecution.command, args, {
71
+ cwd,
72
+ env: resolvedExecution.env,
73
+ shell: true,
74
+ detached: true,
75
+ stdin: options?.quiet ? "pipe" : "inherit",
76
+ stdout: "pipe",
77
+ stderr: "pipe",
78
+ reject: false
79
+ });
80
+ if (options?.quiet) {
81
+ subprocess.stdin?.end();
82
+ }
83
+ subprocess.stdout?.on("data", (data) => {
84
+ const text = data.toString();
85
+ stdoutBuf += text;
86
+ if (!options?.quiet) process.stdout.write(text);
87
+ options?.onData?.(stdoutBuf, stderrBuf);
88
+ });
89
+ subprocess.stderr?.on("data", (data) => {
90
+ const text = data.toString();
91
+ stderrBuf += text;
92
+ if (!options?.quiet) process.stderr.write(text);
93
+ options?.onData?.(stdoutBuf, stderrBuf);
94
+ });
95
+ const promise = subprocess.then(
96
+ (result) => ({
97
+ exitCode: result.exitCode ?? null,
98
+ signal: result.signal ?? null,
99
+ stdout: stdoutBuf,
100
+ stderr: stderrBuf
101
+ }),
102
+ (err) => ({
103
+ exitCode: null,
104
+ signal: null,
105
+ stdout: stdoutBuf,
106
+ stderr: stderrBuf,
107
+ spawnError: err.message
108
+ })
109
+ );
110
+ return {
111
+ promise,
112
+ abort: () => {
113
+ if (subprocess.pid) {
114
+ try {
115
+ process.kill(-subprocess.pid, "SIGTERM");
116
+ } catch {
117
+ }
118
+ }
119
+ },
120
+ pid: subprocess.pid,
121
+ subprocess
122
+ };
123
+ }
124
+ function runInteractiveCommand(execution, args, cwd = process.cwd()) {
125
+ const resolved = typeof execution === "string" ? { command: execution } : execution;
126
+ try {
127
+ const result = execaSync(resolved.command, args, {
128
+ cwd,
129
+ env: resolved.env,
130
+ shell: true,
131
+ stdio: "inherit"
132
+ });
133
+ return {
134
+ exitCode: result.exitCode ?? null,
135
+ signal: null,
136
+ stdout: "",
137
+ stderr: ""
138
+ };
139
+ } catch (err) {
140
+ if (err && typeof err === "object" && "exitCode" in err) {
141
+ const execaErr = err;
142
+ return {
143
+ exitCode: execaErr.exitCode,
144
+ signal: execaErr.signal ?? null,
145
+ stdout: "",
146
+ stderr: ""
147
+ };
148
+ }
149
+ return {
150
+ exitCode: null,
151
+ signal: null,
152
+ stdout: "",
153
+ stderr: "",
154
+ spawnError: err.message
155
+ };
156
+ }
157
+ }
158
+ async function runCommandWithRetry(execution, args, cwd = process.cwd(), options) {
159
+ return pRetry(
160
+ async () => {
161
+ const result = await runCommand(execution, args, cwd, options).promise;
162
+ if (result.spawnError && !result.spawnError.includes("ENOENT")) {
163
+ throw new Error(result.spawnError);
164
+ }
165
+ return result;
166
+ },
167
+ {
168
+ retries: 2,
169
+ shouldRetry: ({ error }) => !error.message.includes("ENOENT")
170
+ }
171
+ );
172
+ }
173
+
174
+ // src/lib/pkgManager.ts
175
+ import { join as join2, dirname as dirname2 } from "path";
176
+ var LOCK_FILES = [
177
+ { file: "bun.lockb", id: "bun" },
178
+ { file: "bun.lock", id: "bun" },
179
+ { file: "pnpm-lock.yaml", id: "pnpm" },
180
+ { file: "yarn.lock", id: "yarn" },
181
+ { file: "package-lock.json", id: "npm" }
182
+ ];
183
+ var MANAGER_META = {
184
+ npm: { lockFile: "package-lock.json", command: "npm" },
185
+ pnpm: { lockFile: "pnpm-lock.yaml", command: "pnpm" },
186
+ yarn: { lockFile: "yarn.lock", command: "yarn" },
187
+ bun: { lockFile: "bun.lockb", command: "bun" }
188
+ };
189
+ function detectPkgManager(cwd = process.cwd()) {
190
+ let dir = cwd;
191
+ const root = dirname2(dir) === dir ? dir : void 0;
192
+ while (true) {
193
+ for (const { file, id } of LOCK_FILES) {
194
+ if (existsSync(join2(dir, file))) {
195
+ const meta = MANAGER_META[id];
196
+ return { id, lockFile: meta.lockFile, command: meta.command };
197
+ }
198
+ }
199
+ const parent = dirname2(dir);
200
+ if (parent === dir || parent === root) break;
201
+ dir = parent;
202
+ }
203
+ return { id: "npm", lockFile: "package-lock.json", command: "npm" };
204
+ }
205
+ var TRANSLATIONS = {
206
+ "install": {},
207
+ // same for all
208
+ "add": {
209
+ npm: ["install"]
210
+ },
211
+ "remove": {
212
+ npm: ["uninstall"]
213
+ },
214
+ "run": {},
215
+ // same for all
216
+ "publish": {
217
+ bun: null
218
+ },
219
+ "audit": {
220
+ bun: null
221
+ },
222
+ "outdated": {
223
+ bun: null
224
+ },
225
+ "ls": {
226
+ yarn: ["list"],
227
+ bun: null
228
+ },
229
+ "pack": {
230
+ bun: null
231
+ },
232
+ "version": {},
233
+ // same for all
234
+ "login": {
235
+ bun: null
236
+ },
237
+ "logout": {
238
+ bun: null
239
+ },
240
+ "config": {},
241
+ // same for all
242
+ "whoami": {
243
+ bun: null
244
+ },
245
+ "info": {
246
+ npm: ["info"],
247
+ pnpm: ["info"],
248
+ yarn: ["info"],
249
+ bun: null
250
+ },
251
+ "search": {
252
+ pnpm: null,
253
+ yarn: null,
254
+ bun: null
255
+ },
256
+ "init": {}
257
+ // same for all
258
+ };
259
+ var UnsupportedCommandError = class extends Error {
260
+ constructor(command, manager) {
261
+ super(`"${command}" is not supported by ${manager}`);
262
+ this.name = "UnsupportedCommandError";
263
+ }
264
+ };
265
+ function translateCommand(base, manager) {
266
+ const [subcommand, ...rest] = base;
267
+ if (!subcommand) {
268
+ return { command: manager, args: [] };
269
+ }
270
+ const translation = TRANSLATIONS[subcommand];
271
+ if (translation) {
272
+ const managerEntry = translation[manager];
273
+ if (managerEntry === null) {
274
+ throw new UnsupportedCommandError(subcommand, manager);
275
+ }
276
+ if (managerEntry !== void 0) {
277
+ return { command: manager, args: [...managerEntry, ...rest] };
278
+ }
279
+ }
280
+ return { command: manager, args: [subcommand, ...rest] };
281
+ }
282
+ function resolvePkgArgs(base, cwd = process.cwd()) {
283
+ const mgr = detectPkgManager(cwd);
284
+ return translateCommand(base, mgr.id);
285
+ }
286
+
287
+ // src/lib/toolResolver.ts
288
+ function getToolDisplayName(toolId, cwd = process.cwd()) {
289
+ if (toolId === "pkg") {
290
+ const mgr = detectPkgManager(cwd);
291
+ return mgr.id;
292
+ }
293
+ const names = {
294
+ supabase: "supabase",
295
+ gh: "github",
296
+ vercel: "vercel",
297
+ git: "git",
298
+ pkg: "npm"
299
+ };
300
+ return names[toolId];
301
+ }
302
+ function resolveToolCommand(toolId, cwd = process.cwd()) {
303
+ if (toolId === "supabase") {
304
+ const resolved = resolveSupabaseCommand(cwd);
305
+ return {
306
+ command: resolved.command,
307
+ env: resolved.env,
308
+ source: resolved.source
309
+ };
310
+ }
311
+ if (toolId === "pkg") {
312
+ const mgr = detectPkgManager(cwd);
313
+ if (!commandExists(mgr.command)) {
314
+ return { command: mgr.command, source: "not-found" };
315
+ }
316
+ return { command: mgr.command, env: { ...process.env }, source: "path" };
317
+ }
318
+ const command = toolId;
319
+ if (!commandExists(command)) {
320
+ return { command, source: "not-found" };
321
+ }
322
+ return { command, env: { ...process.env }, source: "path" };
323
+ }
324
+ function getToolVersion(toolId) {
325
+ try {
326
+ switch (toolId) {
327
+ case "supabase":
328
+ return execCapture("supabase --version").replace(/^supabase\s+/i, "");
329
+ case "gh":
330
+ return execCapture("gh --version").split("\n")[0]?.replace(/^gh\s+version\s+/i, "").split(" ")[0];
331
+ case "vercel":
332
+ return execCapture("vercel --version").split("\n")[0]?.trim();
333
+ case "git":
334
+ return execCapture("git --version").replace(/^git version\s+/i, "").trim();
335
+ case "pkg": {
336
+ const mgr = detectPkgManager();
337
+ return execCapture(`${mgr.command} --version`).split("\n")[0]?.trim();
338
+ }
339
+ default:
340
+ return void 0;
341
+ }
342
+ } catch {
343
+ return void 0;
344
+ }
345
+ }
346
+ var toolInfoCache = /* @__PURE__ */ new Map();
347
+ function getToolInfo(toolId) {
348
+ const cached = toolInfoCache.get(toolId);
349
+ if (cached) return cached;
350
+ const labels = {
351
+ supabase: "Supabase CLI",
352
+ gh: "GitHub CLI",
353
+ vercel: "Vercel CLI",
354
+ git: "Git",
355
+ pkg: "Package Manager"
356
+ };
357
+ const commandName = toolId === "supabase" ? "supabase" : toolId === "pkg" ? detectPkgManager().command : toolId;
358
+ const installed = commandExists(commandName);
359
+ const version = installed ? getToolVersion(toolId) : void 0;
360
+ const info = {
361
+ id: toolId,
362
+ label: labels[toolId],
363
+ installed,
364
+ version
365
+ };
366
+ toolInfoCache.set(toolId, info);
367
+ return info;
368
+ }
369
+ function detectSupabaseLink(cwd) {
370
+ try {
371
+ const configPath = join3(cwd, ".supabase", "config.toml");
372
+ if (existsSync(configPath)) {
373
+ const content = readFileSync(configPath, "utf-8");
374
+ const match = content.match(/project_id\s*=\s*"([^"]+)"/);
375
+ return { linked: true, project: match?.[1] };
376
+ }
377
+ } catch {
378
+ }
379
+ return { linked: false };
380
+ }
381
+ function detectVercelLink(cwd) {
382
+ try {
383
+ const projectPath = join3(cwd, ".vercel", "project.json");
384
+ if (existsSync(projectPath)) {
385
+ const data = JSON.parse(readFileSync(projectPath, "utf-8"));
386
+ return { linked: true, project: data.projectId };
387
+ }
388
+ } catch {
389
+ }
390
+ return { linked: false };
391
+ }
392
+ function detectPkgLink(cwd) {
393
+ try {
394
+ const pkgPath = join3(cwd, "package.json");
395
+ if (existsSync(pkgPath)) {
396
+ const data = JSON.parse(readFileSync(pkgPath, "utf-8"));
397
+ const mgr = detectPkgManager(cwd);
398
+ return { linked: true, project: data.name ? `${data.name} (${mgr.id})` : mgr.id };
399
+ }
400
+ } catch {
401
+ }
402
+ return { linked: false };
403
+ }
404
+ function detectGhLink(cwd) {
405
+ try {
406
+ const remote = execCapture(`git -C "${cwd}" remote get-url origin 2>/dev/null`);
407
+ if (remote) {
408
+ const match = remote.match(/[/:]([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
409
+ return { linked: true, project: match?.[1] ?? remote };
410
+ }
411
+ } catch {
412
+ }
413
+ return { linked: false };
414
+ }
415
+ var toolLinkCache = /* @__PURE__ */ new Map();
416
+ function getToolLinkInfo(toolId, cwd = process.cwd()) {
417
+ const cacheKey = `${toolId}:${cwd}`;
418
+ const cached = toolLinkCache.get(cacheKey);
419
+ if (cached) return cached;
420
+ const base = getToolInfo(toolId);
421
+ let linkStatus = { linked: false };
422
+ if (base.installed) {
423
+ switch (toolId) {
424
+ case "supabase":
425
+ linkStatus = detectSupabaseLink(cwd);
426
+ break;
427
+ case "vercel":
428
+ linkStatus = detectVercelLink(cwd);
429
+ break;
430
+ case "gh":
431
+ linkStatus = detectGhLink(cwd);
432
+ break;
433
+ case "pkg":
434
+ linkStatus = detectPkgLink(cwd);
435
+ break;
436
+ }
437
+ }
438
+ const info = { ...base, ...linkStatus };
439
+ toolLinkCache.set(cacheKey, info);
440
+ return info;
441
+ }
442
+
443
+ export {
444
+ runCommand,
445
+ runInteractiveCommand,
446
+ runCommandWithRetry,
447
+ detectPkgManager,
448
+ translateCommand,
449
+ resolvePkgArgs,
450
+ getToolDisplayName,
451
+ resolveToolCommand,
452
+ getToolVersion,
453
+ getToolInfo,
454
+ getToolLinkInfo
455
+ };
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  parseMessages,
3
3
  serializeMessage
4
- } from "./chunk-CWBIXRZP.js";
4
+ } from "./chunk-EAMHFQKU.js";
5
5
 
6
6
  // src/lib/ipcClient.ts
7
7
  import net from "net";
8
- var DEFAULT_TIMEOUT = 5e3;
8
+ import ms from "ms";
9
+ var DEFAULT_TIMEOUT = ms("5s");
9
10
  function createIpcClient(socketPath) {
10
11
  let socket = null;
11
12
  let connected = false;
@@ -34,9 +35,11 @@ function createIpcClient(socketPath) {
34
35
  return {
35
36
  connect() {
36
37
  return new Promise((resolve, reject) => {
38
+ let settled = false;
37
39
  socket = net.createConnection(socketPath);
38
40
  socket.on("connect", () => {
39
41
  connected = true;
42
+ settled = true;
40
43
  resolve();
41
44
  });
42
45
  socket.on("data", handleData);
@@ -49,7 +52,8 @@ function createIpcClient(socketPath) {
49
52
  });
50
53
  socket.on("error", (err) => {
51
54
  connected = false;
52
- if (!connected) {
55
+ if (!settled) {
56
+ settled = true;
53
57
  reject(err);
54
58
  }
55
59
  });