@markjaquith/agency 1.11.1 → 2.1.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 (132) hide show
  1. package/README.md +313 -117
  2. package/cli.ts +124 -420
  3. package/index.ts +1 -1
  4. package/package.json +12 -17
  5. package/skills/agency/SKILL.md +266 -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 +326 -399
  24. package/src/commands/work.ts +153 -153
  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/work-target.test.ts +77 -0
  53. package/src/workbase/work-target.ts +180 -0
  54. package/src/workbase/worktree-command.test.ts +61 -0
  55. package/src/workbase/worktree-command.ts +52 -0
  56. package/src/commands/base.test.ts +0 -198
  57. package/src/commands/base.ts +0 -198
  58. package/src/commands/clean.test.ts +0 -299
  59. package/src/commands/clean.ts +0 -274
  60. package/src/commands/completions.test.ts +0 -51
  61. package/src/commands/completions.ts +0 -308
  62. package/src/commands/emit.integration.test.ts +0 -549
  63. package/src/commands/emit.test.ts +0 -576
  64. package/src/commands/emit.ts +0 -700
  65. package/src/commands/emitted.test.ts +0 -226
  66. package/src/commands/emitted.ts +0 -57
  67. package/src/commands/loop.test.ts +0 -621
  68. package/src/commands/loop.ts +0 -406
  69. package/src/commands/merge.integration.test.ts +0 -195
  70. package/src/commands/merge.test.ts +0 -246
  71. package/src/commands/merge.ts +0 -244
  72. package/src/commands/pull.test.ts +0 -395
  73. package/src/commands/pull.ts +0 -205
  74. package/src/commands/push.test.ts +0 -641
  75. package/src/commands/push.ts +0 -482
  76. package/src/commands/rebase.test.ts +0 -521
  77. package/src/commands/rebase.ts +0 -232
  78. package/src/commands/save.test.ts +0 -247
  79. package/src/commands/save.ts +0 -162
  80. package/src/commands/source.test.ts +0 -195
  81. package/src/commands/source.ts +0 -72
  82. package/src/commands/switch.test.ts +0 -194
  83. package/src/commands/switch.ts +0 -84
  84. package/src/commands/task-branching.test.ts +0 -633
  85. package/src/commands/task-continue.test.ts +0 -311
  86. package/src/commands/task-edit.test.ts +0 -235
  87. package/src/commands/task-main.test.ts +0 -1066
  88. package/src/commands/task-squash.test.ts +0 -266
  89. package/src/commands/tasks.test.ts +0 -391
  90. package/src/commands/tasks.ts +0 -161
  91. package/src/commands/template-delete.test.ts +0 -178
  92. package/src/commands/template-delete.ts +0 -98
  93. package/src/commands/template-list.test.ts +0 -135
  94. package/src/commands/template-list.ts +0 -87
  95. package/src/commands/template-view.test.ts +0 -158
  96. package/src/commands/template-view.ts +0 -86
  97. package/src/commands/template.test.ts +0 -32
  98. package/src/commands/template.ts +0 -96
  99. package/src/commands/use.test.ts +0 -87
  100. package/src/commands/use.ts +0 -97
  101. package/src/constants.ts +0 -10
  102. package/src/errors.ts +0 -17
  103. package/src/schemas.ts +0 -33
  104. package/src/services/AgencyMetadataService.ts +0 -311
  105. package/src/services/ClaudeService.test.ts +0 -184
  106. package/src/services/ClaudeService.ts +0 -91
  107. package/src/services/ConfigService.ts +0 -115
  108. package/src/services/FilterRepoService.ts +0 -164
  109. package/src/services/FormatterService.test.ts +0 -432
  110. package/src/services/FormatterService.ts +0 -219
  111. package/src/services/GitService.ts +0 -1187
  112. package/src/services/MockFilterRepoService.ts +0 -140
  113. package/src/services/OpencodeService.ts +0 -263
  114. package/src/services/PromptService.ts +0 -183
  115. package/src/services/TemplateService.ts +0 -81
  116. package/src/types/native-exec.d.ts +0 -8
  117. package/src/utils/colors.ts +0 -178
  118. package/src/utils/git-path.ts +0 -6
  119. package/src/utils/glob.test.ts +0 -161
  120. package/src/utils/glob.ts +0 -78
  121. package/src/utils/paths.ts +0 -51
  122. package/src/utils/pr-branch.test.ts +0 -376
  123. package/src/utils/pr-branch.ts +0 -508
  124. package/src/utils/spinner.test.ts +0 -45
  125. package/src/utils/spinner.ts +0 -96
  126. package/src/utils/task-parser.test.ts +0 -172
  127. package/src/utils/task-parser.ts +0 -93
  128. package/templates/AGENCY.md +0 -22
  129. package/templates/AGENTS.md +0 -11
  130. package/templates/CLAUDE.md +0 -3
  131. package/templates/TASK.md +0 -5
  132. package/templates/opencode.json +0 -4
