@relay-baton/cli 1.0.0

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.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/dist/commands/agentFor.d.ts +8 -0
  3. package/dist/commands/agentFor.js +28 -0
  4. package/dist/commands/auditApiKeyEnv.d.ts +6 -0
  5. package/dist/commands/auditApiKeyEnv.js +27 -0
  6. package/dist/commands/budget.d.ts +5 -0
  7. package/dist/commands/budget.js +86 -0
  8. package/dist/commands/chat.d.ts +5 -0
  9. package/dist/commands/chat.js +218 -0
  10. package/dist/commands/checkpoint.d.ts +10 -0
  11. package/dist/commands/checkpoint.js +78 -0
  12. package/dist/commands/compact.d.ts +4 -0
  13. package/dist/commands/compact.js +22 -0
  14. package/dist/commands/compress.d.ts +4 -0
  15. package/dist/commands/compress.js +61 -0
  16. package/dist/commands/compressContext.d.ts +8 -0
  17. package/dist/commands/compressContext.js +51 -0
  18. package/dist/commands/conversation.d.ts +8 -0
  19. package/dist/commands/conversation.js +90 -0
  20. package/dist/commands/diagnostics.d.ts +23 -0
  21. package/dist/commands/diagnostics.js +254 -0
  22. package/dist/commands/doctor.d.ts +5 -0
  23. package/dist/commands/doctor.js +104 -0
  24. package/dist/commands/execute.d.ts +9 -0
  25. package/dist/commands/execute.js +183 -0
  26. package/dist/commands/git.d.ts +6 -0
  27. package/dist/commands/git.js +82 -0
  28. package/dist/commands/guard.d.ts +7 -0
  29. package/dist/commands/guard.js +30 -0
  30. package/dist/commands/handoff.d.ts +10 -0
  31. package/dist/commands/handoff.js +133 -0
  32. package/dist/commands/handoffBundle.d.ts +12 -0
  33. package/dist/commands/handoffBundle.js +64 -0
  34. package/dist/commands/handoffHistory.d.ts +23 -0
  35. package/dist/commands/handoffHistory.js +129 -0
  36. package/dist/commands/handoffShow.d.ts +12 -0
  37. package/dist/commands/handoffShow.js +73 -0
  38. package/dist/commands/init.d.ts +2 -0
  39. package/dist/commands/init.js +19 -0
  40. package/dist/commands/inventory.d.ts +5 -0
  41. package/dist/commands/inventory.js +23 -0
  42. package/dist/commands/login.d.ts +3 -0
  43. package/dist/commands/login.js +80 -0
  44. package/dist/commands/migrate.d.ts +8 -0
  45. package/dist/commands/migrate.js +55 -0
  46. package/dist/commands/plan.d.ts +13 -0
  47. package/dist/commands/plan.js +159 -0
  48. package/dist/commands/profile.d.ts +5 -0
  49. package/dist/commands/profile.js +23 -0
  50. package/dist/commands/project.d.ts +18 -0
  51. package/dist/commands/project.js +173 -0
  52. package/dist/commands/projectOptions.d.ts +7 -0
  53. package/dist/commands/projectOptions.js +21 -0
  54. package/dist/commands/receipt.d.ts +8 -0
  55. package/dist/commands/receipt.js +48 -0
  56. package/dist/commands/replay.d.ts +8 -0
  57. package/dist/commands/replay.js +35 -0
  58. package/dist/commands/report.d.ts +6 -0
  59. package/dist/commands/report.js +54 -0
  60. package/dist/commands/review.d.ts +8 -0
  61. package/dist/commands/review.js +63 -0
  62. package/dist/commands/risk.d.ts +5 -0
  63. package/dist/commands/risk.js +25 -0
  64. package/dist/commands/run.d.ts +31 -0
  65. package/dist/commands/run.js +323 -0
  66. package/dist/commands/session.d.ts +40 -0
  67. package/dist/commands/session.js +158 -0
  68. package/dist/commands/sessionWorkspace.d.ts +25 -0
  69. package/dist/commands/sessionWorkspace.js +193 -0
  70. package/dist/commands/status.d.ts +5 -0
  71. package/dist/commands/status.js +116 -0
  72. package/dist/commands/tui.d.ts +4 -0
  73. package/dist/commands/tui.js +46 -0
  74. package/dist/commands/usage.d.ts +11 -0
  75. package/dist/commands/usage.js +40 -0
  76. package/dist/commands/verify.d.ts +15 -0
  77. package/dist/commands/verify.js +197 -0
  78. package/dist/commands/workspace.d.ts +5 -0
  79. package/dist/commands/workspace.js +27 -0
  80. package/dist/index.d.ts +2 -0
  81. package/dist/index.js +394 -0
  82. package/package.json +57 -0
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.projectAddCommand = projectAddCommand;
37
+ exports.projectListCommand = projectListCommand;
38
+ exports.projectSwitchCommand = projectSwitchCommand;
39
+ exports.projectCurrentCommand = projectCurrentCommand;
40
+ exports.projectRemoveCommand = projectRemoveCommand;
41
+ exports.projectDoctorCommand = projectDoctorCommand;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const core_1 = require("@relay-baton/core");
45
+ const diets = new Set(["off", "lite", "balanced", "caveman", "ultra"]);
46
+ const agents = new Set(["codex", "claude", "opencode", "gemini", "aider"]);
47
+ function assertDiet(value) {
48
+ if (!value)
49
+ return undefined;
50
+ if (!diets.has(value)) {
51
+ console.error(`unknown diet profile: ${value}`);
52
+ process.exit(2);
53
+ }
54
+ return value;
55
+ }
56
+ function assertAgent(value, label = "agent") {
57
+ if (!value)
58
+ return undefined;
59
+ if (!agents.has(value)) {
60
+ console.error(`unknown ${label}: ${value}`);
61
+ process.exit(2);
62
+ }
63
+ return value;
64
+ }
65
+ async function projectAddCommand(projectPath, opts) {
66
+ const resolved = path.resolve(projectPath);
67
+ if (!fs.existsSync(resolved)) {
68
+ console.error(`[relay-baton] path does not exist: ${resolved}`);
69
+ process.exit(2);
70
+ }
71
+ if (!fs.statSync(resolved).isDirectory()) {
72
+ console.error(`[relay-baton] path is not a directory: ${resolved}`);
73
+ process.exit(2);
74
+ }
75
+ const manager = new core_1.ProjectManager();
76
+ const result = manager.addProject({
77
+ path: resolved,
78
+ name: opts.name,
79
+ defaultDiet: assertDiet(opts.diet),
80
+ primaryAgent: assertAgent(opts.primary, "primary agent"),
81
+ fallbackAgent: assertAgent(opts.fallback, "fallback agent"),
82
+ });
83
+ if (!result.added) {
84
+ if (opts.json) {
85
+ console.log(JSON.stringify({ added: false, project: result.project }, null, 2));
86
+ return;
87
+ }
88
+ console.log("[relay-baton] project already registered:");
89
+ printProject(result.project, true);
90
+ return;
91
+ }
92
+ if (opts.json) {
93
+ console.log(JSON.stringify({ added: true, project: result.project }, null, 2));
94
+ return;
95
+ }
96
+ console.log("[relay-baton] project added:");
97
+ printProject(result.project, true);
98
+ }
99
+ async function projectListCommand(opts = {}) {
100
+ const manager = new core_1.ProjectManager();
101
+ const data = manager.getRegistry();
102
+ if (opts.json) {
103
+ console.log(JSON.stringify({
104
+ activeProjectId: data.activeProjectId ?? null,
105
+ projects: data.projects,
106
+ }, null, 2));
107
+ return;
108
+ }
109
+ if (data.projects.length === 0) {
110
+ console.log("[relay-baton] no registered projects.");
111
+ return;
112
+ }
113
+ for (const p of data.projects)
114
+ printProject(p, data.activeProjectId === p.id);
115
+ }
116
+ async function projectSwitchCommand(nameOrId) {
117
+ const project = new core_1.ProjectManager().switchProject(nameOrId);
118
+ if (!project) {
119
+ console.error(`[relay-baton] project not found: ${nameOrId}`);
120
+ process.exit(2);
121
+ }
122
+ console.log(`[relay-baton] active project: ${project.name} (${project.path})`);
123
+ }
124
+ async function projectCurrentCommand(opts = {}) {
125
+ const active = new core_1.ProjectManager().getActiveProject();
126
+ if (opts.json) {
127
+ console.log(JSON.stringify({ active: active ?? null, cwd: process.cwd() }, null, 2));
128
+ return;
129
+ }
130
+ if (!active) {
131
+ console.log(`[relay-baton] no active project. Using cwd: ${process.cwd()}`);
132
+ return;
133
+ }
134
+ printProject(active, true);
135
+ }
136
+ async function projectRemoveCommand(nameOrId, opts = {}) {
137
+ const removed = new core_1.ProjectManager().removeProject(nameOrId);
138
+ if (!removed) {
139
+ console.error(`[relay-baton] project not found: ${nameOrId}`);
140
+ process.exit(2);
141
+ }
142
+ if (opts.json) {
143
+ console.log(JSON.stringify({ removed }, null, 2));
144
+ return;
145
+ }
146
+ console.log(`[relay-baton] project removed: ${removed.name}`);
147
+ }
148
+ async function projectDoctorCommand() {
149
+ const manager = new core_1.ProjectManager();
150
+ const data = manager.getRegistry();
151
+ if (data.projects.length === 0) {
152
+ console.log("[relay-baton] no registered projects.");
153
+ return;
154
+ }
155
+ for (const p of data.projects) {
156
+ const exists = fs.existsSync(p.path);
157
+ console.log(`${data.activeProjectId === p.id ? "*" : " "} ${p.name} (${p.id})`);
158
+ console.log(` path: ${p.path}`);
159
+ console.log(` exists: ${exists ? "yes" : "no"}`);
160
+ console.log(` git repo: ${exists && new core_1.GitService(p.path).isGitRepo() ? "yes" : "no"}`);
161
+ console.log(` .ai-session: ${fs.existsSync(path.join(p.path, ".ai-session")) ? "yes" : "no"}`);
162
+ console.log(` AGENTS.md: ${fs.existsSync(path.join(p.path, "AGENTS.md")) ? "yes" : "no"}`);
163
+ console.log(` CLAUDE.md: ${fs.existsSync(path.join(p.path, "CLAUDE.md")) ? "yes" : "no"}`);
164
+ }
165
+ }
166
+ function printProject(project, active) {
167
+ const marker = active ? "*" : " ";
168
+ console.log(`${marker} ${project.name} (${project.id})`);
169
+ console.log(` path: ${project.path}`);
170
+ console.log(` defaultDiet: ${project.defaultDiet ?? "-"}`);
171
+ console.log(` agents: ${project.primaryAgent ?? "-"} -> ${project.fallbackAgent ?? "-"}`);
172
+ console.log(` lastUsedAt: ${project.lastUsedAt ?? "-"}`);
173
+ }
@@ -0,0 +1,7 @@
1
+ import { ProjectResolveResult } from "@relay-baton/core";
2
+ export interface ProjectOpts {
3
+ project?: string;
4
+ path?: string;
5
+ }
6
+ export declare function resolveRepoRoot(opts?: ProjectOpts): string;
7
+ export declare function resolveProjectContext(opts?: ProjectOpts, touch?: boolean): ProjectResolveResult;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveRepoRoot = resolveRepoRoot;
4
+ exports.resolveProjectContext = resolveProjectContext;
5
+ const core_1 = require("@relay-baton/core");
6
+ function resolveRepoRoot(opts = {}) {
7
+ return resolveProjectContext(opts).repoRoot;
8
+ }
9
+ function resolveProjectContext(opts = {}, touch = false) {
10
+ try {
11
+ const resolver = new core_1.ProjectResolver();
12
+ const result = resolver.resolve(opts);
13
+ if (touch)
14
+ resolver.touch(result);
15
+ return result;
16
+ }
17
+ catch (e) {
18
+ console.error(`[relay-baton] ${e?.message ?? e}`);
19
+ process.exit(2);
20
+ }
21
+ }
@@ -0,0 +1,8 @@
1
+ import { ProjectOpts } from "./projectOptions";
2
+ export interface ReceiptOpts extends ProjectOpts {
3
+ note?: string;
4
+ json?: boolean;
5
+ }
6
+ export declare function receiptDoneCommand(step: string, opts?: ReceiptOpts): Promise<void>;
7
+ export declare function receiptSkipCommand(step: string, opts?: ReceiptOpts): Promise<void>;
8
+ export declare function receiptListCommand(opts?: ReceiptOpts): Promise<void>;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.receiptDoneCommand = receiptDoneCommand;
4
+ exports.receiptSkipCommand = receiptSkipCommand;
5
+ exports.receiptListCommand = receiptListCommand;
6
+ const core_1 = require("@relay-baton/core");
7
+ const projectOptions_1 = require("./projectOptions");
8
+ async function record(status, step, opts) {
9
+ const n = Number(step);
10
+ if (!Number.isInteger(n) || n < 1) {
11
+ console.error(`[relay-baton] step must be a positive integer, got: ${step}`);
12
+ process.exit(2);
13
+ }
14
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
15
+ const { config } = core_1.ConfigLoader.load(repoRoot);
16
+ const meta = new core_1.SessionManager(repoRoot, config).getMeta();
17
+ const receipt = new core_1.PlanReceipts(repoRoot).append(n, status, opts.note ?? "");
18
+ new core_1.ConversationLog(repoRoot).append({
19
+ role: "relay-baton",
20
+ kind: "execute",
21
+ text: `receipt: step ${n} ${status}${receipt.note ? " — " + receipt.note : ""}`,
22
+ sessionId: meta?.id,
23
+ refs: { plan: "plan.md" },
24
+ meta: { stepRefs: [`step-${n}`], confirmed: true },
25
+ });
26
+ console.log(`[relay-baton] recorded [${status}] for step ${n}.`);
27
+ }
28
+ async function receiptDoneCommand(step, opts = {}) {
29
+ return record("done", step, opts);
30
+ }
31
+ async function receiptSkipCommand(step, opts = {}) {
32
+ return record("skipped", step, opts);
33
+ }
34
+ async function receiptListCommand(opts = {}) {
35
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
36
+ const receipts = new core_1.PlanReceipts(repoRoot).list();
37
+ if (opts.json) {
38
+ console.log(JSON.stringify(receipts, null, 2));
39
+ return;
40
+ }
41
+ if (receipts.length === 0) {
42
+ console.log("[relay-baton] no execution receipts recorded.");
43
+ return;
44
+ }
45
+ for (const r of receipts) {
46
+ console.log(`[${r.status}] step ${r.step}${r.note ? " — " + r.note : ""} (${r.ts})`);
47
+ }
48
+ }
@@ -0,0 +1,8 @@
1
+ import { ProjectOpts } from "./projectOptions";
2
+ export interface ReplayOpts extends ProjectOpts {
3
+ json?: boolean;
4
+ session?: string;
5
+ kind?: string;
6
+ limit?: string;
7
+ }
8
+ export declare function replayCommand(opts?: ReplayOpts): Promise<void>;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.replayCommand = replayCommand;
4
+ const core_1 = require("@relay-baton/core");
5
+ const projectOptions_1 = require("./projectOptions");
6
+ async function replayCommand(opts = {}) {
7
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
8
+ const replay = new core_1.ConversationReplay(repoRoot);
9
+ const kinds = opts.kind
10
+ ? opts.kind.split(",").map((k) => k.trim()).filter(Boolean)
11
+ : undefined;
12
+ const limit = opts.limit !== undefined ? Number(opts.limit) : undefined;
13
+ const result = replay.build({
14
+ sessionId: opts.session,
15
+ kinds,
16
+ limit: limit !== undefined && Number.isFinite(limit) ? limit : undefined,
17
+ });
18
+ if (opts.json) {
19
+ console.log(JSON.stringify(result, null, 2));
20
+ return;
21
+ }
22
+ if (result.entries.length === 0) {
23
+ console.log("[relay-baton] no conversation events. (nothing recorded yet)");
24
+ return;
25
+ }
26
+ for (const line of replay.renderLines({
27
+ sessionId: opts.session,
28
+ kinds,
29
+ limit: limit !== undefined && Number.isFinite(limit) ? limit : undefined,
30
+ })) {
31
+ console.log(line);
32
+ }
33
+ const s = result.summary;
34
+ console.log(`\n${s.total} events; roles ${JSON.stringify(s.byRole)}; kinds ${JSON.stringify(s.byKind)}`);
35
+ }
@@ -0,0 +1,6 @@
1
+ import { ProjectOpts } from "./projectOptions";
2
+ export interface ReportOpts extends ProjectOpts {
3
+ out?: string;
4
+ json?: boolean;
5
+ }
6
+ export declare function reportCommand(opts?: ReportOpts): Promise<void>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.reportCommand = reportCommand;
37
+ const fs = __importStar(require("fs"));
38
+ const core_1 = require("@relay-baton/core");
39
+ const projectOptions_1 = require("./projectOptions");
40
+ async function reportCommand(opts = {}) {
41
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
42
+ const { config } = core_1.ConfigLoader.load(repoRoot);
43
+ const markdown = new core_1.HandoffReport(repoRoot, config).generate();
44
+ if (opts.out) {
45
+ fs.writeFileSync(opts.out, markdown, "utf8");
46
+ console.log(`[relay-baton] report written to ${opts.out}`);
47
+ return;
48
+ }
49
+ if (opts.json) {
50
+ console.log(JSON.stringify({ markdown }, null, 2));
51
+ return;
52
+ }
53
+ console.log(markdown);
54
+ }
@@ -0,0 +1,8 @@
1
+ import type { ReviewResult } from "@relay-baton/core";
2
+ import { ProjectOpts } from "./projectOptions";
3
+ export interface ReviewOpts extends ProjectOpts {
4
+ json?: boolean;
5
+ }
6
+ /** Build the one-line summary persisted to the conversation event log. */
7
+ export declare function reviewSummaryLine(r: ReviewResult): string;
8
+ export declare function reviewCommand(opts?: ReviewOpts): Promise<void>;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reviewSummaryLine = reviewSummaryLine;
4
+ exports.reviewCommand = reviewCommand;
5
+ const core_1 = require("@relay-baton/core");
6
+ const projectOptions_1 = require("./projectOptions");
7
+ /** Build the one-line summary persisted to the conversation event log. */
8
+ function reviewSummaryLine(r) {
9
+ return (`plan ${r.planPresent ? "present" : "missing"}; ` +
10
+ `${r.steps.length} steps (${r.touchedSteps} touched, ${r.untouchedSteps} untouched, ` +
11
+ `${r.uncorrelatedSteps} uncorrelated); ` +
12
+ `${r.changedFiles.length} changed, ${r.unplannedFiles.length} unplanned`);
13
+ }
14
+ async function reviewCommand(opts = {}) {
15
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
16
+ const { config } = core_1.ConfigLoader.load(repoRoot);
17
+ const sm = new core_1.SessionManager(repoRoot, config);
18
+ const meta = sm.getMeta();
19
+ const result = new core_1.PlanReview(repoRoot, config).run();
20
+ // Persist as an append-only conversation event (best-effort, summary only).
21
+ new core_1.ConversationLog(repoRoot).append({
22
+ role: "relay-baton",
23
+ kind: "review",
24
+ text: reviewSummaryLine(result),
25
+ sessionId: meta?.id,
26
+ refs: { plan: "plan.md", changedFiles: "changed-files.md" },
27
+ meta: {
28
+ stepRefs: result.steps.filter(s => s.touched).map(s => `step-${s.index}`),
29
+ },
30
+ });
31
+ if (opts.json) {
32
+ console.log(JSON.stringify(result, null, 2));
33
+ return;
34
+ }
35
+ console.log("=== relay-baton review (deterministic, no model call) ===");
36
+ console.log("plan.md present:", result.planPresent);
37
+ if (!result.planPresent) {
38
+ console.log("No plan.md found. Run `relay-baton plan <task>` first.");
39
+ }
40
+ console.log("");
41
+ console.log(`steps: ${result.steps.length} ` +
42
+ `(touched ${result.touchedSteps}, untouched ${result.untouchedSteps}, uncorrelated ${result.uncorrelatedSteps})`);
43
+ for (const s of result.steps) {
44
+ const mark = s.touched ? "[done?]" : s.referencedPaths.length === 0 ? "[----]" : "[todo?]";
45
+ console.log(` ${mark} ${s.index}. ${s.text}`);
46
+ if (s.matchedFiles.length) {
47
+ console.log(` matched: ${s.matchedFiles.join(", ")}`);
48
+ }
49
+ }
50
+ console.log("");
51
+ console.log(`changed files: ${result.changedFiles.length}`);
52
+ if (result.unplannedFiles.length) {
53
+ console.log(`unplanned changes (${result.unplannedFiles.length}):`);
54
+ for (const f of result.unplannedFiles)
55
+ console.log(` - ${f}`);
56
+ }
57
+ else {
58
+ console.log("unplanned changes: none");
59
+ }
60
+ console.log("");
61
+ console.log("test-results summary:", result.testResultsSummary ?? "(none)");
62
+ console.log("handoff.md present:", result.handoffPresent);
63
+ }
@@ -0,0 +1,5 @@
1
+ import { ProjectOpts } from "./projectOptions";
2
+ export interface RiskOpts extends ProjectOpts {
3
+ json?: boolean;
4
+ }
5
+ export declare function riskCommand(opts?: RiskOpts): Promise<void>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.riskCommand = riskCommand;
4
+ const core_1 = require("@relay-baton/core");
5
+ const projectOptions_1 = require("./projectOptions");
6
+ async function riskCommand(opts = {}) {
7
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
8
+ const report = new core_1.RiskClassifier(repoRoot).evaluate();
9
+ if (opts.json) {
10
+ console.log(JSON.stringify(report, null, 2));
11
+ return;
12
+ }
13
+ if (!report.available) {
14
+ console.log("[relay-baton] risk: not a git repository.");
15
+ return;
16
+ }
17
+ if (!report.risky) {
18
+ console.log("[relay-baton] risk: no risky surfaces in the working tree.");
19
+ return;
20
+ }
21
+ console.log(`[relay-baton] risk: ${report.findings.length} finding(s) · ${report.bySeverity.high} high · ${report.bySeverity.medium} medium`);
22
+ for (const f of report.findings) {
23
+ console.log(`- [${f.severity}] ${f.category}: ${f.code} ${f.path} (${f.reason})`);
24
+ }
25
+ }
@@ -0,0 +1,31 @@
1
+ import type { AgentId } from "@relay-baton/shared";
2
+ import { ProjectOpts } from "./projectOptions";
3
+ export interface RunOpts extends ProjectOpts {
4
+ diet?: string;
5
+ force?: boolean;
6
+ allowApiKeyEnv?: boolean;
7
+ /** Override the first agent in the relay chain. */
8
+ primary?: string;
9
+ /** Override the second agent (single-fallback shorthand). */
10
+ fallback?: string;
11
+ /** Explicit N-way chain, comma-separated (e.g. "claude,codex,gemini"). */
12
+ chain?: string;
13
+ /** v2.5: bounded auto-orchestration — max extra continue-steps after the first pass. */
14
+ until?: string;
15
+ /** Pre-approve bounded continue steps (still capped + guardrail-gated). */
16
+ yes?: boolean;
17
+ }
18
+ /**
19
+ * Deterministic "who's next" policy (v2.3): the relay chain is an ordered list
20
+ * of agents. `--chain` wins; otherwise it is [primary, fallback] resolved from
21
+ * flags > project overrides > config. Supports reverse (claude->codex) and
22
+ * longer chains, not just codex->claude.
23
+ */
24
+ export declare function resolveChain(opts: RunOpts, project: {
25
+ primaryAgent?: AgentId;
26
+ fallbackAgent?: AgentId;
27
+ } | undefined, config: {
28
+ primaryAgent: AgentId;
29
+ fallbackAgent: AgentId;
30
+ }, assignedAgent?: AgentId): AgentId[];
31
+ export declare function runCommand(task: string, opts: RunOpts): Promise<void>;