@rigkit/cli 0.2.9 → 0.2.11
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/LICENSE +21 -0
- package/README.md +8 -5
- package/package.json +5 -4
- package/src/cli.test.ts +156 -2
- package/src/cli.ts +110 -15
- package/src/completion.test.ts +337 -9
- package/src/completion.ts +900 -337
- package/src/project.test.ts +3 -3
- package/src/project.ts +3 -3
- package/src/update-check.ts +224 -0
- package/src/version.ts +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rigkit contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ npm i -g @rigkit/cli
|
|
|
7
7
|
rig init
|
|
8
8
|
rig plan
|
|
9
9
|
rig ls
|
|
10
|
-
rig
|
|
10
|
+
rig --chdir=examples/smoke plan
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
`rig init` asks for a project name, Freestyle API key, and package manager. It creates a project folder containing a workflow-based `rig.config.ts`, `.env`, `.env.example`, `package.json`, and local ignore rules.
|
|
@@ -23,12 +23,15 @@ defined by the project run as `rig run <workspace> <operation>`, for example
|
|
|
23
23
|
|
|
24
24
|
`rig ls` lists workspaces for the selected project. `rig ls snapshots` lists cached snapshot runs, and `rig ls config` shows the resolved project paths.
|
|
25
25
|
|
|
26
|
-
Use
|
|
27
|
-
|
|
26
|
+
Use global context options before the command to select another project or
|
|
27
|
+
config:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
rig
|
|
31
|
-
rig
|
|
30
|
+
rig --chdir=examples/smoke plan
|
|
31
|
+
rig --chdir=examples/global-fragments --config=api.rig.config.ts apply
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
Legacy Terraform-style aliases such as `-chdir=...` and `-config=...` are still
|
|
35
|
+
accepted.
|
|
36
|
+
|
|
34
37
|
Projects should install matching `@rigkit/sdk` versions locally.
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigkit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
|
+
"license": "MIT",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
@@ -22,9 +23,9 @@
|
|
|
22
23
|
"chalk": "^5.6.2",
|
|
23
24
|
"commander": "^14.0.3",
|
|
24
25
|
"inquirer": "^13.4.3",
|
|
25
|
-
"@rigkit/
|
|
26
|
-
"@rigkit/
|
|
27
|
-
"@rigkit/
|
|
26
|
+
"@rigkit/runtime-client": "0.2.11",
|
|
27
|
+
"@rigkit/engine": "0.2.11",
|
|
28
|
+
"@rigkit/provider-cmux": "0.2.11"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@types/bun": "latest",
|
package/src/cli.test.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
import { existsSync, mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { createFileProviderHostStorage } from "@rigkit/engine";
|
|
5
6
|
import { projectIdFor, runtimeFingerprintFor, runtimePaths, SUPPORTED_RUNTIME_API_VERSION } from "@rigkit/runtime-client";
|
|
6
7
|
import { RIGKIT_CLI_VERSION } from "./version.ts";
|
|
7
8
|
|
|
@@ -27,6 +28,7 @@ describe("CLI entrypoint", () => {
|
|
|
27
28
|
expect(rootHelp.stdout).toContain("rm Remove a workspace");
|
|
28
29
|
expect(rootHelp.stdout).toContain("run Run a workspace operation");
|
|
29
30
|
expect(rootHelp.stdout).toContain("cache Inspect and clear Rigkit cache");
|
|
31
|
+
expect(rootHelp.stdout).toContain("providers Manage provider-owned local state");
|
|
30
32
|
|
|
31
33
|
const version = await runCli(["version"]);
|
|
32
34
|
expect(version.exitCode).toBe(0);
|
|
@@ -41,6 +43,77 @@ describe("CLI entrypoint", () => {
|
|
|
41
43
|
expect(help.stdout).toContain("rm Remove a workspace");
|
|
42
44
|
expect(help.stdout).toContain("run Run a workspace operation");
|
|
43
45
|
expect(help.stdout).toContain("cache Inspect and clear Rigkit cache");
|
|
46
|
+
expect(help.stdout).toContain("providers Manage provider-owned local state");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("prints an update notice when latest metadata is newer", async () => {
|
|
50
|
+
const rigkitHome = mkdtempSync(join(tmpdir(), "rigkit-cli-update-"));
|
|
51
|
+
const latestVersion = nextPatchVersion(RIGKIT_CLI_VERSION);
|
|
52
|
+
const server = Bun.serve({
|
|
53
|
+
hostname: "127.0.0.1",
|
|
54
|
+
port: 0,
|
|
55
|
+
fetch() {
|
|
56
|
+
return Response.json({
|
|
57
|
+
version: latestVersion,
|
|
58
|
+
installerUrl: "https://www.rigkit.dev/install",
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const result = await runCli(["doctor", "--cli"], {
|
|
65
|
+
env: {
|
|
66
|
+
RIGKIT_HOME: rigkitHome,
|
|
67
|
+
RIGKIT_UPDATE_CHECK: "1",
|
|
68
|
+
RIGKIT_UPDATE_TIMEOUT_MS: "2000",
|
|
69
|
+
RIGKIT_UPDATE_URL: `http://127.0.0.1:${server.port}/latest.json`,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(result.exitCode).toBe(0);
|
|
74
|
+
expect(result.stdout).toContain(RIGKIT_CLI_VERSION);
|
|
75
|
+
expect(result.stderr).toContain(`rig ${latestVersion} is available`);
|
|
76
|
+
expect(result.stderr).toContain("update with: curl -fsSL https://www.rigkit.dev/install | sh");
|
|
77
|
+
} finally {
|
|
78
|
+
server.stop(true);
|
|
79
|
+
rmSync(rigkitHome, { recursive: true, force: true });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("does not print update notices for JSON output", async () => {
|
|
84
|
+
const rigkitHome = mkdtempSync(join(tmpdir(), "rigkit-cli-update-json-"));
|
|
85
|
+
let requests = 0;
|
|
86
|
+
const server = Bun.serve({
|
|
87
|
+
hostname: "127.0.0.1",
|
|
88
|
+
port: 0,
|
|
89
|
+
fetch() {
|
|
90
|
+
requests += 1;
|
|
91
|
+
return Response.json({
|
|
92
|
+
version: nextPatchVersion(RIGKIT_CLI_VERSION),
|
|
93
|
+
installerUrl: "https://www.rigkit.dev/install",
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const result = await runCli(["doctor", "--cli", "--json"], {
|
|
100
|
+
env: {
|
|
101
|
+
RIGKIT_HOME: rigkitHome,
|
|
102
|
+
RIGKIT_UPDATE_CHECK: "1",
|
|
103
|
+
RIGKIT_UPDATE_URL: `http://127.0.0.1:${server.port}/latest.json`,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(result.exitCode).toBe(0);
|
|
108
|
+
expect(result.stderr).toBe("");
|
|
109
|
+
expect(JSON.parse(result.stdout)).toMatchObject({
|
|
110
|
+
cliVersion: RIGKIT_CLI_VERSION,
|
|
111
|
+
});
|
|
112
|
+
expect(requests).toBe(0);
|
|
113
|
+
} finally {
|
|
114
|
+
server.stop(true);
|
|
115
|
+
rmSync(rigkitHome, { recursive: true, force: true });
|
|
116
|
+
}
|
|
44
117
|
});
|
|
45
118
|
|
|
46
119
|
test("rejects operation shorthand at the root", async () => {
|
|
@@ -95,7 +168,28 @@ describe("CLI entrypoint", () => {
|
|
|
95
168
|
expect(result.stderr).toContain("Found named Rigkit configs");
|
|
96
169
|
expect(result.stderr).toContain("api.rig.config.ts");
|
|
97
170
|
expect(result.stderr).toContain("web.rig.config.ts");
|
|
98
|
-
expect(result.stderr).toContain("rig
|
|
171
|
+
expect(result.stderr).toContain("rig --chdir=. --config=api.rig.config.ts <command>");
|
|
172
|
+
} finally {
|
|
173
|
+
rmSync(cwd, { recursive: true, force: true });
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("accepts conventional double-dash global options", async () => {
|
|
178
|
+
const cwd = mkdtempSync(join(tmpdir(), "rigkit-cli-global-options-"));
|
|
179
|
+
mkdirSync(join(cwd, "api"));
|
|
180
|
+
writeFileSync(join(cwd, "api", "rig.config.ts"), "export default {}\n");
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const result = await runCli(["--chdir=api", "projects", "--json"], { cwd });
|
|
184
|
+
|
|
185
|
+
expect(result.exitCode).toBe(0);
|
|
186
|
+
expect(result.stderr).toBe("");
|
|
187
|
+
expect(JSON.parse(result.stdout)).toEqual({
|
|
188
|
+
projects: [{
|
|
189
|
+
projectDir: join(realpathSync(cwd), "api"),
|
|
190
|
+
configPath: join(realpathSync(cwd), "api", "rig.config.ts"),
|
|
191
|
+
}],
|
|
192
|
+
});
|
|
99
193
|
} finally {
|
|
100
194
|
rmSync(cwd, { recursive: true, force: true });
|
|
101
195
|
}
|
|
@@ -124,6 +218,56 @@ describe("CLI entrypoint", () => {
|
|
|
124
218
|
}
|
|
125
219
|
});
|
|
126
220
|
|
|
221
|
+
test("clears Freestyle provider host storage without loading a project", async () => {
|
|
222
|
+
const storageRoot = mkdtempSync(join(tmpdir(), "rigkit-provider-storage-"));
|
|
223
|
+
const storage = createFileProviderHostStorage({ providerId: "freestyle", rootDir: storageRoot });
|
|
224
|
+
storage.set("stack-auth:test", { refreshToken: "refresh-token", updatedAt: 1 });
|
|
225
|
+
storage.set("identity:test", { token: "ssh-token" });
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const result = await runCli(["providers", "freestyle", "clear", "--json"], {
|
|
229
|
+
env: { RIGKIT_HOST_STORAGE_DIR: storageRoot },
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(result.exitCode).toBe(0);
|
|
233
|
+
expect(result.stderr).toBe("");
|
|
234
|
+
expect(JSON.parse(result.stdout)).toMatchObject({
|
|
235
|
+
ok: true,
|
|
236
|
+
providerId: "freestyle",
|
|
237
|
+
deleted: 2,
|
|
238
|
+
storageRoot,
|
|
239
|
+
});
|
|
240
|
+
expect(storage.entries()).toEqual([]);
|
|
241
|
+
} finally {
|
|
242
|
+
rmSync(storageRoot, { recursive: true, force: true });
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test("does not render a success marker when cache invalidation is a no-op", async () => {
|
|
247
|
+
const projectDir = mkdtempSync(join(tmpdir(), "rigkit-cli-cache-invalidate-"));
|
|
248
|
+
|
|
249
|
+
await withWorkspaceRuntime({ projectDir, cacheInvalidated: 0 }, async ({ env }) => {
|
|
250
|
+
const result = await runCli([`-chdir=${projectDir}`, "cache", "invalidate", "missing-task"], { env });
|
|
251
|
+
|
|
252
|
+
expect(result.exitCode).toBe(0);
|
|
253
|
+
expect(result.stderr).toBe("");
|
|
254
|
+
expect(result.stdout.trim()).toBe("no cache entries invalidated");
|
|
255
|
+
expect(result.stdout).not.toContain("✓");
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("preserves JSON output for zero cache invalidations", async () => {
|
|
260
|
+
const projectDir = mkdtempSync(join(tmpdir(), "rigkit-cli-cache-invalidate-json-"));
|
|
261
|
+
|
|
262
|
+
await withWorkspaceRuntime({ projectDir, cacheInvalidated: 0 }, async ({ env }) => {
|
|
263
|
+
const result = await runCli([`-chdir=${projectDir}`, "cache", "invalidate", "missing-task", "--json"], { env });
|
|
264
|
+
|
|
265
|
+
expect(result.exitCode).toBe(0);
|
|
266
|
+
expect(result.stderr).toBe("");
|
|
267
|
+
expect(JSON.parse(result.stdout)).toEqual({ ok: true, invalidated: 0 });
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
127
271
|
test("lists workspaces from the project runtime", async () => {
|
|
128
272
|
const projectDir = mkdtempSync(join(tmpdir(), "rigkit-cli-ls-"));
|
|
129
273
|
|
|
@@ -241,6 +385,7 @@ async function runCli(
|
|
|
241
385
|
stderr: "pipe",
|
|
242
386
|
env: {
|
|
243
387
|
...process.env,
|
|
388
|
+
RIGKIT_UPDATE_CHECK: "0",
|
|
244
389
|
...options.env,
|
|
245
390
|
FORCE_COLOR: "0",
|
|
246
391
|
NO_COLOR: "1",
|
|
@@ -255,8 +400,14 @@ async function runCli(
|
|
|
255
400
|
return { exitCode, stdout, stderr };
|
|
256
401
|
}
|
|
257
402
|
|
|
403
|
+
function nextPatchVersion(version: string): string {
|
|
404
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
405
|
+
if (!match) return "999.0.0";
|
|
406
|
+
return `${match[1]}.${match[2]}.${Number(match[3]) + 1}`;
|
|
407
|
+
}
|
|
408
|
+
|
|
258
409
|
async function withWorkspaceRuntime(
|
|
259
|
-
input: { projectDir: string },
|
|
410
|
+
input: { projectDir: string; cacheInvalidated?: number },
|
|
260
411
|
run: (context: { env: Record<string, string> }) => Promise<void>,
|
|
261
412
|
): Promise<void> {
|
|
262
413
|
const rigkitHome = mkdtempSync(join(tmpdir(), "rigkit-home-"));
|
|
@@ -306,6 +457,9 @@ async function withWorkspaceRuntime(
|
|
|
306
457
|
}],
|
|
307
458
|
});
|
|
308
459
|
}
|
|
460
|
+
if (pathname === "/cache/invalidate") {
|
|
461
|
+
return runtimeJson({ ok: true, invalidated: input.cacheInvalidated ?? 1 });
|
|
462
|
+
}
|
|
309
463
|
if (pathname === "/operations") {
|
|
310
464
|
return runtimeJson({
|
|
311
465
|
operations: [{
|
package/src/cli.ts
CHANGED
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
type RuntimeClient,
|
|
11
11
|
} from "@rigkit/runtime-client";
|
|
12
12
|
import {
|
|
13
|
+
createFileProviderHostStorage,
|
|
14
|
+
defaultProviderHostStorageDir,
|
|
13
15
|
type DevMachineEvent,
|
|
14
16
|
type WorkflowPlan,
|
|
15
17
|
type SnapshotRecord,
|
|
@@ -25,6 +27,7 @@ import { initProject, normalizeMachineName, type InitProjectResult } from "./ini
|
|
|
25
27
|
import { openExternalTarget } from "./interaction.ts";
|
|
26
28
|
import { createRunPresenter, type RunPresenter } from "./run-presenter.ts";
|
|
27
29
|
import { createRunLogger, type RunLogger } from "./run-logger.ts";
|
|
30
|
+
import { maybePrintUpdateNotice } from "./update-check.ts";
|
|
28
31
|
import {
|
|
29
32
|
completeRig,
|
|
30
33
|
formatCompletionItems,
|
|
@@ -161,6 +164,7 @@ const STATIC_COMMANDS = new Set([
|
|
|
161
164
|
"run",
|
|
162
165
|
"ls",
|
|
163
166
|
"cache",
|
|
167
|
+
"providers",
|
|
164
168
|
"projects",
|
|
165
169
|
"doctor",
|
|
166
170
|
"version",
|
|
@@ -204,22 +208,32 @@ async function runCli(argv: string[]): Promise<void> {
|
|
|
204
208
|
.exitOverride()
|
|
205
209
|
.argument("[command]")
|
|
206
210
|
.addOption(new Option("--chdir <dir>", `Switch to a directory containing ${DEFAULT_CONFIG_FILE} before running the command`).hideHelp())
|
|
207
|
-
.addOption(new Option("--config <file>", "Config file to load, relative to
|
|
211
|
+
.addOption(new Option("--config <file>", "Config file to load, relative to --chdir when set").hideHelp())
|
|
208
212
|
.addOption(new Option("--state <file>", "Local runtime state database path").hideHelp())
|
|
209
213
|
.addOption(new Option("--json", "Print machine-readable JSON where supported").hideHelp())
|
|
210
214
|
.addHelpText("after", [
|
|
211
215
|
"",
|
|
212
216
|
"Global Options:",
|
|
213
|
-
"
|
|
214
|
-
"
|
|
215
|
-
"
|
|
216
|
-
"
|
|
217
|
+
" --chdir <dir> Switch to a directory containing rig.config.ts before running the command",
|
|
218
|
+
" --config <file> Config file to load, relative to --chdir when set",
|
|
219
|
+
" --state <file> Local runtime state database path",
|
|
220
|
+
" --json Print machine-readable JSON where supported",
|
|
221
|
+
"",
|
|
222
|
+
"Legacy single-dash global aliases such as -chdir=DIR are still accepted.",
|
|
217
223
|
].join("\n"))
|
|
218
224
|
.action(async (command?: string) => {
|
|
219
225
|
if (command) program.error(`unknown command '${command}'`);
|
|
220
226
|
await runHelp(makeInvocation(rootOptions(program)));
|
|
221
227
|
});
|
|
222
228
|
|
|
229
|
+
program.hook("postAction", async (_thisCommand, actionCommand) => {
|
|
230
|
+
await maybePrintUpdateNotice({
|
|
231
|
+
commandName: actionCommand.name(),
|
|
232
|
+
currentVersion: RIGKIT_CLI_VERSION,
|
|
233
|
+
json: commandWantsJson(program, actionCommand),
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
223
237
|
program
|
|
224
238
|
.command("init")
|
|
225
239
|
.description("Initialize a Rigkit project")
|
|
@@ -351,6 +365,22 @@ async function runCli(argv: string[]): Promise<void> {
|
|
|
351
365
|
});
|
|
352
366
|
});
|
|
353
367
|
|
|
368
|
+
const providers = program
|
|
369
|
+
.command("providers")
|
|
370
|
+
.description("Manage provider-owned local state");
|
|
371
|
+
|
|
372
|
+
const freestyleProvider = providers
|
|
373
|
+
.command("freestyle")
|
|
374
|
+
.description("Manage Freestyle provider local state");
|
|
375
|
+
|
|
376
|
+
freestyleProvider
|
|
377
|
+
.command("clear")
|
|
378
|
+
.description("Clear Freestyle provider local auth and identity state")
|
|
379
|
+
.option("--json", "Print machine-readable JSON")
|
|
380
|
+
.action(async (options: { json?: boolean }) => {
|
|
381
|
+
await runProvidersFreestyleClear(makeInvocation(rootOptions(program), options.json));
|
|
382
|
+
});
|
|
383
|
+
|
|
354
384
|
program
|
|
355
385
|
.command("projects")
|
|
356
386
|
.description("Discover Rigkit projects below the current directory")
|
|
@@ -408,6 +438,11 @@ function rootOptions(program: Command): GlobalOptions {
|
|
|
408
438
|
};
|
|
409
439
|
}
|
|
410
440
|
|
|
441
|
+
function commandWantsJson(program: Command, actionCommand: Command): boolean {
|
|
442
|
+
const options = actionCommand.opts<{ json?: boolean }>();
|
|
443
|
+
return Boolean(rootOptions(program).json || options.json);
|
|
444
|
+
}
|
|
445
|
+
|
|
411
446
|
function parsePackageManagerOption(value: string | undefined): PackageManager | undefined {
|
|
412
447
|
if (value === undefined) return undefined;
|
|
413
448
|
if (isPackageManager(value)) return value;
|
|
@@ -560,7 +595,7 @@ function canPrompt(): boolean {
|
|
|
560
595
|
function resolveInitProjectPaths(invocation: CliInvocation, name: string): { projectDir: string; configPath: string } {
|
|
561
596
|
const options = invocation.global;
|
|
562
597
|
if (options.config) {
|
|
563
|
-
throw new Error(`rig init does not support
|
|
598
|
+
throw new Error(`rig init does not support --config. Use --chdir to choose the parent directory.`);
|
|
564
599
|
}
|
|
565
600
|
|
|
566
601
|
const parentDir = resolve(process.cwd(), options.chdir ?? ".");
|
|
@@ -950,7 +985,7 @@ async function runDiscoveredProjectOperation(
|
|
|
950
985
|
if (!options.all && projects.length > 1) {
|
|
951
986
|
throw new Error([
|
|
952
987
|
"Multiple Rigkit projects found.",
|
|
953
|
-
"Use `rig projects` to list candidates, pass
|
|
988
|
+
"Use `rig projects` to list candidates, pass --chdir or --config to select one, or pass --all to run every discovered project.",
|
|
954
989
|
...projects.map((project) => `- ${project.configPath}`),
|
|
955
990
|
].join("\n"));
|
|
956
991
|
}
|
|
@@ -1068,7 +1103,7 @@ async function runCacheClear(invocation: CliInvocation, options: CacheClearOptio
|
|
|
1068
1103
|
|
|
1069
1104
|
if (options.global && options.all) {
|
|
1070
1105
|
if (invocation.global.chdir || invocation.global.config || invocation.global.state) {
|
|
1071
|
-
throw new Error(`rig cache clear --global --all cannot be combined with
|
|
1106
|
+
throw new Error(`rig cache clear --global --all cannot be combined with --chdir, --config, or --state`);
|
|
1072
1107
|
}
|
|
1073
1108
|
const fragmentRoot = join(defaultRigkitHome(), "fragments");
|
|
1074
1109
|
rmSync(fragmentRoot, { recursive: true, force: true });
|
|
@@ -1090,6 +1125,32 @@ async function runCacheClear(invocation: CliInvocation, options: CacheClearOptio
|
|
|
1090
1125
|
console.log(`Cleared ${result.deleted} cache ${result.deleted === 1 ? "entry" : "entries"}.`);
|
|
1091
1126
|
}
|
|
1092
1127
|
|
|
1128
|
+
async function runProvidersFreestyleClear(invocation: CliInvocation): Promise<void> {
|
|
1129
|
+
const providerId = "freestyle";
|
|
1130
|
+
const storageRoot = defaultProviderHostStorageDir();
|
|
1131
|
+
const storage = createFileProviderHostStorage({ providerId, rootDir: storageRoot });
|
|
1132
|
+
const keys = storage.entries().map((entry) => entry.key);
|
|
1133
|
+
for (const key of keys) storage.delete(key);
|
|
1134
|
+
|
|
1135
|
+
const result = {
|
|
1136
|
+
ok: true,
|
|
1137
|
+
providerId,
|
|
1138
|
+
deleted: keys.length,
|
|
1139
|
+
storageRoot,
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
if (wantsJson(invocation)) {
|
|
1143
|
+
printJson(result);
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
if (keys.length === 0) {
|
|
1148
|
+
console.log("No Freestyle provider local state to clear.");
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
console.log(`Cleared ${keys.length} Freestyle provider ${keys.length === 1 ? "entry" : "entries"}.`);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1093
1154
|
type CacheInvalidateOptions = {
|
|
1094
1155
|
step?: string;
|
|
1095
1156
|
all: boolean;
|
|
@@ -1144,6 +1205,10 @@ async function runCacheInvalidate(invocation: CliInvocation, options: CacheInval
|
|
|
1144
1205
|
printJson(result);
|
|
1145
1206
|
return;
|
|
1146
1207
|
}
|
|
1208
|
+
if (result.invalidated === 0) {
|
|
1209
|
+
console.log(ui.dim("no cache entries invalidated"));
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1147
1212
|
console.log(
|
|
1148
1213
|
`${ui.ok(ui.sym.ok)} invalidated ${result.invalidated} cache ${result.invalidated === 1 ? "entry" : "entries"}`,
|
|
1149
1214
|
);
|
|
@@ -1506,6 +1571,7 @@ async function runHelp(invocation: CliInvocation): Promise<void> {
|
|
|
1506
1571
|
{ name: "run", description: "Run a workspace operation" },
|
|
1507
1572
|
{ name: "ls", description: "List project workspaces" },
|
|
1508
1573
|
{ name: "cache", description: "Inspect and clear Rigkit cache" },
|
|
1574
|
+
{ name: "providers", description: "Manage provider-owned local state" },
|
|
1509
1575
|
{ name: "projects", description: "Discover Rigkit projects below the current directory" },
|
|
1510
1576
|
{ name: "doctor", description: "Show Rigkit runtime diagnostics" },
|
|
1511
1577
|
{ name: "version", description: "Show Rigkit CLI version" },
|
|
@@ -1517,7 +1583,7 @@ async function runHelp(invocation: CliInvocation): Promise<void> {
|
|
|
1517
1583
|
const cmd = (name: string, description: string): string =>
|
|
1518
1584
|
` ${ui.bold(name.padEnd(10))} ${description}`;
|
|
1519
1585
|
const opt = (flag: string, description: string): string =>
|
|
1520
|
-
` ${ui.bold(flag.padEnd(
|
|
1586
|
+
` ${ui.bold(flag.padEnd(17))} ${description}`;
|
|
1521
1587
|
|
|
1522
1588
|
console.log([
|
|
1523
1589
|
`${ui.bold("rig")} ${ui.dim(RIGKIT_CLI_VERSION)}`,
|
|
@@ -1535,16 +1601,19 @@ async function runHelp(invocation: CliInvocation): Promise<void> {
|
|
|
1535
1601
|
cmd("run", "Run a workspace operation"),
|
|
1536
1602
|
cmd("ls", "List project workspaces"),
|
|
1537
1603
|
cmd("cache", "Inspect and clear Rigkit cache"),
|
|
1604
|
+
cmd("providers", "Manage provider-owned local state"),
|
|
1538
1605
|
cmd("projects", "Discover Rigkit projects below the current directory"),
|
|
1539
1606
|
cmd("doctor", "Show Rigkit runtime diagnostics"),
|
|
1540
1607
|
cmd("version", "Show Rigkit CLI version"),
|
|
1541
1608
|
cmd("completion", "Generate shell completion script"),
|
|
1542
1609
|
"",
|
|
1543
1610
|
ui.dim("Options:"),
|
|
1544
|
-
opt("
|
|
1545
|
-
opt("
|
|
1546
|
-
opt("
|
|
1547
|
-
opt("
|
|
1611
|
+
opt("--chdir <dir>", "Switch to a directory containing rig.config.ts before running the command"),
|
|
1612
|
+
opt("--config <file>", "Config file to load, relative to --chdir when set"),
|
|
1613
|
+
opt("--state <file>", "Local runtime state database path"),
|
|
1614
|
+
opt("--json", "Print machine-readable JSON where supported"),
|
|
1615
|
+
"",
|
|
1616
|
+
ui.dim("Legacy single-dash global aliases such as -chdir=DIR are still accepted."),
|
|
1548
1617
|
].join("\n"));
|
|
1549
1618
|
}
|
|
1550
1619
|
|
|
@@ -2098,8 +2167,12 @@ async function promptHostSelect(params: unknown): Promise<string> {
|
|
|
2098
2167
|
.filter((item) => item.value)
|
|
2099
2168
|
: [];
|
|
2100
2169
|
if (options.length === 0) throw new Error(`Host select prompt has no options`);
|
|
2101
|
-
const
|
|
2102
|
-
|
|
2170
|
+
const configuredDefaultValue = stringField(params, "defaultValue");
|
|
2171
|
+
const defaultValue = configuredDefaultValue ?? options[0]!.value;
|
|
2172
|
+
if (!canPrompt()) {
|
|
2173
|
+
if (configuredDefaultValue !== undefined) return configuredDefaultValue;
|
|
2174
|
+
throw new Error(`Host select prompt requires an interactive terminal: ${message}`);
|
|
2175
|
+
}
|
|
2103
2176
|
const answers = await inquirer.prompt<{ value: string }>([{
|
|
2104
2177
|
type: "select",
|
|
2105
2178
|
name: "value",
|
|
@@ -2283,6 +2356,16 @@ function printPlan(plan: WorkflowPlan): void {
|
|
|
2283
2356
|
console.log(`${ui.bold(plan.workflow)} ${ui.dim(`${plan.cachedNodeCount}/${plan.nodeCount} cached`)}`);
|
|
2284
2357
|
console.log("");
|
|
2285
2358
|
|
|
2359
|
+
if (plan.providerChecks?.length) {
|
|
2360
|
+
const rows = plan.providerChecks.map((check) => [
|
|
2361
|
+
{ text: check.label || check.providerName, style: ui.dim },
|
|
2362
|
+
{ text: check.status, style: providerCheckStatusStyle(check.status) },
|
|
2363
|
+
{ text: providerCheckValue(check), style: check.status === "ok" ? ((value: string) => value) : ui.warn },
|
|
2364
|
+
]);
|
|
2365
|
+
console.log(ui.columns(["provider check", "status", "current"], rows));
|
|
2366
|
+
console.log("");
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2286
2369
|
const rows = plan.nodes.map((node) => [
|
|
2287
2370
|
{ text: String(node.index + 1), style: ui.dim },
|
|
2288
2371
|
{ text: node.status, style: planStatusStyle(node.status) },
|
|
@@ -2292,6 +2375,18 @@ function printPlan(plan: WorkflowPlan): void {
|
|
|
2292
2375
|
console.log(ui.columns(["#", "status", "node", "reason"], rows));
|
|
2293
2376
|
}
|
|
2294
2377
|
|
|
2378
|
+
function providerCheckValue(check: NonNullable<WorkflowPlan["providerChecks"]>[number]): string {
|
|
2379
|
+
if (check.status === "required" && check.message) return check.message;
|
|
2380
|
+
if (check.detail && check.detail !== check.value && !check.value.includes(check.detail)) {
|
|
2381
|
+
return `${check.value} ${ui.dim(check.detail)}`;
|
|
2382
|
+
}
|
|
2383
|
+
return check.value;
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
function providerCheckStatusStyle(status: string): (text: string) => string {
|
|
2387
|
+
return status === "ok" ? ui.ok : ui.warn;
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2295
2390
|
function planStatusStyle(status: string): (text: string) => string {
|
|
2296
2391
|
switch (status) {
|
|
2297
2392
|
case "cached":
|