@@ -1,311 +0,0 @@
1
- import { test, expect, describe, beforeEach, afterEach } from "bun:test"
2
- import { join } from "path"
3
- import { task } from "./task"
4
- import {
5
- createTempDir,
6
- cleanupTempDir,
7
- initGitRepo,
8
- initAgency,
9
- fileExists,
10
- readFile,
11
- runTestEffect,
12
- getCurrentBranch,
13
- } from "../test-utils"
14
-
15
- describe("task --continue", () => {
16
- let tempDir: string
17
- let originalCwd: string
18
- let originalConfigDir: string | undefined
19
-
20
- beforeEach(async () => {
21
- tempDir = await createTempDir()
22
- originalCwd = process.cwd()
23
- originalConfigDir = process.env.AGENCY_CONFIG_DIR
24
- process.env.AGENCY_CONFIG_DIR = await createTempDir()
25
- })
26
-
27
- afterEach(async () => {
28
- process.chdir(originalCwd)
29
- if (originalConfigDir !== undefined) {
30
- process.env.AGENCY_CONFIG_DIR = originalConfigDir
31
- } else {
32
- delete process.env.AGENCY_CONFIG_DIR
33
- }
34
- if (
35
- process.env.AGENCY_CONFIG_DIR &&
36
- process.env.AGENCY_CONFIG_DIR !== originalConfigDir
37
- ) {
38
- await cleanupTempDir(process.env.AGENCY_CONFIG_DIR)
39
- }
40
- await cleanupTempDir(tempDir)
41
- })
42
-
43
- test("fails when not on a branch with agency.json", async () => {
44
- await initGitRepo(tempDir)
45
- process.chdir(tempDir)
46
-
47
- await initAgency(tempDir, "test")
48
-
49
- // Try to continue without agency files
50
- await expect(
51
- runTestEffect(
52
- task({ silent: true, continue: true, emit: "new-feature" }),
53
- ),
54
- ).rejects.toThrow("No agency.json found")
55
- })
56
-
57
- test("fails when branch name not provided in silent mode", async () => {
58
- await initGitRepo(tempDir)
59
- process.chdir(tempDir)
60
-
61
- await initAgency(tempDir, "test")
62
-
63
- // First create a task branch with agency files
64
- await runTestEffect(task({ silent: true, emit: "original-feature" }))
65
-
66
- // Try to continue without branch name
67
- await expect(
68
- runTestEffect(task({ silent: true, continue: true })),
69
- ).rejects.toThrow("Branch name is required")
70
- })
71
-
72
- test("creates new branch with agency files from current branch", async () => {
73
- await initGitRepo(tempDir)
74
- process.chdir(tempDir)
75
-
76
- await initAgency(tempDir, "test")
77
-
78
- // Create a task branch with agency files
79
- await runTestEffect(
80
- task({ silent: true, emit: "original-feature", task: "Original task" }),
81
- )
82
-
83
- // Verify we're on the source branch
84
- const originalBranch = await getCurrentBranch(tempDir)
85
- expect(originalBranch).toBe("agency--original-feature")
86
-
87
- // Read the original TASK.md
88
- const originalTaskContent = await readFile(join(tempDir, "TASK.md"))
89
- expect(originalTaskContent).toContain("Original task")
90
-
91
- // Now continue to a new branch
92
- await runTestEffect(
93
- task({
94
- silent: true,
95
- continue: true,
96
- emit: "continued-feature",
97
- }),
98
- )
99
-
100
- // Verify we're on the new branch
101
- const newBranch = await getCurrentBranch(tempDir)
102
- expect(newBranch).toBe("agency--continued-feature")
103
-
104
- // Verify agency files were copied
105
- expect(await fileExists(join(tempDir, "agency.json"))).toBe(true)
106
- expect(await fileExists(join(tempDir, "TASK.md"))).toBe(true)
107
- expect(await fileExists(join(tempDir, "AGENCY.md"))).toBe(true)
108
-
109
- // Verify TASK.md content was preserved
110
- const newTaskContent = await readFile(join(tempDir, "TASK.md"))
111
- expect(newTaskContent).toContain("Original task")
112
- })
113
-
114
- test("updates emitBranch in agency.json", async () => {
115
- await initGitRepo(tempDir)
116
- process.chdir(tempDir)
117
-
118
- await initAgency(tempDir, "test")
119
-
120
- // Create a task branch
121
- await runTestEffect(task({ silent: true, emit: "feature-v1" }))
122
-
123
- // Read original agency.json
124
- const originalAgencyJson = JSON.parse(
125
- await readFile(join(tempDir, "agency.json")),
126
- )
127
- expect(originalAgencyJson.emitBranch).toBe("feature-v1")
128
-
129
- // Continue to new branch
130
- await runTestEffect(
131
- task({ silent: true, continue: true, emit: "feature-v2" }),
132
- )
133
-
134
- // Read new agency.json
135
- const newAgencyJson = JSON.parse(
136
- await readFile(join(tempDir, "agency.json")),
137
- )
138
- expect(newAgencyJson.emitBranch).toBe("feature-v2")
139
- })
140
-
141
- test("fails when new branch already exists", async () => {
142
- await initGitRepo(tempDir)
143
- process.chdir(tempDir)
144
-
145
- await initAgency(tempDir, "test")
146
-
147
- // Create a task branch
148
- await runTestEffect(task({ silent: true, emit: "feature-v1" }))
149
-
150
- // Create another branch that would conflict
151
- await Bun.spawn(["git", "branch", "agency--feature-v2"], {
152
- cwd: tempDir,
153
- stdout: "pipe",
154
- stderr: "pipe",
155
- }).exited
156
-
157
- // Try to continue to the existing branch name
158
- await expect(
159
- runTestEffect(task({ silent: true, continue: true, emit: "feature-v2" })),
160
- ).rejects.toThrow("already exists")
161
- })
162
-
163
- test("copies injected files from metadata", async () => {
164
- await initGitRepo(tempDir)
165
- process.chdir(tempDir)
166
-
167
- await initAgency(tempDir, "test")
168
-
169
- // Create a task branch
170
- await runTestEffect(task({ silent: true, emit: "feature-v1" }))
171
-
172
- // Verify opencode.json exists (it's in injectedFiles)
173
- expect(await fileExists(join(tempDir, "opencode.json"))).toBe(true)
174
-
175
- // Continue to new branch
176
- await runTestEffect(
177
- task({ silent: true, continue: true, emit: "feature-v2" }),
178
- )
179
-
180
- // Verify opencode.json was copied
181
- expect(await fileExists(join(tempDir, "opencode.json"))).toBe(true)
182
- })
183
-
184
- test("new branch is created from main", async () => {
185
- await initGitRepo(tempDir)
186
- process.chdir(tempDir)
187
-
188
- await initAgency(tempDir, "test")
189
-
190
- // Create a task branch and add a file
191
- await runTestEffect(task({ silent: true, emit: "feature-v1" }))
192
-
193
- // Add a file that's NOT in agency files
194
- await Bun.write(join(tempDir, "feature-specific.txt"), "feature content")
195
- await Bun.spawn(["git", "add", "feature-specific.txt"], {
196
- cwd: tempDir,
197
- stdout: "pipe",
198
- stderr: "pipe",
199
- }).exited
200
- await Bun.spawn(
201
- ["git", "commit", "--no-verify", "-m", "Add feature file"],
202
- {
203
- cwd: tempDir,
204
- stdout: "pipe",
205
- stderr: "pipe",
206
- },
207
- ).exited
208
-
209
- // Continue to new branch
210
- await runTestEffect(
211
- task({ silent: true, continue: true, emit: "feature-v2" }),
212
- )
213
-
214
- // The new branch should be based on main, not on the old feature branch
215
- // So feature-specific.txt should NOT exist
216
- expect(await fileExists(join(tempDir, "feature-specific.txt"))).toBe(false)
217
-
218
- // But agency files should exist
219
- expect(await fileExists(join(tempDir, "agency.json"))).toBe(true)
220
- })
221
-
222
- test("supports --from flag to specify base branch", async () => {
223
- await initGitRepo(tempDir)
224
- process.chdir(tempDir)
225
-
226
- // Create a develop branch
227
- await Bun.spawn(["git", "checkout", "-b", "develop"], {
228
- cwd: tempDir,
229
- stdout: "pipe",
230
- stderr: "pipe",
231
- }).exited
232
-
233
- // Add a file to develop
234
- await Bun.write(join(tempDir, "develop-feature.txt"), "develop content")
235
- await Bun.spawn(["git", "add", "develop-feature.txt"], {
236
- cwd: tempDir,
237
- stdout: "pipe",
238
- stderr: "pipe",
239
- }).exited
240
- await Bun.spawn(
241
- ["git", "commit", "--no-verify", "-m", "Add develop file"],
242
- {
243
- cwd: tempDir,
244
- stdout: "pipe",
245
- stderr: "pipe",
246
- },
247
- ).exited
248
-
249
- // Go back to main
250
- await Bun.spawn(["git", "checkout", "main"], {
251
- cwd: tempDir,
252
- stdout: "pipe",
253
- stderr: "pipe",
254
- }).exited
255
-
256
- await initAgency(tempDir, "test")
257
-
258
- // Create a task branch
259
- await runTestEffect(task({ silent: true, emit: "feature-v1" }))
260
-
261
- // Continue to new branch from develop
262
- await runTestEffect(
263
- task({
264
- silent: true,
265
- continue: true,
266
- emit: "feature-v2",
267
- from: "develop",
268
- }),
269
- )
270
-
271
- // The new branch should be based on develop
272
- // So develop-feature.txt should exist
273
- expect(await fileExists(join(tempDir, "develop-feature.txt"))).toBe(true)
274
-
275
- // And agency files should also exist
276
- expect(await fileExists(join(tempDir, "agency.json"))).toBe(true)
277
- })
278
-
279
- test("preserves createdAt in agency.json but updates it", async () => {
280
- await initGitRepo(tempDir)
281
- process.chdir(tempDir)
282
-
283
- await initAgency(tempDir, "test")
284
-
285
- // Create a task branch
286
- await runTestEffect(task({ silent: true, emit: "feature-v1" }))
287
-
288
- // Read original agency.json
289
- const originalAgencyJson = JSON.parse(
290
- await readFile(join(tempDir, "agency.json")),
291
- )
292
- const originalCreatedAt = originalAgencyJson.createdAt
293
-
294
- // Wait a bit to ensure time difference
295
- await new Promise((resolve) => setTimeout(resolve, 10))
296
-
297
- // Continue to new branch
298
- await runTestEffect(
299
- task({ silent: true, continue: true, emit: "feature-v2" }),
300
- )
301
-
302
- // Read new agency.json
303
- const newAgencyJson = JSON.parse(
304
- await readFile(join(tempDir, "agency.json")),
305
- )
306
- const newCreatedAt = newAgencyJson.createdAt
307
-
308
- // createdAt should be updated (different from original)
309
- expect(newCreatedAt).not.toBe(originalCreatedAt)
310
- })
311
- })
@@ -1,235 +0,0 @@
1
- import { test, expect, describe, beforeEach, afterEach } from "bun:test"
2
- import { join } from "path"
3
- import { taskEdit, task } from "./task"
4
- import {
5
- createTempDir,
6
- cleanupTempDir,
7
- initGitRepo,
8
- initAgency,
9
- readFile,
10
- runTestEffect,
11
- createFile,
12
- runGitCommand,
13
- } from "../test-utils"
14
- import { chmod } from "fs/promises"
15
-
16
- describe("edit command", () => {
17
- let tempDir: string
18
- let originalCwd: string
19
- let originalConfigDir: string | undefined
20
- let originalEditor: string | undefined
21
-
22
- beforeEach(async () => {
23
- tempDir = await createTempDir()
24
- originalCwd = process.cwd()
25
- originalConfigDir = process.env.AGENCY_CONFIG_DIR
26
- originalEditor = process.env.EDITOR
27
- // Use a temp config dir to avoid interference from user's actual config
28
- process.env.AGENCY_CONFIG_DIR = await createTempDir()
29
- })
30
-
31
- afterEach(async () => {
32
- process.chdir(originalCwd)
33
- if (originalConfigDir !== undefined) {
34
- process.env.AGENCY_CONFIG_DIR = originalConfigDir
35
- } else {
36
- delete process.env.AGENCY_CONFIG_DIR
37
- }
38
- if (originalEditor !== undefined) {
39
- process.env.EDITOR = originalEditor
40
- } else {
41
- delete process.env.EDITOR
42
- }
43
- if (
44
- process.env.AGENCY_CONFIG_DIR &&
45
- process.env.AGENCY_CONFIG_DIR !== originalConfigDir
46
- ) {
47
- await cleanupTempDir(process.env.AGENCY_CONFIG_DIR)
48
- }
49
- await cleanupTempDir(tempDir)
50
- })
51
-
52
- test("throws error when not in git repo", async () => {
53
- process.chdir(tempDir)
54
-
55
- await expect(runTestEffect(taskEdit({ silent: true }))).rejects.toThrow(
56
- "Not in a git repository",
57
- )
58
- })
59
-
60
- test("throws error when TASK.md does not exist", async () => {
61
- await initGitRepo(tempDir)
62
- process.chdir(tempDir)
63
-
64
- await expect(runTestEffect(taskEdit({ silent: true }))).rejects.toThrow(
65
- "TASK.md not found in repository root",
66
- )
67
- })
68
-
69
- test("opens TASK.md in editor when it exists", async () => {
70
- await initGitRepo(tempDir)
71
- process.chdir(tempDir)
72
-
73
- // Initialize to create TASK.md
74
- await initAgency(tempDir, "test-task")
75
-
76
- await runTestEffect(task({ silent: true, emit: "test-feature" }))
77
-
78
- // Use a mock editor that just exits successfully
79
- process.env.EDITOR = "true" // 'true' is a command that always exits with code 0
80
-
81
- // Should not throw
82
- await expect(
83
- runTestEffect(taskEdit({ silent: true })),
84
- ).resolves.toBeUndefined()
85
- })
86
-
87
- test("uses EDITOR environment variable", async () => {
88
- await initGitRepo(tempDir)
89
- process.chdir(tempDir)
90
-
91
- // Initialize to create TASK.md
92
- await initAgency(tempDir, "test-task")
93
-
94
- await runTestEffect(task({ silent: true, emit: "test-feature" }))
95
-
96
- // Use 'true' command which exits successfully without doing anything
97
- process.env.EDITOR = "true"
98
-
99
- // Should complete without error
100
- await expect(
101
- runTestEffect(taskEdit({ silent: true })),
102
- ).resolves.toBeUndefined()
103
- })
104
-
105
- test("uses VISUAL environment variable over EDITOR", async () => {
106
- await initGitRepo(tempDir)
107
- process.chdir(tempDir)
108
-
109
- // Initialize to create TASK.md
110
- await initAgency(tempDir, "test-task")
111
-
112
- await runTestEffect(task({ silent: true, emit: "test-feature" }))
113
-
114
- // Set VISUAL to 'true' and EDITOR to 'false'
115
- // If VISUAL is used (correct), it should succeed
116
- // If EDITOR is used (incorrect), it should fail
117
- process.env.VISUAL = "true"
118
- process.env.EDITOR = "false"
119
-
120
- // Should complete without error, proving VISUAL was used
121
- await expect(
122
- runTestEffect(taskEdit({ silent: true })),
123
- ).resolves.toBeUndefined()
124
- })
125
-
126
- test("throws error when editor exits with non-zero code", async () => {
127
- await initGitRepo(tempDir)
128
- process.chdir(tempDir)
129
-
130
- // Initialize to create TASK.md
131
- await initAgency(tempDir, "test-task")
132
-
133
- await runTestEffect(task({ silent: true, emit: "test-feature" }))
134
-
135
- // Clear VISUAL to ensure EDITOR is used
136
- delete process.env.VISUAL
137
- // Use a mock editor that fails
138
- process.env.EDITOR = "false" // 'false' is a command that always exits with code 1
139
-
140
- await expect(runTestEffect(taskEdit({ silent: true }))).rejects.toThrow(
141
- "Editor exited with code",
142
- )
143
- })
144
-
145
- test("commits TASK.md with 'chore: agency edit' when file is modified", async () => {
146
- await initGitRepo(tempDir)
147
- process.chdir(tempDir)
148
-
149
- // Initialize to create TASK.md
150
- await initAgency(tempDir, "test-task")
151
- await runTestEffect(task({ silent: true, emit: "test-feature" }))
152
-
153
- // Get initial commit count
154
- const result = Bun.spawnSync({
155
- cmd: ["git", "rev-list", "--count", "HEAD"],
156
- cwd: tempDir,
157
- stdout: "pipe",
158
- })
159
- const initialCommits = new TextDecoder().decode(result.stdout).trim()
160
-
161
- // Use a script that modifies TASK.md
162
- const scriptPath = join(tempDir, "edit-script.sh")
163
- await createFile(
164
- tempDir,
165
- "edit-script.sh",
166
- '#!/bin/bash\necho "Updated task" >> "$1"\n',
167
- )
168
- await chmod(scriptPath, 0o755)
169
- process.env.EDITOR = scriptPath
170
-
171
- // Run edit command
172
- await runTestEffect(taskEdit({ silent: true }))
173
-
174
- // Check that a new commit was created
175
- const finalResult = Bun.spawnSync({
176
- cmd: ["git", "rev-list", "--count", "HEAD"],
177
- cwd: tempDir,
178
- stdout: "pipe",
179
- })
180
- const finalCommits = new TextDecoder().decode(finalResult.stdout).trim()
181
- expect(Number.parseInt(finalCommits)).toBe(
182
- Number.parseInt(initialCommits) + 1,
183
- )
184
-
185
- // Check the commit message
186
- const msgResult = Bun.spawnSync({
187
- cmd: ["git", "log", "-1", "--format=%s"],
188
- cwd: tempDir,
189
- stdout: "pipe",
190
- })
191
- const commitMessage = new TextDecoder().decode(msgResult.stdout).trim()
192
- expect(commitMessage).toBe("chore: agency edit")
193
-
194
- // Check that only TASK.md was committed
195
- const filesResult = Bun.spawnSync({
196
- cmd: ["git", "diff-tree", "--no-commit-id", "--name-only", "-r", "HEAD"],
197
- cwd: tempDir,
198
- stdout: "pipe",
199
- })
200
- const filesInCommit = new TextDecoder().decode(filesResult.stdout).trim()
201
- expect(filesInCommit).toBe("TASK.md")
202
- })
203
-
204
- test("does not commit when TASK.md is not modified", async () => {
205
- await initGitRepo(tempDir)
206
- process.chdir(tempDir)
207
-
208
- // Initialize to create TASK.md
209
- await initAgency(tempDir, "test-task")
210
- await runTestEffect(task({ silent: true, emit: "test-feature" }))
211
-
212
- // Get initial commit count
213
- const result = Bun.spawnSync({
214
- cmd: ["git", "rev-list", "--count", "HEAD"],
215
- cwd: tempDir,
216
- stdout: "pipe",
217
- })
218
- const initialCommits = new TextDecoder().decode(result.stdout).trim()
219
-
220
- // Use a mock editor that doesn't modify the file
221
- process.env.EDITOR = "true"
222
-
223
- // Run edit command
224
- await runTestEffect(taskEdit({ silent: true }))
225
-
226
- // Check that no new commit was created
227
- const finalResult = Bun.spawnSync({
228
- cmd: ["git", "rev-list", "--count", "HEAD"],
229
- cwd: tempDir,
230
- stdout: "pipe",
231
- })
232
- const finalCommits = new TextDecoder().decode(finalResult.stdout).trim()
233
- expect(Number.parseInt(finalCommits)).toBe(Number.parseInt(initialCommits))
234
- })
235
- })