@markjaquith/agency 1.11.1 → 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 -420
- 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 -641
- package/src/commands/push.ts +0 -482
- 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 -1187
- 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,633 +0,0 @@
|
|
|
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("creates new agency--some-branch when using --from some-branch with explicit name", async () => {
|
|
46
|
-
await initGitRepo(tempDir)
|
|
47
|
-
process.chdir(tempDir)
|
|
48
|
-
await initAgency(tempDir, "test")
|
|
49
|
-
|
|
50
|
-
// Create a feature branch called 'some-branch'
|
|
51
|
-
await runGitCommand(tempDir, ["git", "checkout", "-b", "some-branch"])
|
|
52
|
-
await createFile(tempDir, "feature.txt", "content")
|
|
53
|
-
await runGitCommand(tempDir, ["git", "add", "."])
|
|
54
|
-
await runGitCommand(tempDir, ["git", "commit", "-m", "Add feature"])
|
|
55
|
-
|
|
56
|
-
// Run agency task my-feature --from some-branch
|
|
57
|
-
// This should create a NEW branch called agency--my-feature
|
|
58
|
-
await runTestEffect(
|
|
59
|
-
task({
|
|
60
|
-
silent: true,
|
|
61
|
-
emit: "my-feature",
|
|
62
|
-
from: "some-branch",
|
|
63
|
-
}),
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
67
|
-
expect(currentBranch).toBe("agency--my-feature")
|
|
68
|
-
|
|
69
|
-
// Verify feature.txt exists (came from some-branch)
|
|
70
|
-
const featureFile = await Bun.file(join(tempDir, "feature.txt")).text()
|
|
71
|
-
expect(featureFile).toBe("content")
|
|
72
|
-
|
|
73
|
-
// Verify TASK.md was created (agency files added)
|
|
74
|
-
const taskMdExists = await Bun.file(join(tempDir, "TASK.md")).exists()
|
|
75
|
-
expect(taskMdExists).toBe(true)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
test("throws error when agency--some-branch already exists", async () => {
|
|
79
|
-
await initGitRepo(tempDir)
|
|
80
|
-
process.chdir(tempDir)
|
|
81
|
-
await initAgency(tempDir, "test")
|
|
82
|
-
|
|
83
|
-
// Create a feature branch called 'some-branch'
|
|
84
|
-
await runGitCommand(tempDir, ["git", "checkout", "-b", "some-branch"])
|
|
85
|
-
await createFile(tempDir, "feature.txt", "content")
|
|
86
|
-
await runGitCommand(tempDir, ["git", "add", "."])
|
|
87
|
-
await runGitCommand(tempDir, ["git", "commit", "-m", "Add feature"])
|
|
88
|
-
|
|
89
|
-
// Create agency--my-feature first
|
|
90
|
-
await runTestEffect(
|
|
91
|
-
task({
|
|
92
|
-
silent: true,
|
|
93
|
-
emit: "my-feature",
|
|
94
|
-
from: "some-branch",
|
|
95
|
-
}),
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
// Go back to some-branch
|
|
99
|
-
await runGitCommand(tempDir, ["git", "checkout", "some-branch"])
|
|
100
|
-
|
|
101
|
-
// Try to create it again - should fail
|
|
102
|
-
await expect(
|
|
103
|
-
runTestEffect(
|
|
104
|
-
task({
|
|
105
|
-
silent: true,
|
|
106
|
-
emit: "my-feature",
|
|
107
|
-
from: "some-branch",
|
|
108
|
-
}),
|
|
109
|
-
),
|
|
110
|
-
).rejects.toThrow("already exists")
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
test("branches from specified non-agency branch", async () => {
|
|
114
|
-
await initGitRepo(tempDir)
|
|
115
|
-
process.chdir(tempDir)
|
|
116
|
-
await initAgency(tempDir, "test")
|
|
117
|
-
|
|
118
|
-
// Create a feature branch
|
|
119
|
-
await runGitCommand(tempDir, ["git", "checkout", "-b", "feature-base"])
|
|
120
|
-
await createFile(tempDir, "feature.txt", "content")
|
|
121
|
-
await runGitCommand(tempDir, ["git", "add", "."])
|
|
122
|
-
await runGitCommand(tempDir, ["git", "commit", "-m", "Add feature"])
|
|
123
|
-
|
|
124
|
-
// Go back to main
|
|
125
|
-
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
126
|
-
|
|
127
|
-
// Create task branch from feature-base
|
|
128
|
-
await runTestEffect(
|
|
129
|
-
task({
|
|
130
|
-
silent: true,
|
|
131
|
-
emit: "my-task",
|
|
132
|
-
from: "feature-base",
|
|
133
|
-
}),
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
137
|
-
expect(currentBranch).toBe("agency--my-task")
|
|
138
|
-
|
|
139
|
-
// Verify feature.txt exists (came from feature-base)
|
|
140
|
-
const featureFile = await Bun.file(join(tempDir, "feature.txt")).text()
|
|
141
|
-
expect(featureFile).toBe("content")
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
test("throws error if specified branch does not exist", async () => {
|
|
145
|
-
await initGitRepo(tempDir)
|
|
146
|
-
process.chdir(tempDir)
|
|
147
|
-
await initAgency(tempDir, "test")
|
|
148
|
-
|
|
149
|
-
await expect(
|
|
150
|
-
runTestEffect(
|
|
151
|
-
task({
|
|
152
|
-
silent: true,
|
|
153
|
-
emit: "my-task",
|
|
154
|
-
from: "nonexistent-branch",
|
|
155
|
-
}),
|
|
156
|
-
),
|
|
157
|
-
).rejects.toThrow("does not exist")
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
test("detects agency source branch and uses its emit branch", async () => {
|
|
161
|
-
await initGitRepo(tempDir)
|
|
162
|
-
process.chdir(tempDir)
|
|
163
|
-
await initAgency(tempDir, "test")
|
|
164
|
-
|
|
165
|
-
// Create first agency branch
|
|
166
|
-
await runTestEffect(
|
|
167
|
-
task({
|
|
168
|
-
silent: true,
|
|
169
|
-
emit: "first-task",
|
|
170
|
-
}),
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
// Add a unique file to identify this branch
|
|
174
|
-
await createFile(tempDir, "first-task.txt", "first")
|
|
175
|
-
await runGitCommand(tempDir, ["git", "add", "."])
|
|
176
|
-
await runGitCommand(tempDir, [
|
|
177
|
-
"git",
|
|
178
|
-
"commit",
|
|
179
|
-
"-m",
|
|
180
|
-
"Add first task file",
|
|
181
|
-
])
|
|
182
|
-
|
|
183
|
-
// Emit the branch
|
|
184
|
-
await runTestEffect(emit({ silent: true }))
|
|
185
|
-
|
|
186
|
-
// Go back to main
|
|
187
|
-
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
188
|
-
|
|
189
|
-
// Create second task from first agency branch
|
|
190
|
-
await runTestEffect(
|
|
191
|
-
task({
|
|
192
|
-
silent: true,
|
|
193
|
-
emit: "second-task",
|
|
194
|
-
from: "agency--first-task",
|
|
195
|
-
}),
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
199
|
-
expect(currentBranch).toBe("agency--second-task")
|
|
200
|
-
|
|
201
|
-
// Verify first-task.txt exists (came from emit branch)
|
|
202
|
-
const firstTaskFile = await Bun.file(
|
|
203
|
-
join(tempDir, "first-task.txt"),
|
|
204
|
-
).text()
|
|
205
|
-
expect(firstTaskFile).toBe("first")
|
|
206
|
-
|
|
207
|
-
// Verify TASK.md does NOT exist from first-task
|
|
208
|
-
// (because we branched from emit branch, not source branch)
|
|
209
|
-
const taskMdExists = await Bun.file(join(tempDir, "TASK.md")).exists()
|
|
210
|
-
// The new TASK.md should exist (created by second task)
|
|
211
|
-
// but it should be fresh, not from first-task
|
|
212
|
-
expect(taskMdExists).toBe(true)
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
test("throws error if agency source branch has no emit branch", async () => {
|
|
216
|
-
await initGitRepo(tempDir)
|
|
217
|
-
process.chdir(tempDir)
|
|
218
|
-
await initAgency(tempDir, "test")
|
|
219
|
-
|
|
220
|
-
// Create agency branch without emitting
|
|
221
|
-
await runTestEffect(
|
|
222
|
-
task({
|
|
223
|
-
silent: true,
|
|
224
|
-
emit: "unemitted-task",
|
|
225
|
-
}),
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
// Go back to main
|
|
229
|
-
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
230
|
-
|
|
231
|
-
// Try to create task from unemitted agency branch
|
|
232
|
-
await expect(
|
|
233
|
-
runTestEffect(
|
|
234
|
-
task({
|
|
235
|
-
silent: true,
|
|
236
|
-
emit: "second-task",
|
|
237
|
-
from: "agency--unemitted-task",
|
|
238
|
-
}),
|
|
239
|
-
),
|
|
240
|
-
).rejects.toThrow("emit branch")
|
|
241
|
-
})
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
describe("--from-current flag", () => {
|
|
245
|
-
test("branches from current branch when it's not an agency branch", async () => {
|
|
246
|
-
await initGitRepo(tempDir)
|
|
247
|
-
process.chdir(tempDir)
|
|
248
|
-
await initAgency(tempDir, "test")
|
|
249
|
-
|
|
250
|
-
// Create and switch to a feature branch
|
|
251
|
-
await runGitCommand(tempDir, ["git", "checkout", "-b", "feature-current"])
|
|
252
|
-
await createFile(tempDir, "current.txt", "content")
|
|
253
|
-
await runGitCommand(tempDir, ["git", "add", "."])
|
|
254
|
-
await runGitCommand(tempDir, ["git", "commit", "-m", "Add current file"])
|
|
255
|
-
|
|
256
|
-
// Go back to main to create the task
|
|
257
|
-
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
258
|
-
|
|
259
|
-
// Create task from feature-current branch
|
|
260
|
-
await runTestEffect(
|
|
261
|
-
task({
|
|
262
|
-
silent: true,
|
|
263
|
-
emit: "my-task",
|
|
264
|
-
from: "feature-current",
|
|
265
|
-
}),
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
269
|
-
expect(currentBranch).toBe("agency--my-task")
|
|
270
|
-
|
|
271
|
-
// Verify current.txt exists
|
|
272
|
-
const currentFile = await Bun.file(join(tempDir, "current.txt")).text()
|
|
273
|
-
expect(currentFile).toBe("content")
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
test("uses emit branch when --from specifies an agency branch", async () => {
|
|
277
|
-
await initGitRepo(tempDir)
|
|
278
|
-
process.chdir(tempDir)
|
|
279
|
-
await initAgency(tempDir, "test")
|
|
280
|
-
|
|
281
|
-
// Create first agency branch
|
|
282
|
-
await runTestEffect(
|
|
283
|
-
task({
|
|
284
|
-
silent: true,
|
|
285
|
-
emit: "first-task",
|
|
286
|
-
}),
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
await createFile(tempDir, "first.txt", "first")
|
|
290
|
-
await runGitCommand(tempDir, ["git", "add", "."])
|
|
291
|
-
await runGitCommand(tempDir, ["git", "commit", "-m", "Add first file"])
|
|
292
|
-
|
|
293
|
-
// Emit the branch
|
|
294
|
-
await runTestEffect(emit({ silent: true }))
|
|
295
|
-
|
|
296
|
-
// Go back to main
|
|
297
|
-
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
298
|
-
|
|
299
|
-
// Create task from first agency branch
|
|
300
|
-
await runTestEffect(
|
|
301
|
-
task({
|
|
302
|
-
silent: true,
|
|
303
|
-
emit: "second-task",
|
|
304
|
-
from: "agency--first-task",
|
|
305
|
-
}),
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
309
|
-
expect(currentBranch).toBe("agency--second-task")
|
|
310
|
-
|
|
311
|
-
// Verify first.txt exists
|
|
312
|
-
const firstFile = await Bun.file(join(tempDir, "first.txt")).text()
|
|
313
|
-
expect(firstFile).toBe("first")
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
test("throws error if --from agency branch has no emit branch", async () => {
|
|
317
|
-
await initGitRepo(tempDir)
|
|
318
|
-
process.chdir(tempDir)
|
|
319
|
-
await initAgency(tempDir, "test")
|
|
320
|
-
|
|
321
|
-
// Create agency branch without emitting
|
|
322
|
-
await runTestEffect(
|
|
323
|
-
task({
|
|
324
|
-
silent: true,
|
|
325
|
-
emit: "unemitted-task",
|
|
326
|
-
}),
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
// Go back to main
|
|
330
|
-
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
331
|
-
|
|
332
|
-
// Try to create task from unemitted agency branch
|
|
333
|
-
await expect(
|
|
334
|
-
runTestEffect(
|
|
335
|
-
task({
|
|
336
|
-
silent: true,
|
|
337
|
-
emit: "second-task",
|
|
338
|
-
from: "agency--unemitted-task",
|
|
339
|
-
}),
|
|
340
|
-
),
|
|
341
|
-
).rejects.toThrow("emit branch")
|
|
342
|
-
})
|
|
343
|
-
})
|
|
344
|
-
|
|
345
|
-
describe("--from and --from-current validation", () => {
|
|
346
|
-
test("throws error if both flags are used", async () => {
|
|
347
|
-
await initGitRepo(tempDir)
|
|
348
|
-
process.chdir(tempDir)
|
|
349
|
-
await initAgency(tempDir, "test")
|
|
350
|
-
|
|
351
|
-
await expect(
|
|
352
|
-
runTestEffect(
|
|
353
|
-
task({
|
|
354
|
-
silent: true,
|
|
355
|
-
emit: "my-task",
|
|
356
|
-
from: "main",
|
|
357
|
-
fromCurrent: true,
|
|
358
|
-
}),
|
|
359
|
-
),
|
|
360
|
-
).rejects.toThrow("Cannot use both --from and --from-current")
|
|
361
|
-
})
|
|
362
|
-
})
|
|
363
|
-
|
|
364
|
-
describe("--from flag branch naming", () => {
|
|
365
|
-
test("agency task --from foo requires explicit branch name in silent mode", async () => {
|
|
366
|
-
await initGitRepo(tempDir)
|
|
367
|
-
process.chdir(tempDir)
|
|
368
|
-
await initAgency(tempDir, "test")
|
|
369
|
-
|
|
370
|
-
await runGitCommand(tempDir, ["git", "checkout", "-b", "foo"])
|
|
371
|
-
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
372
|
-
|
|
373
|
-
await expect(
|
|
374
|
-
runTestEffect(
|
|
375
|
-
task({
|
|
376
|
-
silent: true,
|
|
377
|
-
from: "foo",
|
|
378
|
-
}),
|
|
379
|
-
),
|
|
380
|
-
).rejects.toThrow("Branch name")
|
|
381
|
-
})
|
|
382
|
-
|
|
383
|
-
test("agency task out --from foo creates agency--out emitting to out", async () => {
|
|
384
|
-
await initGitRepo(tempDir)
|
|
385
|
-
process.chdir(tempDir)
|
|
386
|
-
await initAgency(tempDir, "test")
|
|
387
|
-
|
|
388
|
-
await runGitCommand(tempDir, ["git", "checkout", "-b", "foo"])
|
|
389
|
-
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
390
|
-
|
|
391
|
-
await runTestEffect(
|
|
392
|
-
task({
|
|
393
|
-
silent: true,
|
|
394
|
-
emit: "out",
|
|
395
|
-
from: "foo",
|
|
396
|
-
}),
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
400
|
-
expect(currentBranch).toBe("agency--out")
|
|
401
|
-
|
|
402
|
-
const agencyJson = JSON.parse(
|
|
403
|
-
await Bun.file(join(tempDir, "agency.json")).text(),
|
|
404
|
-
)
|
|
405
|
-
expect(agencyJson.emitBranch).toBe("out")
|
|
406
|
-
})
|
|
407
|
-
})
|
|
408
|
-
|
|
409
|
-
describe("default branching behavior", () => {
|
|
410
|
-
test("branches from auto-detected main branch by default", async () => {
|
|
411
|
-
await initGitRepo(tempDir)
|
|
412
|
-
process.chdir(tempDir)
|
|
413
|
-
await initAgency(tempDir, "test")
|
|
414
|
-
|
|
415
|
-
// Create a commit on main
|
|
416
|
-
await createFile(tempDir, "main.txt", "main content")
|
|
417
|
-
await runGitCommand(tempDir, ["git", "add", "."])
|
|
418
|
-
await runGitCommand(tempDir, ["git", "commit", "-m", "Main commit"])
|
|
419
|
-
|
|
420
|
-
// Create a different branch and switch to it
|
|
421
|
-
await runGitCommand(tempDir, ["git", "checkout", "-b", "other-branch"])
|
|
422
|
-
await createFile(tempDir, "other.txt", "other content")
|
|
423
|
-
await runGitCommand(tempDir, ["git", "add", "."])
|
|
424
|
-
await runGitCommand(tempDir, ["git", "commit", "-m", "Other commit"])
|
|
425
|
-
|
|
426
|
-
// Go back to main to set it as main branch
|
|
427
|
-
await runGitCommand(tempDir, ["git", "checkout", "main"])
|
|
428
|
-
|
|
429
|
-
// Create task (should branch from main)
|
|
430
|
-
await runTestEffect(
|
|
431
|
-
task({
|
|
432
|
-
silent: true,
|
|
433
|
-
emit: "my-task",
|
|
434
|
-
}),
|
|
435
|
-
)
|
|
436
|
-
|
|
437
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
438
|
-
expect(currentBranch).toBe("agency--my-task")
|
|
439
|
-
|
|
440
|
-
// Verify main.txt exists but other.txt does not
|
|
441
|
-
const mainExists = await Bun.file(join(tempDir, "main.txt")).exists()
|
|
442
|
-
const otherExists = await Bun.file(join(tempDir, "other.txt")).exists()
|
|
443
|
-
expect(mainExists).toBe(true)
|
|
444
|
-
expect(otherExists).toBe(false)
|
|
445
|
-
})
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
describe("remote branch preference", () => {
|
|
449
|
-
let remoteDir: string
|
|
450
|
-
|
|
451
|
-
beforeEach(async () => {
|
|
452
|
-
// Create a bare repository to act as the "remote"
|
|
453
|
-
remoteDir = await createTempDir()
|
|
454
|
-
await runGitCommand(remoteDir, ["git", "init", "--bare", "-b", "main"])
|
|
455
|
-
})
|
|
456
|
-
|
|
457
|
-
afterEach(async () => {
|
|
458
|
-
await cleanupTempDir(remoteDir)
|
|
459
|
-
})
|
|
460
|
-
|
|
461
|
-
test("prefers origin/main over local main when remote is ahead", async () => {
|
|
462
|
-
await initGitRepo(tempDir)
|
|
463
|
-
process.chdir(tempDir)
|
|
464
|
-
await initAgency(tempDir, "test")
|
|
465
|
-
|
|
466
|
-
// Add the remote
|
|
467
|
-
await runGitCommand(tempDir, [
|
|
468
|
-
"git",
|
|
469
|
-
"remote",
|
|
470
|
-
"add",
|
|
471
|
-
"origin",
|
|
472
|
-
remoteDir,
|
|
473
|
-
])
|
|
474
|
-
|
|
475
|
-
// Push current state to remote
|
|
476
|
-
await runGitCommand(tempDir, ["git", "push", "-u", "origin", "main"])
|
|
477
|
-
|
|
478
|
-
// Make a commit on local main
|
|
479
|
-
await createFile(tempDir, "local-only.txt", "local content")
|
|
480
|
-
await runGitCommand(tempDir, ["git", "add", "."])
|
|
481
|
-
await runGitCommand(tempDir, ["git", "commit", "-m", "Local-only commit"])
|
|
482
|
-
|
|
483
|
-
// Reset local main back to origin/main (simulate local being behind)
|
|
484
|
-
await runGitCommand(tempDir, ["git", "reset", "--hard", "origin/main"])
|
|
485
|
-
|
|
486
|
-
// Make a NEW commit on origin/main by pushing from a separate clone
|
|
487
|
-
const cloneDir = await createTempDir()
|
|
488
|
-
await runGitCommand(cloneDir, ["git", "clone", remoteDir, "."])
|
|
489
|
-
await runGitCommand(cloneDir, [
|
|
490
|
-
"git",
|
|
491
|
-
"config",
|
|
492
|
-
"user.email",
|
|
493
|
-
"test@example.com",
|
|
494
|
-
])
|
|
495
|
-
await runGitCommand(cloneDir, ["git", "config", "user.name", "Test User"])
|
|
496
|
-
await createFile(cloneDir, "remote-only.txt", "remote content")
|
|
497
|
-
await runGitCommand(cloneDir, ["git", "add", "."])
|
|
498
|
-
await runGitCommand(cloneDir, [
|
|
499
|
-
"git",
|
|
500
|
-
"commit",
|
|
501
|
-
"-m",
|
|
502
|
-
"Remote-only commit",
|
|
503
|
-
])
|
|
504
|
-
await runGitCommand(cloneDir, ["git", "push", "origin", "main"])
|
|
505
|
-
await cleanupTempDir(cloneDir)
|
|
506
|
-
|
|
507
|
-
// Fetch so origin/main is updated but local main stays behind
|
|
508
|
-
await runGitCommand(tempDir, ["git", "fetch", "origin"])
|
|
509
|
-
|
|
510
|
-
// Configure agency.mainBranch to "main" (local) and agency.remote to "origin"
|
|
511
|
-
await runGitCommand(tempDir, [
|
|
512
|
-
"git",
|
|
513
|
-
"config",
|
|
514
|
-
"--local",
|
|
515
|
-
"agency.mainBranch",
|
|
516
|
-
"main",
|
|
517
|
-
])
|
|
518
|
-
await runGitCommand(tempDir, [
|
|
519
|
-
"git",
|
|
520
|
-
"config",
|
|
521
|
-
"--local",
|
|
522
|
-
"agency.remote",
|
|
523
|
-
"origin",
|
|
524
|
-
])
|
|
525
|
-
|
|
526
|
-
// Create task - should branch from origin/main (which has remote-only.txt)
|
|
527
|
-
await runTestEffect(
|
|
528
|
-
task({
|
|
529
|
-
silent: true,
|
|
530
|
-
emit: "my-task",
|
|
531
|
-
}),
|
|
532
|
-
)
|
|
533
|
-
|
|
534
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
535
|
-
expect(currentBranch).toBe("agency--my-task")
|
|
536
|
-
|
|
537
|
-
// The new branch should have remote-only.txt (from origin/main)
|
|
538
|
-
const remoteFileExists = await Bun.file(
|
|
539
|
-
join(tempDir, "remote-only.txt"),
|
|
540
|
-
).exists()
|
|
541
|
-
expect(remoteFileExists).toBe(true)
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
test("falls back to local main when remote branch does not exist", async () => {
|
|
545
|
-
await initGitRepo(tempDir)
|
|
546
|
-
process.chdir(tempDir)
|
|
547
|
-
await initAgency(tempDir, "test")
|
|
548
|
-
|
|
549
|
-
// Create a commit on local main
|
|
550
|
-
await createFile(tempDir, "local.txt", "local content")
|
|
551
|
-
await runGitCommand(tempDir, ["git", "add", "."])
|
|
552
|
-
await runGitCommand(tempDir, ["git", "commit", "-m", "Local commit"])
|
|
553
|
-
|
|
554
|
-
// Add a remote but don't push (so origin/main doesn't exist)
|
|
555
|
-
await runGitCommand(tempDir, [
|
|
556
|
-
"git",
|
|
557
|
-
"remote",
|
|
558
|
-
"add",
|
|
559
|
-
"origin",
|
|
560
|
-
remoteDir,
|
|
561
|
-
])
|
|
562
|
-
|
|
563
|
-
// Configure agency.mainBranch to "main" and agency.remote to "origin"
|
|
564
|
-
await runGitCommand(tempDir, [
|
|
565
|
-
"git",
|
|
566
|
-
"config",
|
|
567
|
-
"--local",
|
|
568
|
-
"agency.mainBranch",
|
|
569
|
-
"main",
|
|
570
|
-
])
|
|
571
|
-
await runGitCommand(tempDir, [
|
|
572
|
-
"git",
|
|
573
|
-
"config",
|
|
574
|
-
"--local",
|
|
575
|
-
"agency.remote",
|
|
576
|
-
"origin",
|
|
577
|
-
])
|
|
578
|
-
|
|
579
|
-
// Create task - should fall back to local main since origin/main doesn't exist
|
|
580
|
-
await runTestEffect(
|
|
581
|
-
task({
|
|
582
|
-
silent: true,
|
|
583
|
-
emit: "my-task",
|
|
584
|
-
}),
|
|
585
|
-
)
|
|
586
|
-
|
|
587
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
588
|
-
expect(currentBranch).toBe("agency--my-task")
|
|
589
|
-
|
|
590
|
-
// The new branch should have local.txt
|
|
591
|
-
const localFileExists = await Bun.file(
|
|
592
|
-
join(tempDir, "local.txt"),
|
|
593
|
-
).exists()
|
|
594
|
-
expect(localFileExists).toBe(true)
|
|
595
|
-
})
|
|
596
|
-
|
|
597
|
-
test("uses configured remote branch as-is when it already has remote prefix", async () => {
|
|
598
|
-
await initGitRepo(tempDir)
|
|
599
|
-
process.chdir(tempDir)
|
|
600
|
-
await initAgency(tempDir, "test")
|
|
601
|
-
|
|
602
|
-
// Add the remote and push
|
|
603
|
-
await runGitCommand(tempDir, [
|
|
604
|
-
"git",
|
|
605
|
-
"remote",
|
|
606
|
-
"add",
|
|
607
|
-
"origin",
|
|
608
|
-
remoteDir,
|
|
609
|
-
])
|
|
610
|
-
await runGitCommand(tempDir, ["git", "push", "-u", "origin", "main"])
|
|
611
|
-
|
|
612
|
-
// Configure agency.mainBranch to "origin/main" (already has remote prefix)
|
|
613
|
-
await runGitCommand(tempDir, [
|
|
614
|
-
"git",
|
|
615
|
-
"config",
|
|
616
|
-
"--local",
|
|
617
|
-
"agency.mainBranch",
|
|
618
|
-
"origin/main",
|
|
619
|
-
])
|
|
620
|
-
|
|
621
|
-
// Create task
|
|
622
|
-
await runTestEffect(
|
|
623
|
-
task({
|
|
624
|
-
silent: true,
|
|
625
|
-
emit: "my-task",
|
|
626
|
-
}),
|
|
627
|
-
)
|
|
628
|
-
|
|
629
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
630
|
-
expect(currentBranch).toBe("agency--my-task")
|
|
631
|
-
})
|
|
632
|
-
})
|
|
633
|
-
})
|