@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 CHANGED
@@ -7,7 +7,7 @@ npm i -g @rigkit/cli
7
7
  rig init
8
8
  rig plan
9
9
  rig ls
10
- rig plan github:owner/repo
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.
@@ -16,10 +16,19 @@ Interactive terminals use Inquirer prompts and a chalk/log-update run timeline.
16
16
 
17
17
  Interactive providers can ask the CLI to open provider-owned URLs. For example, Freestyle terminal sessions are served by the Freestyle provider, while the CLI only opens the presented URL in a browser.
18
18
 
19
- Workspace-specific operations run as `rig run <workspace> <operation>`, for example `rig run website-workspace open-cmux` or `rig run website-workspace remove -y`.
19
+ Workspace lifecycle commands are built in: `rig create <workspace>` creates a
20
+ workspace and `rig rm <workspace>` removes one. Workspace-specific operations
21
+ defined by the project run as `rig run <workspace> <operation>`, for example
22
+ `rig run website-workspace open-cmux`.
20
23
 
21
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.
22
25
 
23
- Remote GitHub targets are materialized into `~/.rigkit/projects`, use state outside the checkout, and require explicit trust before installing dependencies or executing the remote config.
26
+ Use Terraform-style global context options before the command to select another
27
+ project or config:
28
+
29
+ ```bash
30
+ rig -chdir=examples/smoke plan
31
+ rig -chdir=examples/global-fragments -config=api.rig.config.ts apply
32
+ ```
24
33
 
25
34
  Projects should install matching `@rigkit/sdk` versions locally.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigkit/cli",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,12 +22,9 @@
22
22
  "chalk": "^5.6.2",
23
23
  "commander": "^14.0.3",
24
24
  "inquirer": "^13.4.3",
25
- "log-symbols": "^7.0.1",
26
- "log-update": "^8.0.0",
27
- "ora": "^9.4.0",
28
- "@rigkit/provider-cmux": "0.2.7",
29
- "@rigkit/runtime-client": "0.2.7",
30
- "@rigkit/engine": "0.2.7"
25
+ "@rigkit/provider-cmux": "0.2.8",
26
+ "@rigkit/runtime-client": "0.2.8",
27
+ "@rigkit/engine": "0.2.8"
31
28
  },
