@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,193 @@
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.sessionNewCommand = sessionNewCommand;
37
+ exports.sessionSwitchCommand = sessionSwitchCommand;
38
+ exports.sessionItemsCommand = sessionItemsCommand;
39
+ exports.sessionAssignCommand = sessionAssignCommand;
40
+ exports.sessionWorktreeAddCommand = sessionWorktreeAddCommand;
41
+ exports.sessionWorktreeRemoveCommand = sessionWorktreeRemoveCommand;
42
+ exports.sessionRemoveCommand = sessionRemoveCommand;
43
+ const path = __importStar(require("path"));
44
+ const fs = __importStar(require("fs"));
45
+ const core_1 = require("@relay-baton/core");
46
+ const core_2 = require("@relay-baton/core");
47
+ const projectOptions_1 = require("./projectOptions");
48
+ /** Default isolated worktree location: `<parent>/<repo>.worktrees/<name>`. */
49
+ function defaultWorktreePath(repoRoot, name) {
50
+ return path.join(path.dirname(repoRoot), `${path.basename(repoRoot)}.worktrees`, name);
51
+ }
52
+ /** `session new <name>` — create a named work item (optionally switch + init). */
53
+ async function sessionNewCommand(name, opts = {}) {
54
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
55
+ const { config } = core_1.ConfigLoader.load(repoRoot);
56
+ const wm = new core_1.WorkspaceManager(repoRoot);
57
+ if (opts.agent && !(0, core_2.isAgentId)(opts.agent)) {
58
+ console.error(`[relay-baton] unknown agent: ${opts.agent}`);
59
+ process.exit(2);
60
+ }
61
+ try {
62
+ wm.create(name, { assignedAgent: opts.agent, activate: opts.switch });
63
+ }
64
+ catch (e) {
65
+ console.error(`[relay-baton] ${e?.message ?? e}`);
66
+ process.exit(2);
67
+ }
68
+ // Initialize the new work item's .ai-session artifacts (default: yes).
69
+ if (opts.init !== false)
70
+ new core_1.SessionManager(repoRoot, config, name).init("");
71
+ if (opts.json) {
72
+ console.log(JSON.stringify(wm.load(), null, 2));
73
+ return;
74
+ }
75
+ console.log(`[relay-baton] created session "${name}"${opts.agent ? ` (agent: ${opts.agent})` : ""}${opts.switch ? " and switched to it" : ""}.`);
76
+ if (!opts.switch)
77
+ console.log(` → activate with: relay-baton session switch ${name}`);
78
+ }
79
+ /** `session switch|use <name>` — set the active work item. */
80
+ async function sessionSwitchCommand(name, opts = {}) {
81
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
82
+ core_1.ConfigLoader.load(repoRoot);
83
+ const wm = new core_1.WorkspaceManager(repoRoot);
84
+ try {
85
+ wm.switchTo(name);
86
+ }
87
+ catch (e) {
88
+ console.error(`[relay-baton] ${e?.message ?? e}`);
89
+ process.exit(2);
90
+ }
91
+ if (opts.json) {
92
+ console.log(JSON.stringify(wm.load(), null, 2));
93
+ return;
94
+ }
95
+ console.log(`[relay-baton] active session → ${name}`);
96
+ }
97
+ /** `session items` — list work items in this repo (read-only). */
98
+ async function sessionItemsCommand(opts = {}) {
99
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
100
+ core_1.ConfigLoader.load(repoRoot);
101
+ const wm = new core_1.WorkspaceManager(repoRoot);
102
+ const ws = wm.load();
103
+ if (opts.json) {
104
+ console.log(JSON.stringify(ws, null, 2));
105
+ return;
106
+ }
107
+ console.log(`[relay-baton] sessions (active: ${ws.active})`);
108
+ for (const s of ws.sessions) {
109
+ const mark = s.name === ws.active ? "*" : " ";
110
+ const agent = s.assignedAgent ? ` [${s.assignedAgent}]` : "";
111
+ console.log(` ${mark} ${s.name}${agent}`);
112
+ }
113
+ }
114
+ /** `session assign <name> <agent>` — pin a work item to an agent (v2.6 item 2 groundwork). */
115
+ async function sessionAssignCommand(name, agent, opts = {}) {
116
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
117
+ core_1.ConfigLoader.load(repoRoot);
118
+ if (agent !== "none" && !(0, core_2.isAgentId)(agent)) {
119
+ console.error(`[relay-baton] unknown agent: ${agent} (use an agent id or "none")`);
120
+ process.exit(2);
121
+ }
122
+ const wm = new core_1.WorkspaceManager(repoRoot);
123
+ try {
124
+ wm.assignAgent(name, agent === "none" ? undefined : agent);
125
+ }
126
+ catch (e) {
127
+ console.error(`[relay-baton] ${e?.message ?? e}`);
128
+ process.exit(2);
129
+ }
130
+ console.log(`[relay-baton] session "${name}" agent → ${agent}`);
131
+ }
132
+ /** `session worktree add <name>` — back a work item with an isolated git worktree. */
133
+ async function sessionWorktreeAddCommand(name, opts = {}) {
134
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
135
+ core_1.ConfigLoader.load(repoRoot);
136
+ const wm = new core_1.WorkspaceManager(repoRoot);
137
+ if (!wm.has(name)) {
138
+ console.error(`[relay-baton] unknown session: ${name} (create it first with \`session new ${name}\`)`);
139
+ process.exit(2);
140
+ }
141
+ const git = new core_1.GitService(repoRoot);
142
+ if (!git.isGitRepo()) {
143
+ console.error("[relay-baton] not a git repository. worktrees require git.");
144
+ process.exit(2);
145
+ }
146
+ const dir = opts.worktreePath ? path.resolve(opts.worktreePath) : defaultWorktreePath(repoRoot, name);
147
+ if (fs.existsSync(dir)) {
148
+ console.error(`[relay-baton] worktree path already exists: ${dir}`);
149
+ process.exit(2);
150
+ }
151
+ const branch = opts.branch ?? `relay/${name}`;
152
+ const res = git.addWorktree(dir, branch);
153
+ if (!res.ok) {
154
+ console.error(`[relay-baton] git worktree add failed: ${res.error}`);
155
+ process.exit(1);
156
+ }
157
+ wm.setWorktree(name, dir);
158
+ console.log(`[relay-baton] worktree for "${name}" → ${dir} (branch ${branch})`);
159
+ console.log(` run/handoff for "${name}" now execute in this isolated checkout.`);
160
+ }
161
+ /** `session worktree remove <name>` — detach + remove the work item's worktree. */
162
+ async function sessionWorktreeRemoveCommand(name, opts = {}) {
163
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
164
+ core_1.ConfigLoader.load(repoRoot);
165
+ const wm = new core_1.WorkspaceManager(repoRoot);
166
+ const entry = wm.load().sessions.find(s => s.name === name);
167
+ if (!entry?.worktree) {
168
+ console.error(`[relay-baton] session "${name}" has no worktree.`);
169
+ process.exit(2);
170
+ }
171
+ const res = new core_1.GitService(repoRoot).removeWorktree(entry.worktree, opts.force);
172
+ if (!res.ok) {
173
+ console.error(`[relay-baton] git worktree remove failed: ${res.error}`);
174
+ console.error(" (commit/stash changes in the worktree, or pass --force)");
175
+ process.exit(1);
176
+ }
177
+ wm.setWorktree(name, undefined);
178
+ console.log(`[relay-baton] removed worktree for "${name}".`);
179
+ }
180
+ /** `session remove <name>` — drop a named work item (never default). */
181
+ async function sessionRemoveCommand(name, opts = {}) {
182
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
183
+ core_1.ConfigLoader.load(repoRoot);
184
+ const wm = new core_1.WorkspaceManager(repoRoot);
185
+ try {
186
+ wm.remove(name, { deleteFiles: opts.deleteFiles });
187
+ }
188
+ catch (e) {
189
+ console.error(`[relay-baton] ${e?.message ?? e}`);
190
+ process.exit(2);
191
+ }
192
+ console.log(`[relay-baton] removed session "${name}"${opts.deleteFiles ? " (files deleted)" : ""}. active → ${wm.activeName()}`);
193
+ }
@@ -0,0 +1,5 @@
1
+ import { ProjectOpts } from "./projectOptions";
2
+ export interface StatusOpts extends ProjectOpts {
3
+ json?: boolean;
4
+ }
5
+ export declare function statusCommand(opts?: StatusOpts): Promise<void>;
@@ -0,0 +1,116 @@
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.statusCommand = statusCommand;
37
+ const fs = __importStar(require("fs"));
38
+ const core_1 = require("@relay-baton/core");
39
+ const projectOptions_1 = require("./projectOptions");
40
+ async function statusCommand(opts = {}) {
41
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
42
+ const { config } = core_1.ConfigLoader.load(repoRoot);
43
+ const sm = new core_1.SessionManager(repoRoot, config);
44
+ const meta = sm.getMeta();
45
+ if (!meta) {
46
+ if (opts.json) {
47
+ console.log(JSON.stringify({ session: false }, null, 2));
48
+ return;
49
+ }
50
+ console.log("[relay-baton] no session. Run `relay-baton init`.");
51
+ return;
52
+ }
53
+ const handoff = sm.files.p("handoff");
54
+ const budget = sm.files.p("contextBudget");
55
+ const changed = sm.files.p("changedFiles");
56
+ const git = new core_1.GitService(repoRoot);
57
+ const gitSummary = git.summary(20);
58
+ const gitTracking = git.compareBaseline(readGitBaseline(sm.files.p("gitBaseline")));
59
+ let changedCount = 0;
60
+ try {
61
+ const txt = fs.readFileSync(changed, "utf8");
62
+ changedCount = txt.split("\n").filter(l => l.startsWith("- ")).length;
63
+ }
64
+ catch { /**/ }
65
+ if (opts.json) {
66
+ console.log(JSON.stringify({
67
+ session: true,
68
+ task: meta.task || null,
69
+ status: meta.status,
70
+ activeAgent: meta.activeAgent,
71
+ lastAgent: meta.lastAgent,
72
+ primaryAgent: meta.primaryAgent,
73
+ fallbackAgent: meta.fallbackAgent,
74
+ fallbackReason: meta.fallbackReason ?? null,
75
+ tokenDietProfile: meta.tokenDietProfile,
76
+ changedFiles: changedCount,
77
+ handoffExists: fs.existsSync(handoff),
78
+ contextBudgetExists: fs.existsSync(budget),
79
+ git: {
80
+ available: gitSummary.available,
81
+ branch: gitSummary.branch,
82
+ head: gitSummary.head,
83
+ clean: gitSummary.clean,
84
+ changed: gitSummary.changed,
85
+ staged: gitSummary.staged,
86
+ unstaged: gitSummary.unstaged,
87
+ untracked: gitSummary.untracked,
88
+ tracking: gitTracking,
89
+ },
90
+ lastError: meta.lastError ?? null,
91
+ }, null, 2));
92
+ return;
93
+ }
94
+ console.log("task:", meta.task || "(none)");
95
+ console.log("status:", meta.status);
96
+ console.log("activeAgent:", meta.activeAgent);
97
+ console.log("lastAgent:", meta.lastAgent);
98
+ console.log("primaryAgent:", meta.primaryAgent);
99
+ console.log("fallbackAgent:", meta.fallbackAgent);
100
+ console.log("fallbackReason:", meta.fallbackReason ?? "(none)");
101
+ console.log("tokenDietProfile:", meta.tokenDietProfile);
102
+ console.log("changed files:", changedCount);
103
+ console.log("git:", gitSummary.available ? `${gitSummary.branch ?? "(detached)"} · ${gitSummary.changed} changed` : "unavailable");
104
+ console.log("git changed since session start:", gitTracking.changedSinceBaseline == null ? "(no baseline)" : (gitTracking.changedSinceBaseline ? "yes" : "no"));
105
+ console.log("handoff.md exists:", fs.existsSync(handoff));
106
+ console.log("context-budget.json exists:", fs.existsSync(budget));
107
+ console.log("lastError:", meta.lastError ?? "(none)");
108
+ }
109
+ function readGitBaseline(file) {
110
+ try {
111
+ return JSON.parse(fs.readFileSync(file, "utf8"));
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ }
@@ -0,0 +1,4 @@
1
+ export declare function tuiCommand(opts?: {
2
+ project?: string;
3
+ path?: string;
4
+ }): Promise<void>;
@@ -0,0 +1,46 @@
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.tuiCommand = tuiCommand;
37
+ async function tuiCommand(opts = {}) {
38
+ try {
39
+ const mod = await Promise.resolve().then(() => __importStar(require("@relay-baton/tui")));
40
+ await mod.startTui(opts);
41
+ }
42
+ catch (e) {
43
+ console.error("[relay-baton] failed to start TUI:", e?.message ?? e);
44
+ process.exit(1);
45
+ }
46
+ }
@@ -0,0 +1,11 @@
1
+ import { ProjectOpts } from "./projectOptions";
2
+ export interface UsageOpts extends ProjectOpts {
3
+ json?: boolean;
4
+ }
5
+ /**
6
+ * v2.4 — local usage insight. Aggregates the per-session usage ledger
7
+ * (`.ai-session/usage.jsonl`) into a token-proxy summary for budgeting. Read-only
8
+ * and local: nothing is transmitted, and the proxy is ceil(chars/4), not a real
9
+ * tokenizer.
10
+ */
11
+ export declare function usageCommand(opts?: UsageOpts): Promise<void>;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usageCommand = usageCommand;
4
+ const core_1 = require("@relay-baton/core");
5
+ const projectOptions_1 = require("./projectOptions");
6
+ /**
7
+ * v2.4 — local usage insight. Aggregates the per-session usage ledger
8
+ * (`.ai-session/usage.jsonl`) into a token-proxy summary for budgeting. Read-only
9
+ * and local: nothing is transmitted, and the proxy is ceil(chars/4), not a real
10
+ * tokenizer.
11
+ */
12
+ async function usageCommand(opts = {}) {
13
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
14
+ const { config } = core_1.ConfigLoader.load(repoRoot);
15
+ const sm = new core_1.SessionManager(repoRoot, config);
16
+ const activeProfile = (sm.getMeta()?.tokenDietProfile ?? config.tokenDiet.profile);
17
+ const maxHandoffChars = config.tokenDiet.profiles[activeProfile]?.maxHandoffChars;
18
+ const summary = new core_1.UsageLedger(repoRoot).summarize(maxHandoffChars);
19
+ if (opts.json) {
20
+ console.log(JSON.stringify({ activeProfile, maxHandoffChars, ...summary }, null, 2));
21
+ return;
22
+ }
23
+ if (!summary.available) {
24
+ console.log("[relay-baton] no usage recorded yet (run a relay/handoff first).");
25
+ return;
26
+ }
27
+ console.log(`[relay-baton] usage (local only — ${summary.proxyNote})`);
28
+ console.log(`active profile: ${activeProfile} (maxHandoffChars=${maxHandoffChars})`);
29
+ console.log(`events: ${summary.events} · handoffs: ${summary.handoffCount}`);
30
+ console.log(`total: ${summary.totalChars} chars ≈ ${summary.totalTokensProxy} tokens (proxy)`);
31
+ if (summary.budgetRatio !== null) {
32
+ console.log(`budget ratio vs one handoff budget: ${summary.budgetRatio}×`);
33
+ }
34
+ console.log("by type:");
35
+ for (const [k, v] of Object.entries(summary.byType))
36
+ console.log(` - ${k}: ${v} tokens`);
37
+ console.log("by agent:");
38
+ for (const [k, v] of Object.entries(summary.byAgent))
39
+ console.log(` - ${k}: ${v} tokens`);
40
+ }
@@ -0,0 +1,15 @@
1
+ import type { DietProfileName } from "@relay-baton/shared";
2
+ import { ProjectOpts } from "./projectOptions";
3
+ export interface VerifyOpts extends ProjectOpts {
4
+ diet?: DietProfileName;
5
+ realAgents?: boolean;
6
+ keepTemp?: boolean;
7
+ verbose?: boolean;
8
+ }
9
+ /**
10
+ * `verify` runs a deterministic, simulated end-to-end pass over the relay-baton
11
+ * pipeline WITHOUT calling any real model. It proves the wiring works: repo
12
+ * resolution, environment, fallback detection, handoff generation (no-run),
13
+ * token budget, and the API-key env block.
14
+ */
15
+ export declare function verifyCommand(opts?: VerifyOpts): Promise<void>;
@@ -0,0 +1,197 @@
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.verifyCommand = verifyCommand;
37
+ const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const child_process_1 = require("child_process");
41
+ const core_1 = require("@relay-baton/core");
42
+ const projectOptions_1 = require("./projectOptions");
43
+ const diagnostics_1 = require("./diagnostics");
44
+ const GREEN = "\x1b[32m";
45
+ const RED = "\x1b[31m";
46
+ const YELLOW = "\x1b[33m";
47
+ const CYAN = "\x1b[36m";
48
+ const DIM = "\x1b[2m";
49
+ const BOLD = "\x1b[1m";
50
+ const RESET = "\x1b[0m";
51
+ function mark(status) {
52
+ return status === "pass" ? `${GREEN}PASS${RESET}`
53
+ : status === "warn" ? `${YELLOW}WARN${RESET}`
54
+ : `${RED}FAIL${RESET}`;
55
+ }
56
+ /**
57
+ * `verify` runs a deterministic, simulated end-to-end pass over the relay-baton
58
+ * pipeline WITHOUT calling any real model. It proves the wiring works: repo
59
+ * resolution, environment, fallback detection, handoff generation (no-run),
60
+ * token budget, and the API-key env block.
61
+ */
62
+ async function verifyCommand(opts = {}) {
63
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
64
+ const { config } = core_1.ConfigLoader.load(repoRoot);
65
+ const results = [];
66
+ const log = (s) => { if (opts.verbose)
67
+ console.log(`${DIM} ${s}${RESET}`); };
68
+ console.log(`${BOLD}relay-baton verify${RESET} ${DIM}${repoRoot}${RESET}`);
69
+ if (opts.realAgents) {
70
+ console.log(`${YELLOW}--real-agents is experimental scaffolding and is NOT executed; running simulated checks only.${RESET}`);
71
+ }
72
+ // 1) repoRoot resolution + doctor core checks.
73
+ const core = (0, diagnostics_1.coreChecks)(repoRoot, config);
74
+ const coreFail = core.filter(c => c.status === "fail");
75
+ results.push({
76
+ name: "environment (doctor core)",
77
+ status: coreFail.length > 0 ? "fail" : "pass",
78
+ detail: coreFail.length > 0 ? `failing: ${coreFail.map(c => c.label).join(", ")}` : `${core.length} checks ok`,
79
+ });
80
+ // 2) Fallback phrase simulation: real phrases detected, grep/result-like lines ignored.
81
+ const detector = new core_1.FallbackDetector(config.fallbackPatterns);
82
+ const realPhrase = config.fallbackPatterns[0] ?? "usage limit reached";
83
+ const simulatedOutput = [
84
+ "Working on the task...",
85
+ "src/foo.ts:42: // mentions rate limit exceeded in a comment", // grep-like: must be ignored
86
+ `Error: ${realPhrase} — please try again later.`, // real: must be detected
87
+ ];
88
+ let detectedReal = false;
89
+ let detectedNoise = false;
90
+ for (const line of simulatedOutput) {
91
+ const hit = detector.feed(line);
92
+ if (!hit)
93
+ continue;
94
+ if (line.includes(":42:"))
95
+ detectedNoise = true;
96
+ else
97
+ detectedReal = true;
98
+ log(`fallback hit: ${hit.pattern}`);
99
+ }
100
+ results.push({
101
+ name: "fallback detection",
102
+ status: detectedReal && !detectedNoise ? "pass" : "fail",
103
+ detail: detectedReal
104
+ ? (detectedNoise ? "detected real phrase but also matched a grep-like line" : "detected real phrase, ignored grep-like line")
105
+ : "did not detect the simulated fallback phrase",
106
+ });
107
+ // 3) Handoff no-run workflow against a throwaway temp repo (never touches the real .ai-session).
108
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "relay-baton-verify-"));
109
+ let handoffOk = false;
110
+ let handoffDetail = "";
111
+ try {
112
+ // Real git init + empty commit so GitService.diff("HEAD") is clean (no noise).
113
+ const g = (args) => (0, child_process_1.spawnSync)("git", args, { cwd: tmpRoot, encoding: "utf8", stdio: "ignore" });
114
+ g(["init"]);
115
+ g(["config", "user.email", "verify@relay-baton.local"]);
116
+ g(["config", "user.name", "relay-baton verify"]);
117
+ g(["commit", "--allow-empty", "-m", "verify base"]);
118
+ const sm = new core_1.SessionManager(tmpRoot, config);
119
+ sm.init("verify: simulated handoff (no real agent)");
120
+ sm.updateMeta({ fallbackReason: `simulated fallback: ${realPhrase}`, lastAgent: "codex" });
121
+ const diet = (opts.diet ?? config.tokenDiet.profile);
122
+ const wf = new core_1.BatonWorkflow(sm, config);
123
+ const result = wf.buildHandoff({
124
+ profileName: diet,
125
+ fallbackReason: `simulated fallback: ${realPhrase}`,
126
+ previousAgent: "codex",
127
+ nextAgent: "claude",
128
+ });
129
+ handoffOk = fs.existsSync(result.handoffPath) && fs.readFileSync(result.handoffPath, "utf8").trim().length > 0;
130
+ handoffDetail = handoffOk ? `wrote ${path.basename(result.handoffPath)} (diet=${diet}, ${result.usedChars} chars)` : "handoff.md missing or empty";
131
+ log(`temp handoff: ${result.handoffPath}`);
132
+ }
133
+ catch (e) {
134
+ handoffDetail = `error: ${e?.message ?? e}`;
135
+ }
136
+ results.push({ name: "handoff (no-run)", status: handoffOk ? "pass" : "fail", detail: handoffDetail });
137
+ // 4) Token budget check on the generated handoff.
138
+ let budgetOk = false;
139
+ let budgetDetail = "";
140
+ try {
141
+ const sm = new core_1.SessionManager(tmpRoot, config);
142
+ const handoff = fs.readFileSync(sm.files.p("handoff"), "utf8");
143
+ const diet = opts.diet ?? config.tokenDiet.profile;
144
+ const max = config.tokenDiet.profiles[diet]?.maxHandoffChars ?? 0;
145
+ budgetOk = max === 0 || handoff.length <= max;
146
+ budgetDetail = `${handoff.length} chars / ${max} budget (${diet})`;
147
+ }
148
+ catch (e) {
149
+ budgetDetail = `error: ${e?.message ?? e}`;
150
+ }
151
+ results.push({ name: "token budget", status: budgetOk ? "pass" : "warn", detail: budgetDetail });
152
+ // 5) API key env block check (default policy strips blocked vars).
153
+ let envOk = true;
154
+ let envDetail = "blocked vars stripped from child env";
155
+ try {
156
+ const fakeEnv = { ...process.env };
157
+ for (const ev of config.authPolicy.blockedEnvVars)
158
+ fakeEnv[ev] = "test-secret-should-be-stripped";
159
+ const { env: childEnv } = (0, core_1.createAgentEnv)(fakeEnv, config.authPolicy, false);
160
+ const leaked = config.authPolicy.blockedEnvVars.filter(ev => childEnv[ev] != null);
161
+ envOk = leaked.length === 0;
162
+ envDetail = envOk ? "blocked vars stripped from child env" : `LEAKED: ${leaked.join(", ")}`;
163
+ }
164
+ catch (e) {
165
+ envOk = false;
166
+ envDetail = `error: ${e?.message ?? e}`;
167
+ }
168
+ results.push({ name: "api-key env block", status: envOk ? "pass" : "fail", detail: envDetail });
169
+ // Cleanup temp repo.
170
+ if (!opts.keepTemp) {
171
+ try {
172
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
173
+ }
174
+ catch { /* best effort */ }
175
+ }
176
+ else {
177
+ console.log(`${DIM} kept temp repo: ${tmpRoot}${RESET}`);
178
+ }
179
+ // Summary.
180
+ console.log(`\n${CYAN}${BOLD}Verification steps${RESET}`);
181
+ for (const r of results) {
182
+ console.log(` ${mark(r.status)} ${r.name.padEnd(28)} ${DIM}${r.detail}${RESET}`);
183
+ }
184
+ const fails = results.filter(r => r.status === "fail").length;
185
+ const warns = results.filter(r => r.status === "warn").length;
186
+ console.log("");
187
+ if (fails > 0) {
188
+ console.log(`${RED}verify FAILED — ${fails} failing step(s), ${warns} warning(s).${RESET}`);
189
+ process.exitCode = 1;
190
+ }
191
+ else if (warns > 0) {
192
+ console.log(`${YELLOW}verify passed with ${warns} warning(s).${RESET}`);
193
+ }
194
+ else {
195
+ console.log(`${GREEN}verify passed — pipeline wiring is healthy (no real model calls).${RESET}`);
196
+ }
197
+ }
@@ -0,0 +1,5 @@
1
+ import { ProjectOpts } from "./projectOptions";
2
+ export interface WorkspaceOpts extends ProjectOpts {
3
+ json?: boolean;
4
+ }
5
+ export declare function workspaceCommand(opts?: WorkspaceOpts): Promise<void>;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.workspaceCommand = workspaceCommand;
4
+ const core_1 = require("@relay-baton/core");
5
+ const projectOptions_1 = require("./projectOptions");
6
+ async function workspaceCommand(opts = {}) {
7
+ const repoRoot = (0, projectOptions_1.resolveRepoRoot)(opts);
8
+ const map = new core_1.WorkspaceMap(repoRoot).generate();
9
+ if (opts.json) {
10
+ console.log(JSON.stringify(map, null, 2));
11
+ return;
12
+ }
13
+ console.log(`[relay-baton] workspace map: ${repoRoot}`);
14
+ console.log(`package managers: ${map.packageManagers.join(", ") || "-"}`);
15
+ console.log(`languages: ${map.languages.join(", ") || "-"}`);
16
+ console.log(`monorepo: ${map.monorepo ? "yes" : "no"}${map.packages.length ? ` (${map.packages.length} package(s))` : ""}`);
17
+ for (const p of map.packages)
18
+ console.log(` - ${p.name} (${p.path})`);
19
+ console.log(`build: ${map.scripts.build ?? "-"}`);
20
+ console.log(`test: ${map.scripts.test ?? "-"}`);
21
+ console.log(`lint: ${map.scripts.lint ?? "-"}`);
22
+ if (map.scripts.others.length)
23
+ console.log(`other scripts: ${map.scripts.others.join(", ")}`);
24
+ console.log(`entry points: ${map.entryPoints.join(", ") || "-"}`);
25
+ console.log(`docs: ${map.docs.join(", ") || "-"}`);
26
+ console.log(`agents files: ${map.agentsFiles.join(", ") || "-"}`);
27
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};