@markjaquith/agency 1.11.1 → 2.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 (130) hide show
  1. package/README.md +302 -113
  2. package/cli.ts +123 -420
  3. package/index.ts +1 -1
  4. package/package.json +12 -17
  5. package/skills/agency/SKILL.md +258 -0
  6. package/src/cli.test.ts +290 -0
  7. package/src/commands/description.test.ts +103 -0
  8. package/src/commands/epic.test.ts +103 -0
  9. package/src/commands/epic.ts +101 -0
  10. package/src/commands/init.test.ts +42 -290
  11. package/src/commands/init.ts +15 -123
  12. package/src/commands/phase.ts +120 -0
  13. package/src/commands/pr.test.ts +19 -170
  14. package/src/commands/pr.ts +23 -179
  15. package/src/commands/repo.test.ts +80 -0
  16. package/src/commands/repo.ts +83 -0
  17. package/src/commands/status.test.ts +35 -477
  18. package/src/commands/status.ts +24 -213
  19. package/src/commands/task-phase.test.ts +206 -0
  20. package/src/commands/task.ts +83 -1285
  21. package/src/commands/validate.test.ts +91 -0
  22. package/src/commands/validate.ts +50 -0
  23. package/src/commands/work.test.ts +172 -419
  24. package/src/commands/work.ts +47 -166
  25. package/src/services/EpicService.test.ts +71 -0
  26. package/src/services/EpicService.ts +135 -0
  27. package/src/services/FileSystemService.ts +61 -92
  28. package/src/services/PhaseService.ts +323 -0
  29. package/src/services/PullRequestService.test.ts +386 -0
  30. package/src/services/PullRequestService.ts +143 -0
  31. package/src/services/RepositoryService.test.ts +106 -0
  32. package/src/services/RepositoryService.ts +159 -0
  33. package/src/services/TaskPhaseService.test.ts +217 -0
  34. package/src/services/TaskService.ts +192 -0
  35. package/src/services/WorkbaseService.test.ts +446 -0
  36. package/src/services/WorkbaseService.ts +493 -0
  37. package/src/services/WorktreeService.test.ts +599 -0
  38. package/src/services/WorktreeService.ts +357 -0
  39. package/src/test-utils.ts +41 -438
  40. package/src/types.ts +1 -215
  41. package/src/utils/command.ts +1 -5
  42. package/src/utils/effect.test.ts +24 -0
  43. package/src/utils/effect.ts +3 -273
  44. package/src/utils/process.test.ts +0 -15
  45. package/src/utils/process.ts +1 -60
  46. package/src/workbase/frontmatter.test.ts +50 -0
  47. package/src/workbase/frontmatter.ts +77 -0
  48. package/src/workbase/repository-reference.test.ts +25 -0
  49. package/src/workbase/repository-reference.ts +18 -0
  50. package/src/workbase/schemas.test.ts +146 -0
  51. package/src/workbase/schemas.ts +78 -0
  52. package/src/workbase/worktree-command.test.ts +61 -0
  53. package/src/workbase/worktree-command.ts +52 -0
  54. package/src/commands/base.test.ts +0 -198
  55. package/src/commands/base.ts +0 -198
  56. package/src/commands/clean.test.ts +0 -299
  57. package/src/commands/clean.ts +0 -274
  58. package/src/commands/completions.test.ts +0 -51
  59. package/src/commands/completions.ts +0 -308
  60. package/src/commands/emit.integration.test.ts +0 -549
  61. package/src/commands/emit.test.ts +0 -576
  62. package/src/commands/emit.ts +0 -700
  63. package/src/commands/emitted.test.ts +0 -226
  64. package/src/commands/emitted.ts +0 -57
  65. package/src/commands/loop.test.ts +0 -621
  66. package/src/commands/loop.ts +0 -406
  67. package/src/commands/merge.integration.test.ts +0 -195
  68. package/src/commands/merge.test.ts +0 -246
  69. package/src/commands/merge.ts +0 -244
  70. package/src/commands/pull.test.ts +0 -395
  71. package/src/commands/pull.ts +0 -205
  72. package/src/commands/push.test.ts +0 -641
  73. package/src/commands/push.ts +0 -482
  74. package/src/commands/rebase.test.ts +0 -521
  75. package/src/commands/rebase.ts +0 -232
  76. package/src/commands/save.test.ts +0 -247
  77. package/src/commands/save.ts +0 -162
  78. package/src/commands/source.test.ts +0 -195
  79. package/src/commands/source.ts +0 -72
  80. package/src/commands/switch.test.ts +0 -194
  81. package/src/commands/switch.ts +0 -84
  82. package/src/commands/task-branching.test.ts +0 -633
  83. package/src/commands/task-continue.test.ts +0 -311
  84. package/src/commands/task-edit.test.ts +0 -235
  85. package/src/commands/task-main.test.ts +0 -1066
  86. package/src/commands/task-squash.test.ts +0 -266
  87. package/src/commands/tasks.test.ts +0 -391
  88. package/src/commands/tasks.ts +0 -161
  89. package/src/commands/template-delete.test.ts +0 -178
  90. package/src/commands/template-delete.ts +0 -98
  91. package/src/commands/template-list.test.ts +0 -135
  92. package/src/commands/template-list.ts +0 -87
  93. package/src/commands/template-view.test.ts +0 -158
  94. package/src/commands/template-view.ts +0 -86
  95. package/src/commands/template.test.ts +0 -32
  96. package/src/commands/template.ts +0 -96
  97. package/src/commands/use.test.ts +0 -87
  98. package/src/commands/use.ts +0 -97
  99. package/src/constants.ts +0 -10
  100. package/src/errors.ts +0 -17
  101. package/src/schemas.ts +0 -33
  102. package/src/services/AgencyMetadataService.ts +0 -311
  103. package/src/services/ClaudeService.test.ts +0 -184
  104. package/src/services/ClaudeService.ts +0 -91
  105. package/src/services/ConfigService.ts +0 -115
  106. package/src/services/FilterRepoService.ts +0 -164
  107. package/src/services/FormatterService.test.ts +0 -432
  108. package/src/services/FormatterService.ts +0 -219
  109. package/src/services/GitService.ts +0 -1187
  110. package/src/services/MockFilterRepoService.ts +0 -140
  111. package/src/services/OpencodeService.ts +0 -263
  112. package/src/services/PromptService.ts +0 -183
  113. package/src/services/TemplateService.ts +0 -81
  114. package/src/types/native-exec.d.ts +0 -8
  115. package/src/utils/colors.ts +0 -178
  116. package/src/utils/git-path.ts +0 -6
  117. package/src/utils/glob.test.ts +0 -161
  118. package/src/utils/glob.ts +0 -78
  119. package/src/utils/paths.ts +0 -51
  120. package/src/utils/pr-branch.test.ts +0 -376
  121. package/src/utils/pr-branch.ts +0 -508
  122. package/src/utils/spinner.test.ts +0 -45
  123. package/src/utils/spinner.ts +0 -96
  124. package/src/utils/task-parser.test.ts +0 -172
  125. package/src/utils/task-parser.ts +0 -93
  126. package/templates/AGENCY.md +0 -22
  127. package/templates/AGENTS.md +0 -11
  128. package/templates/CLAUDE.md +0 -3
  129. package/templates/TASK.md +0 -5
  130. package/templates/opencode.json +0 -4
