@os-eco/overstory-cli 0.7.3 → 0.7.4
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 +20 -8
- package/agents/builder.md +6 -0
- package/agents/coordinator.md +2 -2
- package/agents/lead.md +4 -1
- package/agents/merger.md +3 -2
- package/agents/monitor.md +1 -1
- package/agents/reviewer.md +1 -0
- package/agents/scout.md +1 -0
- package/package.json +2 -2
- package/src/commands/agents.ts +18 -8
- package/src/commands/prime.test.ts +1 -0
- package/src/commands/prime.ts +1 -16
- package/src/index.ts +1 -1
- package/src/metrics/pricing.ts +80 -0
- package/src/metrics/transcript.test.ts +58 -1
- package/src/metrics/transcript.ts +9 -68
- package/src/runtimes/pi-guards.test.ts +29 -0
- package/src/runtimes/pi-guards.ts +23 -6
- package/src/tracker/beads.test.ts +454 -0
- package/src/tracker/seeds.test.ts +461 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beads tracker adapter tests.
|
|
3
|
+
*
|
|
4
|
+
* Uses Bun.spawn mocks — legitimate exception to "never mock what you can use for real".
|
|
5
|
+
* The `bd` CLI may not be installed in all environments and would modify real tracker
|
|
6
|
+
* state (creating/closing actual beads) if invoked directly in tests.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
10
|
+
import { AgentError } from "../errors.ts";
|
|
11
|
+
import { createBeadsTracker } from "./beads.ts";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Helper to create a mock Bun.spawn return value.
|
|
15
|
+
*
|
|
16
|
+
* The actual code reads stdout/stderr via `new Response(proc.stdout).text()`
|
|
17
|
+
* and `new Response(proc.stderr).text()`, so we need ReadableStreams.
|
|
18
|
+
*/
|
|
19
|
+
function mockSpawnResult(
|
|
20
|
+
stdout: string,
|
|
21
|
+
stderr: string,
|
|
22
|
+
exitCode: number,
|
|
23
|
+
): {
|
|
24
|
+
stdout: ReadableStream<Uint8Array>;
|
|
25
|
+
stderr: ReadableStream<Uint8Array>;
|
|
26
|
+
exited: Promise<number>;
|
|
27
|
+
pid: number;
|
|
28
|
+
} {
|
|
29
|
+
return {
|
|
30
|
+
stdout: new Response(stdout).body as ReadableStream<Uint8Array>,
|
|
31
|
+
stderr: new Response(stderr).body as ReadableStream<Uint8Array>,
|
|
32
|
+
exited: Promise.resolve(exitCode),
|
|
33
|
+
pid: 12345,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const TEST_CWD = "/test/repo";
|
|
38
|
+
|
|
39
|
+
describe("createBeadsTracker — ready()", () => {
|
|
40
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
spawnSpy.mockRestore();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("returns normalized TrackerIssue[] with issue_type → type mapping", async () => {
|
|
51
|
+
const raw = [
|
|
52
|
+
{
|
|
53
|
+
id: "bd-1",
|
|
54
|
+
title: "Fix login",
|
|
55
|
+
status: "open",
|
|
56
|
+
priority: 1,
|
|
57
|
+
issue_type: "bug",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "bd-2",
|
|
61
|
+
title: "Add auth",
|
|
62
|
+
status: "open",
|
|
63
|
+
priority: 2,
|
|
64
|
+
issue_type: "feature",
|
|
65
|
+
assignee: "bob",
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(raw), "", 0));
|
|
69
|
+
|
|
70
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
71
|
+
const issues = await tracker.ready();
|
|
72
|
+
|
|
73
|
+
expect(issues).toHaveLength(2);
|
|
74
|
+
expect(issues[0]).toMatchObject({ id: "bd-1", title: "Fix login", type: "bug" });
|
|
75
|
+
expect(issues[1]).toMatchObject({ id: "bd-2", type: "feature", assignee: "bob" });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("verifies CLI args: [bd, ready, --json]", async () => {
|
|
79
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("[]", "", 0));
|
|
80
|
+
|
|
81
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
82
|
+
await tracker.ready();
|
|
83
|
+
|
|
84
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
85
|
+
const cmd = callArgs[0] as string[];
|
|
86
|
+
expect(cmd).toEqual(["bd", "ready", "--json"]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("throws AgentError on non-zero exit code", async () => {
|
|
90
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "bd: command not found", 1));
|
|
91
|
+
|
|
92
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
93
|
+
await expect(tracker.ready()).rejects.toThrow(AgentError);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("createBeadsTracker — show()", () => {
|
|
98
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
99
|
+
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
afterEach(() => {
|
|
105
|
+
spawnSpy.mockRestore();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("returns normalized TrackerIssue from bd array response", async () => {
|
|
109
|
+
// bd show --json returns an array with a single element
|
|
110
|
+
const raw = [
|
|
111
|
+
{
|
|
112
|
+
id: "bd-42",
|
|
113
|
+
title: "Critical bug",
|
|
114
|
+
status: "open",
|
|
115
|
+
priority: 1,
|
|
116
|
+
issue_type: "bug",
|
|
117
|
+
description: "Crashes on startup",
|
|
118
|
+
blocks: ["bd-50"],
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(raw), "", 0));
|
|
122
|
+
|
|
123
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
124
|
+
const issue = await tracker.show("bd-42");
|
|
125
|
+
|
|
126
|
+
expect(issue).toMatchObject({
|
|
127
|
+
id: "bd-42",
|
|
128
|
+
title: "Critical bug",
|
|
129
|
+
type: "bug",
|
|
130
|
+
description: "Crashes on startup",
|
|
131
|
+
blocks: ["bd-50"],
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("throws AgentError when bd returns empty array", async () => {
|
|
136
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("[]", "", 0));
|
|
137
|
+
|
|
138
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
139
|
+
await expect(tracker.show("bd-99")).rejects.toThrow(AgentError);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("verifies CLI args: [bd, show, <id>, --json]", async () => {
|
|
143
|
+
const raw = [{ id: "bd-1", title: "t", status: "open", priority: 1, issue_type: "task" }];
|
|
144
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(raw), "", 0));
|
|
145
|
+
|
|
146
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
147
|
+
await tracker.show("bd-1");
|
|
148
|
+
|
|
149
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
150
|
+
const cmd = callArgs[0] as string[];
|
|
151
|
+
expect(cmd).toEqual(["bd", "show", "bd-1", "--json"]);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe("createBeadsTracker — create()", () => {
|
|
156
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
157
|
+
|
|
158
|
+
beforeEach(() => {
|
|
159
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
afterEach(() => {
|
|
163
|
+
spawnSpy.mockRestore();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("returns new issue ID from { id: '...' } response", async () => {
|
|
167
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify({ id: "bd-101" }), "", 0));
|
|
168
|
+
|
|
169
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
170
|
+
const id = await tracker.create("New feature");
|
|
171
|
+
|
|
172
|
+
expect(id).toBe("bd-101");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("verifies CLI args: [bd, create, <title>, --json]", async () => {
|
|
176
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify({ id: "bd-1" }), "", 0));
|
|
177
|
+
|
|
178
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
179
|
+
await tracker.create("My Issue");
|
|
180
|
+
|
|
181
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
182
|
+
const cmd = callArgs[0] as string[];
|
|
183
|
+
expect(cmd[0]).toBe("bd");
|
|
184
|
+
expect(cmd[1]).toBe("create");
|
|
185
|
+
expect(cmd[2]).toBe("My Issue");
|
|
186
|
+
expect(cmd).toContain("--json");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("passes optional --type, --priority, --description args", async () => {
|
|
190
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify({ id: "bd-200" }), "", 0));
|
|
191
|
+
|
|
192
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
193
|
+
await tracker.create("My task", {
|
|
194
|
+
type: "feature",
|
|
195
|
+
priority: 2,
|
|
196
|
+
description: "A detailed description",
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
200
|
+
const cmd = callArgs[0] as string[];
|
|
201
|
+
expect(cmd).toContain("--type");
|
|
202
|
+
expect(cmd).toContain("feature");
|
|
203
|
+
expect(cmd).toContain("--priority");
|
|
204
|
+
expect(cmd).toContain("2");
|
|
205
|
+
expect(cmd).toContain("--description");
|
|
206
|
+
expect(cmd).toContain("A detailed description");
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe("createBeadsTracker — claim()", () => {
|
|
211
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
212
|
+
|
|
213
|
+
beforeEach(() => {
|
|
214
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
afterEach(() => {
|
|
218
|
+
spawnSpy.mockRestore();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("calls [bd, update, <id>, --status, in_progress]", async () => {
|
|
222
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "", 0));
|
|
223
|
+
|
|
224
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
225
|
+
await tracker.claim("bd-7");
|
|
226
|
+
|
|
227
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
228
|
+
const cmd = callArgs[0] as string[];
|
|
229
|
+
expect(cmd).toEqual(["bd", "update", "bd-7", "--status", "in_progress"]);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe("createBeadsTracker — close()", () => {
|
|
234
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
235
|
+
|
|
236
|
+
beforeEach(() => {
|
|
237
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
afterEach(() => {
|
|
241
|
+
spawnSpy.mockRestore();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("calls [bd, close, <id>] without reason", async () => {
|
|
245
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "", 0));
|
|
246
|
+
|
|
247
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
248
|
+
await tracker.close("bd-10");
|
|
249
|
+
|
|
250
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
251
|
+
const cmd = callArgs[0] as string[];
|
|
252
|
+
expect(cmd).toEqual(["bd", "close", "bd-10"]);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("calls [bd, close, <id>, --reason, ...] with reason", async () => {
|
|
256
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "", 0));
|
|
257
|
+
|
|
258
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
259
|
+
await tracker.close("bd-10", "Completed implementation");
|
|
260
|
+
|
|
261
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
262
|
+
const cmd = callArgs[0] as string[];
|
|
263
|
+
expect(cmd).toEqual(["bd", "close", "bd-10", "--reason", "Completed implementation"]);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe("createBeadsTracker — list()", () => {
|
|
268
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
269
|
+
|
|
270
|
+
beforeEach(() => {
|
|
271
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
afterEach(() => {
|
|
275
|
+
spawnSpy.mockRestore();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("returns normalized issues from bd array response", async () => {
|
|
279
|
+
const raw = [
|
|
280
|
+
{ id: "bd-1", title: "Task A", status: "open", priority: 1, issue_type: "task" },
|
|
281
|
+
{
|
|
282
|
+
id: "bd-2",
|
|
283
|
+
title: "Bug B",
|
|
284
|
+
status: "in_progress",
|
|
285
|
+
priority: 2,
|
|
286
|
+
issue_type: "bug",
|
|
287
|
+
},
|
|
288
|
+
];
|
|
289
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(raw), "", 0));
|
|
290
|
+
|
|
291
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
292
|
+
const issues = await tracker.list();
|
|
293
|
+
|
|
294
|
+
expect(issues).toHaveLength(2);
|
|
295
|
+
expect(issues[0]).toMatchObject({ id: "bd-1", type: "task" });
|
|
296
|
+
expect(issues[1]).toMatchObject({ id: "bd-2", type: "bug", status: "in_progress" });
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("verifies CLI args: [bd, list, --json]", async () => {
|
|
300
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("[]", "", 0));
|
|
301
|
+
|
|
302
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
303
|
+
await tracker.list();
|
|
304
|
+
|
|
305
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
306
|
+
const cmd = callArgs[0] as string[];
|
|
307
|
+
expect(cmd[0]).toBe("bd");
|
|
308
|
+
expect(cmd[1]).toBe("list");
|
|
309
|
+
expect(cmd).toContain("--json");
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("passes --status and --limit options", async () => {
|
|
313
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("[]", "", 0));
|
|
314
|
+
|
|
315
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
316
|
+
await tracker.list({ status: "open", limit: 5 });
|
|
317
|
+
|
|
318
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
319
|
+
const cmd = callArgs[0] as string[];
|
|
320
|
+
expect(cmd).toContain("--status");
|
|
321
|
+
expect(cmd).toContain("open");
|
|
322
|
+
expect(cmd).toContain("--limit");
|
|
323
|
+
expect(cmd).toContain("5");
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe("createBeadsTracker — sync()", () => {
|
|
328
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
329
|
+
|
|
330
|
+
beforeEach(() => {
|
|
331
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
afterEach(() => {
|
|
335
|
+
spawnSpy.mockRestore();
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
test("calls [bd, sync] directly (not via beads client)", async () => {
|
|
339
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "", 0));
|
|
340
|
+
|
|
341
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
342
|
+
await tracker.sync();
|
|
343
|
+
|
|
344
|
+
// sync() calls Bun.spawn directly in beads.ts, not via the beads client
|
|
345
|
+
expect(spawnSpy).toHaveBeenCalledTimes(1);
|
|
346
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
347
|
+
const cmd = callArgs[0] as string[];
|
|
348
|
+
expect(cmd).toEqual(["bd", "sync"]);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("throws AgentError on failure", async () => {
|
|
352
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "bd sync failed", 1));
|
|
353
|
+
|
|
354
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
355
|
+
await expect(tracker.sync()).rejects.toThrow(AgentError);
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
describe("createBeadsTracker — issue_type normalization", () => {
|
|
360
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
361
|
+
|
|
362
|
+
beforeEach(() => {
|
|
363
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
afterEach(() => {
|
|
367
|
+
spawnSpy.mockRestore();
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test("maps issue_type to type field", async () => {
|
|
371
|
+
const raw = [{ id: "bd-1", title: "t", status: "open", priority: 1, issue_type: "bug" }];
|
|
372
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(raw), "", 0));
|
|
373
|
+
|
|
374
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
375
|
+
const issues = await tracker.ready();
|
|
376
|
+
|
|
377
|
+
expect(issues[0]?.type).toBe("bug");
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
test("falls back to type when issue_type absent", async () => {
|
|
381
|
+
const raw = [{ id: "bd-1", title: "t", status: "open", priority: 1, type: "feature" }];
|
|
382
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(raw), "", 0));
|
|
383
|
+
|
|
384
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
385
|
+
const issues = await tracker.ready();
|
|
386
|
+
|
|
387
|
+
expect(issues[0]?.type).toBe("feature");
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("defaults to 'unknown' when neither issue_type nor type present", async () => {
|
|
391
|
+
const raw = [{ id: "bd-1", title: "t", status: "open", priority: 1 }];
|
|
392
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(raw), "", 0));
|
|
393
|
+
|
|
394
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
395
|
+
const issues = await tracker.ready();
|
|
396
|
+
|
|
397
|
+
expect(issues[0]?.type).toBe("unknown");
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
test("prefers issue_type over type when both present", async () => {
|
|
401
|
+
const raw = [
|
|
402
|
+
{
|
|
403
|
+
id: "bd-1",
|
|
404
|
+
title: "t",
|
|
405
|
+
status: "open",
|
|
406
|
+
priority: 1,
|
|
407
|
+
issue_type: "bug",
|
|
408
|
+
type: "feature",
|
|
409
|
+
},
|
|
410
|
+
];
|
|
411
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(raw), "", 0));
|
|
412
|
+
|
|
413
|
+
const tracker = createBeadsTracker(TEST_CWD);
|
|
414
|
+
const issues = await tracker.ready();
|
|
415
|
+
|
|
416
|
+
expect(issues[0]?.type).toBe("bug");
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
describe("createBeadsTracker — cwd propagation", () => {
|
|
421
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
422
|
+
|
|
423
|
+
beforeEach(() => {
|
|
424
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
afterEach(() => {
|
|
428
|
+
spawnSpy.mockRestore();
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test("propagates cwd to Bun.spawn for ready()", async () => {
|
|
432
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("[]", "", 0));
|
|
433
|
+
|
|
434
|
+
const customCwd = "/my/project/root";
|
|
435
|
+
const tracker = createBeadsTracker(customCwd);
|
|
436
|
+
await tracker.ready();
|
|
437
|
+
|
|
438
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
439
|
+
const opts = callArgs[1] as { cwd: string };
|
|
440
|
+
expect(opts.cwd).toBe(customCwd);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
test("propagates cwd to Bun.spawn for sync()", async () => {
|
|
444
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "", 0));
|
|
445
|
+
|
|
446
|
+
const customCwd = "/my/project/root";
|
|
447
|
+
const tracker = createBeadsTracker(customCwd);
|
|
448
|
+
await tracker.sync();
|
|
449
|
+
|
|
450
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
451
|
+
const opts = callArgs[1] as { cwd: string };
|
|
452
|
+
expect(opts.cwd).toBe(customCwd);
|
|
453
|
+
});
|
|
454
|
+
});
|