32
29
  "devDependencies": {
33
30
  "@types/bun": "latest",
package/src/cli.test.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
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
5
  import { projectIdFor, runtimeFingerprintFor, runtimePaths, SUPPORTED_RUNTIME_API_VERSION } from "@rigkit/runtime-client";
@@ -24,7 +24,9 @@ describe("CLI entrypoint", () => {
24
24
  expect(rootHelp.stderr).toBe("");
25
25
  expect(rootHelp.stdout).toContain("rig ");
26
26
  expect(rootHelp.stdout).toContain("plan Plan project workflow changes");
27
+ expect(rootHelp.stdout).toContain("rm Remove a workspace");
27
28
  expect(rootHelp.stdout).toContain("run Run a workspace operation");
29
+ expect(rootHelp.stdout).toContain("cache Inspect and clear Rigkit cache");
28
30
 
29
31
  const version = await runCli(["version"]);
30
32
  expect(version.exitCode).toBe(0);
@@ -36,7 +38,9 @@ describe("CLI entrypoint", () => {
36
38
  expect(help.stderr).toBe("");
37
39
  expect(help.stdout).toContain("rig ");
38
40
  expect(help.stdout).toContain("plan Plan project workflow changes");
41
+ expect(help.stdout).toContain("rm Remove a workspace");
39
42
  expect(help.stdout).toContain("run Run a workspace operation");
43
+ expect(help.stdout).toContain("cache Inspect and clear Rigkit cache");
40
44
  });
41
45
 
42
46
  test("rejects operation shorthand at the root", async () => {
@@ -52,7 +56,7 @@ describe("CLI entrypoint", () => {
52
56
 
53
57
  expect(result.exitCode).toBe(0);
54
58
  expect(result.stderr).toBe("");
55
- expect(result.stdout.trim()).toBe("version\tshow CLI version");
59
+ expect(result.stdout.trim()).toBe("version\tshow CLI version\t\tCommands");
56
60
  });
57
61
 
58
62
  test("discovers projects without starting a runtime", async () => {
@@ -77,11 +81,54 @@ describe("CLI entrypoint", () => {
77
81
  }
78
82
  });
79
83
 
84
+ test("shows named config choices when the default config is missing", async () => {
85
+ const cwd = mkdtempSync(join(tmpdir(), "rigkit-cli-named-configs-"));
86
+ writeFileSync(join(cwd, "api.rig.config.ts"), "export default {}\n");
87
+ writeFileSync(join(cwd, "web.rig.config.ts"), "export default {}\n");
88
+
89
+ try {
90
+ const result = await runCli(["create", "--json"], { cwd });
91
+
92
+ expect(result.exitCode).toBe(1);
93
+ expect(result.stdout).toBe("");
94
+ expect(result.stderr).toContain("No Rigkit config found from");
95
+ expect(result.stderr).toContain("Found named Rigkit configs");
96
+ expect(result.stderr).toContain("api.rig.config.ts");
97
+ expect(result.stderr).toContain("web.rig.config.ts");
98
+ expect(result.stderr).toContain("rig -chdir=. -config=api.rig.config.ts <command>");
99
+ } finally {
100
+ rmSync(cwd, { recursive: true, force: true });
101
+ }
102
+ });
103
+
104
+ test("clears all global fragment cache without loading a config", async () => {
105
+ const rigkitHome = mkdtempSync(join(tmpdir(), "rigkit-cli-cache-"));
106
+ const fragmentDir = join(rigkitHome, "fragments", "sha256-test");
107
+ mkdirSync(fragmentDir, { recursive: true });
108
+ writeFileSync(join(fragmentDir, "state.sqlite"), "");
109
+
110
+ try {
111
+ const result = await runCli(["cache", "clear", "--global", "--all", "--json"], {
112
+ env: { RIGKIT_HOME: rigkitHome },
113
+ });
114
+
115
+ expect(result.exitCode).toBe(0);
116
+ expect(result.stderr).toBe("");
117
+ expect(JSON.parse(result.stdout)).toMatchObject({
118
+ ok: true,
119
+ scope: "global-all",
120
+ });
121
+ expect(existsSync(fragmentDir)).toBe(false);
122
+ } finally {
123
+ rmSync(rigkitHome, { recursive: true, force: true });
124
+ }
125
+ });
126
+
80
127
  test("lists workspaces from the project runtime", async () => {
81
128
  const projectDir = mkdtempSync(join(tmpdir(), "rigkit-cli-ls-"));
82
129
 
83
130
  await withWorkspaceRuntime({ projectDir }, async ({ env }) => {
84
- const result = await runCli(["-C", projectDir, "ls"], { env });
131
+ const result = await runCli([`-chdir=${projectDir}`, "ls"], { env });
85
132
 
86
133
  expect(result.exitCode).toBe(0);
87
134
  expect(result.stderr).toBe("");
@@ -94,7 +141,7 @@ describe("CLI entrypoint", () => {
94
141
  const projectDir = mkdtempSync(join(tmpdir(), "rigkit-cli-ls-json-"));
95
142
 
96
143
  await withWorkspaceRuntime({ projectDir }, async ({ env }) => {
97
- const result = await runCli(["-C", projectDir, "ls", "--json"], { env });
144
+ const result = await runCli([`-chdir=${projectDir}`, "ls", "--json"], { env });
98
145
 
99
146
  expect(result.exitCode).toBe(0);
100
147
  expect(result.stderr).toBe("");
@@ -112,7 +159,7 @@ describe("CLI entrypoint", () => {
112
159
  const projectDir = mkdtempSync(join(tmpdir(), "rigkit-cli-create-name-"));
113
160
 
114
161
  await withWorkspaceRuntime({ projectDir }, async ({ env }) => {
115
- const result = await runCli(["-C", projectDir, "create", "--name", "some workspace", "--json"], { env });
162
+ const result = await runCli([`-chdir=${projectDir}`, "create", "--name", "some workspace", "--json"], { env });
116
163
 
117
164
  expect(result.exitCode).toBe(1);
118
165
  expect(result.stdout).toBe("");
@@ -120,6 +167,34 @@ describe("CLI entrypoint", () => {
120
167
  });
121
168
  });
122
169
 
170
+ test("accepts create name as a positional argument", async () => {
171
+ const projectDir = mkdtempSync(join(tmpdir(), "rigkit-cli-create-positional-"));
172
+
173
+ await withWorkspaceRuntime({ projectDir }, async ({ env }) => {
174
+ const result = await runCli([`-chdir=${projectDir}`, "create", "new-workspace", "--json"], { env });
175
+
176
+ expect(result.exitCode).toBe(0);
177
+ expect(result.stderr).toBe("");
178
+ expect(JSON.parse(result.stdout)).toMatchObject({
179
+ name: "new-workspace",
180
+ });
181
+ });
182
+ });
183
+
184
+ test("removes a workspace with the built-in rm command", async () => {
185
+ const projectDir = mkdtempSync(join(tmpdir(), "rigkit-cli-rm-"));
186
+
187
+ await withWorkspaceRuntime({ projectDir }, async ({ env }) => {
188
+ const result = await runCli([`-chdir=${projectDir}`, "rm", "api", "-y", "--json"], { env });
189
+
190
+ expect(result.exitCode).toBe(0);
191
+ expect(result.stderr).toBe("");
192
+ expect(JSON.parse(result.stdout)).toMatchObject({
193
+ name: "api",
194
+ });
195
+ });
196
+ });
197
+
123
198
  test("requires discovered projects for operation --all", async () => {
124
199
  const cwd = mkdtempSync(join(tmpdir(), "rigkit-cli-run-all-"));
125
200
 
@@ -196,10 +271,11 @@ async function withWorkspaceRuntime(
196
271
  writeFileSync(paths.tokenPath, `${token}\n`);
197
272
 
198
273
  const now = new Date(0).toISOString();
274
+ let runResult: unknown = undefined;
199
275
  const server = Bun.serve({
200
276
  hostname: "127.0.0.1",
201
277
  port: 0,
202
- fetch(request) {
278
+ async fetch(request) {
203
279
  if (request.headers.get("authorization") !== `Bearer ${token}`) {
204
280
  return runtimeJson({ error: { message: "Unauthorized" } }, { status: 401 });
205
281
  }
@@ -240,6 +316,7 @@ async function withWorkspaceRuntime(
240
316
  description: "Create a workspace",
241
317
  createsWorkspace: true,
242
318
  cli: {
319
+ positionals: [{ name: "name", index: 0 }],
243
320
  options: [{ name: "name", flag: "--name", required: true, type: "string" }],
244
321
  },
245
322
  inputSchema: {
@@ -251,9 +328,64 @@ async function withWorkspaceRuntime(
251
328
  required: ["name"],
252
329
  },
253
330
  }],
254
- workspaceOperations: [],
331
+ workspaceOperations: [{
332
+ id: "remove",
333
+ kind: "workspace-action",
334
+ source: "core",
335
+ title: "Remove",
336
+ description: "remove workspace",
337
+ cli: {
338
+ options: [{ name: "yes", flag: "--yes", aliases: ["-y"], type: "boolean", runtime: false }],
339
+ },
340
+ inputSchema: {
341
+ type: "object",
342
+ additionalProperties: false,
343
+ properties: {},
344
+ },
345
+ }],
255
346
  });
256
347
  }
348
+ if (pathname === "/runs") {
349
+ const body = await request.json() as { operation?: string; input?: { name?: string } };
350
+ runResult = body.operation === "api/remove"
351
+ ? {
352
+ id: "workspace-api",
353
+ name: "api",
354
+ workflow: "smoke",
355
+ ctx: {},
356
+ createdAt: now,
357
+ updatedAt: now,
358
+ }
359
+ : {
360
+ id: "workspace-new",
361
+ name: body.input?.name ?? "new-workspace",
362
+ workflow: "smoke",
363
+ ctx: {},
364
+ createdAt: now,
365
+ updatedAt: now,
366
+ };
367
+ return runtimeJson({
368
+ runId: "run-test",
369
+ operation: body.operation ?? "test",
370
+ status: "running",
371
+ eventsUrl: "/runs/run-test/events",
372
+ sessionUrl: "",
373
+ }, { status: 202 });
374
+ }
375
+ if (pathname === "/runs/run-test/events") {
376
+ return new Response(
377
+ `data: ${JSON.stringify({
378
+ type: "run.completed",
379
+ result: runResult,
380
+ })}\n\n`,
381
+ {
382
+ headers: {
383
+ "content-type": "text/event-stream",
384
+ "x-rigkit-api-version": String(SUPPORTED_RUNTIME_API_VERSION),
385
+ },
386
+ },
387
+ );
388
+ }
257
389
  return runtimeJson({ error: { message: "Not found" } }, { status: 404 });
258
390
  },
259
391
  });