@@ -1,633 +0,0 @@
1
- import { test, expect, describe, beforeEach, afterEach } from "bun:test"
2
- import { join } from "path"
3
- import { task } from "../commands/task"
4
- import { emit } from "../commands/emit"
5
- import {
6
- createTempDir,
7
- cleanupTempDir,
8
- initGitRepo,
9
- initAgency,
10
- runTestEffect,
11
- createFile,
12
- runGitCommand,
13
- getCurrentBranch,
14
- } from "../test-utils"
15
-
16
- describe("task command - branching functionality", () => {
17
- let tempDir: string
18
- let originalCwd: string
19
- let originalConfigDir: string | undefined
20
-
21
- beforeEach(async () => {
22
- tempDir = await createTempDir()
23
- originalCwd = process.cwd()
24
- originalConfigDir = process.env.AGENCY_CONFIG_DIR
25
- process.env.AGENCY_CONFIG_DIR = await createTempDir()
26
- })
27
-
28
- afterEach(async () => {
29
- process.chdir(originalCwd)
30
- if (originalConfigDir !== undefined) {
31
- process.env.AGENCY_CONFIG_DIR = originalConfigDir
32
- } else {
33
- delete process.env.AGENCY_CONFIG_DIR
34
- }
35
- if (
36
- process.env.AGENCY_CONFIG_DIR &&
37
- process.env.AGENCY_CONFIG_DIR !== originalConfigDir
38
- ) {
39
- await cleanupTempDir(process.env.AGENCY_CONFIG_DIR)
40
- }
41
- await cleanupTempDir(tempDir)
42
- })
43
-
44
- describe("--from flag", () => {
45
- test("creates new agency--some-branch when using --from some-branch with explicit name", async () => {
46
- await initGitRepo(tempDir)
47
- process.chdir(tempDir)
48
- await initAgency(tempDir, "test")
49
-
50
- // Create a feature branch called 'some-branch'
51
- await runGitCommand(tempDir, ["git", "checkout", "-b", "some-branch"])
52
- await createFile(tempDir, "feature.txt", "content")
53
- await runGitCommand(tempDir, ["git", "add", "."])
54
- await runGitCommand(tempDir, ["git", "commit", "-m", "Add feature"])
55
-
56
- // Run agency task my-feature --from some-branch
57
- // This should create a NEW branch called agency--my-feature
58
- await runTestEffect(
59
- task({
60
- silent: true,
61
- emit: "my-feature",
62
- from: "some-branch",
63
- }),
64
- )
65
-
66
- const currentBranch = await getCurrentBranch(tempDir)
67
- expect(currentBranch).toBe("agency--my-feature")
68
-
69
- // Verify feature.txt exists (came from some-branch)
70
- const featureFile = await Bun.file(join(tempDir, "feature.txt")).text()
71
- expect(featureFile).toBe("content")
72
-
73
- // Verify TASK.md was created (agency files added)
74
- const taskMdExists = await Bun.file(join(tempDir, "TASK.md")).exists()
75
- expect(taskMdExists).toBe(true)
76
- })
77
-
78
- test("throws error when agency--some-branch already exists", async () => {
79
- await initGitRepo(tempDir)
80
- process.chdir(tempDir)
81
- await initAgency(tempDir, "test")
82
-
83
- // Create a feature branch called 'some-branch'
84
- await runGitCommand(tempDir, ["git", "checkout", "-b", "some-branch"])
85
- await createFile(tempDir, "feature.txt", "content")
86
- await runGitCommand(tempDir, ["git", "add", "."])
87
- await runGitCommand(tempDir, ["git", "commit", "-m", "Add feature"])
88
-
89
- // Create agency--my-feature first
90
- await runTestEffect(
91
- task({
92
- silent: true,
93
- emit: "my-feature",
94
- from: "some-branch",
95
- }),
96
- )
97
-
98
- // Go back to some-branch
99
- await runGitCommand(tempDir, ["git", "checkout", "some-branch"])
100
-
101
- // Try to create it again - should fail
102
- await expect(
103
- runTestEffect(
104
- task({
105
- silent: true,
106
- emit: "my-feature",
107
- from: "some-branch",
108
- }),
109
- ),
110
- ).rejects.toThrow("already exists")
111
- })
112
-
113
- test("branches from specified non-agency branch", async () => {
114
- await initGitRepo(tempDir)
115
- process.chdir(tempDir)
116
- await initAgency(tempDir, "test")
117
-
118
- // Create a feature branch
119
- await runGitCommand(tempDir, ["git", "checkout", "-b", "feature-base"])
120
- await createFile(tempDir, "feature.txt", "content")
121
- await runGitCommand(tempDir, ["git", "add", "."])
122
- await runGitCommand(tempDir, ["git", "commit", "-m", "Add feature"])
123
-
124
- // Go back to main
125
- await runGitCommand(tempDir, ["git", "checkout", "main"])
126
-
127
- // Create task branch from feature-base
128
- await runTestEffect(
129
- task({
130
- silent: true,
131
- emit: "my-task",
132
- from: "feature-base",
133
- }),
134
- )
135
-
136
- const currentBranch = await getCurrentBranch(tempDir)
137
- expect(currentBranch).toBe("agency--my-task")
138
-
139
- // Verify feature.txt exists (came from feature-base)
140
- const featureFile = await Bun.file(join(tempDir, "feature.txt")).text()
141
- expect(featureFile).toBe("content")
142
- })
143
-
144
- test("throws error if specified branch does not exist", async () => {
145
- await initGitRepo(tempDir)
146
- process.chdir(tempDir)
147
- await initAgency(tempDir, "test")
148
-
149
- await expect(
150
- runTestEffect(
151
- task({
152
- silent: true,
153
- emit: "my-task",
154
- from: "nonexistent-branch",
155
- }),
156
- ),
157
- ).rejects.toThrow("does not exist")
158
- })
159
-
160
- test("detects agency source branch and uses its emit branch", async () => {
161
- await initGitRepo(tempDir)
162
- process.chdir(tempDir)
163
- await initAgency(tempDir, "test")
164
-
165
- // Create first agency branch
166
- await runTestEffect(
167
- task({
168
- silent: true,
169
- emit: "first-task",
170
- }),
171
- )
172
-
173
- // Add a unique file to identify this branch
174
- await createFile(tempDir, "first-task.txt", "first")
175
- await runGitCommand(tempDir, ["git", "add", "."])
176
- await runGitCommand(tempDir, [
177
- "git",
178
- "commit",
179
- "-m",
180
- "Add first task file",
181
- ])
182
-
183
- // Emit the branch
184
- await runTestEffect(emit({ silent: true }))
185
-
186
- // Go back to main
187
- await runGitCommand(tempDir, ["git", "checkout", "main"])
188
-
189
- // Create second task from first agency branch
190
- await runTestEffect(
191
- task({
192
- silent: true,
193
- emit: "second-task",
194
- from: "agency--first-task",
195
- }),
196
- )
197
-
198
- const currentBranch = await getCurrentBranch(tempDir)
199
- expect(currentBranch).toBe("agency--second-task")
200
-
201
- // Verify first-task.txt exists (came from emit branch)
202
- const firstTaskFile = await Bun.file(
203
- join(tempDir, "first-task.txt"),
204
- ).text()
205
- expect(firstTaskFile).toBe("first")
206
-
207
- // Verify TASK.md does NOT exist from first-task
208
- // (because we branched from emit branch, not source branch)
209
- const taskMdExists = await Bun.file(join(tempDir, "TASK.md")).exists()
210
- // The new TASK.md should exist (created by second task)
211
- // but it should be fresh, not from first-task
212
- expect(taskMdExists).toBe(true)
213
- })
214
-
215
- test("throws error if agency source branch has no emit branch", async () => {
216
- await initGitRepo(tempDir)
217
- process.chdir(tempDir)
218
- await initAgency(tempDir, "test")
219
-
220
- // Create agency branch without emitting
221
- await runTestEffect(
222
- task({
223
- silent: true,
224
- emit: "unemitted-task",
225
- }),
226
- )
227
-
228
- // Go back to main
229
- await runGitCommand(tempDir, ["git", "checkout", "main"])
230
-
231
- // Try to create task from unemitted agency branch
232
- await expect(
233
- runTestEffect(
234
- task({
235
- silent: true,
236
- emit: "second-task",
237
- from: "agency--unemitted-task",
238
- }),
239
- ),
240
- ).rejects.toThrow("emit branch")
241
- })
242
- })
243
-
244
- describe("--from-current flag", () => {
245
- test("branches from current branch when it's not an agency branch", async () => {
246
- await initGitRepo(tempDir)
247
- process.chdir(tempDir)
248
- await initAgency(tempDir, "test")
249
-
250
- // Create and switch to a feature branch
251
- await runGitCommand(tempDir, ["git", "checkout", "-b", "feature-current"])
252
- await createFile(tempDir, "current.txt", "content")
253
- await runGitCommand(tempDir, ["git", "add", "."])
254
- await runGitCommand(tempDir, ["git", "commit", "-m", "Add current file"])
255
-
256
- // Go back to main to create the task
257
- await runGitCommand(tempDir, ["git", "checkout", "main"])
258
-
259
- // Create task from feature-current branch
260
- await runTestEffect(
261
- task({
262
- silent: true,
263
- emit: "my-task",
264
- from: "feature-current",
265
- }),
266
- )
267
-
268
- const currentBranch = await getCurrentBranch(tempDir)
269
- expect(currentBranch).toBe("agency--my-task")
270
-
271
- // Verify current.txt exists
272
- const currentFile = await Bun.file(join(tempDir, "current.txt")).text()
273
- expect(currentFile).toBe("content")
274
- })
275
-
276
- test("uses emit branch when --from specifies an agency branch", async () => {
277
- await initGitRepo(tempDir)
278
- process.chdir(tempDir)
279
- await initAgency(tempDir, "test")
280
-
281
- // Create first agency branch
282
- await runTestEffect(
283
- task({
284
- silent: true,
285
- emit: "first-task",
286
- }),
287
- )
288
-
289
- await createFile(tempDir, "first.txt", "first")
290
- await runGitCommand(tempDir, ["git", "add", "."])
291
- await runGitCommand(tempDir, ["git", "commit", "-m", "Add first file"])
292
-
293
- // Emit the branch
294
- await runTestEffect(emit({ silent: true }))
295
-
296
- // Go back to main
297
- await runGitCommand(tempDir, ["git", "checkout", "main"])
298
-
299
- // Create task from first agency branch
300
- await runTestEffect(
301
- task({
302
- silent: true,
303
- emit: "second-task",
304
- from: "agency--first-task",
305
- }),
306
- )
307
-
308
- const currentBranch = await getCurrentBranch(tempDir)
309
- expect(currentBranch).toBe("agency--second-task")
310
-
311
- // Verify first.txt exists
312
- const firstFile = await Bun.file(join(tempDir, "first.txt")).text()
313
- expect(firstFile).toBe("first")
314
- })
315
-
316
- test("throws error if --from agency branch has no emit branch", async () => {
317
- await initGitRepo(tempDir)
318
- process.chdir(tempDir)
319
- await initAgency(tempDir, "test")
320
-
321
- // Create agency branch without emitting
322
- await runTestEffect(
323
- task({
324
- silent: true,
325
- emit: "unemitted-task",
326
- }),
327
- )
328
-
329
- // Go back to main
330
- await runGitCommand(tempDir, ["git", "checkout", "main"])
331
-
332
- // Try to create task from unemitted agency branch
333
- await expect(
334
- runTestEffect(
335
- task({
336
- silent: true,
337
- emit: "second-task",
338
- from: "agency--unemitted-task",
339
- }),
340
- ),
341
- ).rejects.toThrow("emit branch")
342
- })
343
- })
344
-
345
- describe("--from and --from-current validation", () => {
346
- test("throws error if both flags are used", async () => {
347
- await initGitRepo(tempDir)
348
- process.chdir(tempDir)
349
- await initAgency(tempDir, "test")
350
-
351
- await expect(
352
- runTestEffect(
353
- task({
354
- silent: true,
355
- emit: "my-task",
356
- from: "main",
357
- fromCurrent: true,
358
- }),
359
- ),
360
- ).rejects.toThrow("Cannot use both --from and --from-current")
361
- })
362
- })
363
-
364
- describe("--from flag branch naming", () => {
365
- test("agency task --from foo requires explicit branch name in silent mode", async () => {
366
- await initGitRepo(tempDir)
367
- process.chdir(tempDir)
368
- await initAgency(tempDir, "test")
369
-
370
- await runGitCommand(tempDir, ["git", "checkout", "-b", "foo"])
371
- await runGitCommand(tempDir, ["git", "checkout", "main"])
372
-
373
- await expect(
374
- runTestEffect(
375
- task({
376
- silent: true,
377
- from: "foo",
378
- }),
379
- ),
380
- ).rejects.toThrow("Branch name")
381
- })
382
-
383
- test("agency task out --from foo creates agency--out emitting to out", async () => {
384
- await initGitRepo(tempDir)
385
- process.chdir(tempDir)
386
- await initAgency(tempDir, "test")
387
-
388
- await runGitCommand(tempDir, ["git", "checkout", "-b", "foo"])
389
- await runGitCommand(tempDir, ["git", "checkout", "main"])
390
-
391
- await runTestEffect(
392
- task({
393
- silent: true,
394
- emit: "out",
395
- from: "foo",
396
- }),
397
- )
398
-
399
- const currentBranch = await getCurrentBranch(tempDir)
400
- expect(currentBranch).toBe("agency--out")
401
-
402
- const agencyJson = JSON.parse(
403
- await Bun.file(join(tempDir, "agency.json")).text(),
404
- )
405
- expect(agencyJson.emitBranch).toBe("out")
406
- })
407
- })
408
-
409
- describe("default branching behavior", () => {
410
- test("branches from auto-detected main branch by default", async () => {
411
- await initGitRepo(tempDir)
412
- process.chdir(tempDir)
413
- await initAgency(tempDir, "test")
414
-
415
- // Create a commit on main
416
- await createFile(tempDir, "main.txt", "main content")
417
- await runGitCommand(tempDir, ["git", "add", "."])
418
- await runGitCommand(tempDir, ["git", "commit", "-m", "Main commit"])
419
-
420
- // Create a different branch and switch to it
421
- await runGitCommand(tempDir, ["git", "checkout", "-b", "other-branch"])
422
- await createFile(tempDir, "other.txt", "other content")
423
- await runGitCommand(tempDir, ["git", "add", "."])
424
- await runGitCommand(tempDir, ["git", "commit", "-m", "Other commit"])
425
-
426
- // Go back to main to set it as main branch
427
- await runGitCommand(tempDir, ["git", "checkout", "main"])
428
-
429
- // Create task (should branch from main)
430
- await runTestEffect(
431
- task({
432
- silent: true,
433
- emit: "my-task",
434
- }),
435
- )
436
-
437
- const currentBranch = await getCurrentBranch(tempDir)
438
- expect(currentBranch).toBe("agency--my-task")
439
-
440
- // Verify main.txt exists but other.txt does not
441
- const mainExists = await Bun.file(join(tempDir, "main.txt")).exists()
442
- const otherExists = await Bun.file(join(tempDir, "other.txt")).exists()
443
- expect(mainExists).toBe(true)
444
- expect(otherExists).toBe(false)
445
- })
446
- })
447
-
448
- describe("remote branch preference", () => {
449
- let remoteDir: string
450
-
451
- beforeEach(async () => {
452
- // Create a bare repository to act as the "remote"
453
- remoteDir = await createTempDir()
454
- await runGitCommand(remoteDir, ["git", "init", "--bare", "-b", "main"])
455
- })
456
-
457
- afterEach(async () => {
458
- await cleanupTempDir(remoteDir)
459
- })
460
-
461
- test("prefers origin/main over local main when remote is ahead", async () => {
462
- await initGitRepo(tempDir)
463
- process.chdir(tempDir)
464
- await initAgency(tempDir, "test")
465
-
466
- // Add the remote
467
- await runGitCommand(tempDir, [
468
- "git",
469
- "remote",
470
- "add",
471
- "origin",
472
- remoteDir,
473
- ])
474
-
475
- // Push current state to remote
476
- await runGitCommand(tempDir, ["git", "push", "-u", "origin", "main"])
477
-
478
- // Make a commit on local main
479
- await createFile(tempDir, "local-only.txt", "local content")
480
- await runGitCommand(tempDir, ["git", "add", "."])
481
- await runGitCommand(tempDir, ["git", "commit", "-m", "Local-only commit"])
482
-
483
- // Reset local main back to origin/main (simulate local being behind)
484
- await runGitCommand(tempDir, ["git", "reset", "--hard", "origin/main"])
485
-
486
- // Make a NEW commit on origin/main by pushing from a separate clone
487
- const cloneDir = await createTempDir()
488
- await runGitCommand(cloneDir, ["git", "clone", remoteDir, "."])
489
- await runGitCommand(cloneDir, [
490
- "git",
491
- "config",
492
- "user.email",
493
- "test@example.com",
494
- ])
495
- await runGitCommand(cloneDir, ["git", "config", "user.name", "Test User"])
496
- await createFile(cloneDir, "remote-only.txt", "remote content")
497
- await runGitCommand(cloneDir, ["git", "add", "."])
498
- await runGitCommand(cloneDir, [
499
- "git",
500
- "commit",
501
- "-m",
502
- "Remote-only commit",
503
- ])
504
- await runGitCommand(cloneDir, ["git", "push", "origin", "main"])
505
- await cleanupTempDir(cloneDir)
506
-
507
- // Fetch so origin/main is updated but local main stays behind
508
- await runGitCommand(tempDir, ["git", "fetch", "origin"])
509
-
510
- // Configure agency.mainBranch to "main" (local) and agency.remote to "origin"
511
- await runGitCommand(tempDir, [
512
- "git",
513
- "config",
514
- "--local",
515
- "agency.mainBranch",
516
- "main",
517
- ])
518
- await runGitCommand(tempDir, [
519
- "git",
520
- "config",
521
- "--local",
522
- "agency.remote",
523
- "origin",
524
- ])
525
-
526
- // Create task - should branch from origin/main (which has remote-only.txt)
527
- await runTestEffect(
528
- task({
529
- silent: true,
530
- emit: "my-task",
531
- }),
532
- )
533
-
534
- const currentBranch = await getCurrentBranch(tempDir)
535
- expect(currentBranch).toBe("agency--my-task")
536
-
537
- // The new branch should have remote-only.txt (from origin/main)
538
- const remoteFileExists = await Bun.file(
539
- join(tempDir, "remote-only.txt"),
540
- ).exists()
541
- expect(remoteFileExists).toBe(true)
542
- })
543
-
544
- test("falls back to local main when remote branch does not exist", async () => {
545
- await initGitRepo(tempDir)
546
- process.chdir(tempDir)
547
- await initAgency(tempDir, "test")
548
-
549
- // Create a commit on local main
550
- await createFile(tempDir, "local.txt", "local content")
551
- await runGitCommand(tempDir, ["git", "add", "."])
552
- await runGitCommand(tempDir, ["git", "commit", "-m", "Local commit"])
553
-
554
- // Add a remote but don't push (so origin/main doesn't exist)
555
- await runGitCommand(tempDir, [
556
- "git",
557
- "remote",
558
- "add",
559
- "origin",
560
- remoteDir,
561
- ])
562
-
563
- // Configure agency.mainBranch to "main" and agency.remote to "origin"
564
- await runGitCommand(tempDir, [
565
- "git",
566
- "config",
567
- "--local",
568
- "agency.mainBranch",
569
- "main",
570
- ])
571
- await runGitCommand(tempDir, [
572
- "git",
573
- "config",
574
- "--local",
575
- "agency.remote",
576
- "origin",
577
- ])
578
-
579
- // Create task - should fall back to local main since origin/main doesn't exist
580
- await runTestEffect(
581
- task({
582
- silent: true,
583
- emit: "my-task",
584
- }),
585
- )
586
-
587
- const currentBranch = await getCurrentBranch(tempDir)
588
- expect(currentBranch).toBe("agency--my-task")
589
-
590
- // The new branch should have local.txt
591
- const localFileExists = await Bun.file(
592
- join(tempDir, "local.txt"),
593
- ).exists()
594
- expect(localFileExists).toBe(true)
595
- })
596
-
597
- test("uses configured remote branch as-is when it already has remote prefix", async () => {
598
- await initGitRepo(tempDir)
599
- process.chdir(tempDir)
600
- await initAgency(tempDir, "test")
601
-
602
- // Add the remote and push
603
- await runGitCommand(tempDir, [
604
- "git",
605
- "remote",
606
- "add",
607
- "origin",
608
- remoteDir,
609
- ])
610
- await runGitCommand(tempDir, ["git", "push", "-u", "origin", "main"])
611
-
612
- // Configure agency.mainBranch to "origin/main" (already has remote prefix)
613
- await runGitCommand(tempDir, [
614
- "git",
615
- "config",
616
- "--local",
617
- "agency.mainBranch",
618
- "origin/main",
619
- ])
620
-
621
- // Create task
622
- await runTestEffect(
623
- task({
624
- silent: true,
625
- emit: "my-task",
626
- }),
627
- )
628
-
629
- const currentBranch = await getCurrentBranch(tempDir)
630
- expect(currentBranch).toBe("agency--my-task")
631
- })
632
- })
633
- })