@h-rig/cli 0.0.6-alpha.24 → 0.0.6-alpha.26
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/dist/bin/rig.js +406 -226
- package/dist/src/commands/_cli-format.js +71 -13
- package/dist/src/commands/_help-catalog.js +174 -0
- package/dist/src/commands/connect.js +131 -23
- package/dist/src/commands/run.js +20 -6
- package/dist/src/commands/server.js +206 -8
- package/dist/src/commands/task.js +32 -10
- package/dist/src/commands.js +406 -226
- package/dist/src/index.js +406 -226
- package/package.json +6 -6
|
@@ -25,21 +25,39 @@ function pad(value, width) {
|
|
|
25
25
|
}
|
|
26
26
|
function statusColor(status) {
|
|
27
27
|
const normalized = status.toLowerCase();
|
|
28
|
-
if (["completed", "merged", "closed", "done", "accepted"].includes(normalized))
|
|
28
|
+
if (["completed", "merged", "closed", "done", "accepted", "pass", "selected"].includes(normalized))
|
|
29
29
|
return pc.green;
|
|
30
|
-
if (["failed", "needs_attention", "needs-attention", "blocked"].includes(normalized))
|
|
30
|
+
if (["failed", "needs_attention", "needs-attention", "blocked", "error"].includes(normalized))
|
|
31
31
|
return pc.red;
|
|
32
|
-
if (["running", "reviewing", "validating", "in_progress", "in-progress"].includes(normalized))
|
|
32
|
+
if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
|
|
33
33
|
return pc.cyan;
|
|
34
|
-
if (["ready", "open", "queued", "created", "preparing"].includes(normalized))
|
|
34
|
+
if (["ready", "open", "queued", "created", "preparing", "local"].includes(normalized))
|
|
35
35
|
return pc.yellow;
|
|
36
36
|
return pc.dim;
|
|
37
37
|
}
|
|
38
|
+
function formatStatusPill(status) {
|
|
39
|
+
const label = status || "unknown";
|
|
40
|
+
return statusColor(label)(`\u25CF ${label}`);
|
|
41
|
+
}
|
|
42
|
+
function formatSection(title, subtitle) {
|
|
43
|
+
return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
|
|
44
|
+
}
|
|
45
|
+
function formatSuccessCard(title, rows = []) {
|
|
46
|
+
const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc.dim("\u2502")} ${pc.dim(key.padEnd(9))} ${value}`);
|
|
47
|
+
return [formatSection(title), ...body].join(`
|
|
48
|
+
`);
|
|
49
|
+
}
|
|
50
|
+
function formatNextSteps(steps) {
|
|
51
|
+
if (steps.length === 0)
|
|
52
|
+
return [];
|
|
53
|
+
return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
|
|
54
|
+
}
|
|
38
55
|
function formatTaskList(tasks, options = {}) {
|
|
39
|
-
if (tasks.length === 0)
|
|
40
|
-
return pc.dim("No matching tasks.");
|
|
41
56
|
if (options.raw)
|
|
42
57
|
return tasks.map((task) => JSON.stringify(task)).join(`
|
|
58
|
+
`);
|
|
59
|
+
if (tasks.length === 0)
|
|
60
|
+
return [formatSection("Tasks", "none found"), ...formatNextSteps(["Try `rig server status` to confirm the selected server.", "Relax filters or run `rig task run --title ... --initial-prompt ...` for ad hoc work."])].join(`
|
|
43
61
|
`);
|
|
44
62
|
const rows = tasks.map((task) => {
|
|
45
63
|
const raw = rawObject(task);
|
|
@@ -62,12 +80,18 @@ function formatTaskList(tasks, options = {}) {
|
|
|
62
80
|
`${row.title}${labels}${source}`
|
|
63
81
|
].join(" ");
|
|
64
82
|
});
|
|
65
|
-
return [
|
|
83
|
+
return [formatSection("Tasks", `${rows.length} shown`), header, ...body, "", ...formatNextSteps(["Run one: `rig task run <id>` or `rig task run --next`", "Attach later: `rig run attach <run-id> --follow`"])].join(`
|
|
66
84
|
`);
|
|
67
85
|
}
|
|
68
86
|
function formatRunList(runs, options = {}) {
|
|
69
87
|
if (runs.length === 0) {
|
|
70
|
-
return
|
|
88
|
+
return [
|
|
89
|
+
formatSection("Runs", "none recorded"),
|
|
90
|
+
options.source === "server" ? pc.dim("No runs recorded on the selected Rig server.") : pc.dim("No runs recorded in .rig/runs."),
|
|
91
|
+
"",
|
|
92
|
+
...formatNextSteps(["Start one: `rig task run --next`", "Check server: `rig server status`"])
|
|
93
|
+
].join(`
|
|
94
|
+
`);
|
|
71
95
|
}
|
|
72
96
|
const rows = runs.map((run) => {
|
|
73
97
|
const runId = stringField(run, "runId", stringField(run, "id", "(unknown-run)"));
|
|
@@ -85,22 +109,56 @@ function formatRunList(runs, options = {}) {
|
|
|
85
109
|
statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
|
|
86
110
|
`${row.title}${row.runtime ? pc.dim(` ${row.runtime}`) : ""}`
|
|
87
111
|
].join(" "));
|
|
88
|
-
return [
|
|
112
|
+
return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), header, ...body, "", ...formatNextSteps(["Follow live: `rig run attach <run-id> --follow`", "Details: `rig run show --run <run-id>`"])].join(`
|
|
89
113
|
`);
|
|
90
114
|
}
|
|
91
115
|
function formatSubmittedRun(input) {
|
|
92
|
-
const
|
|
116
|
+
const rows = [["run", pc.bold(input.runId)]];
|
|
93
117
|
if (input.task) {
|
|
94
118
|
const id = stringField(input.task, "id", "<unknown>");
|
|
95
119
|
const status = stringField(input.task, "status", "unknown");
|
|
96
120
|
const title = stringField(input.task, "title", "Untitled task");
|
|
97
|
-
|
|
121
|
+
rows.push(["task", `${pc.bold(id)} ${formatStatusPill(status)} ${title}`]);
|
|
98
122
|
}
|
|
99
|
-
return
|
|
123
|
+
return [
|
|
124
|
+
formatSuccessCard("Run submitted", rows),
|
|
125
|
+
"",
|
|
126
|
+
...formatNextSteps([`Attach: \`rig run attach ${input.runId} --follow\``, `Inspect: \`rig run show --run ${input.runId}\``])
|
|
127
|
+
].join(`
|
|
128
|
+
`);
|
|
129
|
+
}
|
|
130
|
+
function formatConnectionList(connections) {
|
|
131
|
+
const rows = [["local", { kind: "local", mode: "auto" }], ...Object.entries(connections)];
|
|
132
|
+
const aliasWidth = Math.min(24, Math.max(5, ...rows.map(([alias]) => alias.length)));
|
|
133
|
+
const lines = rows.map(([alias, connection]) => [
|
|
134
|
+
pc.bold(pad(truncate(alias, aliasWidth), aliasWidth)),
|
|
135
|
+
formatStatusPill(connection.kind),
|
|
136
|
+
connection.kind === "remote" ? connection.baseUrl ?? "" : connection.mode ?? "local"
|
|
137
|
+
].join(" "));
|
|
138
|
+
return [formatSection("Rig servers", `${rows.length} available`), `${pc.bold(pad("ALIAS", aliasWidth))} ${pc.bold("KIND")} ${pc.bold("TARGET")}`, ...lines, "", ...formatNextSteps(["Select one: `rig server use <alias|local>`"])].join(`
|
|
139
|
+
`);
|
|
140
|
+
}
|
|
141
|
+
function formatConnectionStatus(selected, connections) {
|
|
142
|
+
const connection = selected === "local" ? { kind: "local", mode: "auto" } : connections[selected];
|
|
143
|
+
const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
|
|
144
|
+
return [
|
|
145
|
+
formatSection("Rig server", "selected for this repo"),
|
|
146
|
+
`${pc.dim("\u2502")} ${pc.dim("selected ")} ${pc.bold(selected)}`,
|
|
147
|
+
`${pc.dim("\u2502")} ${pc.dim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
|
|
148
|
+
`${pc.dim("\u2502")} ${pc.dim("target ")} ${target ?? "not configured"}`,
|
|
149
|
+
"",
|
|
150
|
+
...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
|
|
151
|
+
].join(`
|
|
100
152
|
`);
|
|
101
153
|
}
|
|
102
154
|
export {
|
|
103
155
|
formatTaskList,
|
|
156
|
+
formatSuccessCard,
|
|
104
157
|
formatSubmittedRun,
|
|
105
|
-
|
|
158
|
+
formatStatusPill,
|
|
159
|
+
formatSection,
|
|
160
|
+
formatRunList,
|
|
161
|
+
formatNextSteps,
|
|
162
|
+
formatConnectionStatus,
|
|
163
|
+
formatConnectionList
|
|
106
164
|
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/commands/_help-catalog.ts
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
var PRIMARY_GROUPS = [
|
|
5
|
+
{
|
|
6
|
+
name: "server",
|
|
7
|
+
summary: "Choose, inspect, and start the Rig server that owns tasks and runs.",
|
|
8
|
+
usage: ["rig server <status|list|add|use|start> [options]"],
|
|
9
|
+
commands: [
|
|
10
|
+
{ command: "status", description: "Show the selected server for this repo.", primary: true },
|
|
11
|
+
{ command: "list", description: "List saved local/remote server aliases.", primary: true },
|
|
12
|
+
{ command: "add <alias> <url>", description: "Save a remote Rig server URL.", primary: true },
|
|
13
|
+
{ command: "use [alias|local]", description: "Select a server; prompts in an interactive TTY.", primary: true },
|
|
14
|
+
{ command: "start [--host <host>] [--port <n>]", description: "Start a local rig-server process.", primary: true }
|
|
15
|
+
],
|
|
16
|
+
examples: [
|
|
17
|
+
"rig server status",
|
|
18
|
+
"rig server add prod https://where.rig-does.work",
|
|
19
|
+
"rig server use prod",
|
|
20
|
+
"rig server use local",
|
|
21
|
+
"rig server start --port 3773"
|
|
22
|
+
],
|
|
23
|
+
next: ["Use `rig task list` to see server-owned work.", "Use `rig run list` or `rig run attach <id> --follow` to monitor runs."],
|
|
24
|
+
advanced: ["Compatibility alias: `rig connect ...` remains callable."]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "task",
|
|
28
|
+
summary: "Find work, start Pi-backed runs, and validate task results.",
|
|
29
|
+
usage: ["rig task <list|next|show|run> [options]"],
|
|
30
|
+
commands: [
|
|
31
|
+
{ command: "list [--assignee <login|@me>] [--state open|closed]", description: "List tasks from the selected server/source.", primary: true },
|
|
32
|
+
{ command: "next [filters]", description: "Pick the next matching task.", primary: true },
|
|
33
|
+
{ command: "show <id>|--task <id>", description: "Show task details.", primary: true },
|
|
34
|
+
{ command: "run [#<issue>|<task-id>|--next|--task <id>]", description: "Submit a task run; interactive follows with bundled Pi.", primary: true },
|
|
35
|
+
{ command: "validate|verify [--task <id>]", description: "Run configured task checks/review gates." },
|
|
36
|
+
{ command: "artifacts|artifact-dir|artifact-write", description: "Inspect or write task artifacts." },
|
|
37
|
+
{ command: "report-bug", description: "Create a structured bug report/task." }
|
|
38
|
+
],
|
|
39
|
+
examples: [
|
|
40
|
+
"rig task list --assignee @me --limit 20",
|
|
41
|
+
"rig task run --next",
|
|
42
|
+
"rig task run #123 --runtime-adapter pi",
|
|
43
|
+
"rig task run --title 'Investigate deploy drift' --initial-prompt 'Check server health'"
|
|
44
|
+
],
|
|
45
|
+
next: ["Use `--detach` to submit without attaching.", "Use `rig run attach <run-id> --follow` to rejoin a live run."]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "run",
|
|
49
|
+
summary: "Observe, attach to, and control Rig runs.",
|
|
50
|
+
usage: ["rig run <list|status|show|attach|stop> [options]"],
|
|
51
|
+
commands: [
|
|
52
|
+
{ command: "list", description: "List recent runs from the selected server or local state.", primary: true },
|
|
53
|
+
{ command: "status", description: "Summarize active and recent runs.", primary: true },
|
|
54
|
+
{ command: "show --run <id>", description: "Show one run record.", primary: true },
|
|
55
|
+
{ command: "attach <run-id>|--run <id> [--follow]", description: "Attach to the run; `--follow` launches native bundled Pi for live Pi runs.", primary: true },
|
|
56
|
+
{ command: "stop [<run-id>|--run <id>]", description: "Request stop for one run or local active runs.", primary: true },
|
|
57
|
+
{ command: "timeline --run <id> [--follow]", description: "Stream raw run timeline events." },
|
|
58
|
+
{ command: "delete|cleanup", description: "Remove completed run records/artifacts." }
|
|
59
|
+
],
|
|
60
|
+
examples: [
|
|
61
|
+
"rig run list",
|
|
62
|
+
"rig run attach 01234567-89ab-cdef-0123-456789abcdef --follow",
|
|
63
|
+
"rig run show --run <run-id>",
|
|
64
|
+
"rig run stop <run-id>"
|
|
65
|
+
],
|
|
66
|
+
next: ["Use `rig task run --next` to create a new run.", "Use `--json` when scripts need the full structured record."]
|
|
67
|
+
}
|
|
68
|
+
];
|
|
69
|
+
var ADVANCED_GROUPS = [
|
|
70
|
+
{ name: "init", summary: "Initialize or repair Rig project state.", usage: ["rig init [options]"], commands: [{ command: "init", description: "Configure project/server/GitHub integration." }] },
|
|
71
|
+
{ name: "connect", summary: "Compatibility alias for `rig server` selection commands.", usage: ["rig connect <status|list|add|use>"], commands: [{ command: "status|list|add|use", description: "Use `rig server ...` for the primary UX." }] },
|
|
72
|
+
{ name: "github", summary: "GitHub auth helpers.", usage: ["rig github auth <status|import-gh|token>"], commands: [{ command: "auth status", description: "Show GitHub auth state." }] },
|
|
73
|
+
{ name: "doctor", summary: "Diagnostics for project/server/GitHub/Pi state.", usage: ["rig doctor [check|run|shared|...]"], commands: [{ command: "check", description: "Run diagnostics." }] },
|
|
74
|
+
{ name: "setup", summary: "Bootstrap/check local setup.", usage: ["rig setup <bootstrap|check|preflight>"], commands: [{ command: "bootstrap|check|preflight", description: "Setup helpers." }] },
|
|
75
|
+
{ name: "inspect", summary: "Inspect logs, artifacts, graphs, failures.", usage: ["rig inspect <logs|artifacts|failures|graph|audit|diff>"], commands: [{ command: "logs --task <id>", description: "Inspect task logs." }] },
|
|
76
|
+
{ name: "repo", summary: "Repository sync/baseline helpers.", usage: ["rig repo <sync|reset-baseline>"], commands: [{ command: "sync", description: "Sync project repository state." }] },
|
|
77
|
+
{ name: "profile", summary: "Runtime profile/model defaults.", usage: ["rig profile <show|set>"], commands: [{ command: "show", description: "Show active profile." }] },
|
|
78
|
+
{ name: "review", summary: "Review policy configuration.", usage: ["rig review <show|set>"], commands: [{ command: "show", description: "Show review settings." }] },
|
|
79
|
+
{ name: "browser", summary: "Browser/app diagnostics.", usage: ["rig browser <help|explain|demo|app>"], commands: [{ command: "help", description: "Browser command help." }] },
|
|
80
|
+
{ name: "plugin", summary: "Plugin validation/listing.", usage: ["rig plugin <list|validate>"], commands: [{ command: "list", description: "List plugins." }] },
|
|
81
|
+
{ name: "queue", summary: "Run task queues locally.", usage: ["rig queue run [options]"], commands: [{ command: "run", description: "Process queue work." }] },
|
|
82
|
+
{ name: "agent", summary: "Runtime agent workspace helpers.", usage: ["rig agent <list|prepare|run|cleanup>"], commands: [{ command: "list", description: "List prepared agents." }] },
|
|
83
|
+
{ name: "inspector", summary: "Event stream and drift scanners.", usage: ["rig inspector <stream|scan-upstream-drift>"], commands: [{ command: "stream", description: "Stream events." }] },
|
|
84
|
+
{ name: "dist", summary: "Build/install packaged Rig CLI.", usage: ["rig dist <build|install|doctor>"], commands: [{ command: "build", description: "Build distribution." }] },
|
|
85
|
+
{ name: "workspace", summary: "Workspace topology/service helpers.", usage: ["rig workspace <summary|topology|remote-hosts>"], commands: [{ command: "summary", description: "Show workspace summary." }] },
|
|
86
|
+
{ name: "remote", summary: "Legacy remote orchestration controls.", usage: ["rig remote <status|watch|pause|resume|...>"], commands: [{ command: "status", description: "Show remote state." }] },
|
|
87
|
+
{ name: "inbox", summary: "Approval/input inbox for blocked runs.", usage: ["rig inbox <approvals|approve|inputs|respond>"], commands: [{ command: "approvals", description: "List pending approvals." }] },
|
|
88
|
+
{ name: "git", summary: "Pass through to Rig git-flow helper.", usage: ["rig git <args...>"], commands: [{ command: "<args...>", description: "Advanced git flow operations." }] },
|
|
89
|
+
{ name: "harness", summary: "Pass through to runtime harness CLI.", usage: ["rig harness <args...>"], commands: [{ command: "<args...>", description: "Advanced harness operations." }] },
|
|
90
|
+
{ name: "test", summary: "Project test wrappers.", usage: ["rig test <unit|e2e|all>"], commands: [{ command: "all", description: "Run configured project tests." }] }
|
|
91
|
+
];
|
|
92
|
+
var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
|
|
93
|
+
function heading(title) {
|
|
94
|
+
return pc.bold(pc.cyan(title));
|
|
95
|
+
}
|
|
96
|
+
function commandLine(command, description) {
|
|
97
|
+
const commandColumn = command.length >= 34 ? `${command} ` : command.padEnd(34);
|
|
98
|
+
return ` ${pc.bold(commandColumn)} ${description}`;
|
|
99
|
+
}
|
|
100
|
+
function renderGroup(group) {
|
|
101
|
+
const lines = [
|
|
102
|
+
`${heading(`rig ${group.name}`)} \u2014 ${group.summary}`,
|
|
103
|
+
"",
|
|
104
|
+
pc.bold("Usage"),
|
|
105
|
+
...group.usage.map((line) => ` ${line}`),
|
|
106
|
+
"",
|
|
107
|
+
pc.bold("Commands"),
|
|
108
|
+
...group.commands.map((entry) => commandLine(entry.command, entry.description))
|
|
109
|
+
];
|
|
110
|
+
if (group.examples?.length) {
|
|
111
|
+
lines.push("", pc.bold("Examples"), ...group.examples.map((line) => ` ${pc.dim("$")} ${line}`));
|
|
112
|
+
}
|
|
113
|
+
if (group.next?.length) {
|
|
114
|
+
lines.push("", pc.bold("Next steps"), ...group.next.map((line) => ` ${pc.dim("\u203A")} ${line}`));
|
|
115
|
+
}
|
|
116
|
+
if (group.advanced?.length) {
|
|
117
|
+
lines.push("", pc.bold("Compatibility / advanced"), ...group.advanced.map((line) => ` ${pc.dim("\u203A")} ${line}`));
|
|
118
|
+
}
|
|
119
|
+
return lines.join(`
|
|
120
|
+
`);
|
|
121
|
+
}
|
|
122
|
+
function renderTopLevelHelp() {
|
|
123
|
+
return [
|
|
124
|
+
`${heading("rig")} \u2014 server-owned task/run control plane`,
|
|
125
|
+
"",
|
|
126
|
+
pc.bold("Common workflows"),
|
|
127
|
+
" rig server status Show selected local/remote server",
|
|
128
|
+
" rig task list List available work",
|
|
129
|
+
" rig task run --next Start next task and attach with native Pi",
|
|
130
|
+
" rig run list List recent runs",
|
|
131
|
+
" rig run attach <run-id> --follow Rejoin a live run",
|
|
132
|
+
"",
|
|
133
|
+
pc.bold("Primary groups"),
|
|
134
|
+
...PRIMARY_GROUPS.map((group) => commandLine(group.name, group.summary)),
|
|
135
|
+
"",
|
|
136
|
+
pc.bold("Help"),
|
|
137
|
+
" rig <group> --help Rich help for server, task, run, and other groups",
|
|
138
|
+
" rig help --advanced Legacy/dev/compat command surface",
|
|
139
|
+
" rig --version Print version",
|
|
140
|
+
"",
|
|
141
|
+
pc.bold("Global options"),
|
|
142
|
+
" --project <path> Use a project root instead of auto-discovery",
|
|
143
|
+
" --json Output structured JSON",
|
|
144
|
+
" --dry-run Print command execution plan only"
|
|
145
|
+
].join(`
|
|
146
|
+
`);
|
|
147
|
+
}
|
|
148
|
+
function renderAdvancedHelp() {
|
|
149
|
+
return [
|
|
150
|
+
`${heading("rig advanced")} \u2014 legacy, dev, and compatibility groups`,
|
|
151
|
+
"",
|
|
152
|
+
pc.bold("Primary groups are still"),
|
|
153
|
+
" server, task, run",
|
|
154
|
+
"",
|
|
155
|
+
pc.bold("Advanced groups"),
|
|
156
|
+
...ADVANCED_GROUPS.map((group) => commandLine(group.name, group.summary)),
|
|
157
|
+
"",
|
|
158
|
+
pc.dim("All groups remain callable. Prefer `rig server`, `rig task`, and `rig run` for day-to-day work.")
|
|
159
|
+
].join(`
|
|
160
|
+
`);
|
|
161
|
+
}
|
|
162
|
+
function renderGroupHelp(groupName) {
|
|
163
|
+
const group = ALL_GROUPS.find((candidate) => candidate.name === groupName);
|
|
164
|
+
return group ? renderGroup(group) : null;
|
|
165
|
+
}
|
|
166
|
+
function listHelpGroups() {
|
|
167
|
+
return ALL_GROUPS.map((group) => group.name);
|
|
168
|
+
}
|
|
169
|
+
export {
|
|
170
|
+
renderTopLevelHelp,
|
|
171
|
+
renderGroupHelp,
|
|
172
|
+
renderAdvancedHelp,
|
|
173
|
+
listHelpGroups
|
|
174
|
+
};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// packages/cli/src/commands/connect.ts
|
|
3
|
+
import { cancel, isCancel, select } from "@clack/prompts";
|
|
4
|
+
|
|
2
5
|
// packages/cli/src/runner.ts
|
|
3
6
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
4
7
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
@@ -103,12 +106,81 @@ function writeRepoConnection(projectRoot, state) {
|
|
|
103
106
|
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
104
107
|
}
|
|
105
108
|
|
|
109
|
+
// packages/cli/src/commands/_cli-format.ts
|
|
110
|
+
import pc from "picocolors";
|
|
111
|
+
function truncate(value, width) {
|
|
112
|
+
if (value.length <= width)
|
|
113
|
+
return value;
|
|
114
|
+
if (width <= 1)
|
|
115
|
+
return "\u2026";
|
|
116
|
+
return `${value.slice(0, width - 1)}\u2026`;
|
|
117
|
+
}
|
|
118
|
+
function pad(value, width) {
|
|
119
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
120
|
+
}
|
|
121
|
+
function statusColor(status) {
|
|
122
|
+
const normalized = status.toLowerCase();
|
|
123
|
+
if (["completed", "merged", "closed", "done", "accepted", "pass", "selected"].includes(normalized))
|
|
124
|
+
return pc.green;
|
|
125
|
+
if (["failed", "needs_attention", "needs-attention", "blocked", "error"].includes(normalized))
|
|
126
|
+
return pc.red;
|
|
127
|
+
if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
|
|
128
|
+
return pc.cyan;
|
|
129
|
+
if (["ready", "open", "queued", "created", "preparing", "local"].includes(normalized))
|
|
130
|
+
return pc.yellow;
|
|
131
|
+
return pc.dim;
|
|
132
|
+
}
|
|
133
|
+
function formatStatusPill(status) {
|
|
134
|
+
const label = status || "unknown";
|
|
135
|
+
return statusColor(label)(`\u25CF ${label}`);
|
|
136
|
+
}
|
|
137
|
+
function formatSection(title, subtitle) {
|
|
138
|
+
return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
|
|
139
|
+
}
|
|
140
|
+
function formatSuccessCard(title, rows = []) {
|
|
141
|
+
const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc.dim("\u2502")} ${pc.dim(key.padEnd(9))} ${value}`);
|
|
142
|
+
return [formatSection(title), ...body].join(`
|
|
143
|
+
`);
|
|
144
|
+
}
|
|
145
|
+
function formatNextSteps(steps) {
|
|
146
|
+
if (steps.length === 0)
|
|
147
|
+
return [];
|
|
148
|
+
return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
|
|
149
|
+
}
|
|
150
|
+
function formatConnectionList(connections) {
|
|
151
|
+
const rows = [["local", { kind: "local", mode: "auto" }], ...Object.entries(connections)];
|
|
152
|
+
const aliasWidth = Math.min(24, Math.max(5, ...rows.map(([alias]) => alias.length)));
|
|
153
|
+
const lines = rows.map(([alias, connection]) => [
|
|
154
|
+
pc.bold(pad(truncate(alias, aliasWidth), aliasWidth)),
|
|
155
|
+
formatStatusPill(connection.kind),
|
|
156
|
+
connection.kind === "remote" ? connection.baseUrl ?? "" : connection.mode ?? "local"
|
|
157
|
+
].join(" "));
|
|
158
|
+
return [formatSection("Rig servers", `${rows.length} available`), `${pc.bold(pad("ALIAS", aliasWidth))} ${pc.bold("KIND")} ${pc.bold("TARGET")}`, ...lines, "", ...formatNextSteps(["Select one: `rig server use <alias|local>`"])].join(`
|
|
159
|
+
`);
|
|
160
|
+
}
|
|
161
|
+
function formatConnectionStatus(selected, connections) {
|
|
162
|
+
const connection = selected === "local" ? { kind: "local", mode: "auto" } : connections[selected];
|
|
163
|
+
const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
|
|
164
|
+
return [
|
|
165
|
+
formatSection("Rig server", "selected for this repo"),
|
|
166
|
+
`${pc.dim("\u2502")} ${pc.dim("selected ")} ${pc.bold(selected)}`,
|
|
167
|
+
`${pc.dim("\u2502")} ${pc.dim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
|
|
168
|
+
`${pc.dim("\u2502")} ${pc.dim("target ")} ${target ?? "not configured"}`,
|
|
169
|
+
"",
|
|
170
|
+
...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
|
|
171
|
+
].join(`
|
|
172
|
+
`);
|
|
173
|
+
}
|
|
174
|
+
|
|
106
175
|
// packages/cli/src/commands/connect.ts
|
|
107
|
-
function
|
|
176
|
+
function usageName(options) {
|
|
177
|
+
return `rig ${options.group}`;
|
|
178
|
+
}
|
|
179
|
+
function parseConnection(alias, value, options) {
|
|
108
180
|
if (alias === "local" && !value)
|
|
109
181
|
return { kind: "local", mode: "auto" };
|
|
110
182
|
if (!value)
|
|
111
|
-
throw new CliError2(
|
|
183
|
+
throw new CliError2(`Missing remote server URL. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
112
184
|
let parsed;
|
|
113
185
|
try {
|
|
114
186
|
parsed = new URL(value);
|
|
@@ -127,54 +199,90 @@ function printJsonOrText(context, payload, text) {
|
|
|
127
199
|
console.log(text);
|
|
128
200
|
}
|
|
129
201
|
}
|
|
130
|
-
async function
|
|
202
|
+
async function promptForConnectionAlias(context) {
|
|
203
|
+
const state = readGlobalConnections();
|
|
204
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
205
|
+
const options = [
|
|
206
|
+
{ value: "local", label: "local", hint: "Use/start a local Rig server" },
|
|
207
|
+
...Object.entries(state.connections).map(([alias, connection]) => ({
|
|
208
|
+
value: alias,
|
|
209
|
+
label: alias,
|
|
210
|
+
hint: connection.kind === "remote" ? connection.baseUrl : "local"
|
|
211
|
+
}))
|
|
212
|
+
].filter((option, index, all) => all.findIndex((candidate) => candidate.value === option.value) === index);
|
|
213
|
+
const answer = await select({
|
|
214
|
+
message: "Select Rig server for this repo",
|
|
215
|
+
initialValue: repo?.selected ?? "local",
|
|
216
|
+
options
|
|
217
|
+
});
|
|
218
|
+
if (isCancel(answer)) {
|
|
219
|
+
cancel("No server selected.");
|
|
220
|
+
throw new CliError2("No server selected.", 3);
|
|
221
|
+
}
|
|
222
|
+
return String(answer);
|
|
223
|
+
}
|
|
224
|
+
async function executeConnectionCommand(context, args, options) {
|
|
131
225
|
const [command, ...rest] = args;
|
|
132
226
|
switch (command ?? "status") {
|
|
133
227
|
case "list": {
|
|
134
|
-
requireNoExtraArgs(rest,
|
|
228
|
+
requireNoExtraArgs(rest, `${usageName(options)} list`);
|
|
135
229
|
const state = readGlobalConnections();
|
|
136
|
-
printJsonOrText(context, state,
|
|
137
|
-
|
|
138
|
-
return { ok: true, group: "connect", command: "list", details: state };
|
|
230
|
+
printJsonOrText(context, state, formatConnectionList(state.connections));
|
|
231
|
+
return { ok: true, group: options.group, command: "list", details: state };
|
|
139
232
|
}
|
|
140
233
|
case "add": {
|
|
141
234
|
const [alias, url, ...extra] = rest;
|
|
142
235
|
if (!alias)
|
|
143
|
-
throw new CliError2(
|
|
144
|
-
requireNoExtraArgs(extra,
|
|
145
|
-
const connection = parseConnection(alias, url);
|
|
236
|
+
throw new CliError2(`Missing alias. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
237
|
+
requireNoExtraArgs(extra, `${usageName(options)} add <alias> <url>`);
|
|
238
|
+
const connection = parseConnection(alias, url, options);
|
|
146
239
|
const state = upsertGlobalConnection(alias, connection);
|
|
147
|
-
printJsonOrText(context, { alias, connection },
|
|
148
|
-
|
|
240
|
+
printJsonOrText(context, { alias, connection }, formatSuccessCard("Rig server saved", [
|
|
241
|
+
["alias", alias],
|
|
242
|
+
["target", connection.kind === "remote" ? connection.baseUrl : "local"],
|
|
243
|
+
["next", `${usageName(options)} use ${alias}`]
|
|
244
|
+
]));
|
|
245
|
+
return { ok: true, group: options.group, command: "add", details: { alias, connection, count: Object.keys(state.connections).length } };
|
|
149
246
|
}
|
|
150
247
|
case "use": {
|
|
151
|
-
|
|
248
|
+
let [alias, ...extra] = rest;
|
|
249
|
+
requireNoExtraArgs(extra, `${usageName(options)} use <alias|local>`);
|
|
250
|
+
if (!alias && options.interactiveUse && context.outputMode === "text" && process.stdin.isTTY) {
|
|
251
|
+
alias = await promptForConnectionAlias(context);
|
|
252
|
+
}
|
|
152
253
|
if (!alias)
|
|
153
|
-
throw new CliError2(
|
|
154
|
-
requireNoExtraArgs(extra, "rig connect use <alias|local>");
|
|
254
|
+
throw new CliError2(`Missing alias. Usage: ${usageName(options)} use <alias|local>`, 1);
|
|
155
255
|
if (alias !== "local") {
|
|
156
256
|
const state = readGlobalConnections();
|
|
157
257
|
if (!state.connections[alias])
|
|
158
|
-
throw new CliError2(`Unknown Rig
|
|
258
|
+
throw new CliError2(`Unknown Rig server: ${alias}`, 1);
|
|
159
259
|
}
|
|
160
260
|
const repoState = { selected: alias, linkedAt: new Date().toISOString() };
|
|
161
261
|
writeRepoConnection(context.projectRoot, repoState);
|
|
162
|
-
printJsonOrText(context, repoState,
|
|
163
|
-
|
|
262
|
+
printJsonOrText(context, repoState, formatSuccessCard("Rig server selected", [
|
|
263
|
+
["selected", alias],
|
|
264
|
+
["scope", "this repo"],
|
|
265
|
+
["next", "rig task list"]
|
|
266
|
+
]));
|
|
267
|
+
return { ok: true, group: options.group, command: "use", details: repoState };
|
|
164
268
|
}
|
|
165
269
|
case "status": {
|
|
166
|
-
requireNoExtraArgs(rest,
|
|
270
|
+
requireNoExtraArgs(rest, `${usageName(options)} status`);
|
|
167
271
|
const repo = readRepoConnection(context.projectRoot);
|
|
168
272
|
const global = readGlobalConnections();
|
|
169
273
|
const details = { selected: repo?.selected ?? "local", repo, connections: global.connections };
|
|
170
|
-
printJsonOrText(context, details,
|
|
171
|
-
return { ok: true, group:
|
|
274
|
+
printJsonOrText(context, details, formatConnectionStatus(details.selected, global.connections));
|
|
275
|
+
return { ok: true, group: options.group, command: "status", details };
|
|
172
276
|
}
|
|
173
277
|
default:
|
|
174
|
-
throw new CliError2(`Unknown
|
|
175
|
-
Usage:
|
|
278
|
+
throw new CliError2(`Unknown ${options.group} command: ${String(command)}
|
|
279
|
+
Usage: ${usageName(options)} <list|add|use|status>`, 1);
|
|
176
280
|
}
|
|
177
281
|
}
|
|
282
|
+
async function executeConnect(context, args) {
|
|
283
|
+
return executeConnectionCommand(context, args, { group: "connect" });
|
|
284
|
+
}
|
|
178
285
|
export {
|
|
286
|
+
executeConnectionCommand,
|
|
179
287
|
executeConnect
|
|
180
288
|
};
|
package/dist/src/commands/run.js
CHANGED
|
@@ -1236,19 +1236,33 @@ function pad(value, width) {
|
|
|
1236
1236
|
}
|
|
1237
1237
|
function statusColor(status) {
|
|
1238
1238
|
const normalized = status.toLowerCase();
|
|
1239
|
-
if (["completed", "merged", "closed", "done", "accepted"].includes(normalized))
|
|
1239
|
+
if (["completed", "merged", "closed", "done", "accepted", "pass", "selected"].includes(normalized))
|
|
1240
1240
|
return pc.green;
|
|
1241
|
-
if (["failed", "needs_attention", "needs-attention", "blocked"].includes(normalized))
|
|
1241
|
+
if (["failed", "needs_attention", "needs-attention", "blocked", "error"].includes(normalized))
|
|
1242
1242
|
return pc.red;
|
|
1243
|
-
if (["running", "reviewing", "validating", "in_progress", "in-progress"].includes(normalized))
|
|
1243
|
+
if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
|
|
1244
1244
|
return pc.cyan;
|
|
1245
|
-
if (["ready", "open", "queued", "created", "preparing"].includes(normalized))
|
|
1245
|
+
if (["ready", "open", "queued", "created", "preparing", "local"].includes(normalized))
|
|
1246
1246
|
return pc.yellow;
|
|
1247
1247
|
return pc.dim;
|
|
1248
1248
|
}
|
|
1249
|
+
function formatSection(title, subtitle) {
|
|
1250
|
+
return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
|
|
1251
|
+
}
|
|
1252
|
+
function formatNextSteps(steps) {
|
|
1253
|
+
if (steps.length === 0)
|
|
1254
|
+
return [];
|
|
1255
|
+
return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
|
|
1256
|
+
}
|
|
1249
1257
|
function formatRunList(runs, options = {}) {
|
|
1250
1258
|
if (runs.length === 0) {
|
|
1251
|
-
return
|
|
1259
|
+
return [
|
|
1260
|
+
formatSection("Runs", "none recorded"),
|
|
1261
|
+
options.source === "server" ? pc.dim("No runs recorded on the selected Rig server.") : pc.dim("No runs recorded in .rig/runs."),
|
|
1262
|
+
"",
|
|
1263
|
+
...formatNextSteps(["Start one: `rig task run --next`", "Check server: `rig server status`"])
|
|
1264
|
+
].join(`
|
|
1265
|
+
`);
|
|
1252
1266
|
}
|
|
1253
1267
|
const rows = runs.map((run) => {
|
|
1254
1268
|
const runId = stringField(run, "runId", stringField(run, "id", "(unknown-run)"));
|
|
@@ -1266,7 +1280,7 @@ function formatRunList(runs, options = {}) {
|
|
|
1266
1280
|
statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
|
|
1267
1281
|
`${row.title}${row.runtime ? pc.dim(` ${row.runtime}`) : ""}`
|
|
1268
1282
|
].join(" "));
|
|
1269
|
-
return [
|
|
1283
|
+
return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), header, ...body, "", ...formatNextSteps(["Follow live: `rig run attach <run-id> --follow`", "Details: `rig run show --run <run-id>`"])].join(`
|
|
1270
1284
|
`);
|
|
1271
1285
|
}
|
|
1272
1286
|
|