@rigkit/cli 0.2.7 → 0.2.8
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 +12 -3
- package/package.json +4 -7
- package/src/cli.test.ts +139 -7
- package/src/cli.ts +699 -235
- package/src/completion.test.ts +139 -10
- package/src/completion.ts +427 -72
- package/src/init.ts +6 -4
- package/src/project.test.ts +33 -3
- package/src/project.ts +99 -11
- package/src/run-logger.test.ts +92 -0
- package/src/run-logger.ts +203 -0
- package/src/run-presenter.ts +176 -299
- package/src/ui.ts +159 -0
- package/src/version.ts +1 -1
- package/src/remote-project.test.ts +0 -55
- package/src/remote-project.ts +0 -225
package/src/completion.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { readdirSync } from "node:fs";
|
|
1
2
|
import { dirname, join, resolve } from "node:path";
|
|
2
3
|
import { getOrStartRuntime } from "@rigkit/runtime-client";
|
|
4
|
+
import { isRigConfigFileName } from "./project.ts";
|
|
3
5
|
|
|
4
6
|
export type CompletionShell = "bash" | "fish" | "zsh";
|
|
5
7
|
|
|
@@ -7,6 +9,7 @@ export type CompletionItem = {
|
|
|
7
9
|
value: string;
|
|
8
10
|
description?: string;
|
|
9
11
|
noSpace?: boolean;
|
|
12
|
+
group?: string;
|
|
10
13
|
};
|
|
11
14
|
|
|
12
15
|
type CompleteRigInput = {
|
|
@@ -15,78 +18,121 @@ type CompleteRigInput = {
|
|
|
15
18
|
cwd?: string;
|
|
16
19
|
};
|
|
17
20
|
|
|
18
|
-
const
|
|
21
|
+
const GROUP_COMMANDS = "Commands";
|
|
22
|
+
const GROUP_SUBCOMMANDS = "Subcommands";
|
|
23
|
+
const GROUP_FLAGS = "Flags";
|
|
24
|
+
const GROUP_GLOBAL = "Global flags";
|
|
25
|
+
const GROUP_TARGETS = "Targets";
|
|
26
|
+
const GROUP_WORKSPACES = "Workspaces";
|
|
27
|
+
const GROUP_OPERATIONS = "Operations";
|
|
28
|
+
const GROUP_VALUES = "Values";
|
|
29
|
+
const GROUP_PATHS = "Paths";
|
|
30
|
+
const GROUP_SHELLS = "Shells";
|
|
31
|
+
|
|
32
|
+
const COMMANDS: CompletionItem[] = withGroup(GROUP_COMMANDS, [
|
|
19
33
|
{ value: "help", description: "show CLI help" },
|
|
20
34
|
{ value: "init", description: "initialize a Rigkit project" },
|
|
21
35
|
{ value: "plan", description: "plan project workflow changes" },
|
|
22
36
|
{ value: "apply", description: "apply project workflow changes" },
|
|
23
37
|
{ value: "create", description: "create a workspace" },
|
|
38
|
+
{ value: "rm", description: "remove a workspace" },
|
|
24
39
|
{ value: "run", description: "run a workspace operation" },
|
|
25
40
|
{ value: "ls", description: "list project workspaces" },
|
|
41
|
+
{ value: "cache", description: "inspect and clear Rigkit cache" },
|
|
26
42
|
{ value: "projects", description: "discover Rigkit projects" },
|
|
27
43
|
{ value: "doctor", description: "show runtime diagnostics" },
|
|
28
44
|
{ value: "version", description: "show CLI version" },
|
|
29
45
|
{ value: "completion", description: "generate shell completion" },
|
|
30
|
-
];
|
|
46
|
+
]);
|
|
31
47
|
|
|
32
48
|
const COMMAND_ALIASES = new Map<string, string>();
|
|
33
49
|
|
|
34
|
-
const GLOBAL_OPTIONS: CompletionItem[] = [
|
|
35
|
-
{ value: "-
|
|
36
|
-
{ value: "
|
|
37
|
-
{ value: "
|
|
38
|
-
{ value: "
|
|
39
|
-
{ value: "
|
|
40
|
-
{ value: "
|
|
41
|
-
|
|
42
|
-
];
|
|
50
|
+
const GLOBAL_OPTIONS: CompletionItem[] = withGroup(GROUP_GLOBAL, [
|
|
51
|
+
{ value: "-chdir=", description: "working directory", noSpace: true },
|
|
52
|
+
{ value: "-config=", description: "config file", noSpace: true },
|
|
53
|
+
{ value: "-state=", description: "state database path", noSpace: true },
|
|
54
|
+
{ value: "-json", description: "print JSON" },
|
|
55
|
+
{ value: "-help", description: "show help" },
|
|
56
|
+
{ value: "-version", description: "show version" },
|
|
57
|
+
]);
|
|
43
58
|
|
|
44
59
|
const COMMAND_OPTIONS: Record<string, CompletionItem[]> = {
|
|
45
|
-
init: [
|
|
60
|
+
init: withGroup(GROUP_FLAGS, [
|
|
46
61
|
{ value: "--name", description: "project and workflow name" },
|
|
47
62
|
{ value: "--api-key", description: "Freestyle API key" },
|
|
48
63
|
{ value: "--package-manager", description: "npm, bun, pnpm, or skip" },
|
|
49
64
|
{ value: "--force", description: "overwrite existing config" },
|
|
50
65
|
{ value: "--json", description: "print JSON" },
|
|
51
|
-
],
|
|
52
|
-
plan: [
|
|
66
|
+
]),
|
|
67
|
+
plan: withGroup(GROUP_FLAGS, [
|
|
53
68
|
{ value: "--all", description: "run against every discovered project" },
|
|
54
69
|
{ value: "--discover", description: "discover projects below the selected directory" },
|
|
55
70
|
{ value: "--json", description: "print JSON" },
|
|
56
|
-
],
|
|
57
|
-
apply: [
|
|
71
|
+
]),
|
|
72
|
+
apply: withGroup(GROUP_FLAGS, [
|
|
58
73
|
{ value: "--all", description: "run against every discovered project" },
|
|
59
74
|
{ value: "--discover", description: "discover projects below the selected directory" },
|
|
60
75
|
{ value: "--json", description: "print JSON" },
|
|
61
|
-
],
|
|
62
|
-
create: [
|
|
76
|
+
]),
|
|
77
|
+
create: withGroup(GROUP_FLAGS, [
|
|
63
78
|
{ value: "--json", description: "print JSON" },
|
|
64
|
-
],
|
|
65
|
-
|
|
79
|
+
]),
|
|
80
|
+
rm: withGroup(GROUP_FLAGS, [
|
|
81
|
+
{ value: "-y", description: "skip confirmation" },
|
|
82
|
+
{ value: "--yes", description: "skip confirmation" },
|
|
66
83
|
{ value: "--json", description: "print JSON" },
|
|
67
|
-
],
|
|
68
|
-
|
|
69
|
-
{ value: "workspaces", description: "list workspaces" },
|
|
70
|
-
{ value: "snapshots", description: "list snapshots" },
|
|
71
|
-
{ value: "config", description: "show project config" },
|
|
84
|
+
]),
|
|
85
|
+
run: withGroup(GROUP_FLAGS, [
|
|
72
86
|
{ value: "--json", description: "print JSON" },
|
|
87
|
+
]),
|
|
88
|
+
ls: [
|
|
89
|
+
...withGroup(GROUP_TARGETS, [
|
|
90
|
+
{ value: "workspaces", description: "list workspaces" },
|
|
91
|
+
{ value: "snapshots", description: "list snapshots" },
|
|
92
|
+
{ value: "config", description: "show project config" },
|
|
93
|
+
]),
|
|
94
|
+
...withGroup(GROUP_FLAGS, [
|
|
95
|
+
{ value: "--json", description: "print JSON" },
|
|
96
|
+
]),
|
|
73
97
|
],
|
|
74
|
-
projects: [
|
|
98
|
+
projects: withGroup(GROUP_FLAGS, [
|
|
75
99
|
{ value: "--json", description: "print JSON" },
|
|
76
|
-
],
|
|
77
|
-
|
|
100
|
+
]),
|
|
101
|
+
cache: withGroup(GROUP_SUBCOMMANDS, [
|
|
102
|
+
{ value: "ls", description: "list cache entries" },
|
|
103
|
+
{ value: "clear", description: "clear cache entries" },
|
|
104
|
+
]),
|
|
105
|
+
completion: withGroup(GROUP_SHELLS, [
|
|
78
106
|
{ value: "bash", description: "Bash completion" },
|
|
79
107
|
{ value: "fish", description: "fish completion" },
|
|
80
108
|
{ value: "zsh", description: "zsh completion" },
|
|
81
|
-
],
|
|
109
|
+
]),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const CACHE_SUBCOMMAND_OPTIONS: Record<string, CompletionItem[]> = {
|
|
113
|
+
ls: withGroup(GROUP_FLAGS, [
|
|
114
|
+
{ value: "--json", description: "print JSON" },
|
|
115
|
+
]),
|
|
116
|
+
clear: withGroup(GROUP_FLAGS, [
|
|
117
|
+
{ value: "--local", description: "clear local cache entries" },
|
|
118
|
+
{ value: "--global", description: "clear global cache fragments" },
|
|
119
|
+
{ value: "--all", description: "clear every global fragment" },
|
|
120
|
+
{ value: "--json", description: "print JSON" },
|
|
121
|
+
]),
|
|
82
122
|
};
|
|
83
123
|
|
|
124
|
+
function withGroup(group: string, items: Omit<CompletionItem, "group">[]): CompletionItem[] {
|
|
125
|
+
return items.map((item) => ({ ...item, group }));
|
|
126
|
+
}
|
|
127
|
+
|
|
84
128
|
const PROJECT_OPERATION_COMMANDS = new Set(["plan", "apply", "create"]);
|
|
85
129
|
|
|
86
130
|
const OPTIONS_WITH_VALUES = new Set([
|
|
87
|
-
"-
|
|
88
|
-
"--
|
|
131
|
+
"-chdir",
|
|
132
|
+
"--chdir",
|
|
133
|
+
"-config",
|
|
89
134
|
"--config",
|
|
135
|
+
"-state",
|
|
90
136
|
"--state",
|
|
91
137
|
"--name",
|
|
92
138
|
"--api-key",
|
|
@@ -123,7 +169,26 @@ export async function completeRig(input: CompleteRigInput): Promise<CompletionIt
|
|
|
123
169
|
const before = words.slice(1, currentIndex);
|
|
124
170
|
const command = findCommand(before);
|
|
125
171
|
|
|
126
|
-
|
|
172
|
+
const inlineOption = parseInlineValueOption(current);
|
|
173
|
+
if (inlineOption) {
|
|
174
|
+
return await completeOptionValue({
|
|
175
|
+
option: inlineOption.option,
|
|
176
|
+
current: inlineOption.value,
|
|
177
|
+
cwd,
|
|
178
|
+
words,
|
|
179
|
+
inlinePrefix: inlineOption.prefix,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const valueOption = optionExpectingValue(before);
|
|
184
|
+
if (valueOption) {
|
|
185
|
+
return await completeOptionValue({
|
|
186
|
+
option: valueOption,
|
|
187
|
+
current,
|
|
188
|
+
cwd,
|
|
189
|
+
words,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
127
192
|
|
|
128
193
|
if (!command) {
|
|
129
194
|
return filterItems(
|
|
@@ -135,14 +200,28 @@ export async function completeRig(input: CompleteRigInput): Promise<CompletionIt
|
|
|
135
200
|
}
|
|
136
201
|
|
|
137
202
|
if (current.startsWith("-")) {
|
|
203
|
+
if (command === "rm") {
|
|
204
|
+
const remove = parseRemoveCommand(before);
|
|
205
|
+
if (remove.workspace) {
|
|
206
|
+
const operation = await safeResolveWorkspaceOperation(resolveProjectDir(words, cwd), "remove");
|
|
207
|
+
return filterItems([
|
|
208
|
+
...(operation?.cli?.options ?? []).flatMap((option) => [
|
|
209
|
+
{ value: option.flag, description: option.name, group: GROUP_FLAGS },
|
|
210
|
+
...(option.aliases ?? []).map((alias) => ({ value: alias, description: option.name, group: GROUP_FLAGS })),
|
|
211
|
+
]),
|
|
212
|
+
...COMMAND_OPTIONS.rm,
|
|
213
|
+
...GLOBAL_OPTIONS,
|
|
214
|
+
], current);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
138
217
|
if (command === "run") {
|
|
139
218
|
const run = parseWorkspaceRunCommand(before);
|
|
140
219
|
if (run.workspace && run.operation) {
|
|
141
220
|
const operation = await safeResolveWorkspaceOperation(resolveProjectDir(words, cwd), run.operation);
|
|
142
221
|
return filterItems([
|
|
143
222
|
...(operation?.cli?.options ?? []).flatMap((option) => [
|
|
144
|
-
{ value: option.flag, description: option.name },
|
|
145
|
-
...(option.aliases ?? []).map((alias) => ({ value: alias, description: option.name })),
|
|
223
|
+
{ value: option.flag, description: option.name, group: GROUP_FLAGS },
|
|
224
|
+
...(option.aliases ?? []).map((alias) => ({ value: alias, description: option.name, group: GROUP_FLAGS })),
|
|
146
225
|
]),
|
|
147
226
|
...COMMAND_OPTIONS.run,
|
|
148
227
|
...GLOBAL_OPTIONS,
|
|
@@ -160,6 +239,12 @@ export async function completeRig(input: CompleteRigInput): Promise<CompletionIt
|
|
|
160
239
|
...GLOBAL_OPTIONS,
|
|
161
240
|
], current);
|
|
162
241
|
}
|
|
242
|
+
if (command === "cache") {
|
|
243
|
+
return filterItems([
|
|
244
|
+
...cacheOptionTargets(before),
|
|
245
|
+
...GLOBAL_OPTIONS,
|
|
246
|
+
], current);
|
|
247
|
+
}
|
|
163
248
|
return filterItems([...(COMMAND_OPTIONS[command] ?? []), ...GLOBAL_OPTIONS], current);
|
|
164
249
|
}
|
|
165
250
|
|
|
@@ -171,6 +256,11 @@ export async function completeRig(input: CompleteRigInput): Promise<CompletionIt
|
|
|
171
256
|
if (!run.operation) return filterItems(await safeWorkspaceOperationTargets(resolveProjectDir(words, cwd)), current);
|
|
172
257
|
}
|
|
173
258
|
|
|
259
|
+
if (command === "rm") {
|
|
260
|
+
const remove = parseRemoveCommand(before);
|
|
261
|
+
if (!remove.workspace) return filterItems(await workspaceTargets(resolveProjectDir(words, cwd)), current);
|
|
262
|
+
}
|
|
263
|
+
|
|
174
264
|
if (command === "completion" && positionalCount === 0) {
|
|
175
265
|
return filterItems(COMMAND_OPTIONS.completion, current);
|
|
176
266
|
}
|
|
@@ -179,15 +269,25 @@ export async function completeRig(input: CompleteRigInput): Promise<CompletionIt
|
|
|
179
269
|
return filterItems(COMMAND_OPTIONS.ls, current);
|
|
180
270
|
}
|
|
181
271
|
|
|
272
|
+
if (command === "cache") {
|
|
273
|
+
const cache = parseCacheCommand(before);
|
|
274
|
+
if (!cache.subcommand) return filterItems(COMMAND_OPTIONS.cache ?? [], current);
|
|
275
|
+
return filterItems(cacheOptionTargets(before), current);
|
|
276
|
+
}
|
|
277
|
+
|
|
182
278
|
return [];
|
|
183
279
|
}
|
|
184
280
|
|
|
185
281
|
export function formatCompletionItems(items: CompletionItem[], shell: CompletionShell): string {
|
|
186
282
|
const lines = items.map((item) => {
|
|
187
283
|
if (shell === "bash") return item.value;
|
|
188
|
-
if (shell === "zsh"
|
|
189
|
-
|
|
284
|
+
if (shell === "zsh") {
|
|
285
|
+
const description = item.description ?? "";
|
|
286
|
+
const marker = item.noSpace ? "nospace" : "";
|
|
287
|
+
const group = item.group ?? "";
|
|
288
|
+
return `${item.value}\t${description}\t${marker}\t${group}`;
|
|
190
289
|
}
|
|
290
|
+
// fish: legacy two-column format works fine; descriptions render dim by default
|
|
191
291
|
return item.description ? `${item.value}\t${item.description}` : item.value;
|
|
192
292
|
});
|
|
193
293
|
return lines.join("\n");
|
|
@@ -228,37 +328,55 @@ complete -c rig -f -a "(__rig_complete)"
|
|
|
228
328
|
}
|
|
229
329
|
|
|
230
330
|
return `#compdef rig
|
|
231
|
-
# rig zsh completion
|
|
331
|
+
# rig zsh completion — auto-generated by \`rig completion zsh\`.
|
|
332
|
+
# Visual defaults are scoped to :completion:*:rig:* so they don't override your
|
|
333
|
+
# global completion theme. Group headers render bold blue; descriptions inherit
|
|
334
|
+
# your usual style.
|
|
335
|
+
|
|
336
|
+
() {
|
|
337
|
+
zstyle ':completion:*:rig:*:descriptions' format $'\\e[1;34m%d\\e[0m'
|
|
338
|
+
zstyle ':completion:*:rig:*' group-name ''
|
|
339
|
+
zstyle ':completion:*:rig:*' verbose true
|
|
340
|
+
}
|
|
341
|
+
|
|
232
342
|
_rig() {
|
|
233
|
-
local
|
|
234
|
-
local
|
|
343
|
+
local raw line value description marker group key tag
|
|
344
|
+
local -A bucket_specs bucket_groups bucket_data
|
|
235
345
|
raw=("\${(@f)$(command rig __complete --shell zsh --index $((CURRENT - 1)) -- "\${words[@]}" 2>/dev/null)}")
|
|
346
|
+
|
|
236
347
|
for line in "\${raw[@]}"; do
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
348
|
+
[[ -z "$line" ]] && continue
|
|
349
|
+
local -a parts
|
|
350
|
+
parts=("\${(@s: :)line}")
|
|
351
|
+
value="\${parts[1]}"
|
|
352
|
+
description="\${parts[2]:-}"
|
|
353
|
+
marker="\${parts[3]:-}"
|
|
354
|
+
group="\${parts[4]:-}"
|
|
355
|
+
[[ -z "$group" ]] && group="rig"
|
|
356
|
+
key="\${group}|\${marker}"
|
|
357
|
+
bucket_groups[$key]="$group"
|
|
358
|
+
bucket_specs[$key]="$marker"
|
|
248
359
|
if [[ -n "$description" ]]; then
|
|
249
|
-
|
|
360
|
+
bucket_data[$key]+="\${value}:\${description}"$'\\n'
|
|
361
|
+
else
|
|
362
|
+
bucket_data[$key]+="\${value}"$'\\n'
|
|
250
363
|
fi
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
364
|
+
done
|
|
365
|
+
|
|
366
|
+
for key in "\${(@k)bucket_data}"; do
|
|
367
|
+
local -a matches
|
|
368
|
+
matches=("\${(@f)bucket_data[$key]}")
|
|
369
|
+
matches=("\${(@)matches:#}")
|
|
370
|
+
tag="\${bucket_groups[$key]//[^A-Za-z0-9]/_}"
|
|
371
|
+
[[ -z "$tag" ]] && tag="rig"
|
|
372
|
+
if [[ "\${bucket_specs[$key]}" == "nospace" ]]; then
|
|
373
|
+
_describe -t "$tag" "\${bucket_groups[$key]}" matches -S ''
|
|
254
374
|
else
|
|
255
|
-
|
|
256
|
-
displays+=("\${display}")
|
|
375
|
+
_describe -t "$tag" "\${bucket_groups[$key]}" matches
|
|
257
376
|
fi
|
|
258
377
|
done
|
|
259
|
-
(( \${#nospace_values} )) && compadd -S '' -ld nospace_displays -a nospace_values
|
|
260
|
-
(( \${#values} )) && compadd -ld displays -a values
|
|
261
378
|
}
|
|
379
|
+
|
|
262
380
|
compdef _rig rig
|
|
263
381
|
`;
|
|
264
382
|
}
|
|
@@ -323,38 +441,269 @@ function parseWorkspaceRunCommand(words: string[]): { workspace?: string; operat
|
|
|
323
441
|
return { workspace: args[0], operation: args[1], args: args.slice(2) };
|
|
324
442
|
}
|
|
325
443
|
|
|
326
|
-
function
|
|
444
|
+
function parseRemoveCommand(words: string[]): { workspace?: string; args: string[] } {
|
|
445
|
+
let foundRemove = false;
|
|
446
|
+
const args: string[] = [];
|
|
447
|
+
for (let index = 0; index < words.length; index += 1) {
|
|
448
|
+
const word = words[index]!;
|
|
449
|
+
if (OPTIONS_WITH_VALUES.has(word)) {
|
|
450
|
+
index += 1;
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (word.includes("=") && OPTIONS_WITH_VALUES.has(word.slice(0, word.indexOf("=")))) continue;
|
|
454
|
+
if (word.startsWith("-")) continue;
|
|
455
|
+
if (!foundRemove) {
|
|
456
|
+
if (word === "rm") foundRemove = true;
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
args.push(word);
|
|
460
|
+
}
|
|
461
|
+
return { workspace: args[0], args: args.slice(1) };
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function parseCacheCommand(words: string[]): { subcommand?: string; args: string[] } {
|
|
465
|
+
let foundCache = false;
|
|
466
|
+
const args: string[] = [];
|
|
467
|
+
for (let index = 0; index < words.length; index += 1) {
|
|
468
|
+
const word = words[index]!;
|
|
469
|
+
if (OPTIONS_WITH_VALUES.has(word)) {
|
|
470
|
+
index += 1;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
if (word.startsWith("--") && word.includes("=")) continue;
|
|
474
|
+
if (word.startsWith("-")) continue;
|
|
475
|
+
if (!foundCache) {
|
|
476
|
+
if (word === "cache") foundCache = true;
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
args.push(word);
|
|
480
|
+
}
|
|
481
|
+
return { subcommand: args[0], args: args.slice(1) };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function cacheOptionTargets(words: string[]): CompletionItem[] {
|
|
485
|
+
const subcommand = parseCacheCommand(words).subcommand;
|
|
486
|
+
return subcommand ? CACHE_SUBCOMMAND_OPTIONS[subcommand] ?? [] : [];
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function optionExpectingValue(words: string[]): string | undefined {
|
|
327
490
|
const previous = words.at(-1);
|
|
328
|
-
return
|
|
491
|
+
return previous && OPTIONS_WITH_VALUES.has(previous) ? previous : undefined;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async function completeOptionValue(input: {
|
|
495
|
+
option: string;
|
|
496
|
+
current: string;
|
|
497
|
+
cwd: string;
|
|
498
|
+
words: string[];
|
|
499
|
+
inlinePrefix?: string;
|
|
500
|
+
}): Promise<CompletionItem[]> {
|
|
501
|
+
let items: CompletionItem[];
|
|
502
|
+
switch (input.option) {
|
|
503
|
+
case "-chdir":
|
|
504
|
+
case "--chdir":
|
|
505
|
+
items = completeDirectories(input.cwd, input.current);
|
|
506
|
+
break;
|
|
507
|
+
case "-config":
|
|
508
|
+
case "--config":
|
|
509
|
+
items = completeConfigPaths(projectBaseDir(input.words, input.cwd), input.current);
|
|
510
|
+
break;
|
|
511
|
+
case "--package-manager":
|
|
512
|
+
items = filterItems([
|
|
513
|
+
{ value: "npm", group: GROUP_VALUES },
|
|
514
|
+
{ value: "bun", group: GROUP_VALUES },
|
|
515
|
+
{ value: "pnpm", group: GROUP_VALUES },
|
|
516
|
+
{ value: "skip", group: GROUP_VALUES },
|
|
517
|
+
], input.current);
|
|
518
|
+
break;
|
|
519
|
+
case "-state":
|
|
520
|
+
case "--state":
|
|
521
|
+
items = completeFilesystemPaths(input.cwd, input.current);
|
|
522
|
+
break;
|
|
523
|
+
default:
|
|
524
|
+
items = [];
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (!input.inlinePrefix) return items;
|
|
528
|
+
return items.map((item) => ({
|
|
529
|
+
...item,
|
|
530
|
+
value: `${input.inlinePrefix}${item.value}`,
|
|
531
|
+
}));
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function parseInlineValueOption(current: string): { option: string; value: string; prefix: string } | undefined {
|
|
535
|
+
const index = current.indexOf("=");
|
|
536
|
+
if (index < 0) return undefined;
|
|
537
|
+
const option = current.slice(0, index);
|
|
538
|
+
if (!OPTIONS_WITH_VALUES.has(option)) return undefined;
|
|
539
|
+
return {
|
|
540
|
+
option,
|
|
541
|
+
value: current.slice(index + 1),
|
|
542
|
+
prefix: current.slice(0, index + 1),
|
|
543
|
+
};
|
|
329
544
|
}
|
|
330
545
|
|
|
331
546
|
function resolveProjectDir(words: string[], cwd: string): { projectDir: string; configPath: string } {
|
|
547
|
+
let chdir: string | undefined;
|
|
548
|
+
let config: string | undefined;
|
|
332
549
|
for (let index = 0; index < words.length; index += 1) {
|
|
333
550
|
const word = words[index]!;
|
|
334
|
-
if (word === "-
|
|
335
|
-
|
|
336
|
-
|
|
551
|
+
if (word === "-chdir" || word === "--chdir") {
|
|
552
|
+
chdir = words[index + 1];
|
|
553
|
+
index += 1;
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (word.startsWith("-chdir=")) {
|
|
557
|
+
chdir = word.slice("-chdir=".length);
|
|
558
|
+
continue;
|
|
337
559
|
}
|
|
338
|
-
if (word.startsWith("--
|
|
339
|
-
|
|
560
|
+
if (word.startsWith("--chdir=")) {
|
|
561
|
+
chdir = word.slice("--chdir=".length);
|
|
562
|
+
continue;
|
|
340
563
|
}
|
|
341
|
-
if (word === "--config") {
|
|
342
|
-
|
|
343
|
-
|
|
564
|
+
if (word === "-config" || word === "--config") {
|
|
565
|
+
config = words[index + 1];
|
|
566
|
+
index += 1;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (word.startsWith("-config=")) {
|
|
570
|
+
config = word.slice("-config=".length);
|
|
571
|
+
continue;
|
|
344
572
|
}
|
|
345
573
|
if (word.startsWith("--config=")) {
|
|
346
|
-
|
|
347
|
-
|
|
574
|
+
config = word.slice("--config=".length);
|
|
575
|
+
continue;
|
|
348
576
|
}
|
|
349
577
|
}
|
|
350
578
|
|
|
351
|
-
|
|
579
|
+
const baseDir = resolve(cwd, chdir ?? ".");
|
|
580
|
+
if (config) {
|
|
581
|
+
const configPath = resolve(baseDir, config);
|
|
582
|
+
return { projectDir: dirname(configPath), configPath };
|
|
583
|
+
}
|
|
584
|
+
return projectPaths(baseDir);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function projectBaseDir(words: string[], cwd: string): string {
|
|
588
|
+
for (let index = 0; index < words.length; index += 1) {
|
|
589
|
+
const word = words[index]!;
|
|
590
|
+
if (word === "-chdir" || word === "--chdir") {
|
|
591
|
+
const value = words[index + 1];
|
|
592
|
+
if (value) return resolve(cwd, value);
|
|
593
|
+
}
|
|
594
|
+
if (word.startsWith("-chdir=")) {
|
|
595
|
+
return resolve(cwd, word.slice("-chdir=".length));
|
|
596
|
+
}
|
|
597
|
+
if (word.startsWith("--chdir=")) {
|
|
598
|
+
return resolve(cwd, word.slice("--chdir=".length));
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return cwd;
|
|
352
602
|
}
|
|
353
603
|
|
|
354
604
|
function projectPaths(projectDir: string): { projectDir: string; configPath: string } {
|
|
355
605
|
return { projectDir, configPath: join(projectDir, "rig.config.ts") };
|
|
356
606
|
}
|
|
357
607
|
|
|
608
|
+
function completeDirectories(baseDir: string, current: string): CompletionItem[] {
|
|
609
|
+
return completePathEntries(baseDir, current, {
|
|
610
|
+
includeFiles: false,
|
|
611
|
+
includeDirectories: true,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function completeConfigPaths(baseDir: string, current: string): CompletionItem[] {
|
|
616
|
+
return completePathEntries(baseDir, current, {
|
|
617
|
+
includeFiles: true,
|
|
618
|
+
includeDirectories: true,
|
|
619
|
+
fileFilter: isRigConfigFileName,
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function completeFilesystemPaths(baseDir: string, current: string): CompletionItem[] {
|
|
624
|
+
return completePathEntries(baseDir, current, {
|
|
625
|
+
includeFiles: true,
|
|
626
|
+
includeDirectories: true,
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function completePathEntries(
|
|
631
|
+
baseDir: string,
|
|
632
|
+
current: string,
|
|
633
|
+
options: {
|
|
634
|
+
includeFiles: boolean;
|
|
635
|
+
includeDirectories: boolean;
|
|
636
|
+
fileFilter?: (name: string) => boolean;
|
|
637
|
+
},
|
|
638
|
+
): CompletionItem[] {
|
|
639
|
+
const { dirPart, namePrefix, dir } = splitCompletionPath(baseDir, current);
|
|
640
|
+
let entries;
|
|
641
|
+
try {
|
|
642
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
643
|
+
} catch {
|
|
644
|
+
return [];
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const items = entries
|
|
648
|
+
.filter((entry) => entry.name.startsWith(namePrefix))
|
|
649
|
+
.flatMap((entry): CompletionItem[] => {
|
|
650
|
+
if (entry.isDirectory()) {
|
|
651
|
+
if (!options.includeDirectories) return [];
|
|
652
|
+
if (shouldSkipCompletionDirectory(entry.name, namePrefix)) return [];
|
|
653
|
+
return [{
|
|
654
|
+
value: `${dirPart}${entry.name}/`,
|
|
655
|
+
description: "directory",
|
|
656
|
+
noSpace: true,
|
|
657
|
+
group: GROUP_PATHS,
|
|
658
|
+
}];
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (!entry.isFile() || !options.includeFiles) return [];
|
|
662
|
+
if (options.fileFilter && !options.fileFilter(entry.name)) return [];
|
|
663
|
+
return [{
|
|
664
|
+
value: `${dirPart}${entry.name}`,
|
|
665
|
+
description: "config",
|
|
666
|
+
group: GROUP_PATHS,
|
|
667
|
+
}];
|
|
668
|
+
})
|
|
669
|
+
.sort((left, right) => {
|
|
670
|
+
if (left.noSpace && !right.noSpace) return -1;
|
|
671
|
+
if (!left.noSpace && right.noSpace) return 1;
|
|
672
|
+
return left.value.localeCompare(right.value);
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
return dedupeItems(items);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function shouldSkipCompletionDirectory(name: string, namePrefix: string): boolean {
|
|
679
|
+
if (
|
|
680
|
+
name === ".git" ||
|
|
681
|
+
name === ".rigkit" ||
|
|
682
|
+
name === ".turbo" ||
|
|
683
|
+
name === "node_modules" ||
|
|
684
|
+
name === "dist" ||
|
|
685
|
+
name === "build"
|
|
686
|
+
) {
|
|
687
|
+
return true;
|
|
688
|
+
}
|
|
689
|
+
return name.startsWith(".") && !namePrefix.startsWith(".");
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function splitCompletionPath(baseDir: string, current: string): {
|
|
693
|
+
dirPart: string;
|
|
694
|
+
namePrefix: string;
|
|
695
|
+
dir: string;
|
|
696
|
+
} {
|
|
697
|
+
const slashIndex = current.lastIndexOf("/");
|
|
698
|
+
const dirPart = slashIndex >= 0 ? current.slice(0, slashIndex + 1) : "";
|
|
699
|
+
const namePrefix = slashIndex >= 0 ? current.slice(slashIndex + 1) : current;
|
|
700
|
+
return {
|
|
701
|
+
dirPart,
|
|
702
|
+
namePrefix,
|
|
703
|
+
dir: resolve(baseDir, dirPart || "."),
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
358
707
|
async function workspaceTargets(
|
|
359
708
|
paths: { projectDir: string; configPath: string },
|
|
360
709
|
): Promise<CompletionItem[]> {
|
|
@@ -362,6 +711,7 @@ async function workspaceTargets(
|
|
|
362
711
|
const items = workspaces.map((workspace) => ({
|
|
363
712
|
value: workspace.name,
|
|
364
713
|
description: workspaceDescription(workspace),
|
|
714
|
+
group: GROUP_WORKSPACES,
|
|
365
715
|
}));
|
|
366
716
|
|
|
367
717
|
return dedupeItems(items);
|
|
@@ -391,10 +741,15 @@ async function safeWorkspaceOperationTargets(
|
|
|
391
741
|
|
|
392
742
|
function workspaceOperationTargets(manifest: RuntimeOperationManifest): CompletionItem[] {
|
|
393
743
|
return (manifest.workspaceOperations ?? []).flatMap((operation) => [
|
|
394
|
-
{
|
|
744
|
+
{
|
|
745
|
+
value: operation.id,
|
|
746
|
+
description: operation.description ?? "workspace operation",
|
|
747
|
+
group: GROUP_OPERATIONS,
|
|
748
|
+
},
|
|
395
749
|
...(operation.aliases ?? []).map((alias) => ({
|
|
396
750
|
value: alias,
|
|
397
751
|
description: operation.description ?? "workspace operation",
|
|
752
|
+
group: GROUP_OPERATIONS,
|
|
398
753
|
})),
|
|
399
754
|
]);
|
|
400
755
|
}
|
package/src/init.ts
CHANGED
|
@@ -113,10 +113,11 @@ const vmSpec = new VmSpec()
|
|
|
113
113
|
const freestyleProvider = freestyle.provider();
|
|
114
114
|
|
|
115
115
|
const dev = sequence(${workflowName})
|
|
116
|
-
.step("verify-node-22", async ({ providers
|
|
116
|
+
.step("verify-node-22", async ({ providers }) => {
|
|
117
|
+
console.log("creating verification vm");
|
|
117
118
|
const { vm, vmId } = await providers.freestyle.client.vms.create({
|
|
118
119
|
spec: vmSpec,
|
|
119
|
-
logger:
|
|
120
|
+
logger: console.log,
|
|
120
121
|
});
|
|
121
122
|
try {
|
|
122
123
|
const result = await vm.exec("node --version");
|
|
@@ -130,11 +131,12 @@ const dev = sequence(${workflowName})
|
|
|
130
131
|
}
|
|
131
132
|
})
|
|
132
133
|
.workspace({
|
|
133
|
-
create: async ({ workflow, providers
|
|
134
|
+
create: async ({ workflow, providers }) => {
|
|
135
|
+
console.log("booting workspace vm");
|
|
134
136
|
const { vmId } = await providers.freestyle.client.vms.create({
|
|
135
137
|
snapshotId: workflow.ctx.snapshotId,
|
|
136
138
|
idleTimeoutSeconds: vmIdleTimeoutSeconds,
|
|
137
|
-
logger:
|
|
139
|
+
logger: console.log,
|
|
138
140
|
});
|
|
139
141
|
return {
|
|
140
142
|
vmId,
|