@markjaquith/agency 1.11.0 → 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 -415
  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 -518
  73. package/src/commands/push.ts +0 -434
  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 -1183
  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,549 +0,0 @@
1
- import { test, expect, describe, beforeEach, afterEach } from "bun:test"
2
- import { join } from "path"
3
- import { emit } from "../commands/emit"
4
- import { task } from "../commands/task"
5
- import {
6
- createTempDir,
7
- cleanupTempDir,
8
- initGitRepo,
9
- initAgency,
10
- getGitOutput,
11
- getCurrentBranch,
12
- createCommit,
13
- checkoutBranch,
14
- createBranch,
15
- addAndCommit,
16
- setupRemote,
17
- runTestEffect,
18
- } from "../test-utils"
19
-
20
- // Cache the git-filter-repo availability check (it doesn't change during test run)
21
- let hasGitFilterRepoCache: boolean | null = null
22
- async function checkGitFilterRepo(): Promise<boolean> {
23
- if (hasGitFilterRepoCache === null) {
24
- const proc = Bun.spawn(["which", "git-filter-repo"], {
25
- stdout: "pipe",
26
- stderr: "pipe",
27
- })
28
- await proc.exited
29
- hasGitFilterRepoCache = proc.exitCode === 0
30
- }
31
- return hasGitFilterRepoCache
32
- }
33
-
34
- describe("emit command - integration tests (requires git-filter-repo)", () => {
35
- let tempDir: string
36
- let originalCwd: string
37
- let hasGitFilterRepo: boolean
38
-
39
- beforeEach(async () => {
40
- tempDir = await createTempDir()
41
- originalCwd = process.cwd()
42
- process.chdir(tempDir)
43
-
44
- // Set config path to non-existent file to use defaults
45
- process.env.AGENCY_CONFIG_PATH = join(tempDir, "non-existent-config.json")
46
- // Set config dir to temp dir to avoid picking up user's config files
47
- process.env.AGENCY_CONFIG_DIR = await createTempDir()
48
-
49
- // Check if git-filter-repo is available (cached)
50
- hasGitFilterRepo = await checkGitFilterRepo()
51
-
52
- // Initialize git repo with main branch (already includes initial commit)
53
- await initGitRepo(tempDir)
54
-
55
- // Create a source branch (with agency-- prefix)
56
- await createBranch(tempDir, "agency--test-feature")
57
-
58
- // Initialize AGENTS.md and commit in one go
59
- await initAgency(tempDir, "test")
60
-
61
- await runTestEffect(task({ silent: true, fromCurrent: true }))
62
- await addAndCommit(tempDir, "AGENTS.md", "Add AGENTS.md")
63
-
64
- // Set up origin/main for git-filter-repo
65
- await setupRemote(tempDir, "origin", tempDir)
66
- })
67
-
68
- afterEach(async () => {
69
- process.chdir(originalCwd)
70
- delete process.env.AGENCY_CONFIG_PATH
71
- if (process.env.AGENCY_CONFIG_DIR) {
72
- await cleanupTempDir(process.env.AGENCY_CONFIG_DIR)
73
- delete process.env.AGENCY_CONFIG_DIR
74
- }
75
- await cleanupTempDir(tempDir)
76
- })
77
-
78
- test("filters AGENTS.md from emit branch", async () => {
79
- if (!hasGitFilterRepo) {
80
- console.log("Skipping test: git-filter-repo not installed")
81
- return
82
- }
83
-
84
- // Go back to main and create a fresh source branch
85
- await checkoutBranch(tempDir, "main")
86
- await createBranch(tempDir, "agency--feature")
87
- // Create agency.json with AGENTS.md as managed file
88
- await Bun.write(
89
- join(tempDir, "agency.json"),
90
- JSON.stringify({
91
- version: 1,
92
- injectedFiles: ["AGENTS.md"],
93
- template: "test",
94
- createdAt: new Date().toISOString(),
95
- }),
96
- )
97
- await Bun.write(join(tempDir, "AGENTS.md"), "# Test AGENTS\n")
98
- await addAndCommit(tempDir, "agency.json AGENTS.md", "Add agency files")
99
- // Also create a test.txt file via createCommit
100
- await createCommit(tempDir, "Feature commit")
101
-
102
- // Create emit branch (this runs git-filter-repo)
103
- await runTestEffect(emit({ silent: true }))
104
-
105
- // Should still be on feature branch
106
- const currentBranch = await getCurrentBranch(tempDir)
107
- expect(currentBranch).toBe("agency--feature")
108
-
109
- // Switch to emit branch to verify files
110
- await checkoutBranch(tempDir, "feature")
111
-
112
- // AGENTS.md should be filtered out
113
- const files = await getGitOutput(tempDir, ["ls-files"])
114
- expect(files).not.toContain("AGENTS.md")
115
- expect(files).not.toContain("AGENCY.md")
116
-
117
- // But test.txt should still exist
118
- expect(files).toContain("test.txt")
119
- })
120
-
121
- test("handles emit branch recreation after source branch rebase", async () => {
122
- if (!hasGitFilterRepo) {
123
- console.log("Skipping test: git-filter-repo not installed")
124
- return
125
- }
126
-
127
- // We're on test-feature which has AGENTS.md
128
- // Add a feature-specific file to avoid conflicts
129
- await Bun.write(join(tempDir, "feature.txt"), "feature content\n")
130
- await addAndCommit(tempDir, "feature.txt", "Add feature file")
131
-
132
- // Store merge-base before advancing main
133
- const initialMergeBase = await getGitOutput(tempDir, [
134
- "merge-base",
135
- "agency--test-feature",
136
- "main",
137
- ])
138
-
139
- // Create initial emit branch
140
- await runTestEffect(emit({ silent: true, baseBranch: "main" }))
141
-
142
- // Should still be on test-feature branch
143
- let currentBranch = await getCurrentBranch(tempDir)
144
- expect(currentBranch).toBe("agency--test-feature")
145
-
146
- // Switch to emit branch to verify AGENTS.md is filtered
147
- await checkoutBranch(tempDir, "test-feature")
148
-
149
- let files = await getGitOutput(tempDir, ["ls-files"])
150
- expect(files).not.toContain("AGENTS.md")
151
- expect(files).toContain("feature.txt")
152
-
153
- // Switch back to source branch
154
- await checkoutBranch(tempDir, "agency--test-feature")
155
-
156
- // Simulate advancing main branch with a different file
157
- await checkoutBranch(tempDir, "main")
158
- await Bun.write(join(tempDir, "main-file.txt"), "main content\n")
159
- await addAndCommit(tempDir, "main-file.txt", "Main branch advancement")
160
-
161
- // Rebase test-feature onto new main
162
- await checkoutBranch(tempDir, "agency--test-feature")
163
- const rebaseProc = Bun.spawn(["git", "rebase", "main"], {
164
- cwd: tempDir,
165
- stdout: "pipe",
166
- stderr: "pipe",
167
- })
168
- await rebaseProc.exited
169
- if (rebaseProc.exitCode !== 0) {
170
- const stderr = await new Response(rebaseProc.stderr).text()
171
- throw new Error(`Rebase failed: ${stderr}`)
172
- }
173
-
174
- // Verify merge-base has changed after rebase
175
- const newMergeBase = await getGitOutput(tempDir, [
176
- "merge-base",
177
- "agency--test-feature",
178
- "main",
179
- ])
180
- expect(newMergeBase.trim()).not.toBe(initialMergeBase.trim())
181
-
182
- // Recreate emit branch after rebase (this is where the bug would manifest)
183
- await runTestEffect(emit({ silent: true, baseBranch: "main" }))
184
-
185
- // Should still be on test-feature branch
186
- currentBranch = await getCurrentBranch(tempDir)
187
- expect(currentBranch).toBe("agency--test-feature")
188
-
189
- // Switch to emit branch to verify files
190
- await checkoutBranch(tempDir, "test-feature")
191
-
192
- // Verify AGENTS.md is still filtered and no extraneous changes
193
- files = await getGitOutput(tempDir, ["ls-files"])
194
- expect(files).not.toContain("AGENTS.md")
195
- expect(files).toContain("feature.txt")
196
- expect(files).toContain("main-file.txt") // Should have main's file after rebase
197
-
198
- // Verify that our feature commits exist but AGENTS.md commit is filtered
199
- const logOutput = await getGitOutput(tempDir, [
200
- "log",
201
- "--oneline",
202
- "main..test-feature",
203
- ])
204
- expect(logOutput).toContain("Add feature file")
205
- expect(logOutput).not.toContain("Add AGENTS.md")
206
- })
207
-
208
- test("filters pre-existing CLAUDE.md that gets edited by agency", async () => {
209
- if (!hasGitFilterRepo) {
210
- console.log("Skipping test: git-filter-repo not installed")
211
- return
212
- }
213
-
214
- // Start fresh on main branch
215
- await checkoutBranch(tempDir, "main")
216
-
217
- // Create CLAUDE.md on main branch (simulating pre-existing file)
218
- await Bun.write(
219
- join(tempDir, "CLAUDE.md"),
220
- "# Original Claude Instructions\n\nSome content here.\n",
221
- )
222
- await addAndCommit(tempDir, "CLAUDE.md", "Add CLAUDE.md")
223
-
224
- // Create a new feature branch
225
- await createBranch(tempDir, "agency--claude-test")
226
-
227
- // Initialize agency on this branch - first commit has agency files (NOT including CLAUDE.md)
228
- await Bun.write(
229
- join(tempDir, "agency.json"),
230
- JSON.stringify({
231
- version: 1,
232
- injectedFiles: ["AGENTS.md"],
233
- template: "test",
234
- createdAt: new Date().toISOString(),
235
- }),
236
- )
237
- await Bun.write(join(tempDir, "AGENTS.md"), "# Test AGENTS\n")
238
- await addAndCommit(
239
- tempDir,
240
- "agency.json AGENTS.md",
241
- "Initialize agency files",
242
- )
243
-
244
- // Simulate what agency task now does - CLAUDE.md modification in a separate commit
245
- // with AGENCY_REMOVE_COMMIT marker so the commit gets dropped during emit
246
- const originalClaude = await Bun.file(join(tempDir, "CLAUDE.md")).text()
247
- const modifiedClaude = `${originalClaude}\n# Agency References\n@AGENTS.md\n@TASK.md\n`
248
- await Bun.write(join(tempDir, "CLAUDE.md"), modifiedClaude)
249
- await addAndCommit(
250
- tempDir,
251
- "CLAUDE.md",
252
- "chore: agency edit CLAUDE.md\n\nAGENCY_REMOVE_COMMIT",
253
- )
254
-
255
- // Add a feature file
256
- await createCommit(tempDir, "Feature commit")
257
-
258
- // Create emit branch (this should drop the CLAUDE.md modification commit)
259
- await runTestEffect(emit({ silent: true, baseBranch: "main" }))
260
-
261
- // Should still be on source branch
262
- const currentBranch = await getCurrentBranch(tempDir)
263
- expect(currentBranch).toBe("agency--claude-test")
264
-
265
- // Switch to emit branch to verify CLAUDE.md is reverted to main's version
266
- await checkoutBranch(tempDir, "claude-test")
267
-
268
- const files = await getGitOutput(tempDir, ["ls-files"])
269
- expect(files).toContain("CLAUDE.md") // File should exist (from main)
270
- expect(files).not.toContain("AGENTS.md") // Should be filtered
271
- expect(files).not.toContain("TASK.md") // Should be filtered
272
- expect(files).toContain("test.txt") // Feature file should exist
273
-
274
- // Verify CLAUDE.md was reverted to original (no agency references)
275
- const claudeContent = await Bun.file(join(tempDir, "CLAUDE.md")).text()
276
- expect(claudeContent).toBe(
277
- "# Original Claude Instructions\n\nSome content here.\n",
278
- )
279
- expect(claudeContent).not.toContain("@AGENTS.md")
280
- expect(claudeContent).not.toContain("@TASK.md")
281
- })
282
-
283
- describe("AGENCY_REMOVE_COMMIT edge cases", () => {
284
- test("handles two contiguous AGENCY_REMOVE_COMMIT commits", async () => {
285
- if (!hasGitFilterRepo) {
286
- console.log("Skipping test: git-filter-repo not installed")
287
- return
288
- }
289
-
290
- await checkoutBranch(tempDir, "main")
291
-
292
- // Create two files on main that will be modified
293
- await Bun.write(join(tempDir, "FILE1.md"), "Original content 1\n")
294
- await Bun.write(join(tempDir, "FILE2.md"), "Original content 2\n")
295
- await addAndCommit(tempDir, "FILE1.md FILE2.md", "Add files")
296
-
297
- await createBranch(tempDir, "agency--contiguous-test")
298
-
299
- // Create agency.json
300
- await Bun.write(
301
- join(tempDir, "agency.json"),
302
- JSON.stringify({
303
- version: 1,
304
- injectedFiles: [],
305
- template: "test",
306
- createdAt: new Date().toISOString(),
307
- }),
308
- )
309
- await addAndCommit(tempDir, "agency.json", "Add agency.json")
310
-
311
- // Two contiguous AGENCY_REMOVE_COMMIT commits
312
- await Bun.write(join(tempDir, "FILE1.md"), "Modified content 1\n")
313
- await addAndCommit(
314
- tempDir,
315
- "FILE1.md",
316
- "chore: edit FILE1\n\nAGENCY_REMOVE_COMMIT",
317
- )
318
-
319
- await Bun.write(join(tempDir, "FILE2.md"), "Modified content 2\n")
320
- await addAndCommit(
321
- tempDir,
322
- "FILE2.md",
323
- "chore: edit FILE2\n\nAGENCY_REMOVE_COMMIT",
324
- )
325
-
326
- // Add a feature commit after
327
- await createCommit(tempDir, "Feature commit")
328
-
329
- await runTestEffect(emit({ silent: true, baseBranch: "main" }))
330
- await checkoutBranch(tempDir, "contiguous-test")
331
-
332
- // Both files should be reverted to original
333
- const file1 = await Bun.file(join(tempDir, "FILE1.md")).text()
334
- const file2 = await Bun.file(join(tempDir, "FILE2.md")).text()
335
- expect(file1).toBe("Original content 1\n")
336
- expect(file2).toBe("Original content 2\n")
337
-
338
- // Feature commit should still exist
339
- const files = await getGitOutput(tempDir, ["ls-files"])
340
- expect(files).toContain("test.txt")
341
- })
342
-
343
- test("handles two non-contiguous AGENCY_REMOVE_COMMIT commits", async () => {
344
- if (!hasGitFilterRepo) {
345
- console.log("Skipping test: git-filter-repo not installed")
346
- return
347
- }
348
-
349
- await checkoutBranch(tempDir, "main")
350
-
351
- await Bun.write(join(tempDir, "FILE1.md"), "Original content 1\n")
352
- await Bun.write(join(tempDir, "FILE2.md"), "Original content 2\n")
353
- await addAndCommit(tempDir, "FILE1.md FILE2.md", "Add files")
354
-
355
- await createBranch(tempDir, "agency--non-contiguous-test")
356
-
357
- await Bun.write(
358
- join(tempDir, "agency.json"),
359
- JSON.stringify({
360
- version: 1,
361
- injectedFiles: [],
362
- template: "test",
363
- createdAt: new Date().toISOString(),
364
- }),
365
- )
366
- await addAndCommit(tempDir, "agency.json", "Add agency.json")
367
-
368
- // First AGENCY_REMOVE_COMMIT
369
- await Bun.write(join(tempDir, "FILE1.md"), "Modified content 1\n")
370
- await addAndCommit(
371
- tempDir,
372
- "FILE1.md",
373
- "chore: edit FILE1\n\nAGENCY_REMOVE_COMMIT",
374
- )
375
-
376
- // Regular commit in between
377
- await Bun.write(join(tempDir, "feature1.txt"), "feature 1\n")
378
- await addAndCommit(tempDir, "feature1.txt", "Add feature 1")
379
-
380
- // Second AGENCY_REMOVE_COMMIT
381
- await Bun.write(join(tempDir, "FILE2.md"), "Modified content 2\n")
382
- await addAndCommit(
383
- tempDir,
384
- "FILE2.md",
385
- "chore: edit FILE2\n\nAGENCY_REMOVE_COMMIT",
386
- )
387
-
388
- // Another regular commit
389
- await Bun.write(join(tempDir, "feature2.txt"), "feature 2\n")
390
- await addAndCommit(tempDir, "feature2.txt", "Add feature 2")
391
-
392
- await runTestEffect(emit({ silent: true, baseBranch: "main" }))
393
- await checkoutBranch(tempDir, "non-contiguous-test")
394
-
395
- // Both files should be reverted to original
396
- const file1 = await Bun.file(join(tempDir, "FILE1.md")).text()
397
- const file2 = await Bun.file(join(tempDir, "FILE2.md")).text()
398
- expect(file1).toBe("Original content 1\n")
399
- expect(file2).toBe("Original content 2\n")
400
-
401
- // Feature commits should still exist
402
- const files = await getGitOutput(tempDir, ["ls-files"])
403
- expect(files).toContain("feature1.txt")
404
- expect(files).toContain("feature2.txt")
405
- })
406
-
407
- test("handles AGENCY_REMOVE_COMMIT as first commit after fork point", async () => {
408
- if (!hasGitFilterRepo) {
409
- console.log("Skipping test: git-filter-repo not installed")
410
- return
411
- }
412
-
413
- await checkoutBranch(tempDir, "main")
414
-
415
- await Bun.write(join(tempDir, "EXISTING.md"), "Original content\n")
416
- await addAndCommit(tempDir, "EXISTING.md", "Add existing file")
417
-
418
- await createBranch(tempDir, "agency--first-commit-test")
419
-
420
- // First commit is AGENCY_REMOVE_COMMIT (modifying existing file)
421
- await Bun.write(join(tempDir, "EXISTING.md"), "Modified content\n")
422
- await addAndCommit(
423
- tempDir,
424
- "EXISTING.md",
425
- "chore: edit EXISTING\n\nAGENCY_REMOVE_COMMIT",
426
- )
427
-
428
- // Then agency.json
429
- await Bun.write(
430
- join(tempDir, "agency.json"),
431
- JSON.stringify({
432
- version: 1,
433
- injectedFiles: [],
434
- template: "test",
435
- createdAt: new Date().toISOString(),
436
- }),
437
- )
438
- await addAndCommit(tempDir, "agency.json", "Add agency.json")
439
-
440
- // Feature commit
441
- await createCommit(tempDir, "Feature commit")
442
-
443
- await runTestEffect(emit({ silent: true, baseBranch: "main" }))
444
- await checkoutBranch(tempDir, "first-commit-test")
445
-
446
- // File should be reverted to original
447
- const content = await Bun.file(join(tempDir, "EXISTING.md")).text()
448
- expect(content).toBe("Original content\n")
449
-
450
- // Feature commit should exist
451
- const files = await getGitOutput(tempDir, ["ls-files"])
452
- expect(files).toContain("test.txt")
453
- })
454
-
455
- test("handles AGENCY_REMOVE_COMMIT as last commit", async () => {
456
- if (!hasGitFilterRepo) {
457
- console.log("Skipping test: git-filter-repo not installed")
458
- return
459
- }
460
-
461
- await checkoutBranch(tempDir, "main")
462
-
463
- await Bun.write(join(tempDir, "EXISTING.md"), "Original content\n")
464
- await addAndCommit(tempDir, "EXISTING.md", "Add existing file")
465
-
466
- await createBranch(tempDir, "agency--last-commit-test")
467
-
468
- // agency.json first
469
- await Bun.write(
470
- join(tempDir, "agency.json"),
471
- JSON.stringify({
472
- version: 1,
473
- injectedFiles: [],
474
- template: "test",
475
- createdAt: new Date().toISOString(),
476
- }),
477
- )
478
- await addAndCommit(tempDir, "agency.json", "Add agency.json")
479
-
480
- // Feature commit
481
- await Bun.write(join(tempDir, "feature.txt"), "feature\n")
482
- await addAndCommit(tempDir, "feature.txt", "Add feature")
483
-
484
- // Last commit is AGENCY_REMOVE_COMMIT
485
- await Bun.write(join(tempDir, "EXISTING.md"), "Modified content\n")
486
- await addAndCommit(
487
- tempDir,
488
- "EXISTING.md",
489
- "chore: edit EXISTING\n\nAGENCY_REMOVE_COMMIT",
490
- )
491
-
492
- await runTestEffect(emit({ silent: true, baseBranch: "main" }))
493
- await checkoutBranch(tempDir, "last-commit-test")
494
-
495
- // File should be reverted to original
496
- const content = await Bun.file(join(tempDir, "EXISTING.md")).text()
497
- expect(content).toBe("Original content\n")
498
-
499
- // Feature commit should exist
500
- const files = await getGitOutput(tempDir, ["ls-files"])
501
- expect(files).toContain("feature.txt")
502
- })
503
-
504
- test("handles AGENCY_REMOVE_COMMIT as only commit (besides agency.json)", async () => {
505
- if (!hasGitFilterRepo) {
506
- console.log("Skipping test: git-filter-repo not installed")
507
- return
508
- }
509
-
510
- await checkoutBranch(tempDir, "main")
511
-
512
- await Bun.write(join(tempDir, "EXISTING.md"), "Original content\n")
513
- await addAndCommit(tempDir, "EXISTING.md", "Add existing file")
514
-
515
- await createBranch(tempDir, "agency--only-commit-test")
516
-
517
- // agency.json
518
- await Bun.write(
519
- join(tempDir, "agency.json"),
520
- JSON.stringify({
521
- version: 1,
522
- injectedFiles: [],
523
- template: "test",
524
- createdAt: new Date().toISOString(),
525
- }),
526
- )
527
- await addAndCommit(tempDir, "agency.json", "Add agency.json")
528
-
529
- // Only other commit is AGENCY_REMOVE_COMMIT
530
- await Bun.write(join(tempDir, "EXISTING.md"), "Modified content\n")
531
- await addAndCommit(
532
- tempDir,
533
- "EXISTING.md",
534
- "chore: edit EXISTING\n\nAGENCY_REMOVE_COMMIT",
535
- )
536
-
537
- await runTestEffect(emit({ silent: true, baseBranch: "main" }))
538
- await checkoutBranch(tempDir, "only-commit-test")
539
-
540
- // File should be reverted to original
541
- const content = await Bun.file(join(tempDir, "EXISTING.md")).text()
542
- expect(content).toBe("Original content\n")
543
-
544
- // Emit branch should exist and have the file
545
- const files = await getGitOutput(tempDir, ["ls-files"])
546
- expect(files).toContain("EXISTING.md")
547
- })
548
- })
549
- })