@polterware/polter 0.4.2 → 0.5.1
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.
- package/README.md +70 -184
- package/dist/api.js +62 -30
- package/dist/app-HGIGWI7F.js +393 -0
- package/dist/appPanel-EZOHLTBX.js +1365 -0
- package/dist/applier-OEXIUYYO.js +10 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-45CQFZU7.js +262 -0
- package/dist/chunk-57CZSEY5.js +5398 -0
- package/dist/chunk-6IBRTRLX.js +257 -0
- package/dist/chunk-AK3NTS3Y.js +220 -0
- package/dist/chunk-BGT5TT2A.js +32 -0
- package/dist/chunk-BIN7BDA2.js +77 -0
- package/dist/chunk-E2B5FFBU.js +81 -0
- package/dist/chunk-EAMHFQKU.js +222 -0
- package/dist/chunk-ELSIHPJL.js +455 -0
- package/dist/{chunk-XCCKD3RZ.js → chunk-GCS7JEYU.js} +7 -3
- package/dist/chunk-GKROVUDG.js +15 -0
- package/dist/chunk-GVIKF6UI.js +738 -0
- package/dist/chunk-JQB2A3CA.js +72 -0
- package/dist/chunk-KEGROLGX.js +50 -0
- package/dist/chunk-OKHPN6X7.js +49 -0
- package/dist/chunk-RVMOIUSL.js +22 -0
- package/dist/chunk-TD6YNU6L.js +22 -0
- package/dist/chunk-U64WZOJ3.js +101 -0
- package/dist/chunk-U6725U7K.js +138 -0
- package/dist/chunk-XNRIN3VM.js +125 -0
- package/dist/chunk-ZU5VZHYD.js +28 -0
- package/dist/commands-BIIWGCVS.js +15 -0
- package/dist/editor-AUFJZ4PE.js +11 -0
- package/dist/engine-EZQ26HDJ.js +11 -0
- package/dist/globalConf-AGMMIKSL.js +7 -0
- package/dist/index.js +49 -7601
- package/dist/ipcServer-HXOPKNBP.js +10 -0
- package/dist/mcp.js +182 -13892
- package/dist/mcpInstaller-J2AGFNWR.js +19 -0
- package/dist/parser-4ZBGSI2U.js +10 -0
- package/dist/planner-ZVBA66V6.js +9 -0
- package/dist/processManager-6T5DBURV.js +37 -0
- package/dist/projectConfig-TRCJS3VI.js +21 -0
- package/dist/skillSetup-ZQEHJ5ZG.js +14 -0
- package/dist/status-QMRCV4XJ.js +8 -0
- package/dist/storage-C3D7TLJW.js +17 -0
- package/dist/toolResolver-A2BUT3NK.js +17 -0
- package/package.json +28 -3
- package/dist/chunk-CWBIXRZP.js +0 -2607
package/dist/chunk-CWBIXRZP.js
DELETED
|
@@ -1,2607 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __export = (target, all) => {
|
|
3
|
-
for (var name in all)
|
|
4
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
// src/data/commands/supabase.ts
|
|
8
|
-
var supabaseCommands = [
|
|
9
|
-
// Quick Start
|
|
10
|
-
{
|
|
11
|
-
id: "supabase:bootstrap",
|
|
12
|
-
tool: "supabase",
|
|
13
|
-
base: ["bootstrap"],
|
|
14
|
-
label: "bootstrap",
|
|
15
|
-
hint: "Bootstrap from a starter template",
|
|
16
|
-
interactive: true
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
id: "supabase:init",
|
|
20
|
-
tool: "supabase",
|
|
21
|
-
base: ["init"],
|
|
22
|
-
label: "init",
|
|
23
|
-
hint: "Initialize a local project",
|
|
24
|
-
interactive: true
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
id: "supabase:login",
|
|
28
|
-
tool: "supabase",
|
|
29
|
-
base: ["login"],
|
|
30
|
-
label: "login",
|
|
31
|
-
hint: "Authenticate with access token",
|
|
32
|
-
interactive: true
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: "supabase:logout",
|
|
36
|
-
tool: "supabase",
|
|
37
|
-
base: ["logout"],
|
|
38
|
-
label: "logout",
|
|
39
|
-
hint: "Remove local auth token"
|
|
40
|
-
},
|
|
41
|
-
// Local Dev
|
|
42
|
-
{
|
|
43
|
-
id: "supabase:start",
|
|
44
|
-
tool: "supabase",
|
|
45
|
-
base: ["start"],
|
|
46
|
-
label: "start",
|
|
47
|
-
hint: "Start local Supabase containers"
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
id: "supabase:stop",
|
|
51
|
-
tool: "supabase",
|
|
52
|
-
base: ["stop"],
|
|
53
|
-
label: "stop",
|
|
54
|
-
hint: "Stop local Supabase containers"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: "supabase:status",
|
|
58
|
-
tool: "supabase",
|
|
59
|
-
base: ["status"],
|
|
60
|
-
label: "status",
|
|
61
|
-
hint: "Show container status"
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
id: "supabase:db",
|
|
65
|
-
tool: "supabase",
|
|
66
|
-
base: ["db"],
|
|
67
|
-
label: "db",
|
|
68
|
-
hint: "Manage Postgres databases",
|
|
69
|
-
suggestedArgs: [
|
|
70
|
-
{ value: "pull", label: "pull", hint: "Pull schema from remote", args: ["pull"] },
|
|
71
|
-
{ value: "push", label: "push", hint: "Push local migrations", args: ["push"] },
|
|
72
|
-
{ value: "reset", label: "reset", hint: "Reset local database", args: ["reset"] },
|
|
73
|
-
{ value: "dump", label: "dump", hint: "Dump schema/data", args: ["dump"] },
|
|
74
|
-
{ value: "diff", label: "diff", hint: "Show migration diff", args: ["diff"] },
|
|
75
|
-
{ value: "lint", label: "lint", hint: "Lint SQL migrations", args: ["lint"] }
|
|
76
|
-
]
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
id: "supabase:migration",
|
|
80
|
-
tool: "supabase",
|
|
81
|
-
base: ["migration"],
|
|
82
|
-
label: "migration",
|
|
83
|
-
hint: "Manage migration scripts",
|
|
84
|
-
suggestedArgs: [
|
|
85
|
-
{ value: "new", label: "new <name>", hint: "Create migration file", args: ["new"] },
|
|
86
|
-
{ value: "up", label: "up", hint: "Apply pending migrations", args: ["up"] },
|
|
87
|
-
{ value: "down", label: "down", hint: "Rollback last migration", args: ["down"] },
|
|
88
|
-
{ value: "list", label: "list", hint: "List local migrations", args: ["list"] },
|
|
89
|
-
{ value: "repair", label: "repair", hint: "Repair migration history", args: ["repair"] },
|
|
90
|
-
{ value: "fetch", label: "fetch", hint: "Fetch migration history", args: ["fetch"] }
|
|
91
|
-
]
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
id: "supabase:seed",
|
|
95
|
-
tool: "supabase",
|
|
96
|
-
base: ["seed"],
|
|
97
|
-
label: "seed",
|
|
98
|
-
hint: "Seed from config.toml",
|
|
99
|
-
suggestedArgs: [
|
|
100
|
-
{ value: "run", label: "run", hint: "Run configured seed file", args: ["run"] }
|
|
101
|
-
]
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
id: "supabase:test",
|
|
105
|
-
tool: "supabase",
|
|
106
|
-
base: ["test"],
|
|
107
|
-
label: "test",
|
|
108
|
-
hint: "Run tests on local stack"
|
|
109
|
-
},
|
|
110
|
-
// Inspect & Generate
|
|
111
|
-
{
|
|
112
|
-
id: "supabase:inspect",
|
|
113
|
-
tool: "supabase",
|
|
114
|
-
base: ["inspect"],
|
|
115
|
-
label: "inspect",
|
|
116
|
-
hint: "Inspect project resources"
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
id: "supabase:gen",
|
|
120
|
-
tool: "supabase",
|
|
121
|
-
base: ["gen"],
|
|
122
|
-
label: "gen",
|
|
123
|
-
hint: "Run code generation tools"
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
id: "supabase:services",
|
|
127
|
-
tool: "supabase",
|
|
128
|
-
base: ["services"],
|
|
129
|
-
label: "services",
|
|
130
|
-
hint: "Show service versions"
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
id: "supabase:link",
|
|
134
|
-
tool: "supabase",
|
|
135
|
-
base: ["link"],
|
|
136
|
-
label: "link",
|
|
137
|
-
hint: "Link to remote project",
|
|
138
|
-
interactive: true
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
id: "supabase:unlink",
|
|
142
|
-
tool: "supabase",
|
|
143
|
-
base: ["unlink"],
|
|
144
|
-
label: "unlink",
|
|
145
|
-
hint: "Unlink remote project"
|
|
146
|
-
},
|
|
147
|
-
// Cloud Management
|
|
148
|
-
{
|
|
149
|
-
id: "supabase:projects",
|
|
150
|
-
tool: "supabase",
|
|
151
|
-
base: ["projects"],
|
|
152
|
-
label: "projects",
|
|
153
|
-
hint: "Manage Supabase projects",
|
|
154
|
-
suggestedArgs: [
|
|
155
|
-
{ value: "list", label: "list", hint: "List projects", args: ["list"] },
|
|
156
|
-
{ value: "create", label: "create", hint: "Create project", args: ["create"] },
|
|
157
|
-
{ value: "delete", label: "delete", hint: "Delete project", args: ["delete"] }
|
|
158
|
-
]
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
id: "supabase:functions",
|
|
162
|
-
tool: "supabase",
|
|
163
|
-
base: ["functions"],
|
|
164
|
-
label: "functions",
|
|
165
|
-
hint: "Manage Edge Functions",
|
|
166
|
-
suggestedArgs: [
|
|
167
|
-
{ value: "list", label: "list", hint: "List functions", args: ["list"] },
|
|
168
|
-
{ value: "new", label: "new <name>", hint: "Create a function", args: ["new"] },
|
|
169
|
-
{ value: "serve", label: "serve", hint: "Serve functions locally", args: ["serve"] },
|
|
170
|
-
{ value: "deploy", label: "deploy <name>", hint: "Deploy function", args: ["deploy"] },
|
|
171
|
-
{ value: "delete", label: "delete <name>", hint: "Delete function", args: ["delete"] }
|
|
172
|
-
]
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
id: "supabase:secrets",
|
|
176
|
-
tool: "supabase",
|
|
177
|
-
base: ["secrets"],
|
|
178
|
-
label: "secrets",
|
|
179
|
-
hint: "Manage project secrets"
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
id: "supabase:config",
|
|
183
|
-
tool: "supabase",
|
|
184
|
-
base: ["config"],
|
|
185
|
-
label: "config",
|
|
186
|
-
hint: "Manage project configuration"
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
id: "supabase:storage",
|
|
190
|
-
tool: "supabase",
|
|
191
|
-
base: ["storage"],
|
|
192
|
-
label: "storage",
|
|
193
|
-
hint: "Manage Storage objects",
|
|
194
|
-
suggestedArgs: [
|
|
195
|
-
{ value: "ls", label: "ls", hint: "List storage objects", args: ["ls"] },
|
|
196
|
-
{ value: "cp", label: "cp", hint: "Copy storage object", args: ["cp"] },
|
|
197
|
-
{ value: "mv", label: "mv", hint: "Move storage object", args: ["mv"] },
|
|
198
|
-
{ value: "rm", label: "rm", hint: "Remove storage object", args: ["rm"] }
|
|
199
|
-
]
|
|
200
|
-
},
|
|
201
|
-
// Networking & Security
|
|
202
|
-
{
|
|
203
|
-
id: "supabase:domains",
|
|
204
|
-
tool: "supabase",
|
|
205
|
-
base: ["domains"],
|
|
206
|
-
label: "domains",
|
|
207
|
-
hint: "Manage custom domains"
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
id: "supabase:ssl-enforcement",
|
|
211
|
-
tool: "supabase",
|
|
212
|
-
base: ["ssl-enforcement"],
|
|
213
|
-
label: "ssl-enforcement",
|
|
214
|
-
hint: "Manage SSL config"
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
id: "supabase:network-bans",
|
|
218
|
-
tool: "supabase",
|
|
219
|
-
base: ["network-bans"],
|
|
220
|
-
label: "network-bans",
|
|
221
|
-
hint: "Manage network bans"
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
id: "supabase:network-restrictions",
|
|
225
|
-
tool: "supabase",
|
|
226
|
-
base: ["network-restrictions"],
|
|
227
|
-
label: "network-restrictions",
|
|
228
|
-
hint: "Manage network restrictions"
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
id: "supabase:vanity-subdomains",
|
|
232
|
-
tool: "supabase",
|
|
233
|
-
base: ["vanity-subdomains"],
|
|
234
|
-
label: "vanity-subdomains",
|
|
235
|
-
hint: "Manage vanity subdomains"
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
id: "supabase:encryption",
|
|
239
|
-
tool: "supabase",
|
|
240
|
-
base: ["encryption"],
|
|
241
|
-
label: "encryption",
|
|
242
|
-
hint: "Manage encryption keys"
|
|
243
|
-
},
|
|
244
|
-
// Organizations & Admin
|
|
245
|
-
{
|
|
246
|
-
id: "supabase:orgs",
|
|
247
|
-
tool: "supabase",
|
|
248
|
-
base: ["orgs"],
|
|
249
|
-
label: "orgs",
|
|
250
|
-
hint: "Manage organizations",
|
|
251
|
-
suggestedArgs: [
|
|
252
|
-
{ value: "list", label: "list", hint: "List organizations", args: ["list"] },
|
|
253
|
-
{ value: "create", label: "create", hint: "Create organization", args: ["create"] }
|
|
254
|
-
]
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
id: "supabase:sso",
|
|
258
|
-
tool: "supabase",
|
|
259
|
-
base: ["sso"],
|
|
260
|
-
label: "sso",
|
|
261
|
-
hint: "Manage Single Sign-On"
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
id: "supabase:branches",
|
|
265
|
-
tool: "supabase",
|
|
266
|
-
base: ["branches"],
|
|
267
|
-
label: "branches",
|
|
268
|
-
hint: "Manage preview branches"
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
id: "supabase:backups",
|
|
272
|
-
tool: "supabase",
|
|
273
|
-
base: ["backups"],
|
|
274
|
-
label: "backups",
|
|
275
|
-
hint: "Manage physical backups",
|
|
276
|
-
suggestedArgs: [
|
|
277
|
-
{ value: "list", label: "list", hint: "List backups", args: ["list"] },
|
|
278
|
-
{ value: "download", label: "download", hint: "Download backup", args: ["download"] }
|
|
279
|
-
]
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
id: "supabase:snippets",
|
|
283
|
-
tool: "supabase",
|
|
284
|
-
base: ["snippets"],
|
|
285
|
-
label: "snippets",
|
|
286
|
-
hint: "Manage SQL snippets",
|
|
287
|
-
suggestedArgs: [
|
|
288
|
-
{ value: "list", label: "list", hint: "List SQL snippets", args: ["list"] },
|
|
289
|
-
{ value: "create", label: "create", hint: "Create SQL snippet", args: ["create"] },
|
|
290
|
-
{ value: "delete", label: "delete", hint: "Delete SQL snippet", args: ["delete"] }
|
|
291
|
-
]
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
id: "supabase:postgres-config",
|
|
295
|
-
tool: "supabase",
|
|
296
|
-
base: ["postgres-config"],
|
|
297
|
-
label: "postgres-config",
|
|
298
|
-
hint: "Manage Postgres config"
|
|
299
|
-
},
|
|
300
|
-
// Utilities
|
|
301
|
-
{
|
|
302
|
-
id: "supabase:completion",
|
|
303
|
-
tool: "supabase",
|
|
304
|
-
base: ["completion"],
|
|
305
|
-
label: "completion",
|
|
306
|
-
hint: "Generate shell completion script"
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
id: "supabase:help",
|
|
310
|
-
tool: "supabase",
|
|
311
|
-
base: ["help"],
|
|
312
|
-
label: "help",
|
|
313
|
-
hint: "Help about any command"
|
|
314
|
-
}
|
|
315
|
-
];
|
|
316
|
-
|
|
317
|
-
// src/data/commands/gh.ts
|
|
318
|
-
var ghCommands = [
|
|
319
|
-
// Repository
|
|
320
|
-
{
|
|
321
|
-
id: "gh:repo:clone",
|
|
322
|
-
tool: "gh",
|
|
323
|
-
base: ["repo", "clone"],
|
|
324
|
-
label: "repo clone",
|
|
325
|
-
hint: "Clone a repository"
|
|
326
|
-
},
|
|
327
|
-
{
|
|
328
|
-
id: "gh:repo:create",
|
|
329
|
-
tool: "gh",
|
|
330
|
-
base: ["repo", "create"],
|
|
331
|
-
label: "repo create",
|
|
332
|
-
hint: "Create a new repository",
|
|
333
|
-
interactive: true
|
|
334
|
-
},
|
|
335
|
-
{
|
|
336
|
-
id: "gh:repo:view",
|
|
337
|
-
tool: "gh",
|
|
338
|
-
base: ["repo", "view"],
|
|
339
|
-
label: "repo view",
|
|
340
|
-
hint: "View repository details"
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
id: "gh:repo:list",
|
|
344
|
-
tool: "gh",
|
|
345
|
-
base: ["repo", "list"],
|
|
346
|
-
label: "repo list",
|
|
347
|
-
hint: "List your repositories"
|
|
348
|
-
},
|
|
349
|
-
// Pull Requests
|
|
350
|
-
{
|
|
351
|
-
id: "gh:pr:create",
|
|
352
|
-
tool: "gh",
|
|
353
|
-
base: ["pr", "create"],
|
|
354
|
-
label: "pr create",
|
|
355
|
-
hint: "Create a pull request"
|
|
356
|
-
},
|
|
357
|
-
{
|
|
358
|
-
id: "gh:pr:list",
|
|
359
|
-
tool: "gh",
|
|
360
|
-
base: ["pr", "list"],
|
|
361
|
-
label: "pr list",
|
|
362
|
-
hint: "List pull requests"
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
id: "gh:pr:view",
|
|
366
|
-
tool: "gh",
|
|
367
|
-
base: ["pr", "view"],
|
|
368
|
-
label: "pr view",
|
|
369
|
-
hint: "View a pull request"
|
|
370
|
-
},
|
|
371
|
-
{
|
|
372
|
-
id: "gh:pr:merge",
|
|
373
|
-
tool: "gh",
|
|
374
|
-
base: ["pr", "merge"],
|
|
375
|
-
label: "pr merge",
|
|
376
|
-
hint: "Merge a pull request"
|
|
377
|
-
},
|
|
378
|
-
{
|
|
379
|
-
id: "gh:pr:checkout",
|
|
380
|
-
tool: "gh",
|
|
381
|
-
base: ["pr", "checkout"],
|
|
382
|
-
label: "pr checkout",
|
|
383
|
-
hint: "Check out a pull request"
|
|
384
|
-
},
|
|
385
|
-
{
|
|
386
|
-
id: "gh:pr:review",
|
|
387
|
-
tool: "gh",
|
|
388
|
-
base: ["pr", "review"],
|
|
389
|
-
label: "pr review",
|
|
390
|
-
hint: "Review a pull request",
|
|
391
|
-
suggestedArgs: [
|
|
392
|
-
{ value: "approve", label: "Approve", hint: "Approve the PR", args: ["--approve"] },
|
|
393
|
-
{ value: "comment", label: "Comment", hint: "Leave a comment", args: ["--comment"] },
|
|
394
|
-
{ value: "request-changes", label: "Request Changes", hint: "Request changes", args: ["--request-changes"] }
|
|
395
|
-
]
|
|
396
|
-
},
|
|
397
|
-
// Issues
|
|
398
|
-
{
|
|
399
|
-
id: "gh:issue:create",
|
|
400
|
-
tool: "gh",
|
|
401
|
-
base: ["issue", "create"],
|
|
402
|
-
label: "issue create",
|
|
403
|
-
hint: "Create an issue"
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
id: "gh:issue:list",
|
|
407
|
-
tool: "gh",
|
|
408
|
-
base: ["issue", "list"],
|
|
409
|
-
label: "issue list",
|
|
410
|
-
hint: "List issues"
|
|
411
|
-
},
|
|
412
|
-
{
|
|
413
|
-
id: "gh:issue:view",
|
|
414
|
-
tool: "gh",
|
|
415
|
-
base: ["issue", "view"],
|
|
416
|
-
label: "issue view",
|
|
417
|
-
hint: "View an issue"
|
|
418
|
-
},
|
|
419
|
-
{
|
|
420
|
-
id: "gh:issue:close",
|
|
421
|
-
tool: "gh",
|
|
422
|
-
base: ["issue", "close"],
|
|
423
|
-
label: "issue close",
|
|
424
|
-
hint: "Close an issue"
|
|
425
|
-
},
|
|
426
|
-
// Workflows & Runs
|
|
427
|
-
{
|
|
428
|
-
id: "gh:workflow:list",
|
|
429
|
-
tool: "gh",
|
|
430
|
-
base: ["workflow", "list"],
|
|
431
|
-
label: "workflow list",
|
|
432
|
-
hint: "List workflows"
|
|
433
|
-
},
|
|
434
|
-
{
|
|
435
|
-
id: "gh:workflow:run",
|
|
436
|
-
tool: "gh",
|
|
437
|
-
base: ["workflow", "run"],
|
|
438
|
-
label: "workflow run",
|
|
439
|
-
hint: "Run a workflow"
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
id: "gh:workflow:view",
|
|
443
|
-
tool: "gh",
|
|
444
|
-
base: ["workflow", "view"],
|
|
445
|
-
label: "workflow view",
|
|
446
|
-
hint: "View a workflow"
|
|
447
|
-
},
|
|
448
|
-
{
|
|
449
|
-
id: "gh:run:list",
|
|
450
|
-
tool: "gh",
|
|
451
|
-
base: ["run", "list"],
|
|
452
|
-
label: "run list",
|
|
453
|
-
hint: "List workflow runs"
|
|
454
|
-
},
|
|
455
|
-
{
|
|
456
|
-
id: "gh:run:view",
|
|
457
|
-
tool: "gh",
|
|
458
|
-
base: ["run", "view"],
|
|
459
|
-
label: "run view",
|
|
460
|
-
hint: "View a workflow run"
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
id: "gh:run:watch",
|
|
464
|
-
tool: "gh",
|
|
465
|
-
base: ["run", "watch"],
|
|
466
|
-
label: "run watch",
|
|
467
|
-
hint: "Watch a workflow run"
|
|
468
|
-
},
|
|
469
|
-
// Releases
|
|
470
|
-
{
|
|
471
|
-
id: "gh:release:create",
|
|
472
|
-
tool: "gh",
|
|
473
|
-
base: ["release", "create"],
|
|
474
|
-
label: "release create",
|
|
475
|
-
hint: "Create a release"
|
|
476
|
-
},
|
|
477
|
-
{
|
|
478
|
-
id: "gh:release:list",
|
|
479
|
-
tool: "gh",
|
|
480
|
-
base: ["release", "list"],
|
|
481
|
-
label: "release list",
|
|
482
|
-
hint: "List releases"
|
|
483
|
-
},
|
|
484
|
-
{
|
|
485
|
-
id: "gh:release:view",
|
|
486
|
-
tool: "gh",
|
|
487
|
-
base: ["release", "view"],
|
|
488
|
-
label: "release view",
|
|
489
|
-
hint: "View a release"
|
|
490
|
-
},
|
|
491
|
-
// Auth
|
|
492
|
-
{
|
|
493
|
-
id: "gh:auth:login",
|
|
494
|
-
tool: "gh",
|
|
495
|
-
base: ["auth", "login"],
|
|
496
|
-
label: "auth login",
|
|
497
|
-
hint: "Log in to GitHub",
|
|
498
|
-
interactive: true
|
|
499
|
-
},
|
|
500
|
-
{
|
|
501
|
-
id: "gh:auth:status",
|
|
502
|
-
tool: "gh",
|
|
503
|
-
base: ["auth", "status"],
|
|
504
|
-
label: "auth status",
|
|
505
|
-
hint: "View auth status"
|
|
506
|
-
}
|
|
507
|
-
];
|
|
508
|
-
|
|
509
|
-
// src/data/commands/vercel.ts
|
|
510
|
-
var vercelCommands = [
|
|
511
|
-
// Deploy
|
|
512
|
-
{
|
|
513
|
-
id: "vercel:deploy",
|
|
514
|
-
tool: "vercel",
|
|
515
|
-
base: ["deploy"],
|
|
516
|
-
label: "deploy",
|
|
517
|
-
hint: "Deploy to Vercel"
|
|
518
|
-
},
|
|
519
|
-
{
|
|
520
|
-
id: "vercel:deploy:prod",
|
|
521
|
-
tool: "vercel",
|
|
522
|
-
base: ["deploy", "--prod"],
|
|
523
|
-
label: "deploy --prod",
|
|
524
|
-
hint: "Deploy to production"
|
|
525
|
-
},
|
|
526
|
-
{
|
|
527
|
-
id: "vercel:promote",
|
|
528
|
-
tool: "vercel",
|
|
529
|
-
base: ["promote"],
|
|
530
|
-
label: "promote",
|
|
531
|
-
hint: "Promote a deployment"
|
|
532
|
-
},
|
|
533
|
-
{
|
|
534
|
-
id: "vercel:rollback",
|
|
535
|
-
tool: "vercel",
|
|
536
|
-
base: ["rollback"],
|
|
537
|
-
label: "rollback",
|
|
538
|
-
hint: "Rollback to previous deployment"
|
|
539
|
-
},
|
|
540
|
-
// Domains
|
|
541
|
-
{
|
|
542
|
-
id: "vercel:domains:list",
|
|
543
|
-
tool: "vercel",
|
|
544
|
-
base: ["domains", "ls"],
|
|
545
|
-
label: "domains ls",
|
|
546
|
-
hint: "List domains"
|
|
547
|
-
},
|
|
548
|
-
{
|
|
549
|
-
id: "vercel:domains:add",
|
|
550
|
-
tool: "vercel",
|
|
551
|
-
base: ["domains", "add"],
|
|
552
|
-
label: "domains add",
|
|
553
|
-
hint: "Add a domain"
|
|
554
|
-
},
|
|
555
|
-
{
|
|
556
|
-
id: "vercel:domains:rm",
|
|
557
|
-
tool: "vercel",
|
|
558
|
-
base: ["domains", "rm"],
|
|
559
|
-
label: "domains rm",
|
|
560
|
-
hint: "Remove a domain"
|
|
561
|
-
},
|
|
562
|
-
// Environment Variables
|
|
563
|
-
{
|
|
564
|
-
id: "vercel:env:ls",
|
|
565
|
-
tool: "vercel",
|
|
566
|
-
base: ["env", "ls"],
|
|
567
|
-
label: "env ls",
|
|
568
|
-
hint: "List environment variables"
|
|
569
|
-
},
|
|
570
|
-
{
|
|
571
|
-
id: "vercel:env:add",
|
|
572
|
-
tool: "vercel",
|
|
573
|
-
base: ["env", "add"],
|
|
574
|
-
label: "env add",
|
|
575
|
-
hint: "Add an environment variable"
|
|
576
|
-
},
|
|
577
|
-
{
|
|
578
|
-
id: "vercel:env:rm",
|
|
579
|
-
tool: "vercel",
|
|
580
|
-
base: ["env", "rm"],
|
|
581
|
-
label: "env rm",
|
|
582
|
-
hint: "Remove an environment variable"
|
|
583
|
-
},
|
|
584
|
-
{
|
|
585
|
-
id: "vercel:env:pull",
|
|
586
|
-
tool: "vercel",
|
|
587
|
-
base: ["env", "pull"],
|
|
588
|
-
label: "env pull",
|
|
589
|
-
hint: "Pull env vars to .env file"
|
|
590
|
-
},
|
|
591
|
-
// Project
|
|
592
|
-
{
|
|
593
|
-
id: "vercel:project:ls",
|
|
594
|
-
tool: "vercel",
|
|
595
|
-
base: ["project", "ls"],
|
|
596
|
-
label: "project ls",
|
|
597
|
-
hint: "List projects"
|
|
598
|
-
},
|
|
599
|
-
{
|
|
600
|
-
id: "vercel:project:add",
|
|
601
|
-
tool: "vercel",
|
|
602
|
-
base: ["project", "add"],
|
|
603
|
-
label: "project add",
|
|
604
|
-
hint: "Add a project"
|
|
605
|
-
},
|
|
606
|
-
{
|
|
607
|
-
id: "vercel:project:rm",
|
|
608
|
-
tool: "vercel",
|
|
609
|
-
base: ["project", "rm"],
|
|
610
|
-
label: "project rm",
|
|
611
|
-
hint: "Remove a project"
|
|
612
|
-
},
|
|
613
|
-
// Setup
|
|
614
|
-
{
|
|
615
|
-
id: "vercel:link",
|
|
616
|
-
tool: "vercel",
|
|
617
|
-
base: ["link"],
|
|
618
|
-
label: "link",
|
|
619
|
-
hint: "Link to a Vercel project",
|
|
620
|
-
interactive: true
|
|
621
|
-
},
|
|
622
|
-
{
|
|
623
|
-
id: "vercel:login",
|
|
624
|
-
tool: "vercel",
|
|
625
|
-
base: ["login"],
|
|
626
|
-
label: "login",
|
|
627
|
-
hint: "Log in to Vercel",
|
|
628
|
-
interactive: true
|
|
629
|
-
},
|
|
630
|
-
{
|
|
631
|
-
id: "vercel:whoami",
|
|
632
|
-
tool: "vercel",
|
|
633
|
-
base: ["whoami"],
|
|
634
|
-
label: "whoami",
|
|
635
|
-
hint: "Show current user"
|
|
636
|
-
},
|
|
637
|
-
// Logs & Inspect
|
|
638
|
-
{
|
|
639
|
-
id: "vercel:logs",
|
|
640
|
-
tool: "vercel",
|
|
641
|
-
base: ["logs"],
|
|
642
|
-
label: "logs",
|
|
643
|
-
hint: "View deployment logs"
|
|
644
|
-
},
|
|
645
|
-
{
|
|
646
|
-
id: "vercel:inspect",
|
|
647
|
-
tool: "vercel",
|
|
648
|
-
base: ["inspect"],
|
|
649
|
-
label: "inspect",
|
|
650
|
-
hint: "Inspect a deployment"
|
|
651
|
-
},
|
|
652
|
-
{
|
|
653
|
-
id: "vercel:ls",
|
|
654
|
-
tool: "vercel",
|
|
655
|
-
base: ["ls"],
|
|
656
|
-
label: "ls",
|
|
657
|
-
hint: "List deployments"
|
|
658
|
-
}
|
|
659
|
-
];
|
|
660
|
-
|
|
661
|
-
// src/data/commands/git.ts
|
|
662
|
-
var gitCommands = [
|
|
663
|
-
{ id: "git:status", tool: "git", base: ["status"], label: "status", hint: "Show working tree status" },
|
|
664
|
-
{ id: "git:add", tool: "git", base: ["add"], label: "add", hint: "Stage changes" },
|
|
665
|
-
{ id: "git:commit", tool: "git", base: ["commit"], label: "commit", hint: "Create a commit" },
|
|
666
|
-
{ id: "git:push", tool: "git", base: ["push"], label: "push", hint: "Push to remote" },
|
|
667
|
-
{ id: "git:pull", tool: "git", base: ["pull"], label: "pull", hint: "Pull from remote" },
|
|
668
|
-
{ id: "git:checkout", tool: "git", base: ["checkout"], label: "checkout", hint: "Switch branches" },
|
|
669
|
-
{ id: "git:branch", tool: "git", base: ["branch"], label: "branch", hint: "List or create branches" },
|
|
670
|
-
{ id: "git:stash", tool: "git", base: ["stash"], label: "stash", hint: "Stash changes" },
|
|
671
|
-
{ id: "git:diff", tool: "git", base: ["diff"], label: "diff", hint: "Show changes" },
|
|
672
|
-
{ id: "git:log", tool: "git", base: ["log"], label: "log", hint: "View commit history" },
|
|
673
|
-
{ id: "git:merge", tool: "git", base: ["merge"], label: "merge", hint: "Merge branches" }
|
|
674
|
-
];
|
|
675
|
-
|
|
676
|
-
// src/data/commands/pkg.ts
|
|
677
|
-
var pkgCommands = [
|
|
678
|
-
// Publish & Version
|
|
679
|
-
{ id: "pkg:publish", tool: "pkg", base: ["publish"], label: "publish", hint: "Publish package to registry" },
|
|
680
|
-
{ id: "pkg:pack", tool: "pkg", base: ["pack"], label: "pack", hint: "Create tarball from package" },
|
|
681
|
-
{ id: "pkg:version:patch", tool: "pkg", base: ["version", "patch"], label: "version patch", hint: "Bump patch version" },
|
|
682
|
-
{ id: "pkg:version:minor", tool: "pkg", base: ["version", "minor"], label: "version minor", hint: "Bump minor version" },
|
|
683
|
-
{ id: "pkg:version:major", tool: "pkg", base: ["version", "major"], label: "version major", hint: "Bump major version" },
|
|
684
|
-
{ id: "pkg:login", tool: "pkg", base: ["login"], label: "login", hint: "Log in to registry" },
|
|
685
|
-
{ id: "pkg:logout", tool: "pkg", base: ["logout"], label: "logout", hint: "Log out from registry" },
|
|
686
|
-
// Dependency Management
|
|
687
|
-
{ id: "pkg:install", tool: "pkg", base: ["install"], label: "install", hint: "Install all dependencies" },
|
|
688
|
-
{ id: "pkg:add", tool: "pkg", base: ["add"], label: "add", hint: "Add a dependency" },
|
|
689
|
-
{ id: "pkg:remove", tool: "pkg", base: ["remove"], label: "remove", hint: "Remove a dependency" },
|
|
690
|
-
{ id: "pkg:update", tool: "pkg", base: ["update"], label: "update", hint: "Update dependencies" },
|
|
691
|
-
{ id: "pkg:outdated", tool: "pkg", base: ["outdated"], label: "outdated", hint: "List outdated packages" },
|
|
692
|
-
{ id: "pkg:audit", tool: "pkg", base: ["audit"], label: "audit", hint: "Run security audit" },
|
|
693
|
-
{ id: "pkg:ls", tool: "pkg", base: ["ls"], label: "ls", hint: "List installed packages" },
|
|
694
|
-
// Registry & Config
|
|
695
|
-
{ id: "pkg:config:list", tool: "pkg", base: ["config", "list"], label: "config list", hint: "Show config" },
|
|
696
|
-
{ id: "pkg:whoami", tool: "pkg", base: ["whoami"], label: "whoami", hint: "Show logged-in user" },
|
|
697
|
-
// Info & Registry
|
|
698
|
-
{ id: "pkg:info", tool: "pkg", base: ["info"], label: "info", hint: "Show package info" },
|
|
699
|
-
{ id: "pkg:search", tool: "pkg", base: ["search"], label: "search", hint: "Search packages in registry" },
|
|
700
|
-
{ id: "pkg:init", tool: "pkg", base: ["init"], label: "init", hint: "Initialize a new package.json" }
|
|
701
|
-
];
|
|
702
|
-
|
|
703
|
-
// src/data/commands/index.ts
|
|
704
|
-
var allCommands = [
|
|
705
|
-
...supabaseCommands,
|
|
706
|
-
...ghCommands,
|
|
707
|
-
...vercelCommands,
|
|
708
|
-
...gitCommands,
|
|
709
|
-
...pkgCommands
|
|
710
|
-
];
|
|
711
|
-
var commandById = new Map(allCommands.map((cmd) => [cmd.id, cmd]));
|
|
712
|
-
var commandByValue = new Map(
|
|
713
|
-
allCommands.map((cmd) => [getCommandValue(cmd), cmd])
|
|
714
|
-
);
|
|
715
|
-
function getCommandById(id) {
|
|
716
|
-
return commandById.get(id);
|
|
717
|
-
}
|
|
718
|
-
function getCommandsByTool(tool) {
|
|
719
|
-
return allCommands.filter((cmd) => cmd.tool === tool);
|
|
720
|
-
}
|
|
721
|
-
function getCommandValue(cmd) {
|
|
722
|
-
if (cmd.tool === "supabase") {
|
|
723
|
-
return cmd.base.join(" ");
|
|
724
|
-
}
|
|
725
|
-
return `${cmd.tool}:${cmd.base.join(" ")}`;
|
|
726
|
-
}
|
|
727
|
-
function findCommandByValue(value) {
|
|
728
|
-
const byId = commandById.get(value);
|
|
729
|
-
if (byId) return byId;
|
|
730
|
-
return commandByValue.get(value);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
// src/data/features.ts
|
|
734
|
-
var allSources = [...supabaseCommands, ...ghCommands, ...vercelCommands, ...gitCommands, ...pkgCommands];
|
|
735
|
-
function pick(ids) {
|
|
736
|
-
const idSet = new Set(ids);
|
|
737
|
-
return allSources.filter((cmd) => idSet.has(cmd.id));
|
|
738
|
-
}
|
|
739
|
-
var features = [
|
|
740
|
-
{
|
|
741
|
-
id: "database",
|
|
742
|
-
icon: "\u{1F5C4}\uFE0F",
|
|
743
|
-
label: "Database",
|
|
744
|
-
commands: pick([
|
|
745
|
-
"supabase:db",
|
|
746
|
-
"supabase:migration",
|
|
747
|
-
"supabase:seed",
|
|
748
|
-
"supabase:inspect"
|
|
749
|
-
])
|
|
750
|
-
},
|
|
751
|
-
{
|
|
752
|
-
id: "functions",
|
|
753
|
-
icon: "\u26A1",
|
|
754
|
-
label: "Functions",
|
|
755
|
-
commands: pick([
|
|
756
|
-
"supabase:functions"
|
|
757
|
-
])
|
|
758
|
-
},
|
|
759
|
-
{
|
|
760
|
-
id: "deploy",
|
|
761
|
-
icon: "\u{1F680}",
|
|
762
|
-
label: "Deploy",
|
|
763
|
-
commands: pick([
|
|
764
|
-
"supabase:db",
|
|
765
|
-
"supabase:functions",
|
|
766
|
-
"vercel:deploy",
|
|
767
|
-
"vercel:deploy:prod",
|
|
768
|
-
"vercel:promote",
|
|
769
|
-
"vercel:rollback",
|
|
770
|
-
"git:commit",
|
|
771
|
-
"git:push"
|
|
772
|
-
])
|
|
773
|
-
},
|
|
774
|
-
{
|
|
775
|
-
id: "repo",
|
|
776
|
-
icon: "\u{1F5C3}\uFE0F",
|
|
777
|
-
label: "Repo",
|
|
778
|
-
commands: pick([
|
|
779
|
-
"git:status",
|
|
780
|
-
"git:add",
|
|
781
|
-
"git:commit",
|
|
782
|
-
"git:push",
|
|
783
|
-
"git:pull",
|
|
784
|
-
"git:branch",
|
|
785
|
-
"git:checkout",
|
|
786
|
-
"git:log",
|
|
787
|
-
"git:diff",
|
|
788
|
-
"git:merge",
|
|
789
|
-
"git:stash",
|
|
790
|
-
"gh:repo:clone",
|
|
791
|
-
"gh:repo:create",
|
|
792
|
-
"gh:repo:view",
|
|
793
|
-
"gh:repo:list",
|
|
794
|
-
"gh:pr:create",
|
|
795
|
-
"gh:pr:list",
|
|
796
|
-
"gh:pr:view",
|
|
797
|
-
"gh:pr:merge",
|
|
798
|
-
"gh:pr:checkout",
|
|
799
|
-
"gh:pr:review",
|
|
800
|
-
"gh:issue:create",
|
|
801
|
-
"gh:issue:list",
|
|
802
|
-
"gh:issue:view",
|
|
803
|
-
"gh:issue:close",
|
|
804
|
-
"gh:release:create",
|
|
805
|
-
"gh:release:list",
|
|
806
|
-
"gh:release:view"
|
|
807
|
-
])
|
|
808
|
-
},
|
|
809
|
-
{
|
|
810
|
-
id: "cicd",
|
|
811
|
-
icon: "\u{1F504}",
|
|
812
|
-
label: "CI/CD",
|
|
813
|
-
commands: pick([
|
|
814
|
-
"vercel:env:ls",
|
|
815
|
-
"vercel:env:add",
|
|
816
|
-
"vercel:env:rm",
|
|
817
|
-
"vercel:env:pull",
|
|
818
|
-
"gh:workflow:list",
|
|
819
|
-
"gh:workflow:run",
|
|
820
|
-
"gh:workflow:view",
|
|
821
|
-
"gh:run:list",
|
|
822
|
-
"gh:run:view",
|
|
823
|
-
"gh:run:watch"
|
|
824
|
-
])
|
|
825
|
-
},
|
|
826
|
-
{
|
|
827
|
-
id: "auth-storage",
|
|
828
|
-
icon: "\u{1F510}",
|
|
829
|
-
label: "Auth & Storage",
|
|
830
|
-
commands: pick([
|
|
831
|
-
"supabase:storage",
|
|
832
|
-
"supabase:secrets",
|
|
833
|
-
"supabase:sso"
|
|
834
|
-
])
|
|
835
|
-
},
|
|
836
|
-
{
|
|
837
|
-
id: "networking",
|
|
838
|
-
icon: "\u{1F310}",
|
|
839
|
-
label: "Networking",
|
|
840
|
-
commands: pick([
|
|
841
|
-
"supabase:domains",
|
|
842
|
-
"supabase:ssl-enforcement",
|
|
843
|
-
"supabase:network-bans",
|
|
844
|
-
"supabase:network-restrictions",
|
|
845
|
-
"supabase:vanity-subdomains",
|
|
846
|
-
"supabase:encryption",
|
|
847
|
-
"vercel:domains:list",
|
|
848
|
-
"vercel:domains:add",
|
|
849
|
-
"vercel:domains:rm"
|
|
850
|
-
])
|
|
851
|
-
},
|
|
852
|
-
{
|
|
853
|
-
id: "packages",
|
|
854
|
-
icon: "\u{1F4CB}",
|
|
855
|
-
label: "Packages",
|
|
856
|
-
commands: pick([
|
|
857
|
-
"pkg:install",
|
|
858
|
-
"pkg:add",
|
|
859
|
-
"pkg:remove",
|
|
860
|
-
"pkg:update",
|
|
861
|
-
"pkg:outdated",
|
|
862
|
-
"pkg:audit",
|
|
863
|
-
"pkg:ls",
|
|
864
|
-
"pkg:publish",
|
|
865
|
-
"pkg:pack",
|
|
866
|
-
"pkg:version:patch",
|
|
867
|
-
"pkg:version:minor",
|
|
868
|
-
"pkg:version:major",
|
|
869
|
-
"pkg:login",
|
|
870
|
-
"pkg:logout",
|
|
871
|
-
"pkg:config:list",
|
|
872
|
-
"pkg:whoami",
|
|
873
|
-
"pkg:info",
|
|
874
|
-
"pkg:search",
|
|
875
|
-
"pkg:init"
|
|
876
|
-
])
|
|
877
|
-
},
|
|
878
|
-
{
|
|
879
|
-
id: "setup",
|
|
880
|
-
icon: "\u2699\uFE0F",
|
|
881
|
-
label: "Setup",
|
|
882
|
-
commands: pick([
|
|
883
|
-
"supabase:init",
|
|
884
|
-
"supabase:link",
|
|
885
|
-
"supabase:login",
|
|
886
|
-
"vercel:link",
|
|
887
|
-
"vercel:login",
|
|
888
|
-
"gh:auth:login",
|
|
889
|
-
"gh:auth:status"
|
|
890
|
-
])
|
|
891
|
-
}
|
|
892
|
-
];
|
|
893
|
-
var featureById = new Map(features.map((f) => [f.id, f]));
|
|
894
|
-
function getFeatureById(id) {
|
|
895
|
-
return featureById.get(id);
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// src/data/flags.ts
|
|
899
|
-
var globalFlags = [
|
|
900
|
-
{ value: "--debug", label: "--debug", hint: "Output debug logs to stderr" },
|
|
901
|
-
{ value: "--yes", label: "--yes", hint: "Answer yes to all prompts" },
|
|
902
|
-
{
|
|
903
|
-
value: "--create-ticket",
|
|
904
|
-
label: "--create-ticket",
|
|
905
|
-
hint: "Create support ticket on error"
|
|
906
|
-
},
|
|
907
|
-
{
|
|
908
|
-
value: "--experimental",
|
|
909
|
-
label: "--experimental",
|
|
910
|
-
hint: "Enable experimental features"
|
|
911
|
-
}
|
|
912
|
-
];
|
|
913
|
-
var toolFlags = {
|
|
914
|
-
supabase: globalFlags,
|
|
915
|
-
gh: [],
|
|
916
|
-
vercel: [
|
|
917
|
-
{ value: "--yes", label: "--yes", hint: "Skip confirmation prompts" },
|
|
918
|
-
{ value: "--debug", label: "--debug", hint: "Debug mode" }
|
|
919
|
-
],
|
|
920
|
-
git: [],
|
|
921
|
-
pkg: [
|
|
922
|
-
{ value: "--save-dev", label: "--save-dev", hint: "Save as dev dependency" },
|
|
923
|
-
{ value: "--global", label: "--global", hint: "Install globally" },
|
|
924
|
-
{ value: "--dry-run", label: "--dry-run", hint: "Show what would happen" }
|
|
925
|
-
]
|
|
926
|
-
};
|
|
927
|
-
function getFlagsForTool(toolId) {
|
|
928
|
-
return toolFlags[toolId] ?? globalFlags;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// src/lib/runner.ts
|
|
932
|
-
import { spawn, spawnSync } from "child_process";
|
|
933
|
-
import { existsSync } from "fs";
|
|
934
|
-
import { delimiter, dirname, join, resolve } from "path";
|
|
935
|
-
function getSupabaseBinaryCandidates() {
|
|
936
|
-
if (process.platform === "win32") {
|
|
937
|
-
return ["supabase.cmd", "supabase.exe", "supabase"];
|
|
938
|
-
}
|
|
939
|
-
return ["supabase"];
|
|
940
|
-
}
|
|
941
|
-
function hasLocalSupabaseBinary(binDir) {
|
|
942
|
-
return getSupabaseBinaryCandidates().some(
|
|
943
|
-
(candidate) => existsSync(join(binDir, candidate))
|
|
944
|
-
);
|
|
945
|
-
}
|
|
946
|
-
function getPathEnvKey(env) {
|
|
947
|
-
return Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "PATH";
|
|
948
|
-
}
|
|
949
|
-
function findLocalSupabaseBinDir(startDir = process.cwd()) {
|
|
950
|
-
let currentDir = resolve(startDir);
|
|
951
|
-
while (true) {
|
|
952
|
-
const binDir = join(currentDir, "node_modules", ".bin");
|
|
953
|
-
if (hasLocalSupabaseBinary(binDir)) {
|
|
954
|
-
return binDir;
|
|
955
|
-
}
|
|
956
|
-
const parentDir = dirname(currentDir);
|
|
957
|
-
if (parentDir === currentDir) {
|
|
958
|
-
return void 0;
|
|
959
|
-
}
|
|
960
|
-
currentDir = parentDir;
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
function resolveSupabaseCommand(startDir = process.cwd(), env = process.env) {
|
|
964
|
-
const localBinDir = findLocalSupabaseBinDir(startDir);
|
|
965
|
-
if (!localBinDir) {
|
|
966
|
-
return {
|
|
967
|
-
command: "supabase",
|
|
968
|
-
env: { ...env },
|
|
969
|
-
source: "path"
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
const pathKey = getPathEnvKey(env);
|
|
973
|
-
const currentPath = env[pathKey];
|
|
974
|
-
return {
|
|
975
|
-
command: "supabase",
|
|
976
|
-
env: {
|
|
977
|
-
...env,
|
|
978
|
-
[pathKey]: currentPath ? `${localBinDir}${delimiter}${currentPath}` : localBinDir
|
|
979
|
-
},
|
|
980
|
-
source: "repository",
|
|
981
|
-
localBinDir
|
|
982
|
-
};
|
|
983
|
-
}
|
|
984
|
-
function runCommand(execution, args, cwd = process.cwd(), options) {
|
|
985
|
-
let stdout = "";
|
|
986
|
-
let stderr = "";
|
|
987
|
-
const resolvedExecution = typeof execution === "string" ? { command: execution } : execution;
|
|
988
|
-
const child = spawn(resolvedExecution.command, args, {
|
|
989
|
-
cwd,
|
|
990
|
-
env: resolvedExecution.env,
|
|
991
|
-
shell: true,
|
|
992
|
-
detached: true,
|
|
993
|
-
stdio: [options?.quiet ? "pipe" : "inherit", "pipe", "pipe"]
|
|
994
|
-
});
|
|
995
|
-
if (options?.quiet) {
|
|
996
|
-
child.stdin?.end();
|
|
997
|
-
}
|
|
998
|
-
const promise = new Promise((resolve3) => {
|
|
999
|
-
child.stdout?.on("data", (data) => {
|
|
1000
|
-
const text = data.toString();
|
|
1001
|
-
stdout += text;
|
|
1002
|
-
if (!options?.quiet) process.stdout.write(text);
|
|
1003
|
-
options?.onData?.(stdout, stderr);
|
|
1004
|
-
});
|
|
1005
|
-
child.stderr?.on("data", (data) => {
|
|
1006
|
-
const text = data.toString();
|
|
1007
|
-
stderr += text;
|
|
1008
|
-
if (!options?.quiet) process.stderr.write(text);
|
|
1009
|
-
options?.onData?.(stdout, stderr);
|
|
1010
|
-
});
|
|
1011
|
-
child.on("error", (err) => {
|
|
1012
|
-
resolve3({
|
|
1013
|
-
exitCode: null,
|
|
1014
|
-
signal: null,
|
|
1015
|
-
stdout,
|
|
1016
|
-
stderr,
|
|
1017
|
-
spawnError: err.message
|
|
1018
|
-
});
|
|
1019
|
-
});
|
|
1020
|
-
child.on("exit", (code, signal) => {
|
|
1021
|
-
resolve3({ exitCode: code, signal, stdout, stderr });
|
|
1022
|
-
});
|
|
1023
|
-
});
|
|
1024
|
-
return {
|
|
1025
|
-
promise,
|
|
1026
|
-
abort: () => {
|
|
1027
|
-
if (child.pid) {
|
|
1028
|
-
try {
|
|
1029
|
-
process.kill(-child.pid, "SIGTERM");
|
|
1030
|
-
} catch {
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
},
|
|
1034
|
-
child
|
|
1035
|
-
};
|
|
1036
|
-
}
|
|
1037
|
-
function runInteractiveCommand(execution, args, cwd = process.cwd()) {
|
|
1038
|
-
const resolved = typeof execution === "string" ? { command: execution } : execution;
|
|
1039
|
-
const result = spawnSync(resolved.command, args, {
|
|
1040
|
-
cwd,
|
|
1041
|
-
env: resolved.env,
|
|
1042
|
-
shell: true,
|
|
1043
|
-
stdio: "inherit"
|
|
1044
|
-
});
|
|
1045
|
-
return {
|
|
1046
|
-
exitCode: result.status,
|
|
1047
|
-
signal: result.signal,
|
|
1048
|
-
stdout: "",
|
|
1049
|
-
stderr: "",
|
|
1050
|
-
spawnError: result.error?.message
|
|
1051
|
-
};
|
|
1052
|
-
}
|
|
1053
|
-
async function runSupabaseCommand(args, cwd = process.cwd()) {
|
|
1054
|
-
return runCommand(resolveSupabaseCommand(cwd), args, cwd).promise;
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
// src/lib/pkgManager.ts
|
|
1058
|
-
import { existsSync as existsSync2 } from "fs";
|
|
1059
|
-
import { join as join2, dirname as dirname2 } from "path";
|
|
1060
|
-
var LOCK_FILES = [
|
|
1061
|
-
{ file: "bun.lockb", id: "bun" },
|
|
1062
|
-
{ file: "bun.lock", id: "bun" },
|
|
1063
|
-
{ file: "pnpm-lock.yaml", id: "pnpm" },
|
|
1064
|
-
{ file: "yarn.lock", id: "yarn" },
|
|
1065
|
-
{ file: "package-lock.json", id: "npm" }
|
|
1066
|
-
];
|
|
1067
|
-
var MANAGER_META = {
|
|
1068
|
-
npm: { lockFile: "package-lock.json", command: "npm" },
|
|
1069
|
-
pnpm: { lockFile: "pnpm-lock.yaml", command: "pnpm" },
|
|
1070
|
-
yarn: { lockFile: "yarn.lock", command: "yarn" },
|
|
1071
|
-
bun: { lockFile: "bun.lockb", command: "bun" }
|
|
1072
|
-
};
|
|
1073
|
-
function detectPkgManager(cwd = process.cwd()) {
|
|
1074
|
-
let dir = cwd;
|
|
1075
|
-
const root = dirname2(dir) === dir ? dir : void 0;
|
|
1076
|
-
while (true) {
|
|
1077
|
-
for (const { file, id } of LOCK_FILES) {
|
|
1078
|
-
if (existsSync2(join2(dir, file))) {
|
|
1079
|
-
const meta = MANAGER_META[id];
|
|
1080
|
-
return { id, lockFile: meta.lockFile, command: meta.command };
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
const parent = dirname2(dir);
|
|
1084
|
-
if (parent === dir || parent === root) break;
|
|
1085
|
-
dir = parent;
|
|
1086
|
-
}
|
|
1087
|
-
return { id: "npm", lockFile: "package-lock.json", command: "npm" };
|
|
1088
|
-
}
|
|
1089
|
-
var TRANSLATIONS = {
|
|
1090
|
-
"install": {},
|
|
1091
|
-
// same for all
|
|
1092
|
-
"add": {
|
|
1093
|
-
npm: ["install"]
|
|
1094
|
-
},
|
|
1095
|
-
"remove": {
|
|
1096
|
-
npm: ["uninstall"]
|
|
1097
|
-
},
|
|
1098
|
-
"run": {},
|
|
1099
|
-
// same for all
|
|
1100
|
-
"publish": {
|
|
1101
|
-
bun: null
|
|
1102
|
-
},
|
|
1103
|
-
"audit": {
|
|
1104
|
-
bun: null
|
|
1105
|
-
},
|
|
1106
|
-
"outdated": {
|
|
1107
|
-
bun: null
|
|
1108
|
-
},
|
|
1109
|
-
"ls": {
|
|
1110
|
-
yarn: ["list"],
|
|
1111
|
-
bun: null
|
|
1112
|
-
},
|
|
1113
|
-
"pack": {
|
|
1114
|
-
bun: null
|
|
1115
|
-
},
|
|
1116
|
-
"version": {},
|
|
1117
|
-
// same for all
|
|
1118
|
-
"login": {
|
|
1119
|
-
bun: null
|
|
1120
|
-
},
|
|
1121
|
-
"logout": {
|
|
1122
|
-
bun: null
|
|
1123
|
-
},
|
|
1124
|
-
"config": {},
|
|
1125
|
-
// same for all
|
|
1126
|
-
"whoami": {
|
|
1127
|
-
bun: null
|
|
1128
|
-
},
|
|
1129
|
-
"info": {
|
|
1130
|
-
npm: ["info"],
|
|
1131
|
-
pnpm: ["info"],
|
|
1132
|
-
yarn: ["info"],
|
|
1133
|
-
bun: null
|
|
1134
|
-
},
|
|
1135
|
-
"search": {
|
|
1136
|
-
pnpm: null,
|
|
1137
|
-
yarn: null,
|
|
1138
|
-
bun: null
|
|
1139
|
-
},
|
|
1140
|
-
"init": {}
|
|
1141
|
-
// same for all
|
|
1142
|
-
};
|
|
1143
|
-
var UnsupportedCommandError = class extends Error {
|
|
1144
|
-
constructor(command, manager) {
|
|
1145
|
-
super(`"${command}" is not supported by ${manager}`);
|
|
1146
|
-
this.name = "UnsupportedCommandError";
|
|
1147
|
-
}
|
|
1148
|
-
};
|
|
1149
|
-
function translateCommand(base, manager) {
|
|
1150
|
-
const [subcommand, ...rest] = base;
|
|
1151
|
-
if (!subcommand) {
|
|
1152
|
-
return { command: manager, args: [] };
|
|
1153
|
-
}
|
|
1154
|
-
const translation = TRANSLATIONS[subcommand];
|
|
1155
|
-
if (translation) {
|
|
1156
|
-
const managerEntry = translation[manager];
|
|
1157
|
-
if (managerEntry === null) {
|
|
1158
|
-
throw new UnsupportedCommandError(subcommand, manager);
|
|
1159
|
-
}
|
|
1160
|
-
if (managerEntry !== void 0) {
|
|
1161
|
-
return { command: manager, args: [...managerEntry, ...rest] };
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
return { command: manager, args: [subcommand, ...rest] };
|
|
1165
|
-
}
|
|
1166
|
-
function resolvePkgArgs(base, cwd = process.cwd()) {
|
|
1167
|
-
const mgr = detectPkgManager(cwd);
|
|
1168
|
-
return translateCommand(base, mgr.id);
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
// src/lib/toolResolver.ts
|
|
1172
|
-
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
1173
|
-
import { join as join3 } from "path";
|
|
1174
|
-
|
|
1175
|
-
// src/lib/system.ts
|
|
1176
|
-
import { execSync } from "child_process";
|
|
1177
|
-
function commandExists(command) {
|
|
1178
|
-
try {
|
|
1179
|
-
execSync(`command -v ${command}`, { stdio: "ignore" });
|
|
1180
|
-
return true;
|
|
1181
|
-
} catch {
|
|
1182
|
-
return false;
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
function execCapture(command) {
|
|
1186
|
-
return execSync(command, {
|
|
1187
|
-
encoding: "utf-8",
|
|
1188
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1189
|
-
}).trim();
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
// src/lib/toolResolver.ts
|
|
1193
|
-
function getToolDisplayName(toolId, cwd = process.cwd()) {
|
|
1194
|
-
if (toolId === "pkg") {
|
|
1195
|
-
const mgr = detectPkgManager(cwd);
|
|
1196
|
-
return mgr.id;
|
|
1197
|
-
}
|
|
1198
|
-
const names = {
|
|
1199
|
-
supabase: "supabase",
|
|
1200
|
-
gh: "github",
|
|
1201
|
-
vercel: "vercel",
|
|
1202
|
-
git: "git",
|
|
1203
|
-
pkg: "npm"
|
|
1204
|
-
};
|
|
1205
|
-
return names[toolId];
|
|
1206
|
-
}
|
|
1207
|
-
function resolveToolCommand(toolId, cwd = process.cwd()) {
|
|
1208
|
-
if (toolId === "supabase") {
|
|
1209
|
-
const resolved = resolveSupabaseCommand(cwd);
|
|
1210
|
-
return {
|
|
1211
|
-
command: resolved.command,
|
|
1212
|
-
env: resolved.env,
|
|
1213
|
-
source: resolved.source
|
|
1214
|
-
};
|
|
1215
|
-
}
|
|
1216
|
-
if (toolId === "pkg") {
|
|
1217
|
-
const mgr = detectPkgManager(cwd);
|
|
1218
|
-
if (!commandExists(mgr.command)) {
|
|
1219
|
-
return { command: mgr.command, source: "not-found" };
|
|
1220
|
-
}
|
|
1221
|
-
return { command: mgr.command, env: { ...process.env }, source: "path" };
|
|
1222
|
-
}
|
|
1223
|
-
const command = toolId;
|
|
1224
|
-
if (!commandExists(command)) {
|
|
1225
|
-
return { command, source: "not-found" };
|
|
1226
|
-
}
|
|
1227
|
-
return { command, env: { ...process.env }, source: "path" };
|
|
1228
|
-
}
|
|
1229
|
-
function getToolVersion(toolId) {
|
|
1230
|
-
try {
|
|
1231
|
-
switch (toolId) {
|
|
1232
|
-
case "supabase":
|
|
1233
|
-
return execCapture("supabase --version").replace(/^supabase\s+/i, "");
|
|
1234
|
-
case "gh":
|
|
1235
|
-
return execCapture("gh --version").split("\n")[0]?.replace(/^gh\s+version\s+/i, "").split(" ")[0];
|
|
1236
|
-
case "vercel":
|
|
1237
|
-
return execCapture("vercel --version").split("\n")[0]?.trim();
|
|
1238
|
-
case "git":
|
|
1239
|
-
return execCapture("git --version").replace(/^git version\s+/i, "").trim();
|
|
1240
|
-
case "pkg": {
|
|
1241
|
-
const mgr = detectPkgManager();
|
|
1242
|
-
return execCapture(`${mgr.command} --version`).split("\n")[0]?.trim();
|
|
1243
|
-
}
|
|
1244
|
-
default:
|
|
1245
|
-
return void 0;
|
|
1246
|
-
}
|
|
1247
|
-
} catch {
|
|
1248
|
-
return void 0;
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
var toolInfoCache = /* @__PURE__ */ new Map();
|
|
1252
|
-
function getToolInfo(toolId) {
|
|
1253
|
-
const cached = toolInfoCache.get(toolId);
|
|
1254
|
-
if (cached) return cached;
|
|
1255
|
-
const labels = {
|
|
1256
|
-
supabase: "Supabase CLI",
|
|
1257
|
-
gh: "GitHub CLI",
|
|
1258
|
-
vercel: "Vercel CLI",
|
|
1259
|
-
git: "Git",
|
|
1260
|
-
pkg: "Package Manager"
|
|
1261
|
-
};
|
|
1262
|
-
const commandName = toolId === "supabase" ? "supabase" : toolId === "pkg" ? detectPkgManager().command : toolId;
|
|
1263
|
-
const installed = commandExists(commandName);
|
|
1264
|
-
const version = installed ? getToolVersion(toolId) : void 0;
|
|
1265
|
-
const info = {
|
|
1266
|
-
id: toolId,
|
|
1267
|
-
label: labels[toolId],
|
|
1268
|
-
installed,
|
|
1269
|
-
version
|
|
1270
|
-
};
|
|
1271
|
-
toolInfoCache.set(toolId, info);
|
|
1272
|
-
return info;
|
|
1273
|
-
}
|
|
1274
|
-
function detectSupabaseLink(cwd) {
|
|
1275
|
-
try {
|
|
1276
|
-
const configPath = join3(cwd, ".supabase", "config.toml");
|
|
1277
|
-
if (existsSync3(configPath)) {
|
|
1278
|
-
const content = readFileSync(configPath, "utf-8");
|
|
1279
|
-
const match = content.match(/project_id\s*=\s*"([^"]+)"/);
|
|
1280
|
-
return { linked: true, project: match?.[1] };
|
|
1281
|
-
}
|
|
1282
|
-
} catch {
|
|
1283
|
-
}
|
|
1284
|
-
return { linked: false };
|
|
1285
|
-
}
|
|
1286
|
-
function detectVercelLink(cwd) {
|
|
1287
|
-
try {
|
|
1288
|
-
const projectPath = join3(cwd, ".vercel", "project.json");
|
|
1289
|
-
if (existsSync3(projectPath)) {
|
|
1290
|
-
const data = JSON.parse(readFileSync(projectPath, "utf-8"));
|
|
1291
|
-
return { linked: true, project: data.projectId };
|
|
1292
|
-
}
|
|
1293
|
-
} catch {
|
|
1294
|
-
}
|
|
1295
|
-
return { linked: false };
|
|
1296
|
-
}
|
|
1297
|
-
function detectPkgLink(cwd) {
|
|
1298
|
-
try {
|
|
1299
|
-
const pkgPath = join3(cwd, "package.json");
|
|
1300
|
-
if (existsSync3(pkgPath)) {
|
|
1301
|
-
const data = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1302
|
-
const mgr = detectPkgManager(cwd);
|
|
1303
|
-
return { linked: true, project: data.name ? `${data.name} (${mgr.id})` : mgr.id };
|
|
1304
|
-
}
|
|
1305
|
-
} catch {
|
|
1306
|
-
}
|
|
1307
|
-
return { linked: false };
|
|
1308
|
-
}
|
|
1309
|
-
function detectGhLink(cwd) {
|
|
1310
|
-
try {
|
|
1311
|
-
const remote = execCapture(`git -C "${cwd}" remote get-url origin 2>/dev/null`);
|
|
1312
|
-
if (remote) {
|
|
1313
|
-
const match = remote.match(/[/:]([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
|
|
1314
|
-
return { linked: true, project: match?.[1] ?? remote };
|
|
1315
|
-
}
|
|
1316
|
-
} catch {
|
|
1317
|
-
}
|
|
1318
|
-
return { linked: false };
|
|
1319
|
-
}
|
|
1320
|
-
var toolLinkCache = /* @__PURE__ */ new Map();
|
|
1321
|
-
function getToolLinkInfo(toolId, cwd = process.cwd()) {
|
|
1322
|
-
const cached = toolLinkCache.get(toolId);
|
|
1323
|
-
if (cached) return cached;
|
|
1324
|
-
const base = getToolInfo(toolId);
|
|
1325
|
-
let linkStatus = { linked: false };
|
|
1326
|
-
if (base.installed) {
|
|
1327
|
-
switch (toolId) {
|
|
1328
|
-
case "supabase":
|
|
1329
|
-
linkStatus = detectSupabaseLink(cwd);
|
|
1330
|
-
break;
|
|
1331
|
-
case "vercel":
|
|
1332
|
-
linkStatus = detectVercelLink(cwd);
|
|
1333
|
-
break;
|
|
1334
|
-
case "gh":
|
|
1335
|
-
linkStatus = detectGhLink(cwd);
|
|
1336
|
-
break;
|
|
1337
|
-
case "pkg":
|
|
1338
|
-
linkStatus = detectPkgLink(cwd);
|
|
1339
|
-
break;
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
const info = { ...base, ...linkStatus };
|
|
1343
|
-
toolLinkCache.set(toolId, info);
|
|
1344
|
-
return info;
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
// src/lib/processManager.ts
|
|
1348
|
-
import { spawn as spawn2 } from "child_process";
|
|
1349
|
-
import { join as join5 } from "path";
|
|
1350
|
-
|
|
1351
|
-
// src/lib/packageRoot.ts
|
|
1352
|
-
import { existsSync as existsSync4 } from "fs";
|
|
1353
|
-
import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
|
|
1354
|
-
var rootCache = /* @__PURE__ */ new Map();
|
|
1355
|
-
function findNearestPackageRoot(startDir = process.cwd()) {
|
|
1356
|
-
const resolvedStart = resolve2(startDir);
|
|
1357
|
-
if (rootCache.has(resolvedStart)) return rootCache.get(resolvedStart);
|
|
1358
|
-
let currentDir = resolvedStart;
|
|
1359
|
-
while (true) {
|
|
1360
|
-
if (existsSync4(join4(currentDir, "package.json"))) {
|
|
1361
|
-
rootCache.set(resolvedStart, currentDir);
|
|
1362
|
-
return currentDir;
|
|
1363
|
-
}
|
|
1364
|
-
const parentDir = dirname3(currentDir);
|
|
1365
|
-
if (parentDir === currentDir) {
|
|
1366
|
-
rootCache.set(resolvedStart, void 0);
|
|
1367
|
-
return void 0;
|
|
1368
|
-
}
|
|
1369
|
-
currentDir = parentDir;
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
// src/lib/processManager.ts
|
|
1374
|
-
var DEFAULT_BUFFER_CAP = 1e3;
|
|
1375
|
-
function createRingBuffer(cap = DEFAULT_BUFFER_CAP) {
|
|
1376
|
-
return { lines: [], cap, totalLines: 0 };
|
|
1377
|
-
}
|
|
1378
|
-
function appendToBuffer(buf, data) {
|
|
1379
|
-
const newLines = data.split("\n");
|
|
1380
|
-
if (newLines.length > 0 && newLines[newLines.length - 1] === "") {
|
|
1381
|
-
newLines.pop();
|
|
1382
|
-
}
|
|
1383
|
-
if (newLines.length === 0) return;
|
|
1384
|
-
buf.lines.push(...newLines);
|
|
1385
|
-
buf.totalLines += newLines.length;
|
|
1386
|
-
if (buf.lines.length > buf.cap) {
|
|
1387
|
-
buf.lines.splice(0, buf.lines.length - buf.cap);
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
function tailBuffer(buf, n) {
|
|
1391
|
-
if (n === void 0 || n >= buf.lines.length) return [...buf.lines];
|
|
1392
|
-
return buf.lines.slice(-n);
|
|
1393
|
-
}
|
|
1394
|
-
var registry = /* @__PURE__ */ new Map();
|
|
1395
|
-
var cleanupRegistered = false;
|
|
1396
|
-
function registerCleanup() {
|
|
1397
|
-
if (cleanupRegistered) return;
|
|
1398
|
-
cleanupRegistered = true;
|
|
1399
|
-
const cleanup = () => {
|
|
1400
|
-
for (const proc of registry.values()) {
|
|
1401
|
-
if (proc.status === "running" && proc.child.pid) {
|
|
1402
|
-
try {
|
|
1403
|
-
process.kill(-proc.child.pid, "SIGKILL");
|
|
1404
|
-
} catch {
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
};
|
|
1409
|
-
process.on("exit", cleanup);
|
|
1410
|
-
process.on("SIGINT", () => {
|
|
1411
|
-
cleanup();
|
|
1412
|
-
process.exit(130);
|
|
1413
|
-
});
|
|
1414
|
-
process.on("SIGTERM", () => {
|
|
1415
|
-
cleanup();
|
|
1416
|
-
process.exit(143);
|
|
1417
|
-
});
|
|
1418
|
-
}
|
|
1419
|
-
function generateProcessId(command, args) {
|
|
1420
|
-
const slug = [command, ...args].join("-").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
1421
|
-
if (!registry.has(slug)) return slug;
|
|
1422
|
-
let i = 2;
|
|
1423
|
-
while (registry.has(`${slug}-${i}`)) i++;
|
|
1424
|
-
return `${slug}-${i}`;
|
|
1425
|
-
}
|
|
1426
|
-
function startProcess(id, command, args = [], cwd = process.cwd(), env) {
|
|
1427
|
-
const existing = registry.get(id);
|
|
1428
|
-
if (existing && existing.status === "running") {
|
|
1429
|
-
throw new Error(`Process "${id}" is already running (pid ${existing.child.pid})`);
|
|
1430
|
-
}
|
|
1431
|
-
registerCleanup();
|
|
1432
|
-
const child = spawn2(command, args, {
|
|
1433
|
-
cwd,
|
|
1434
|
-
env: env ?? process.env,
|
|
1435
|
-
detached: true,
|
|
1436
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
1437
|
-
shell: true
|
|
1438
|
-
});
|
|
1439
|
-
const tracked = {
|
|
1440
|
-
id,
|
|
1441
|
-
command,
|
|
1442
|
-
args,
|
|
1443
|
-
cwd,
|
|
1444
|
-
child,
|
|
1445
|
-
status: "running",
|
|
1446
|
-
exitCode: null,
|
|
1447
|
-
signal: null,
|
|
1448
|
-
startedAt: /* @__PURE__ */ new Date(),
|
|
1449
|
-
exitedAt: null,
|
|
1450
|
-
stdout: createRingBuffer(),
|
|
1451
|
-
stderr: createRingBuffer()
|
|
1452
|
-
};
|
|
1453
|
-
child.stdout?.on("data", (data) => {
|
|
1454
|
-
appendToBuffer(tracked.stdout, data.toString());
|
|
1455
|
-
});
|
|
1456
|
-
child.stderr?.on("data", (data) => {
|
|
1457
|
-
appendToBuffer(tracked.stderr, data.toString());
|
|
1458
|
-
});
|
|
1459
|
-
child.on("exit", (code, signal) => {
|
|
1460
|
-
tracked.status = "exited";
|
|
1461
|
-
tracked.exitCode = code;
|
|
1462
|
-
tracked.signal = signal;
|
|
1463
|
-
tracked.exitedAt = /* @__PURE__ */ new Date();
|
|
1464
|
-
});
|
|
1465
|
-
child.on("error", (err) => {
|
|
1466
|
-
tracked.status = "errored";
|
|
1467
|
-
tracked.exitedAt = /* @__PURE__ */ new Date();
|
|
1468
|
-
appendToBuffer(tracked.stderr, `spawn error: ${err.message}
|
|
1469
|
-
`);
|
|
1470
|
-
});
|
|
1471
|
-
registry.set(id, tracked);
|
|
1472
|
-
return toProcessInfo(tracked);
|
|
1473
|
-
}
|
|
1474
|
-
function registerForegroundProcess(id, command, args, cwd, child) {
|
|
1475
|
-
const existing = registry.get(id);
|
|
1476
|
-
if (existing && existing.status === "running") {
|
|
1477
|
-
throw new Error(`Process "${id}" is already running (pid ${existing.child.pid})`);
|
|
1478
|
-
}
|
|
1479
|
-
registerCleanup();
|
|
1480
|
-
const tracked = {
|
|
1481
|
-
id,
|
|
1482
|
-
command,
|
|
1483
|
-
args,
|
|
1484
|
-
cwd,
|
|
1485
|
-
child,
|
|
1486
|
-
status: "running",
|
|
1487
|
-
exitCode: null,
|
|
1488
|
-
signal: null,
|
|
1489
|
-
startedAt: /* @__PURE__ */ new Date(),
|
|
1490
|
-
exitedAt: null,
|
|
1491
|
-
stdout: createRingBuffer(),
|
|
1492
|
-
stderr: createRingBuffer()
|
|
1493
|
-
};
|
|
1494
|
-
child.stdout?.on("data", (data) => {
|
|
1495
|
-
appendToBuffer(tracked.stdout, data.toString());
|
|
1496
|
-
});
|
|
1497
|
-
child.stderr?.on("data", (data) => {
|
|
1498
|
-
appendToBuffer(tracked.stderr, data.toString());
|
|
1499
|
-
});
|
|
1500
|
-
child.on("exit", (code, signal) => {
|
|
1501
|
-
tracked.status = "exited";
|
|
1502
|
-
tracked.exitCode = code;
|
|
1503
|
-
tracked.signal = signal;
|
|
1504
|
-
tracked.exitedAt = /* @__PURE__ */ new Date();
|
|
1505
|
-
});
|
|
1506
|
-
child.on("error", (err) => {
|
|
1507
|
-
tracked.status = "errored";
|
|
1508
|
-
tracked.exitedAt = /* @__PURE__ */ new Date();
|
|
1509
|
-
appendToBuffer(tracked.stderr, `spawn error: ${err.message}
|
|
1510
|
-
`);
|
|
1511
|
-
});
|
|
1512
|
-
registry.set(id, tracked);
|
|
1513
|
-
return toProcessInfo(tracked);
|
|
1514
|
-
}
|
|
1515
|
-
function stopProcess(id) {
|
|
1516
|
-
const proc = registry.get(id);
|
|
1517
|
-
if (!proc) throw new Error(`No tracked process with id "${id}"`);
|
|
1518
|
-
if (proc.status !== "running") return Promise.resolve(toProcessInfo(proc));
|
|
1519
|
-
return new Promise((resolve3) => {
|
|
1520
|
-
const escalateTimer = setTimeout(() => {
|
|
1521
|
-
if (proc.status === "running" && proc.child.pid) {
|
|
1522
|
-
try {
|
|
1523
|
-
process.kill(-proc.child.pid, "SIGKILL");
|
|
1524
|
-
} catch {
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
}, 5e3);
|
|
1528
|
-
const onDone = () => {
|
|
1529
|
-
clearTimeout(escalateTimer);
|
|
1530
|
-
resolve3(toProcessInfo(proc));
|
|
1531
|
-
};
|
|
1532
|
-
proc.child.once("exit", onDone);
|
|
1533
|
-
if (proc.child.pid) {
|
|
1534
|
-
try {
|
|
1535
|
-
process.kill(-proc.child.pid, "SIGTERM");
|
|
1536
|
-
} catch {
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
if (proc.status !== "running") {
|
|
1540
|
-
clearTimeout(escalateTimer);
|
|
1541
|
-
proc.child.removeListener("exit", onDone);
|
|
1542
|
-
resolve3(toProcessInfo(proc));
|
|
1543
|
-
}
|
|
1544
|
-
});
|
|
1545
|
-
}
|
|
1546
|
-
function listProcesses() {
|
|
1547
|
-
return Array.from(registry.values()).map(toProcessInfo);
|
|
1548
|
-
}
|
|
1549
|
-
function getProcessOutput(id, tail, stream) {
|
|
1550
|
-
const proc = registry.get(id);
|
|
1551
|
-
if (!proc) throw new Error(`No tracked process with id "${id}"`);
|
|
1552
|
-
const which = stream ?? "both";
|
|
1553
|
-
return {
|
|
1554
|
-
stdout: which === "stderr" ? [] : tailBuffer(proc.stdout, tail),
|
|
1555
|
-
stderr: which === "stdout" ? [] : tailBuffer(proc.stderr, tail),
|
|
1556
|
-
stdoutLineCount: proc.stdout.totalLines,
|
|
1557
|
-
stderrLineCount: proc.stderr.totalLines
|
|
1558
|
-
};
|
|
1559
|
-
}
|
|
1560
|
-
function isProcessRunning(id) {
|
|
1561
|
-
const proc = registry.get(id);
|
|
1562
|
-
if (!proc) return false;
|
|
1563
|
-
if (proc.status !== "running") return false;
|
|
1564
|
-
if (proc.child.pid) {
|
|
1565
|
-
try {
|
|
1566
|
-
process.kill(proc.child.pid, 0);
|
|
1567
|
-
return true;
|
|
1568
|
-
} catch {
|
|
1569
|
-
return false;
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
return false;
|
|
1573
|
-
}
|
|
1574
|
-
function removeProcess(id) {
|
|
1575
|
-
const proc = registry.get(id);
|
|
1576
|
-
if (!proc) throw new Error(`No tracked process with id "${id}"`);
|
|
1577
|
-
if (proc.status === "running") {
|
|
1578
|
-
throw new Error(`Cannot remove running process "${id}". Stop it first.`);
|
|
1579
|
-
}
|
|
1580
|
-
registry.delete(id);
|
|
1581
|
-
}
|
|
1582
|
-
function findProcessesByCwd(cwd) {
|
|
1583
|
-
const normalized = cwd.replace(/\/+$/, "");
|
|
1584
|
-
return Array.from(registry.values()).filter((proc) => proc.cwd.replace(/\/+$/, "") === normalized).map(toProcessInfo);
|
|
1585
|
-
}
|
|
1586
|
-
function findRunningByCommand(cwd, command, args) {
|
|
1587
|
-
const normalized = cwd.replace(/\/+$/, "");
|
|
1588
|
-
const argsStr = args.join(" ");
|
|
1589
|
-
for (const proc of registry.values()) {
|
|
1590
|
-
if (proc.cwd.replace(/\/+$/, "") === normalized && proc.status === "running" && proc.command === command && proc.args.join(" ") === argsStr) {
|
|
1591
|
-
return toProcessInfo(proc);
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
return void 0;
|
|
1595
|
-
}
|
|
1596
|
-
function toProcessInfo(proc) {
|
|
1597
|
-
const now = Date.now();
|
|
1598
|
-
const start = proc.startedAt.getTime();
|
|
1599
|
-
const end = proc.exitedAt ? proc.exitedAt.getTime() : now;
|
|
1600
|
-
return {
|
|
1601
|
-
id: proc.id,
|
|
1602
|
-
command: proc.command,
|
|
1603
|
-
args: proc.args,
|
|
1604
|
-
cwd: proc.cwd,
|
|
1605
|
-
pid: proc.child.pid,
|
|
1606
|
-
status: proc.status,
|
|
1607
|
-
exitCode: proc.exitCode,
|
|
1608
|
-
signal: proc.signal,
|
|
1609
|
-
startedAt: proc.startedAt.toISOString(),
|
|
1610
|
-
exitedAt: proc.exitedAt?.toISOString() ?? null,
|
|
1611
|
-
uptime: end - start
|
|
1612
|
-
};
|
|
1613
|
-
}
|
|
1614
|
-
function getSocketPath(cwd) {
|
|
1615
|
-
const root = findNearestPackageRoot(cwd);
|
|
1616
|
-
if (!root) return void 0;
|
|
1617
|
-
return join5(root, ".polter", "polter.sock");
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
// src/lib/ipcServer.ts
|
|
1621
|
-
import net from "net";
|
|
1622
|
-
import { mkdirSync, unlinkSync, existsSync as existsSync5 } from "fs";
|
|
1623
|
-
import { dirname as dirname4 } from "path";
|
|
1624
|
-
|
|
1625
|
-
// src/lib/ipcProtocol.ts
|
|
1626
|
-
var DELIMITER = "\n";
|
|
1627
|
-
function serializeMessage(msg) {
|
|
1628
|
-
return JSON.stringify(msg) + DELIMITER;
|
|
1629
|
-
}
|
|
1630
|
-
function parseMessages(buffer) {
|
|
1631
|
-
const messages = [];
|
|
1632
|
-
let remainder = buffer;
|
|
1633
|
-
let idx;
|
|
1634
|
-
while ((idx = remainder.indexOf(DELIMITER)) !== -1) {
|
|
1635
|
-
const line = remainder.slice(0, idx);
|
|
1636
|
-
remainder = remainder.slice(idx + 1);
|
|
1637
|
-
if (line.length === 0) continue;
|
|
1638
|
-
try {
|
|
1639
|
-
messages.push(JSON.parse(line));
|
|
1640
|
-
} catch {
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
return { messages, remainder };
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
// src/lib/ipcServer.ts
|
|
1647
|
-
var handlers = {
|
|
1648
|
-
"ps.list": () => listProcesses(),
|
|
1649
|
-
"ps.start": (params) => {
|
|
1650
|
-
const { command, args, cwd, id } = params;
|
|
1651
|
-
const processArgs = args ?? [];
|
|
1652
|
-
const processId = id ?? generateProcessId(command, processArgs);
|
|
1653
|
-
const processCwd = cwd ?? process.cwd();
|
|
1654
|
-
return startProcess(processId, command, processArgs, processCwd);
|
|
1655
|
-
},
|
|
1656
|
-
"ps.stop": async (params) => {
|
|
1657
|
-
const { id } = params;
|
|
1658
|
-
return stopProcess(id);
|
|
1659
|
-
},
|
|
1660
|
-
"ps.logs": (params) => {
|
|
1661
|
-
const { id, tail, stream } = params;
|
|
1662
|
-
return getProcessOutput(id, tail, stream);
|
|
1663
|
-
},
|
|
1664
|
-
"ps.remove": (params) => {
|
|
1665
|
-
const { id } = params;
|
|
1666
|
-
removeProcess(id);
|
|
1667
|
-
return null;
|
|
1668
|
-
},
|
|
1669
|
-
"ps.find_by_cwd": (params) => {
|
|
1670
|
-
const { cwd, filter } = params;
|
|
1671
|
-
let processes = findProcessesByCwd(cwd);
|
|
1672
|
-
if (filter) {
|
|
1673
|
-
const f = filter.toLowerCase();
|
|
1674
|
-
processes = processes.filter(
|
|
1675
|
-
(p) => (p.command + " " + p.args.join(" ")).toLowerCase().includes(f)
|
|
1676
|
-
);
|
|
1677
|
-
}
|
|
1678
|
-
return processes;
|
|
1679
|
-
},
|
|
1680
|
-
"ps.find_running": (params) => {
|
|
1681
|
-
const { cwd, command, args } = params;
|
|
1682
|
-
return findRunningByCommand(cwd, command, args) ?? null;
|
|
1683
|
-
},
|
|
1684
|
-
"ps.generate_id": (params) => {
|
|
1685
|
-
const { command, args } = params;
|
|
1686
|
-
return generateProcessId(command, args);
|
|
1687
|
-
},
|
|
1688
|
-
status: () => ({ tui: true, pid: process.pid })
|
|
1689
|
-
};
|
|
1690
|
-
async function handleRequest(req) {
|
|
1691
|
-
const handler = handlers[req.method];
|
|
1692
|
-
if (!handler) {
|
|
1693
|
-
return {
|
|
1694
|
-
jsonrpc: "2.0",
|
|
1695
|
-
id: req.id,
|
|
1696
|
-
error: { code: -32601, message: `Method not found: ${req.method}` }
|
|
1697
|
-
};
|
|
1698
|
-
}
|
|
1699
|
-
try {
|
|
1700
|
-
const result = await handler(req.params ?? {});
|
|
1701
|
-
return { jsonrpc: "2.0", id: req.id, result };
|
|
1702
|
-
} catch (err) {
|
|
1703
|
-
return {
|
|
1704
|
-
jsonrpc: "2.0",
|
|
1705
|
-
id: req.id,
|
|
1706
|
-
error: { code: -32e3, message: err.message }
|
|
1707
|
-
};
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
1710
|
-
function createIpcServer(socketPath) {
|
|
1711
|
-
let server = null;
|
|
1712
|
-
const connections = /* @__PURE__ */ new Set();
|
|
1713
|
-
function handleConnection(socket) {
|
|
1714
|
-
connections.add(socket);
|
|
1715
|
-
let buffer = "";
|
|
1716
|
-
socket.on("data", async (data) => {
|
|
1717
|
-
buffer += data.toString();
|
|
1718
|
-
const { messages, remainder } = parseMessages(buffer);
|
|
1719
|
-
buffer = remainder;
|
|
1720
|
-
for (const msg of messages) {
|
|
1721
|
-
if ("method" in msg) {
|
|
1722
|
-
const response = await handleRequest(msg);
|
|
1723
|
-
try {
|
|
1724
|
-
socket.write(serializeMessage(response));
|
|
1725
|
-
} catch {
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
});
|
|
1730
|
-
socket.on("close", () => {
|
|
1731
|
-
connections.delete(socket);
|
|
1732
|
-
});
|
|
1733
|
-
socket.on("error", () => {
|
|
1734
|
-
connections.delete(socket);
|
|
1735
|
-
});
|
|
1736
|
-
}
|
|
1737
|
-
async function cleanStaleSocket() {
|
|
1738
|
-
if (!existsSync5(socketPath)) return;
|
|
1739
|
-
return new Promise((resolve3) => {
|
|
1740
|
-
const probe = net.createConnection(socketPath);
|
|
1741
|
-
probe.on("connect", () => {
|
|
1742
|
-
probe.destroy();
|
|
1743
|
-
resolve3();
|
|
1744
|
-
});
|
|
1745
|
-
probe.on("error", () => {
|
|
1746
|
-
try {
|
|
1747
|
-
unlinkSync(socketPath);
|
|
1748
|
-
} catch {
|
|
1749
|
-
}
|
|
1750
|
-
resolve3();
|
|
1751
|
-
});
|
|
1752
|
-
});
|
|
1753
|
-
}
|
|
1754
|
-
return {
|
|
1755
|
-
async start() {
|
|
1756
|
-
mkdirSync(dirname4(socketPath), { recursive: true });
|
|
1757
|
-
await cleanStaleSocket();
|
|
1758
|
-
return new Promise((resolve3, reject) => {
|
|
1759
|
-
server = net.createServer(handleConnection);
|
|
1760
|
-
server.on("error", (err) => {
|
|
1761
|
-
if (err.code === "EADDRINUSE") {
|
|
1762
|
-
try {
|
|
1763
|
-
unlinkSync(socketPath);
|
|
1764
|
-
} catch {
|
|
1765
|
-
}
|
|
1766
|
-
server.listen(socketPath, () => resolve3());
|
|
1767
|
-
} else {
|
|
1768
|
-
reject(err);
|
|
1769
|
-
}
|
|
1770
|
-
});
|
|
1771
|
-
server.listen(socketPath, () => resolve3());
|
|
1772
|
-
});
|
|
1773
|
-
},
|
|
1774
|
-
async stop() {
|
|
1775
|
-
for (const conn of connections) {
|
|
1776
|
-
conn.destroy();
|
|
1777
|
-
}
|
|
1778
|
-
connections.clear();
|
|
1779
|
-
return new Promise((resolve3) => {
|
|
1780
|
-
if (!server) {
|
|
1781
|
-
resolve3();
|
|
1782
|
-
return;
|
|
1783
|
-
}
|
|
1784
|
-
server.close(() => {
|
|
1785
|
-
try {
|
|
1786
|
-
unlinkSync(socketPath);
|
|
1787
|
-
} catch {
|
|
1788
|
-
}
|
|
1789
|
-
server = null;
|
|
1790
|
-
resolve3();
|
|
1791
|
-
});
|
|
1792
|
-
});
|
|
1793
|
-
}
|
|
1794
|
-
};
|
|
1795
|
-
}
|
|
1796
|
-
|
|
1797
|
-
// src/pipeline/engine.ts
|
|
1798
|
-
async function executePipeline(pipeline, onProgress, cwd = process.cwd()) {
|
|
1799
|
-
const stepResults = pipeline.steps.map((step) => ({
|
|
1800
|
-
step,
|
|
1801
|
-
status: "pending"
|
|
1802
|
-
}));
|
|
1803
|
-
let aborted = false;
|
|
1804
|
-
for (let i = 0; i < pipeline.steps.length; i++) {
|
|
1805
|
-
const step = pipeline.steps[i];
|
|
1806
|
-
if (aborted) {
|
|
1807
|
-
stepResults[i] = { step, status: "skipped" };
|
|
1808
|
-
onProgress({ stepResults: [...stepResults], currentStepIndex: i, done: false });
|
|
1809
|
-
continue;
|
|
1810
|
-
}
|
|
1811
|
-
stepResults[i] = { step, status: "running" };
|
|
1812
|
-
onProgress({ stepResults: [...stepResults], currentStepIndex: i, done: false });
|
|
1813
|
-
const cmdDef = getCommandById(step.commandId);
|
|
1814
|
-
const toolId = cmdDef?.tool ?? "supabase";
|
|
1815
|
-
const resolved = resolveToolCommand(toolId, cwd);
|
|
1816
|
-
let baseArgs = cmdDef?.base ?? [];
|
|
1817
|
-
if (toolId === "pkg" && cmdDef) {
|
|
1818
|
-
try {
|
|
1819
|
-
const translated = resolvePkgArgs(cmdDef.base, cwd);
|
|
1820
|
-
baseArgs = translated.args;
|
|
1821
|
-
} catch {
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
const allArgs = [...baseArgs, ...step.args, ...step.flags];
|
|
1825
|
-
const result = await runCommand(
|
|
1826
|
-
{ command: resolved.command, env: resolved.env },
|
|
1827
|
-
allArgs,
|
|
1828
|
-
cwd
|
|
1829
|
-
).promise;
|
|
1830
|
-
const success = !result.spawnError && result.exitCode === 0;
|
|
1831
|
-
stepResults[i] = {
|
|
1832
|
-
step,
|
|
1833
|
-
status: success ? "success" : "error",
|
|
1834
|
-
result
|
|
1835
|
-
};
|
|
1836
|
-
if (!success && !step.continueOnError) {
|
|
1837
|
-
aborted = true;
|
|
1838
|
-
}
|
|
1839
|
-
onProgress({ stepResults: [...stepResults], currentStepIndex: i, done: false });
|
|
1840
|
-
}
|
|
1841
|
-
onProgress({
|
|
1842
|
-
stepResults: [...stepResults],
|
|
1843
|
-
currentStepIndex: pipeline.steps.length - 1,
|
|
1844
|
-
done: true
|
|
1845
|
-
});
|
|
1846
|
-
return stepResults;
|
|
1847
|
-
}
|
|
1848
|
-
|
|
1849
|
-
// src/config/projectConfig.ts
|
|
1850
|
-
import { existsSync as existsSync6, readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
1851
|
-
import { join as join6 } from "path";
|
|
1852
|
-
var CONFIG_DIR = ".polter";
|
|
1853
|
-
var CONFIG_FILE = "config.json";
|
|
1854
|
-
function defaultConfig() {
|
|
1855
|
-
return {
|
|
1856
|
-
version: 1,
|
|
1857
|
-
tools: {},
|
|
1858
|
-
pipelines: []
|
|
1859
|
-
};
|
|
1860
|
-
}
|
|
1861
|
-
function getProjectConfigPath(startDir) {
|
|
1862
|
-
const root = findNearestPackageRoot(startDir);
|
|
1863
|
-
if (!root) return void 0;
|
|
1864
|
-
const dir = join6(root, CONFIG_DIR);
|
|
1865
|
-
return { dir, file: join6(dir, CONFIG_FILE) };
|
|
1866
|
-
}
|
|
1867
|
-
function readProjectConfig(startDir) {
|
|
1868
|
-
const paths = getProjectConfigPath(startDir);
|
|
1869
|
-
if (!paths) return void 0;
|
|
1870
|
-
if (!existsSync6(paths.file)) return void 0;
|
|
1871
|
-
try {
|
|
1872
|
-
const raw = readFileSync2(paths.file, "utf-8");
|
|
1873
|
-
return JSON.parse(raw);
|
|
1874
|
-
} catch {
|
|
1875
|
-
return void 0;
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1878
|
-
function writeProjectConfig(config2, startDir) {
|
|
1879
|
-
const paths = getProjectConfigPath(startDir);
|
|
1880
|
-
if (!paths) return false;
|
|
1881
|
-
mkdirSync2(paths.dir, { recursive: true });
|
|
1882
|
-
writeFileSync(paths.file, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
1883
|
-
return true;
|
|
1884
|
-
}
|
|
1885
|
-
function getOrCreateProjectConfig(startDir) {
|
|
1886
|
-
return readProjectConfig(startDir) ?? defaultConfig();
|
|
1887
|
-
}
|
|
1888
|
-
function getProjectPipelines(startDir) {
|
|
1889
|
-
const config2 = readProjectConfig(startDir);
|
|
1890
|
-
return config2?.pipelines ?? [];
|
|
1891
|
-
}
|
|
1892
|
-
function saveProjectPipeline(pipeline, startDir) {
|
|
1893
|
-
const config2 = getOrCreateProjectConfig(startDir);
|
|
1894
|
-
const idx = config2.pipelines.findIndex((p) => p.id === pipeline.id);
|
|
1895
|
-
if (idx >= 0) {
|
|
1896
|
-
config2.pipelines[idx] = pipeline;
|
|
1897
|
-
} else {
|
|
1898
|
-
config2.pipelines.push(pipeline);
|
|
1899
|
-
}
|
|
1900
|
-
return writeProjectConfig(config2, startDir);
|
|
1901
|
-
}
|
|
1902
|
-
function deleteProjectPipeline(pipelineId, startDir) {
|
|
1903
|
-
const config2 = getOrCreateProjectConfig(startDir);
|
|
1904
|
-
config2.pipelines = config2.pipelines.filter((p) => p.id !== pipelineId);
|
|
1905
|
-
return writeProjectConfig(config2, startDir);
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
// src/config/store.ts
|
|
1909
|
-
import Conf from "conf";
|
|
1910
|
-
var config = new Conf({
|
|
1911
|
-
projectName: process.env.NODE_ENV === "test" ? "polter-test" : "polter"
|
|
1912
|
-
});
|
|
1913
|
-
var GLOBAL_PIPELINES_KEY = "globalPipelinesV1";
|
|
1914
|
-
function getGlobalPipelines() {
|
|
1915
|
-
if (!config.has(GLOBAL_PIPELINES_KEY)) {
|
|
1916
|
-
config.set(GLOBAL_PIPELINES_KEY, []);
|
|
1917
|
-
}
|
|
1918
|
-
return config.get(GLOBAL_PIPELINES_KEY) || [];
|
|
1919
|
-
}
|
|
1920
|
-
function saveGlobalPipeline(pipeline) {
|
|
1921
|
-
const pipelines = getGlobalPipelines();
|
|
1922
|
-
const idx = pipelines.findIndex((p) => p.id === pipeline.id);
|
|
1923
|
-
if (idx >= 0) {
|
|
1924
|
-
pipelines[idx] = pipeline;
|
|
1925
|
-
} else {
|
|
1926
|
-
pipelines.push(pipeline);
|
|
1927
|
-
}
|
|
1928
|
-
config.set(GLOBAL_PIPELINES_KEY, pipelines);
|
|
1929
|
-
}
|
|
1930
|
-
function deleteGlobalPipeline(pipelineId) {
|
|
1931
|
-
const pipelines = getGlobalPipelines().filter((p) => p.id !== pipelineId);
|
|
1932
|
-
config.set(GLOBAL_PIPELINES_KEY, pipelines);
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
|
-
// src/pipeline/storage.ts
|
|
1936
|
-
function getAllPipelines(startDir) {
|
|
1937
|
-
const projectPipelines = getProjectPipelines(startDir).map((p) => ({
|
|
1938
|
-
...p,
|
|
1939
|
-
source: "project"
|
|
1940
|
-
}));
|
|
1941
|
-
const globalPipelines = getGlobalPipelines().map((p) => ({
|
|
1942
|
-
...p,
|
|
1943
|
-
source: "global"
|
|
1944
|
-
}));
|
|
1945
|
-
const seen = new Set(projectPipelines.map((p) => p.id));
|
|
1946
|
-
const merged = [
|
|
1947
|
-
...projectPipelines,
|
|
1948
|
-
...globalPipelines.filter((p) => !seen.has(p.id))
|
|
1949
|
-
];
|
|
1950
|
-
return merged;
|
|
1951
|
-
}
|
|
1952
|
-
function savePipeline(pipeline, source, startDir) {
|
|
1953
|
-
if (source === "project") {
|
|
1954
|
-
saveProjectPipeline(pipeline, startDir);
|
|
1955
|
-
} else {
|
|
1956
|
-
saveGlobalPipeline(pipeline);
|
|
1957
|
-
}
|
|
1958
|
-
}
|
|
1959
|
-
function deletePipeline(pipelineId, source, startDir) {
|
|
1960
|
-
if (source === "project") {
|
|
1961
|
-
deleteProjectPipeline(pipelineId, startDir);
|
|
1962
|
-
} else {
|
|
1963
|
-
deleteGlobalPipeline(pipelineId);
|
|
1964
|
-
}
|
|
1965
|
-
}
|
|
1966
|
-
function findPipelineByName(name, startDir) {
|
|
1967
|
-
return getAllPipelines(startDir).find((p) => p.name === name);
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
// src/declarative/parser.ts
|
|
1971
|
-
import { existsSync as existsSync7, readFileSync as readFileSync3 } from "fs";
|
|
1972
|
-
import { join as join7 } from "path";
|
|
1973
|
-
var YAML_FILE = "polter.yaml";
|
|
1974
|
-
function parseSimpleYaml(content) {
|
|
1975
|
-
const lines = content.split("\n");
|
|
1976
|
-
const result = {};
|
|
1977
|
-
const stack = [
|
|
1978
|
-
{ obj: result, indent: -1 }
|
|
1979
|
-
];
|
|
1980
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1981
|
-
const line = lines[i];
|
|
1982
|
-
const trimmed = line.replace(/\s+$/, "");
|
|
1983
|
-
if (!trimmed || trimmed.match(/^\s*#/)) continue;
|
|
1984
|
-
const indent = line.search(/\S/);
|
|
1985
|
-
if (indent < 0) continue;
|
|
1986
|
-
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
|
|
1987
|
-
stack.pop();
|
|
1988
|
-
}
|
|
1989
|
-
const current = stack[stack.length - 1].obj;
|
|
1990
|
-
const content_ = trimmed.trim();
|
|
1991
|
-
if (content_.startsWith("- ")) {
|
|
1992
|
-
const parentKey = Object.keys(current).pop();
|
|
1993
|
-
if (parentKey) {
|
|
1994
|
-
const arr = current[parentKey];
|
|
1995
|
-
if (Array.isArray(arr)) {
|
|
1996
|
-
const val = content_.slice(2).trim();
|
|
1997
|
-
const colonIdx2 = val.indexOf(":");
|
|
1998
|
-
if (colonIdx2 > 0 && !val.startsWith('"') && !val.startsWith("'")) {
|
|
1999
|
-
const key = val.slice(0, colonIdx2).trim();
|
|
2000
|
-
const rest = val.slice(colonIdx2 + 1).trim();
|
|
2001
|
-
const obj = { [key]: parseValue(rest) };
|
|
2002
|
-
arr.push(obj);
|
|
2003
|
-
stack.push({ obj, indent: indent + 2 });
|
|
2004
|
-
} else {
|
|
2005
|
-
arr.push(parseValue(val));
|
|
2006
|
-
}
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
|
-
continue;
|
|
2010
|
-
}
|
|
2011
|
-
const colonIdx = content_.indexOf(":");
|
|
2012
|
-
if (colonIdx > 0) {
|
|
2013
|
-
const key = content_.slice(0, colonIdx).trim();
|
|
2014
|
-
const rest = content_.slice(colonIdx + 1).trim();
|
|
2015
|
-
if (rest === "" || rest === "|" || rest === ">") {
|
|
2016
|
-
const nextLine = lines[i + 1];
|
|
2017
|
-
const nextTrimmed = nextLine?.trim();
|
|
2018
|
-
if (nextTrimmed?.startsWith("- ")) {
|
|
2019
|
-
current[key] = [];
|
|
2020
|
-
} else {
|
|
2021
|
-
const nested = {};
|
|
2022
|
-
current[key] = nested;
|
|
2023
|
-
stack.push({ obj: nested, indent });
|
|
2024
|
-
}
|
|
2025
|
-
} else {
|
|
2026
|
-
current[key] = parseValue(rest);
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
return result;
|
|
2031
|
-
}
|
|
2032
|
-
function parseValue(raw) {
|
|
2033
|
-
if (!raw) return "";
|
|
2034
|
-
if (raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("'") && raw.endsWith("'")) {
|
|
2035
|
-
return raw.slice(1, -1);
|
|
2036
|
-
}
|
|
2037
|
-
if (raw === "true") return true;
|
|
2038
|
-
if (raw === "false") return false;
|
|
2039
|
-
const num = Number(raw);
|
|
2040
|
-
if (!isNaN(num) && raw !== "") return num;
|
|
2041
|
-
return raw;
|
|
2042
|
-
}
|
|
2043
|
-
function findPolterYaml(startDir = process.cwd()) {
|
|
2044
|
-
const filePath = join7(startDir, YAML_FILE);
|
|
2045
|
-
return existsSync7(filePath) ? filePath : void 0;
|
|
2046
|
-
}
|
|
2047
|
-
function parsePolterYaml(startDir = process.cwd()) {
|
|
2048
|
-
const filePath = findPolterYaml(startDir);
|
|
2049
|
-
if (!filePath) return void 0;
|
|
2050
|
-
const content = readFileSync3(filePath, "utf-8");
|
|
2051
|
-
const raw = parseSimpleYaml(content);
|
|
2052
|
-
return raw;
|
|
2053
|
-
}
|
|
2054
|
-
|
|
2055
|
-
// src/declarative/status.ts
|
|
2056
|
-
import { execSync as execSync2 } from "child_process";
|
|
2057
|
-
function safeExec(cmd) {
|
|
2058
|
-
try {
|
|
2059
|
-
return execSync2(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
2060
|
-
} catch {
|
|
2061
|
-
return void 0;
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
function getCurrentStatus(cwd = process.cwd()) {
|
|
2065
|
-
const result = {};
|
|
2066
|
-
if (commandExists("supabase")) {
|
|
2067
|
-
const linked = safeExec(`cd "${cwd}" && supabase projects list 2>/dev/null`) !== void 0;
|
|
2068
|
-
result.supabase = {
|
|
2069
|
-
linked,
|
|
2070
|
-
projectRef: void 0,
|
|
2071
|
-
functions: []
|
|
2072
|
-
};
|
|
2073
|
-
const functionsOutput = safeExec(`cd "${cwd}" && supabase functions list 2>/dev/null`);
|
|
2074
|
-
if (functionsOutput) {
|
|
2075
|
-
result.supabase.functions = functionsOutput.split("\n").filter((line) => line.trim() && !line.startsWith("\u2502") && !line.startsWith("\u250C")).slice(1);
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
if (commandExists("vercel")) {
|
|
2079
|
-
const whoami = safeExec("vercel whoami 2>/dev/null");
|
|
2080
|
-
result.vercel = {
|
|
2081
|
-
linked: !!whoami,
|
|
2082
|
-
projectId: void 0
|
|
2083
|
-
};
|
|
2084
|
-
}
|
|
2085
|
-
if (commandExists("gh")) {
|
|
2086
|
-
const authStatus = safeExec("gh auth status 2>&1");
|
|
2087
|
-
const authenticated = authStatus?.includes("Logged in") ?? false;
|
|
2088
|
-
const repoOutput = safeExec(`cd "${cwd}" && gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null`);
|
|
2089
|
-
result.github = {
|
|
2090
|
-
repo: repoOutput || void 0,
|
|
2091
|
-
authenticated
|
|
2092
|
-
};
|
|
2093
|
-
}
|
|
2094
|
-
return result;
|
|
2095
|
-
}
|
|
2096
|
-
|
|
2097
|
-
// src/declarative/planner.ts
|
|
2098
|
-
function planChanges(desired, cwd = process.cwd()) {
|
|
2099
|
-
const current = getCurrentStatus(cwd);
|
|
2100
|
-
const actions = [];
|
|
2101
|
-
if (desired.supabase?.functions) {
|
|
2102
|
-
const currentFunctions = new Set(current.supabase?.functions ?? []);
|
|
2103
|
-
for (const fn of desired.supabase.functions) {
|
|
2104
|
-
if (!currentFunctions.has(fn.name)) {
|
|
2105
|
-
const args = ["functions", "deploy", fn.name];
|
|
2106
|
-
if (fn.verify_jwt === false) {
|
|
2107
|
-
args.push("--no-verify-jwt");
|
|
2108
|
-
}
|
|
2109
|
-
actions.push({
|
|
2110
|
-
tool: "supabase",
|
|
2111
|
-
action: "create",
|
|
2112
|
-
resource: `function:${fn.name}`,
|
|
2113
|
-
description: `Deploy function "${fn.name}"`,
|
|
2114
|
-
args
|
|
2115
|
-
});
|
|
2116
|
-
}
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
if (desired.supabase?.secrets) {
|
|
2120
|
-
for (const secret of desired.supabase.secrets) {
|
|
2121
|
-
actions.push({
|
|
2122
|
-
tool: "supabase",
|
|
2123
|
-
action: "update",
|
|
2124
|
-
resource: `secret:${secret}`,
|
|
2125
|
-
description: `Ensure secret "${secret}" is set`,
|
|
2126
|
-
args: ["secrets", "set", secret]
|
|
2127
|
-
});
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
if (desired.vercel?.domains) {
|
|
2131
|
-
for (const domain of desired.vercel.domains) {
|
|
2132
|
-
actions.push({
|
|
2133
|
-
tool: "vercel",
|
|
2134
|
-
action: "create",
|
|
2135
|
-
resource: `domain:${domain}`,
|
|
2136
|
-
description: `Add domain "${domain}"`,
|
|
2137
|
-
args: ["domains", "add", domain]
|
|
2138
|
-
});
|
|
2139
|
-
}
|
|
2140
|
-
}
|
|
2141
|
-
if (desired.vercel?.env) {
|
|
2142
|
-
for (const [env, vars] of Object.entries(desired.vercel.env)) {
|
|
2143
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
2144
|
-
actions.push({
|
|
2145
|
-
tool: "vercel",
|
|
2146
|
-
action: "update",
|
|
2147
|
-
resource: `env:${env}:${key}`,
|
|
2148
|
-
description: `Set env var "${key}" for ${env}`,
|
|
2149
|
-
args: ["env", "add", key, env]
|
|
2150
|
-
});
|
|
2151
|
-
}
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
if (desired.github?.secrets) {
|
|
2155
|
-
for (const secret of desired.github.secrets) {
|
|
2156
|
-
actions.push({
|
|
2157
|
-
tool: "gh",
|
|
2158
|
-
action: "update",
|
|
2159
|
-
resource: `secret:${secret}`,
|
|
2160
|
-
description: `Ensure GitHub secret "${secret}" is set`,
|
|
2161
|
-
args: ["secret", "set", secret]
|
|
2162
|
-
});
|
|
2163
|
-
}
|
|
2164
|
-
}
|
|
2165
|
-
return {
|
|
2166
|
-
actions,
|
|
2167
|
-
noChanges: actions.length === 0
|
|
2168
|
-
};
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
// src/declarative/applier.ts
|
|
2172
|
-
async function applyActions(actions, cwd = process.cwd(), onProgress) {
|
|
2173
|
-
const results = [];
|
|
2174
|
-
for (let i = 0; i < actions.length; i++) {
|
|
2175
|
-
const action = actions[i];
|
|
2176
|
-
onProgress?.(i, actions.length, action);
|
|
2177
|
-
const resolved = resolveToolCommand(action.tool, cwd);
|
|
2178
|
-
const result = await runCommand(
|
|
2179
|
-
{ command: resolved.command, env: resolved.env },
|
|
2180
|
-
action.args,
|
|
2181
|
-
cwd
|
|
2182
|
-
).promise;
|
|
2183
|
-
const success = !result.spawnError && result.exitCode === 0;
|
|
2184
|
-
results.push({ action, success, result });
|
|
2185
|
-
}
|
|
2186
|
-
return results;
|
|
2187
|
-
}
|
|
2188
|
-
|
|
2189
|
-
// src/lib/mcpInstaller.ts
|
|
2190
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
2191
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
|
|
2192
|
-
import { join as join8, dirname as dirname5 } from "path";
|
|
2193
|
-
import { fileURLToPath } from "url";
|
|
2194
|
-
import { homedir } from "os";
|
|
2195
|
-
import pc from "picocolors";
|
|
2196
|
-
var __dirname = dirname5(fileURLToPath(import.meta.url));
|
|
2197
|
-
function readPkgVersion() {
|
|
2198
|
-
for (const rel of ["../../package.json", "../package.json"]) {
|
|
2199
|
-
const p = join8(__dirname, rel);
|
|
2200
|
-
if (existsSync8(p)) {
|
|
2201
|
-
const pkg = JSON.parse(readFileSync4(p, "utf-8"));
|
|
2202
|
-
return pkg.version;
|
|
2203
|
-
}
|
|
2204
|
-
}
|
|
2205
|
-
return "0.0.0";
|
|
2206
|
-
}
|
|
2207
|
-
var MCP_ARGS = ["npx", "-y", "-p", "@polterware/polter@latest", "polter-mcp"];
|
|
2208
|
-
var SCOPE_LABELS = {
|
|
2209
|
-
local: "local (this machine)",
|
|
2210
|
-
project: "project (shared via repo)",
|
|
2211
|
-
user: "global (all projects)"
|
|
2212
|
-
};
|
|
2213
|
-
function tryClaudeCli(scope) {
|
|
2214
|
-
if (!commandExists("claude")) return false;
|
|
2215
|
-
const result = spawnSync2("claude", ["mcp", "add", "-s", scope, "polter", "--", ...MCP_ARGS], {
|
|
2216
|
-
stdio: "inherit",
|
|
2217
|
-
shell: true
|
|
2218
|
-
});
|
|
2219
|
-
return result.status === 0;
|
|
2220
|
-
}
|
|
2221
|
-
function getSettingsPath(scope) {
|
|
2222
|
-
if (scope === "project") {
|
|
2223
|
-
return join8(process.cwd(), ".mcp.json");
|
|
2224
|
-
}
|
|
2225
|
-
return join8(homedir(), ".claude", "settings.json");
|
|
2226
|
-
}
|
|
2227
|
-
function tryManualInstall(scope) {
|
|
2228
|
-
const settingsPath = getSettingsPath(scope);
|
|
2229
|
-
const dir = join8(settingsPath, "..");
|
|
2230
|
-
let settings = {};
|
|
2231
|
-
if (existsSync8(settingsPath)) {
|
|
2232
|
-
try {
|
|
2233
|
-
settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
|
|
2234
|
-
} catch {
|
|
2235
|
-
process.stderr.write(pc.red(`Failed to parse ${settingsPath}
|
|
2236
|
-
`));
|
|
2237
|
-
return false;
|
|
2238
|
-
}
|
|
2239
|
-
} else {
|
|
2240
|
-
mkdirSync3(dir, { recursive: true });
|
|
2241
|
-
}
|
|
2242
|
-
const mcpServers = settings.mcpServers ?? {};
|
|
2243
|
-
mcpServers.polter = {
|
|
2244
|
-
command: "npx",
|
|
2245
|
-
args: ["-y", "-p", "@polterware/polter@latest", "polter-mcp"]
|
|
2246
|
-
};
|
|
2247
|
-
settings.mcpServers = mcpServers;
|
|
2248
|
-
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
2249
|
-
return true;
|
|
2250
|
-
}
|
|
2251
|
-
async function installMcpServer(scope) {
|
|
2252
|
-
process.stdout.write(pc.bold(`Installing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}
|
|
2253
|
-
|
|
2254
|
-
`));
|
|
2255
|
-
if (commandExists("claude")) {
|
|
2256
|
-
process.stdout.write(` Using 'claude mcp add -s ${scope}'...
|
|
2257
|
-
`);
|
|
2258
|
-
if (tryClaudeCli(scope)) {
|
|
2259
|
-
process.stdout.write(pc.green("\n Done! Restart Claude Code to use Polter tools.\n"));
|
|
2260
|
-
return;
|
|
2261
|
-
}
|
|
2262
|
-
process.stdout.write(pc.yellow(" 'claude mcp add' failed, falling back to manual install...\n\n"));
|
|
2263
|
-
}
|
|
2264
|
-
const settingsPath = getSettingsPath(scope);
|
|
2265
|
-
process.stdout.write(` Writing to ${settingsPath}...
|
|
2266
|
-
`);
|
|
2267
|
-
if (tryManualInstall(scope)) {
|
|
2268
|
-
process.stdout.write(pc.green("\n Done! Restart Claude Code to use Polter tools.\n"));
|
|
2269
|
-
} else {
|
|
2270
|
-
process.stderr.write(pc.red("\n Failed to install. Add manually:\n\n"));
|
|
2271
|
-
process.stderr.write(` ${pc.dim(JSON.stringify({ mcpServers: { polter: { command: "npx", args: ["-y", "-p", "@polterware/polter@latest", "polter-mcp"] } } }, null, 2))}
|
|
2272
|
-
`);
|
|
2273
|
-
process.exit(1);
|
|
2274
|
-
}
|
|
2275
|
-
}
|
|
2276
|
-
async function removeMcpServer(scope) {
|
|
2277
|
-
process.stdout.write(pc.bold(`Removing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}
|
|
2278
|
-
|
|
2279
|
-
`));
|
|
2280
|
-
if (commandExists("claude")) {
|
|
2281
|
-
const result = spawnSync2("claude", ["mcp", "remove", "-s", scope, "polter"], {
|
|
2282
|
-
stdio: "inherit",
|
|
2283
|
-
shell: true
|
|
2284
|
-
});
|
|
2285
|
-
if (result.status === 0) {
|
|
2286
|
-
process.stdout.write(pc.green("\n Done! Polter MCP server removed.\n"));
|
|
2287
|
-
return;
|
|
2288
|
-
}
|
|
2289
|
-
process.stdout.write(pc.yellow(" 'claude mcp remove' failed, falling back to manual removal...\n\n"));
|
|
2290
|
-
}
|
|
2291
|
-
const settingsPath = getSettingsPath(scope);
|
|
2292
|
-
if (!existsSync8(settingsPath)) {
|
|
2293
|
-
process.stdout.write(pc.yellow(" No settings file found. Nothing to remove.\n"));
|
|
2294
|
-
return;
|
|
2295
|
-
}
|
|
2296
|
-
let settings;
|
|
2297
|
-
try {
|
|
2298
|
-
settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
|
|
2299
|
-
} catch {
|
|
2300
|
-
process.stderr.write(pc.red(` Failed to parse ${settingsPath}
|
|
2301
|
-
`));
|
|
2302
|
-
process.exit(1);
|
|
2303
|
-
return;
|
|
2304
|
-
}
|
|
2305
|
-
const mcpServers = settings.mcpServers;
|
|
2306
|
-
if (!mcpServers?.polter) {
|
|
2307
|
-
process.stdout.write(pc.yellow(" Polter MCP server not found in settings. Nothing to remove.\n"));
|
|
2308
|
-
return;
|
|
2309
|
-
}
|
|
2310
|
-
delete mcpServers.polter;
|
|
2311
|
-
settings.mcpServers = mcpServers;
|
|
2312
|
-
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
2313
|
-
process.stdout.write(pc.green(" Done! Polter MCP server removed.\n"));
|
|
2314
|
-
}
|
|
2315
|
-
function getMcpStatusInfo() {
|
|
2316
|
-
const version = readPkgVersion();
|
|
2317
|
-
const scopeDefs = [
|
|
2318
|
-
{ label: "Project (.mcp.json)", path: join8(process.cwd(), ".mcp.json"), scope: "project" },
|
|
2319
|
-
{ label: "User (~/.claude/settings.json)", path: join8(homedir(), ".claude", "settings.json"), scope: "user" }
|
|
2320
|
-
];
|
|
2321
|
-
const scopes = scopeDefs.map((s) => {
|
|
2322
|
-
if (!existsSync8(s.path)) {
|
|
2323
|
-
return { label: s.label, scope: s.scope, registered: false };
|
|
2324
|
-
}
|
|
2325
|
-
try {
|
|
2326
|
-
const settings = JSON.parse(readFileSync4(s.path, "utf-8"));
|
|
2327
|
-
const mcpServers = settings.mcpServers;
|
|
2328
|
-
return { label: s.label, scope: s.scope, registered: !!mcpServers?.polter };
|
|
2329
|
-
} catch {
|
|
2330
|
-
return { label: s.label, scope: s.scope, registered: false, error: "error reading file" };
|
|
2331
|
-
}
|
|
2332
|
-
});
|
|
2333
|
-
return { installedVersion: version, latestVersion: null, scopes };
|
|
2334
|
-
}
|
|
2335
|
-
async function installMcpServerSilent(scope) {
|
|
2336
|
-
const messages = [];
|
|
2337
|
-
messages.push(`Installing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}`);
|
|
2338
|
-
if (commandExists("claude")) {
|
|
2339
|
-
messages.push(`Using 'claude mcp add -s ${scope}'...`);
|
|
2340
|
-
if (tryClaudeCli(scope)) {
|
|
2341
|
-
messages.push("Done! Restart Claude Code to use Polter tools.");
|
|
2342
|
-
return { success: true, message: messages.join("\n") };
|
|
2343
|
-
}
|
|
2344
|
-
messages.push("'claude mcp add' failed, falling back to manual install...");
|
|
2345
|
-
}
|
|
2346
|
-
const settingsPath = getSettingsPath(scope);
|
|
2347
|
-
messages.push(`Writing to ${settingsPath}...`);
|
|
2348
|
-
if (tryManualInstall(scope)) {
|
|
2349
|
-
messages.push("Done! Restart Claude Code to use Polter tools.");
|
|
2350
|
-
return { success: true, message: messages.join("\n") };
|
|
2351
|
-
}
|
|
2352
|
-
return { success: false, message: "Failed to install. Try manual installation." };
|
|
2353
|
-
}
|
|
2354
|
-
async function removeMcpServerSilent(scope) {
|
|
2355
|
-
const messages = [];
|
|
2356
|
-
messages.push(`Removing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}`);
|
|
2357
|
-
if (commandExists("claude")) {
|
|
2358
|
-
const result = spawnSync2("claude", ["mcp", "remove", "-s", scope, "polter"], {
|
|
2359
|
-
encoding: "utf-8",
|
|
2360
|
-
shell: true
|
|
2361
|
-
});
|
|
2362
|
-
if (result.status === 0) {
|
|
2363
|
-
messages.push("Done! Polter MCP server removed.");
|
|
2364
|
-
return { success: true, message: messages.join("\n") };
|
|
2365
|
-
}
|
|
2366
|
-
messages.push("'claude mcp remove' failed, falling back to manual removal...");
|
|
2367
|
-
}
|
|
2368
|
-
const settingsPath = getSettingsPath(scope);
|
|
2369
|
-
if (!existsSync8(settingsPath)) {
|
|
2370
|
-
messages.push("No settings file found. Nothing to remove.");
|
|
2371
|
-
return { success: true, message: messages.join("\n") };
|
|
2372
|
-
}
|
|
2373
|
-
let settings;
|
|
2374
|
-
try {
|
|
2375
|
-
settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
|
|
2376
|
-
} catch {
|
|
2377
|
-
return { success: false, message: `Failed to parse ${settingsPath}` };
|
|
2378
|
-
}
|
|
2379
|
-
const mcpServers = settings.mcpServers;
|
|
2380
|
-
if (!mcpServers?.polter) {
|
|
2381
|
-
messages.push("Polter MCP server not found in settings. Nothing to remove.");
|
|
2382
|
-
return { success: true, message: messages.join("\n") };
|
|
2383
|
-
}
|
|
2384
|
-
delete mcpServers.polter;
|
|
2385
|
-
settings.mcpServers = mcpServers;
|
|
2386
|
-
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
2387
|
-
messages.push("Done! Polter MCP server removed.");
|
|
2388
|
-
return { success: true, message: messages.join("\n") };
|
|
2389
|
-
}
|
|
2390
|
-
async function mcpStatus() {
|
|
2391
|
-
process.stdout.write(pc.bold("Polter MCP Server Status\n\n"));
|
|
2392
|
-
const pkgVersion = readPkgVersion();
|
|
2393
|
-
process.stdout.write(` Installed version: ${pc.cyan(pkgVersion)}
|
|
2394
|
-
`);
|
|
2395
|
-
const latestResult = spawnSync2("npm", ["view", "@polterware/polter", "version"], {
|
|
2396
|
-
encoding: "utf-8",
|
|
2397
|
-
shell: true,
|
|
2398
|
-
timeout: 1e4
|
|
2399
|
-
});
|
|
2400
|
-
const latest = latestResult.stdout?.trim();
|
|
2401
|
-
if (latest) {
|
|
2402
|
-
const upToDate = latest === pkgVersion;
|
|
2403
|
-
process.stdout.write(` Latest version: ${upToDate ? pc.green(latest) : pc.yellow(`${latest} (update available)`)}
|
|
2404
|
-
`);
|
|
2405
|
-
}
|
|
2406
|
-
process.stdout.write("\n");
|
|
2407
|
-
const scopes = [
|
|
2408
|
-
{ label: "Project (.mcp.json)", path: join8(process.cwd(), ".mcp.json"), key: "project" },
|
|
2409
|
-
{ label: "User (~/.claude/settings.json)", path: join8(homedir(), ".claude", "settings.json"), key: "user" }
|
|
2410
|
-
];
|
|
2411
|
-
for (const scope of scopes) {
|
|
2412
|
-
if (!existsSync8(scope.path)) {
|
|
2413
|
-
process.stdout.write(` ${scope.label}: ${pc.dim("not found")}
|
|
2414
|
-
`);
|
|
2415
|
-
continue;
|
|
2416
|
-
}
|
|
2417
|
-
try {
|
|
2418
|
-
const settings = JSON.parse(readFileSync4(scope.path, "utf-8"));
|
|
2419
|
-
const mcpServers = settings.mcpServers;
|
|
2420
|
-
if (mcpServers?.polter) {
|
|
2421
|
-
process.stdout.write(` ${scope.label}: ${pc.green("registered")}
|
|
2422
|
-
`);
|
|
2423
|
-
} else {
|
|
2424
|
-
process.stdout.write(` ${scope.label}: ${pc.dim("not registered")}
|
|
2425
|
-
`);
|
|
2426
|
-
}
|
|
2427
|
-
} catch {
|
|
2428
|
-
process.stdout.write(` ${scope.label}: ${pc.red("error reading file")}
|
|
2429
|
-
`);
|
|
2430
|
-
}
|
|
2431
|
-
}
|
|
2432
|
-
}
|
|
2433
|
-
|
|
2434
|
-
// src/lib/skillSetup.ts
|
|
2435
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync9 } from "fs";
|
|
2436
|
-
import { join as join9 } from "path";
|
|
2437
|
-
import { homedir as homedir2 } from "os";
|
|
2438
|
-
import pc2 from "picocolors";
|
|
2439
|
-
var SKILL_DIR = join9(homedir2(), ".claude", "skills", "polter");
|
|
2440
|
-
var SKILL_PATH = join9(SKILL_DIR, "SKILL.md");
|
|
2441
|
-
var SKILL_CONTENT = `---
|
|
2442
|
-
name: polter
|
|
2443
|
-
description: "Polter - Infrastructure Orchestrator. Use when developing in any project to: monitor dev processes and check logs for errors after code changes, manage pipelines (multi-step command sequences), run CLI commands (Supabase, GitHub, Vercel, Git), manage packages (install, build, publish, audit), and apply declarative infrastructure from polter.yaml."
|
|
2444
|
-
---
|
|
2445
|
-
|
|
2446
|
-
# Polter Skill
|
|
2447
|
-
|
|
2448
|
-
You have access to Polter MCP tools for infrastructure orchestration. Use them proactively during development.
|
|
2449
|
-
|
|
2450
|
-
## Process Monitoring
|
|
2451
|
-
|
|
2452
|
-
**Tools:** \`polter_ps\`, \`polter_logs\`, \`polter_start\`, \`polter_stop\`, \`polter_find_process\`, \`polter_smart_start\`, \`polter_run_script_bg\`
|
|
2453
|
-
|
|
2454
|
-
- At the start of a session, run \`polter_ps\` to check for active dev processes
|
|
2455
|
-
- After significant code edits, check \`polter_logs\` for compilation or runtime errors
|
|
2456
|
-
- Use \`polter_find_process\` to find processes running in the current directory
|
|
2457
|
-
- Use \`polter_smart_start\` to start package.json scripts as background processes (e.g. dev servers)
|
|
2458
|
-
- Use \`polter_run_script_bg\` to run arbitrary scripts in the background
|
|
2459
|
-
- Use \`polter_stop\` to stop processes that are no longer needed
|
|
2460
|
-
|
|
2461
|
-
## CLI Commands
|
|
2462
|
-
|
|
2463
|
-
**Tools:** \`polter_list_commands\`, \`polter_run_command\`, \`polter_status\`
|
|
2464
|
-
|
|
2465
|
-
- Execute commands for Supabase, GitHub CLI, Vercel, and Git via their command IDs
|
|
2466
|
-
- Use \`polter_list_commands\` to discover available commands, optionally filtered by tool
|
|
2467
|
-
- Use \`polter_run_command\` to execute a command by its ID with additional args/flags
|
|
2468
|
-
- Check \`polter_status\` to see which CLI tools are installed and their versions
|
|
2469
|
-
|
|
2470
|
-
## Pipelines
|
|
2471
|
-
|
|
2472
|
-
**Tools:** \`polter_list_pipelines\`, \`polter_run_pipeline\`, \`polter_create_pipeline\`, \`polter_update_pipeline\`, \`polter_delete_pipeline\`
|
|
2473
|
-
|
|
2474
|
-
- List and run saved multi-step command sequences with \`polter_list_pipelines\` and \`polter_run_pipeline\`
|
|
2475
|
-
- Create new pipelines for repetitive workflows (e.g. build + test + deploy) with \`polter_create_pipeline\`
|
|
2476
|
-
- Suggest creating pipelines when you notice the user repeating the same sequence of commands
|
|
2477
|
-
|
|
2478
|
-
## Package Management
|
|
2479
|
-
|
|
2480
|
-
**Tools:** \`polter_pkg_build\`, \`polter_pkg_install\`, \`polter_pkg_publish\`, \`polter_pkg_run_script\`, \`polter_pkg_version_bump\`, \`polter_pkg_audit\`, \`polter_pkg_info\`
|
|
2481
|
-
|
|
2482
|
-
- Auto-detects the package manager (npm, pnpm, yarn, bun) from lockfiles
|
|
2483
|
-
- Use \`polter_pkg_build\` for building, \`polter_pkg_install\` for installing dependencies
|
|
2484
|
-
- Use \`polter_pkg_run_script\` to run package.json scripts
|
|
2485
|
-
- Use \`polter_pkg_audit\` to check for vulnerabilities
|
|
2486
|
-
- Use \`polter_pkg_version_bump\` before publishing, then \`polter_pkg_publish\`
|
|
2487
|
-
|
|
2488
|
-
## Declarative Infrastructure
|
|
2489
|
-
|
|
2490
|
-
**Tools:** \`polter_plan\`, \`polter_apply\`
|
|
2491
|
-
|
|
2492
|
-
- Use \`polter_plan\` to read \`polter.yaml\` and compute a diff of desired vs current state
|
|
2493
|
-
- Use \`polter_apply\` to execute the planned infrastructure changes
|
|
2494
|
-
- Always run \`polter_plan\` first to review changes before applying
|
|
2495
|
-
|
|
2496
|
-
## Workflow Recommendations
|
|
2497
|
-
|
|
2498
|
-
1. **Starting a session:** Run \`polter_ps\` to see what's already running
|
|
2499
|
-
2. **After code changes:** Check \`polter_logs\` for errors in dev server output
|
|
2500
|
-
3. **Setting up a project:** Use \`polter_status\` to verify tools, then \`polter_smart_start\` for dev server
|
|
2501
|
-
4. **Deploying:** Create a pipeline with build + test + deploy steps
|
|
2502
|
-
5. **Infrastructure changes:** Edit \`polter.yaml\`, run \`polter_plan\`, then \`polter_apply\`
|
|
2503
|
-
`;
|
|
2504
|
-
function setupSkill() {
|
|
2505
|
-
if (existsSync9(SKILL_PATH)) {
|
|
2506
|
-
const existing = readFileSync5(SKILL_PATH, "utf-8");
|
|
2507
|
-
if (existing === SKILL_CONTENT) {
|
|
2508
|
-
return { status: "already-up-to-date", path: SKILL_PATH };
|
|
2509
|
-
}
|
|
2510
|
-
writeFileSync3(SKILL_PATH, SKILL_CONTENT, "utf-8");
|
|
2511
|
-
return { status: "updated", path: SKILL_PATH };
|
|
2512
|
-
}
|
|
2513
|
-
mkdirSync4(SKILL_DIR, { recursive: true });
|
|
2514
|
-
writeFileSync3(SKILL_PATH, SKILL_CONTENT, "utf-8");
|
|
2515
|
-
return { status: "created", path: SKILL_PATH };
|
|
2516
|
-
}
|
|
2517
|
-
function setupSkillCli() {
|
|
2518
|
-
const result = setupSkill();
|
|
2519
|
-
switch (result.status) {
|
|
2520
|
-
case "created":
|
|
2521
|
-
process.stdout.write(pc2.green(`
|
|
2522
|
-
Skill installed at ${result.path}
|
|
2523
|
-
`));
|
|
2524
|
-
process.stdout.write(pc2.dim(" Use /polter in Claude Code to activate.\n\n"));
|
|
2525
|
-
break;
|
|
2526
|
-
case "updated":
|
|
2527
|
-
process.stdout.write(pc2.green(`
|
|
2528
|
-
Skill updated at ${result.path}
|
|
2529
|
-
`));
|
|
2530
|
-
process.stdout.write(pc2.dim(" Use /polter in Claude Code to activate.\n\n"));
|
|
2531
|
-
break;
|
|
2532
|
-
case "already-up-to-date":
|
|
2533
|
-
process.stdout.write(pc2.cyan(`
|
|
2534
|
-
Skill already up to date at ${result.path}
|
|
2535
|
-
|
|
2536
|
-
`));
|
|
2537
|
-
break;
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
|
-
function getSkillContent() {
|
|
2541
|
-
return SKILL_CONTENT;
|
|
2542
|
-
}
|
|
2543
|
-
function getSkillPath() {
|
|
2544
|
-
return SKILL_PATH;
|
|
2545
|
-
}
|
|
2546
|
-
|
|
2547
|
-
export {
|
|
2548
|
-
__export,
|
|
2549
|
-
allCommands,
|
|
2550
|
-
getCommandById,
|
|
2551
|
-
getCommandsByTool,
|
|
2552
|
-
getCommandValue,
|
|
2553
|
-
findCommandByValue,
|
|
2554
|
-
features,
|
|
2555
|
-
getFeatureById,
|
|
2556
|
-
toolFlags,
|
|
2557
|
-
getFlagsForTool,
|
|
2558
|
-
runCommand,
|
|
2559
|
-
runInteractiveCommand,
|
|
2560
|
-
runSupabaseCommand,
|
|
2561
|
-
commandExists,
|
|
2562
|
-
detectPkgManager,
|
|
2563
|
-
translateCommand,
|
|
2564
|
-
resolvePkgArgs,
|
|
2565
|
-
getToolDisplayName,
|
|
2566
|
-
resolveToolCommand,
|
|
2567
|
-
getToolInfo,
|
|
2568
|
-
getToolLinkInfo,
|
|
2569
|
-
findNearestPackageRoot,
|
|
2570
|
-
generateProcessId,
|
|
2571
|
-
startProcess,
|
|
2572
|
-
registerForegroundProcess,
|
|
2573
|
-
stopProcess,
|
|
2574
|
-
listProcesses,
|
|
2575
|
-
getProcessOutput,
|
|
2576
|
-
isProcessRunning,
|
|
2577
|
-
removeProcess,
|
|
2578
|
-
findProcessesByCwd,
|
|
2579
|
-
findRunningByCommand,
|
|
2580
|
-
getSocketPath,
|
|
2581
|
-
serializeMessage,
|
|
2582
|
-
parseMessages,
|
|
2583
|
-
createIpcServer,
|
|
2584
|
-
executePipeline,
|
|
2585
|
-
getProjectConfigPath,
|
|
2586
|
-
writeProjectConfig,
|
|
2587
|
-
getOrCreateProjectConfig,
|
|
2588
|
-
getAllPipelines,
|
|
2589
|
-
savePipeline,
|
|
2590
|
-
deletePipeline,
|
|
2591
|
-
findPipelineByName,
|
|
2592
|
-
findPolterYaml,
|
|
2593
|
-
parsePolterYaml,
|
|
2594
|
-
getCurrentStatus,
|
|
2595
|
-
planChanges,
|
|
2596
|
-
applyActions,
|
|
2597
|
-
installMcpServer,
|
|
2598
|
-
removeMcpServer,
|
|
2599
|
-
getMcpStatusInfo,
|
|
2600
|
-
installMcpServerSilent,
|
|
2601
|
-
removeMcpServerSilent,
|
|
2602
|
-
mcpStatus,
|
|
2603
|
-
setupSkill,
|
|
2604
|
-
setupSkillCli,
|
|
2605
|
-
getSkillContent,
|
|
2606
|
-
getSkillPath
|
|
2607
|
-
};
|