@markjaquith/agency 0.5.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/LICENSE +21 -0
- package/README.md +109 -0
- package/cli.ts +569 -0
- package/index.ts +1 -0
- package/package.json +65 -0
- package/src/commands/base.test.ts +198 -0
- package/src/commands/base.ts +198 -0
- package/src/commands/clean.test.ts +299 -0
- package/src/commands/clean.ts +320 -0
- package/src/commands/emit.test.ts +412 -0
- package/src/commands/emit.ts +521 -0
- package/src/commands/emitted.test.ts +226 -0
- package/src/commands/emitted.ts +57 -0
- package/src/commands/init.test.ts +311 -0
- package/src/commands/init.ts +140 -0
- package/src/commands/merge.test.ts +365 -0
- package/src/commands/merge.ts +253 -0
- package/src/commands/pull.test.ts +385 -0
- package/src/commands/pull.ts +205 -0
- package/src/commands/push.test.ts +394 -0
- package/src/commands/push.ts +346 -0
- package/src/commands/save.test.ts +247 -0
- package/src/commands/save.ts +162 -0
- package/src/commands/source.test.ts +195 -0
- package/src/commands/source.ts +72 -0
- package/src/commands/status.test.ts +489 -0
- package/src/commands/status.ts +258 -0
- package/src/commands/switch.test.ts +194 -0
- package/src/commands/switch.ts +84 -0
- package/src/commands/task-branching.test.ts +334 -0
- package/src/commands/task-edit.test.ts +141 -0
- package/src/commands/task-main.test.ts +872 -0
- package/src/commands/task.ts +712 -0
- package/src/commands/tasks.test.ts +335 -0
- package/src/commands/tasks.ts +155 -0
- package/src/commands/template-delete.test.ts +178 -0
- package/src/commands/template-delete.ts +98 -0
- package/src/commands/template-list.test.ts +135 -0
- package/src/commands/template-list.ts +87 -0
- package/src/commands/template-view.test.ts +158 -0
- package/src/commands/template-view.ts +86 -0
- package/src/commands/template.test.ts +32 -0
- package/src/commands/template.ts +96 -0
- package/src/commands/use.test.ts +87 -0
- package/src/commands/use.ts +97 -0
- package/src/commands/work.test.ts +462 -0
- package/src/commands/work.ts +193 -0
- package/src/errors.ts +17 -0
- package/src/schemas.ts +33 -0
- package/src/services/AgencyMetadataService.ts +287 -0
- package/src/services/ClaudeService.test.ts +184 -0
- package/src/services/ClaudeService.ts +91 -0
- package/src/services/ConfigService.ts +115 -0
- package/src/services/FileSystemService.ts +222 -0
- package/src/services/GitService.ts +751 -0
- package/src/services/OpencodeService.ts +263 -0
- package/src/services/PromptService.ts +183 -0
- package/src/services/TemplateService.ts +75 -0
- package/src/test-utils.ts +362 -0
- package/src/types/native-exec.d.ts +8 -0
- package/src/types.ts +216 -0
- package/src/utils/colors.ts +178 -0
- package/src/utils/command.ts +17 -0
- package/src/utils/effect.ts +281 -0
- package/src/utils/exec.ts +48 -0
- package/src/utils/paths.ts +51 -0
- package/src/utils/pr-branch.test.ts +372 -0
- package/src/utils/pr-branch.ts +473 -0
- package/src/utils/process.ts +110 -0
- package/src/utils/spinner.ts +82 -0
- package/templates/AGENCY.md +20 -0
- package/templates/AGENTS.md +11 -0
- package/templates/CLAUDE.md +3 -0
- package/templates/TASK.md +5 -0
- package/templates/opencode.json +4 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { test, expect, describe, beforeEach, afterEach } from "bun:test"
|
|
2
|
+
import { join } from "path"
|
|
3
|
+
import { task } from "../commands/task"
|
|
4
|
+
import { emit } from "../commands/emit"
|
|
5
|
+
import {
|
|
6
|
+
createTempDir,
|
|
7
|
+
cleanupTempDir,
|
|
8
|
+
initGitRepo,
|
|
9
|
+
initAgency,
|
|
10
|
+
runTestEffect,
|
|
11
|
+
createFile,
|
|
12
|
+
runGitCommand,
|
|
13
|
+
getCurrentBranch,
|
|
14
|
+
} from "../test-utils"
|
|
15
|
+
|
|
16
|
+
describe("task command - branching functionality", () => {
|
|
17
|
+
let tempDir: string
|
|
18
|
+
let originalCwd: string
|
|
19
|
+
let originalConfigDir: string | undefined
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
tempDir = await createTempDir()
|
|
23
|
+
originalCwd = process.cwd()
|
|
24
|
+
originalConfigDir = process.env.AGENCY_CONFIG_DIR
|
|
25
|
+
process.env.AGENCY_CONFIG_DIR = await createTempDir()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
process.chdir(originalCwd)
|
|
30
|
+
if (originalConfigDir !== undefined) {
|
|
31
|
+
process.env.AGENCY_CONFIG_DIR = originalConfigDir
|
|
32
|
+
} else {
|
|
33
|
+
delete process.env.AGENCY_CONFIG_DIR
|
|
34
|
+
}
|
|
35
|
+
if (
|
|
36
|
+
process.env.AGENCY_CONFIG_DIR &&
|
|
37
|
+
process.env.AGENCY_CONFIG_DIR !== originalConfigDir
|
|
38
|
+
) {
|
|
39
|
+
await cleanupTempDir(process.env.AGENCY_CONFIG_DIR)
|
|
40
|
+
}
|
|
41
|
+
await cleanupTempDir(tempDir)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
describe("--from flag", () => {
|
|
45
|
+
test("branches from specified non-agency branch", async () => {
|
|
46
|
+
await initGitRepo(tempDir)
|
|
47
|
+
process.chdir(tempDir)
|
|
48
|
+
await initAgency(tempDir, "test")
|
|
49
|
+
|
|
50
|
+
// Create a feature branch
|
|
51
|
+
await runGitCommand(tempDir, ["git", "checkout", "-b", "feature-base"])
|
|
52
|
+
await createFile(tempDir, "feature.txt", "content")
|
|
53
|
+
await runGitCommand(tempDir, ["git", "add", "."])
|
|
54
|
+
await runGitCommand(tempDir, ["git", "commit", "-m", "Add feature"])
|
|
55
|
+
|
|
56
|
+
// Go back to main
|
|
57
|
+
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
58
|
+
|
|
59
|
+
// Create task branch from feature-base
|
|
60
|
+
await runTestEffect(
|
|
61
|
+
task({
|
|
62
|
+
silent: true,
|
|
63
|
+
branch: "my-task",
|
|
64
|
+
from: "feature-base",
|
|
65
|
+
}),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
const currentBranch = await getCurrentBranch(tempDir)
|
|
69
|
+
expect(currentBranch).toBe("agency/my-task")
|
|
70
|
+
|
|
71
|
+
// Verify feature.txt exists (came from feature-base)
|
|
72
|
+
const featureFile = await Bun.file(join(tempDir, "feature.txt")).text()
|
|
73
|
+
expect(featureFile).toBe("content")
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test("throws error if specified branch does not exist", async () => {
|
|
77
|
+
await initGitRepo(tempDir)
|
|
78
|
+
process.chdir(tempDir)
|
|
79
|
+
await initAgency(tempDir, "test")
|
|
80
|
+
|
|
81
|
+
await expect(
|
|
82
|
+
runTestEffect(
|
|
83
|
+
task({
|
|
84
|
+
silent: true,
|
|
85
|
+
branch: "my-task",
|
|
86
|
+
from: "nonexistent-branch",
|
|
87
|
+
}),
|
|
88
|
+
),
|
|
89
|
+
).rejects.toThrow("does not exist")
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test("detects agency source branch and uses its emit branch", async () => {
|
|
93
|
+
await initGitRepo(tempDir)
|
|
94
|
+
process.chdir(tempDir)
|
|
95
|
+
await initAgency(tempDir, "test")
|
|
96
|
+
|
|
97
|
+
// Create first agency branch
|
|
98
|
+
await runTestEffect(
|
|
99
|
+
task({
|
|
100
|
+
silent: true,
|
|
101
|
+
branch: "first-task",
|
|
102
|
+
}),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
// Add a unique file to identify this branch
|
|
106
|
+
await createFile(tempDir, "first-task.txt", "first")
|
|
107
|
+
await runGitCommand(tempDir, ["git", "add", "."])
|
|
108
|
+
await runGitCommand(tempDir, [
|
|
109
|
+
"git",
|
|
110
|
+
"commit",
|
|
111
|
+
"-m",
|
|
112
|
+
"Add first task file",
|
|
113
|
+
])
|
|
114
|
+
|
|
115
|
+
// Emit the branch
|
|
116
|
+
await runTestEffect(emit({ silent: true }))
|
|
117
|
+
|
|
118
|
+
// Go back to main
|
|
119
|
+
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
120
|
+
|
|
121
|
+
// Create second task from first agency branch
|
|
122
|
+
await runTestEffect(
|
|
123
|
+
task({
|
|
124
|
+
silent: true,
|
|
125
|
+
branch: "second-task",
|
|
126
|
+
from: "agency/first-task",
|
|
127
|
+
}),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
const currentBranch = await getCurrentBranch(tempDir)
|
|
131
|
+
expect(currentBranch).toBe("agency/second-task")
|
|
132
|
+
|
|
133
|
+
// Verify first-task.txt exists (came from emit branch)
|
|
134
|
+
const firstTaskFile = await Bun.file(
|
|
135
|
+
join(tempDir, "first-task.txt"),
|
|
136
|
+
).text()
|
|
137
|
+
expect(firstTaskFile).toBe("first")
|
|
138
|
+
|
|
139
|
+
// Verify TASK.md does NOT exist from first-task
|
|
140
|
+
// (because we branched from emit branch, not source branch)
|
|
141
|
+
const taskMdExists = await Bun.file(join(tempDir, "TASK.md")).exists()
|
|
142
|
+
// The new TASK.md should exist (created by second task)
|
|
143
|
+
// but it should be fresh, not from first-task
|
|
144
|
+
expect(taskMdExists).toBe(true)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test("throws error if agency source branch has no emit branch", async () => {
|
|
148
|
+
await initGitRepo(tempDir)
|
|
149
|
+
process.chdir(tempDir)
|
|
150
|
+
await initAgency(tempDir, "test")
|
|
151
|
+
|
|
152
|
+
// Create agency branch without emitting
|
|
153
|
+
await runTestEffect(
|
|
154
|
+
task({
|
|
155
|
+
silent: true,
|
|
156
|
+
branch: "unemitted-task",
|
|
157
|
+
}),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
// Go back to main
|
|
161
|
+
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
162
|
+
|
|
163
|
+
// Try to create task from unemitted agency branch
|
|
164
|
+
await expect(
|
|
165
|
+
runTestEffect(
|
|
166
|
+
task({
|
|
167
|
+
silent: true,
|
|
168
|
+
branch: "second-task",
|
|
169
|
+
from: "agency/unemitted-task",
|
|
170
|
+
}),
|
|
171
|
+
),
|
|
172
|
+
).rejects.toThrow("emit branch")
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe("--from-current flag", () => {
|
|
177
|
+
test("branches from current branch when it's not an agency branch", async () => {
|
|
178
|
+
await initGitRepo(tempDir)
|
|
179
|
+
process.chdir(tempDir)
|
|
180
|
+
await initAgency(tempDir, "test")
|
|
181
|
+
|
|
182
|
+
// Create and switch to a feature branch
|
|
183
|
+
await runGitCommand(tempDir, ["git", "checkout", "-b", "feature-current"])
|
|
184
|
+
await createFile(tempDir, "current.txt", "content")
|
|
185
|
+
await runGitCommand(tempDir, ["git", "add", "."])
|
|
186
|
+
await runGitCommand(tempDir, ["git", "commit", "-m", "Add current file"])
|
|
187
|
+
|
|
188
|
+
// Go back to main to create the task
|
|
189
|
+
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
190
|
+
|
|
191
|
+
// Create task from feature-current branch
|
|
192
|
+
await runTestEffect(
|
|
193
|
+
task({
|
|
194
|
+
silent: true,
|
|
195
|
+
branch: "my-task",
|
|
196
|
+
from: "feature-current",
|
|
197
|
+
}),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
const currentBranch = await getCurrentBranch(tempDir)
|
|
201
|
+
expect(currentBranch).toBe("agency/my-task")
|
|
202
|
+
|
|
203
|
+
// Verify current.txt exists
|
|
204
|
+
const currentFile = await Bun.file(join(tempDir, "current.txt")).text()
|
|
205
|
+
expect(currentFile).toBe("content")
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
test("uses emit branch when --from specifies an agency branch", async () => {
|
|
209
|
+
await initGitRepo(tempDir)
|
|
210
|
+
process.chdir(tempDir)
|
|
211
|
+
await initAgency(tempDir, "test")
|
|
212
|
+
|
|
213
|
+
// Create first agency branch
|
|
214
|
+
await runTestEffect(
|
|
215
|
+
task({
|
|
216
|
+
silent: true,
|
|
217
|
+
branch: "first-task",
|
|
218
|
+
}),
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
await createFile(tempDir, "first.txt", "first")
|
|
222
|
+
await runGitCommand(tempDir, ["git", "add", "."])
|
|
223
|
+
await runGitCommand(tempDir, ["git", "commit", "-m", "Add first file"])
|
|
224
|
+
|
|
225
|
+
// Emit the branch
|
|
226
|
+
await runTestEffect(emit({ silent: true }))
|
|
227
|
+
|
|
228
|
+
// Go back to main
|
|
229
|
+
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
230
|
+
|
|
231
|
+
// Create task from first agency branch
|
|
232
|
+
await runTestEffect(
|
|
233
|
+
task({
|
|
234
|
+
silent: true,
|
|
235
|
+
branch: "second-task",
|
|
236
|
+
from: "agency/first-task",
|
|
237
|
+
}),
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
const currentBranch = await getCurrentBranch(tempDir)
|
|
241
|
+
expect(currentBranch).toBe("agency/second-task")
|
|
242
|
+
|
|
243
|
+
// Verify first.txt exists
|
|
244
|
+
const firstFile = await Bun.file(join(tempDir, "first.txt")).text()
|
|
245
|
+
expect(firstFile).toBe("first")
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test("throws error if --from agency branch has no emit branch", async () => {
|
|
249
|
+
await initGitRepo(tempDir)
|
|
250
|
+
process.chdir(tempDir)
|
|
251
|
+
await initAgency(tempDir, "test")
|
|
252
|
+
|
|
253
|
+
// Create agency branch without emitting
|
|
254
|
+
await runTestEffect(
|
|
255
|
+
task({
|
|
256
|
+
silent: true,
|
|
257
|
+
branch: "unemitted-task",
|
|
258
|
+
}),
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
// Go back to main
|
|
262
|
+
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
263
|
+
|
|
264
|
+
// Try to create task from unemitted agency branch
|
|
265
|
+
await expect(
|
|
266
|
+
runTestEffect(
|
|
267
|
+
task({
|
|
268
|
+
silent: true,
|
|
269
|
+
branch: "second-task",
|
|
270
|
+
from: "agency/unemitted-task",
|
|
271
|
+
}),
|
|
272
|
+
),
|
|
273
|
+
).rejects.toThrow("emit branch")
|
|
274
|
+
})
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
describe("--from and --from-current validation", () => {
|
|
278
|
+
test("throws error if both flags are used", async () => {
|
|
279
|
+
await initGitRepo(tempDir)
|
|
280
|
+
process.chdir(tempDir)
|
|
281
|
+
await initAgency(tempDir, "test")
|
|
282
|
+
|
|
283
|
+
await expect(
|
|
284
|
+
runTestEffect(
|
|
285
|
+
task({
|
|
286
|
+
silent: true,
|
|
287
|
+
branch: "my-task",
|
|
288
|
+
from: "main",
|
|
289
|
+
fromCurrent: true,
|
|
290
|
+
}),
|
|
291
|
+
),
|
|
292
|
+
).rejects.toThrow("Cannot use both --from and --from-current")
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
describe("default branching behavior", () => {
|
|
297
|
+
test("branches from auto-detected main branch by default", async () => {
|
|
298
|
+
await initGitRepo(tempDir)
|
|
299
|
+
process.chdir(tempDir)
|
|
300
|
+
await initAgency(tempDir, "test")
|
|
301
|
+
|
|
302
|
+
// Create a commit on main
|
|
303
|
+
await createFile(tempDir, "main.txt", "main content")
|
|
304
|
+
await runGitCommand(tempDir, ["git", "add", "."])
|
|
305
|
+
await runGitCommand(tempDir, ["git", "commit", "-m", "Main commit"])
|
|
306
|
+
|
|
307
|
+
// Create a different branch and switch to it
|
|
308
|
+
await runGitCommand(tempDir, ["git", "checkout", "-b", "other-branch"])
|
|
309
|
+
await createFile(tempDir, "other.txt", "other content")
|
|
310
|
+
await runGitCommand(tempDir, ["git", "add", "."])
|
|
311
|
+
await runGitCommand(tempDir, ["git", "commit", "-m", "Other commit"])
|
|
312
|
+
|
|
313
|
+
// Go back to main to set it as main branch
|
|
314
|
+
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
315
|
+
|
|
316
|
+
// Create task (should branch from main)
|
|
317
|
+
await runTestEffect(
|
|
318
|
+
task({
|
|
319
|
+
silent: true,
|
|
320
|
+
branch: "my-task",
|
|
321
|
+
}),
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
const currentBranch = await getCurrentBranch(tempDir)
|
|
325
|
+
expect(currentBranch).toBe("agency/my-task")
|
|
326
|
+
|
|
327
|
+
// Verify main.txt exists but other.txt does not
|
|
328
|
+
const mainExists = await Bun.file(join(tempDir, "main.txt")).exists()
|
|
329
|
+
const otherExists = await Bun.file(join(tempDir, "other.txt")).exists()
|
|
330
|
+
expect(mainExists).toBe(true)
|
|
331
|
+
expect(otherExists).toBe(false)
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
})
|
|
@@ -0,0 +1,141 @@
|
|
|
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
|
+
} from "../test-utils"
|
|
12
|
+
|
|
13
|
+
describe("edit command", () => {
|
|
14
|
+
let tempDir: string
|
|
15
|
+
let originalCwd: string
|
|
16
|
+
let originalConfigDir: string | undefined
|
|
17
|
+
let originalEditor: string | undefined
|
|
18
|
+
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
tempDir = await createTempDir()
|
|
21
|
+
originalCwd = process.cwd()
|
|
22
|
+
originalConfigDir = process.env.AGENCY_CONFIG_DIR
|
|
23
|
+
originalEditor = process.env.EDITOR
|
|
24
|
+
// Use a temp config dir to avoid interference from user's actual config
|
|
25
|
+
process.env.AGENCY_CONFIG_DIR = await createTempDir()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
process.chdir(originalCwd)
|
|
30
|
+
if (originalConfigDir !== undefined) {
|
|
31
|
+
process.env.AGENCY_CONFIG_DIR = originalConfigDir
|
|
32
|
+
} else {
|
|
33
|
+
delete process.env.AGENCY_CONFIG_DIR
|
|
34
|
+
}
|
|
35
|
+
if (originalEditor !== undefined) {
|
|
36
|
+
process.env.EDITOR = originalEditor
|
|
37
|
+
} else {
|
|
38
|
+
delete process.env.EDITOR
|
|
39
|
+
}
|
|
40
|
+
if (
|
|
41
|
+
process.env.AGENCY_CONFIG_DIR &&
|
|
42
|
+
process.env.AGENCY_CONFIG_DIR !== originalConfigDir
|
|
43
|
+
) {
|
|
44
|
+
await cleanupTempDir(process.env.AGENCY_CONFIG_DIR)
|
|
45
|
+
}
|
|
46
|
+
await cleanupTempDir(tempDir)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test("throws error when not in git repo", async () => {
|
|
50
|
+
process.chdir(tempDir)
|
|
51
|
+
|
|
52
|
+
await expect(runTestEffect(taskEdit({ silent: true }))).rejects.toThrow(
|
|
53
|
+
"Not in a git repository",
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test("throws error when TASK.md does not exist", async () => {
|
|
58
|
+
await initGitRepo(tempDir)
|
|
59
|
+
process.chdir(tempDir)
|
|
60
|
+
|
|
61
|
+
await expect(runTestEffect(taskEdit({ silent: true }))).rejects.toThrow(
|
|
62
|
+
"TASK.md not found in repository root",
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test("opens TASK.md in editor when it exists", async () => {
|
|
67
|
+
await initGitRepo(tempDir)
|
|
68
|
+
process.chdir(tempDir)
|
|
69
|
+
|
|
70
|
+
// Initialize to create TASK.md
|
|
71
|
+
await initAgency(tempDir, "test-task")
|
|
72
|
+
|
|
73
|
+
await runTestEffect(task({ silent: true, branch: "test-feature" }))
|
|
74
|
+
|
|
75
|
+
// Use a mock editor that just exits successfully
|
|
76
|
+
process.env.EDITOR = "true" // 'true' is a command that always exits with code 0
|
|
77
|
+
|
|
78
|
+
// Should not throw
|
|
79
|
+
await expect(
|
|
80
|
+
runTestEffect(taskEdit({ silent: true })),
|
|
81
|
+
).resolves.toBeUndefined()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test("uses EDITOR environment variable", async () => {
|
|
85
|
+
await initGitRepo(tempDir)
|
|
86
|
+
process.chdir(tempDir)
|
|
87
|
+
|
|
88
|
+
// Initialize to create TASK.md
|
|
89
|
+
await initAgency(tempDir, "test-task")
|
|
90
|
+
|
|
91
|
+
await runTestEffect(task({ silent: true, branch: "test-feature" }))
|
|
92
|
+
|
|
93
|
+
// Use 'true' command which exits successfully without doing anything
|
|
94
|
+
process.env.EDITOR = "true"
|
|
95
|
+
|
|
96
|
+
// Should complete without error
|
|
97
|
+
await expect(
|
|
98
|
+
runTestEffect(taskEdit({ silent: true })),
|
|
99
|
+
).resolves.toBeUndefined()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test("uses VISUAL environment variable over EDITOR", async () => {
|
|
103
|
+
await initGitRepo(tempDir)
|
|
104
|
+
process.chdir(tempDir)
|
|
105
|
+
|
|
106
|
+
// Initialize to create TASK.md
|
|
107
|
+
await initAgency(tempDir, "test-task")
|
|
108
|
+
|
|
109
|
+
await runTestEffect(task({ silent: true, branch: "test-feature" }))
|
|
110
|
+
|
|
111
|
+
// Set VISUAL to 'true' and EDITOR to 'false'
|
|
112
|
+
// If VISUAL is used (correct), it should succeed
|
|
113
|
+
// If EDITOR is used (incorrect), it should fail
|
|
114
|
+
process.env.VISUAL = "true"
|
|
115
|
+
process.env.EDITOR = "false"
|
|
116
|
+
|
|
117
|
+
// Should complete without error, proving VISUAL was used
|
|
118
|
+
await expect(
|
|
119
|
+
runTestEffect(taskEdit({ silent: true })),
|
|
120
|
+
).resolves.toBeUndefined()
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test("throws error when editor exits with non-zero code", async () => {
|
|
124
|
+
await initGitRepo(tempDir)
|
|
125
|
+
process.chdir(tempDir)
|
|
126
|
+
|
|
127
|
+
// Initialize to create TASK.md
|
|
128
|
+
await initAgency(tempDir, "test-task")
|
|
129
|
+
|
|
130
|
+
await runTestEffect(task({ silent: true, branch: "test-feature" }))
|
|
131
|
+
|
|
132
|
+
// Clear VISUAL to ensure EDITOR is used
|
|
133
|
+
delete process.env.VISUAL
|
|
134
|
+
// Use a mock editor that fails
|
|
135
|
+
process.env.EDITOR = "false" // 'false' is a command that always exits with code 1
|
|
136
|
+
|
|
137
|
+
await expect(runTestEffect(taskEdit({ silent: true }))).rejects.toThrow(
|
|
138
|
+
"Editor exited with code",
|
|
139
|
+
)
|
|
140
|
+
})
|
|
141
|
+
})
|