@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.
- package/README.md +302 -113
- package/cli.ts +123 -415
- package/index.ts +1 -1
- package/package.json +12 -17
- package/skills/agency/SKILL.md +258 -0
- package/src/cli.test.ts +290 -0
- package/src/commands/description.test.ts +103 -0
- package/src/commands/epic.test.ts +103 -0
- package/src/commands/epic.ts +101 -0
- package/src/commands/init.test.ts +42 -290
- package/src/commands/init.ts +15 -123
- package/src/commands/phase.ts +120 -0
- package/src/commands/pr.test.ts +19 -170
- package/src/commands/pr.ts +23 -179
- package/src/commands/repo.test.ts +80 -0
- package/src/commands/repo.ts +83 -0
- package/src/commands/status.test.ts +35 -477
- package/src/commands/status.ts +24 -213
- package/src/commands/task-phase.test.ts +206 -0
- package/src/commands/task.ts +83 -1285
- package/src/commands/validate.test.ts +91 -0
- package/src/commands/validate.ts +50 -0
- package/src/commands/work.test.ts +172 -419
- package/src/commands/work.ts +47 -166
- package/src/services/EpicService.test.ts +71 -0
- package/src/services/EpicService.ts +135 -0
- package/src/services/FileSystemService.ts +61 -92
- package/src/services/PhaseService.ts +323 -0
- package/src/services/PullRequestService.test.ts +386 -0
- package/src/services/PullRequestService.ts +143 -0
- package/src/services/RepositoryService.test.ts +106 -0
- package/src/services/RepositoryService.ts +159 -0
- package/src/services/TaskPhaseService.test.ts +217 -0
- package/src/services/TaskService.ts +192 -0
- package/src/services/WorkbaseService.test.ts +446 -0
- package/src/services/WorkbaseService.ts +493 -0
- package/src/services/WorktreeService.test.ts +599 -0
- package/src/services/WorktreeService.ts +357 -0
- package/src/test-utils.ts +41 -438
- package/src/types.ts +1 -215
- package/src/utils/command.ts +1 -5
- package/src/utils/effect.test.ts +24 -0
- package/src/utils/effect.ts +3 -273
- package/src/utils/process.test.ts +0 -15
- package/src/utils/process.ts +1 -60
- package/src/workbase/frontmatter.test.ts +50 -0
- package/src/workbase/frontmatter.ts +77 -0
- package/src/workbase/repository-reference.test.ts +25 -0
- package/src/workbase/repository-reference.ts +18 -0
- package/src/workbase/schemas.test.ts +146 -0
- package/src/workbase/schemas.ts +78 -0
- package/src/workbase/worktree-command.test.ts +61 -0
- package/src/workbase/worktree-command.ts +52 -0
- package/src/commands/base.test.ts +0 -198
- package/src/commands/base.ts +0 -198
- package/src/commands/clean.test.ts +0 -299
- package/src/commands/clean.ts +0 -274
- package/src/commands/completions.test.ts +0 -51
- package/src/commands/completions.ts +0 -308
- package/src/commands/emit.integration.test.ts +0 -549
- package/src/commands/emit.test.ts +0 -576
- package/src/commands/emit.ts +0 -700
- package/src/commands/emitted.test.ts +0 -226
- package/src/commands/emitted.ts +0 -57
- package/src/commands/loop.test.ts +0 -621
- package/src/commands/loop.ts +0 -406
- package/src/commands/merge.integration.test.ts +0 -195
- package/src/commands/merge.test.ts +0 -246
- package/src/commands/merge.ts +0 -244
- package/src/commands/pull.test.ts +0 -395
- package/src/commands/pull.ts +0 -205
- package/src/commands/push.test.ts +0 -518
- package/src/commands/push.ts +0 -434
- package/src/commands/rebase.test.ts +0 -521
- package/src/commands/rebase.ts +0 -232
- package/src/commands/save.test.ts +0 -247
- package/src/commands/save.ts +0 -162
- package/src/commands/source.test.ts +0 -195
- package/src/commands/source.ts +0 -72
- package/src/commands/switch.test.ts +0 -194
- package/src/commands/switch.ts +0 -84
- package/src/commands/task-branching.test.ts +0 -633
- package/src/commands/task-continue.test.ts +0 -311
- package/src/commands/task-edit.test.ts +0 -235
- package/src/commands/task-main.test.ts +0 -1066
- package/src/commands/task-squash.test.ts +0 -266
- package/src/commands/tasks.test.ts +0 -391
- package/src/commands/tasks.ts +0 -161
- package/src/commands/template-delete.test.ts +0 -178
- package/src/commands/template-delete.ts +0 -98
- package/src/commands/template-list.test.ts +0 -135
- package/src/commands/template-list.ts +0 -87
- package/src/commands/template-view.test.ts +0 -158
- package/src/commands/template-view.ts +0 -86
- package/src/commands/template.test.ts +0 -32
- package/src/commands/template.ts +0 -96
- package/src/commands/use.test.ts +0 -87
- package/src/commands/use.ts +0 -97
- package/src/constants.ts +0 -10
- package/src/errors.ts +0 -17
- package/src/schemas.ts +0 -33
- package/src/services/AgencyMetadataService.ts +0 -311
- package/src/services/ClaudeService.test.ts +0 -184
- package/src/services/ClaudeService.ts +0 -91
- package/src/services/ConfigService.ts +0 -115
- package/src/services/FilterRepoService.ts +0 -164
- package/src/services/FormatterService.test.ts +0 -432
- package/src/services/FormatterService.ts +0 -219
- package/src/services/GitService.ts +0 -1183
- package/src/services/MockFilterRepoService.ts +0 -140
- package/src/services/OpencodeService.ts +0 -263
- package/src/services/PromptService.ts +0 -183
- package/src/services/TemplateService.ts +0 -81
- package/src/types/native-exec.d.ts +0 -8
- package/src/utils/colors.ts +0 -178
- package/src/utils/git-path.ts +0 -6
- package/src/utils/glob.test.ts +0 -161
- package/src/utils/glob.ts +0 -78
- package/src/utils/paths.ts +0 -51
- package/src/utils/pr-branch.test.ts +0 -376
- package/src/utils/pr-branch.ts +0 -508
- package/src/utils/spinner.test.ts +0 -45
- package/src/utils/spinner.ts +0 -96
- package/src/utils/task-parser.test.ts +0 -172
- package/src/utils/task-parser.ts +0 -93
- package/templates/AGENCY.md +0 -22
- package/templates/AGENTS.md +0 -11
- package/templates/CLAUDE.md +0 -3
- package/templates/TASK.md +0 -5
- 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
|
-
})
|