@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,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
- })