@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,266 +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
|
-
getGitOutput,
|
|
14
|
-
} from "../test-utils"
|
|
15
|
-
|
|
16
|
-
describe("task --continue --squash", () => {
|
|
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
|
-
test("fails when --squash is used without --continue", async () => {
|
|
45
|
-
await initGitRepo(tempDir)
|
|
46
|
-
process.chdir(tempDir)
|
|
47
|
-
|
|
48
|
-
await initAgency(tempDir, "test")
|
|
49
|
-
|
|
50
|
-
await expect(
|
|
51
|
-
runTestEffect(task({ silent: true, squash: true, emit: "some-branch" })),
|
|
52
|
-
).rejects.toThrow("--squash flag can only be used with --continue")
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test("squashes emitted commits into a single commit on new branch", async () => {
|
|
56
|
-
await initGitRepo(tempDir)
|
|
57
|
-
process.chdir(tempDir)
|
|
58
|
-
|
|
59
|
-
await initAgency(tempDir, "test")
|
|
60
|
-
|
|
61
|
-
// Create a task branch with agency files
|
|
62
|
-
await runTestEffect(
|
|
63
|
-
task({ silent: true, emit: "feature-v1", task: "Original task" }),
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
// Add two code commits to the branch
|
|
67
|
-
await Bun.write(join(tempDir, "feature-a.txt"), "feature A content")
|
|
68
|
-
await Bun.spawn(["git", "add", "feature-a.txt"], {
|
|
69
|
-
cwd: tempDir,
|
|
70
|
-
stdout: "pipe",
|
|
71
|
-
stderr: "pipe",
|
|
72
|
-
}).exited
|
|
73
|
-
await Bun.spawn(["git", "commit", "--no-verify", "-m", "Add feature A"], {
|
|
74
|
-
cwd: tempDir,
|
|
75
|
-
stdout: "pipe",
|
|
76
|
-
stderr: "pipe",
|
|
77
|
-
}).exited
|
|
78
|
-
|
|
79
|
-
await Bun.write(join(tempDir, "feature-b.txt"), "feature B content")
|
|
80
|
-
await Bun.spawn(["git", "add", "feature-b.txt"], {
|
|
81
|
-
cwd: tempDir,
|
|
82
|
-
stdout: "pipe",
|
|
83
|
-
stderr: "pipe",
|
|
84
|
-
}).exited
|
|
85
|
-
await Bun.spawn(["git", "commit", "--no-verify", "-m", "Add feature B"], {
|
|
86
|
-
cwd: tempDir,
|
|
87
|
-
stdout: "pipe",
|
|
88
|
-
stderr: "pipe",
|
|
89
|
-
}).exited
|
|
90
|
-
|
|
91
|
-
// Now continue with squash
|
|
92
|
-
await runTestEffect(
|
|
93
|
-
task({
|
|
94
|
-
silent: true,
|
|
95
|
-
continue: true,
|
|
96
|
-
squash: true,
|
|
97
|
-
emit: "feature-v2",
|
|
98
|
-
task: "New task for v2",
|
|
99
|
-
}),
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
// Verify we're on the new branch
|
|
103
|
-
const newBranch = await getCurrentBranch(tempDir)
|
|
104
|
-
expect(newBranch).toBe("agency--feature-v2")
|
|
105
|
-
|
|
106
|
-
// Verify both feature files exist (squash brought them in)
|
|
107
|
-
expect(await fileExists(join(tempDir, "feature-a.txt"))).toBe(true)
|
|
108
|
-
expect(await fileExists(join(tempDir, "feature-b.txt"))).toBe(true)
|
|
109
|
-
|
|
110
|
-
// Verify agency files exist
|
|
111
|
-
expect(await fileExists(join(tempDir, "agency.json"))).toBe(true)
|
|
112
|
-
expect(await fileExists(join(tempDir, "TASK.md"))).toBe(true)
|
|
113
|
-
expect(await fileExists(join(tempDir, "AGENCY.md"))).toBe(true)
|
|
114
|
-
|
|
115
|
-
// Verify TASK.md has FRESH content (not the old "Original task")
|
|
116
|
-
const taskContent = await readFile(join(tempDir, "TASK.md"))
|
|
117
|
-
expect(taskContent).toContain("New task for v2")
|
|
118
|
-
expect(taskContent).not.toContain("Original task")
|
|
119
|
-
|
|
120
|
-
// Verify agency.json has updated emitBranch
|
|
121
|
-
const agencyJson = JSON.parse(await readFile(join(tempDir, "agency.json")))
|
|
122
|
-
expect(agencyJson.emitBranch).toBe("feature-v2")
|
|
123
|
-
|
|
124
|
-
// Verify commit history: should be initial commit + squash commit + agency files commit
|
|
125
|
-
// (not the individual "Add feature A" and "Add feature B" commits)
|
|
126
|
-
const logOutput = await getGitOutput(tempDir, [
|
|
127
|
-
"log",
|
|
128
|
-
"--oneline",
|
|
129
|
-
"--format=%s",
|
|
130
|
-
])
|
|
131
|
-
const commits = logOutput.trim().split("\n")
|
|
132
|
-
|
|
133
|
-
// Should NOT contain individual feature commits
|
|
134
|
-
expect(logOutput).not.toContain("Add feature A")
|
|
135
|
-
expect(logOutput).not.toContain("Add feature B")
|
|
136
|
-
|
|
137
|
-
// Should contain a squash commit
|
|
138
|
-
expect(logOutput).toContain("squash: prior work from feature-v1")
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
test("creates fresh TASK.md with default placeholder when no task provided in silent mode", async () => {
|
|
142
|
-
await initGitRepo(tempDir)
|
|
143
|
-
process.chdir(tempDir)
|
|
144
|
-
|
|
145
|
-
await initAgency(tempDir, "test")
|
|
146
|
-
|
|
147
|
-
// Create a task branch
|
|
148
|
-
await runTestEffect(
|
|
149
|
-
task({ silent: true, emit: "feature-v1", task: "Old task" }),
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
// Add a code commit
|
|
153
|
-
await Bun.write(join(tempDir, "code.txt"), "code content")
|
|
154
|
-
await Bun.spawn(["git", "add", "code.txt"], {
|
|
155
|
-
cwd: tempDir,
|
|
156
|
-
stdout: "pipe",
|
|
157
|
-
stderr: "pipe",
|
|
158
|
-
}).exited
|
|
159
|
-
await Bun.spawn(["git", "commit", "--no-verify", "-m", "Add code"], {
|
|
160
|
-
cwd: tempDir,
|
|
161
|
-
stdout: "pipe",
|
|
162
|
-
stderr: "pipe",
|
|
163
|
-
}).exited
|
|
164
|
-
|
|
165
|
-
// Continue with squash but no --task option (silent mode)
|
|
166
|
-
await runTestEffect(
|
|
167
|
-
task({
|
|
168
|
-
silent: true,
|
|
169
|
-
continue: true,
|
|
170
|
-
squash: true,
|
|
171
|
-
emit: "feature-v2",
|
|
172
|
-
}),
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
// TASK.md should have the default placeholder, not the old task
|
|
176
|
-
const taskContent = await readFile(join(tempDir, "TASK.md"))
|
|
177
|
-
expect(taskContent).toContain("{task}")
|
|
178
|
-
expect(taskContent).not.toContain("Old task")
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
test("handles empty squash when old branch has no code commits", async () => {
|
|
182
|
-
await initGitRepo(tempDir)
|
|
183
|
-
process.chdir(tempDir)
|
|
184
|
-
|
|
185
|
-
await initAgency(tempDir, "test")
|
|
186
|
-
|
|
187
|
-
// Create a task branch WITHOUT any code commits
|
|
188
|
-
await runTestEffect(
|
|
189
|
-
task({ silent: true, emit: "feature-v1", task: "Just agency files" }),
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
// Continue with squash - should succeed even with no code changes
|
|
193
|
-
await runTestEffect(
|
|
194
|
-
task({
|
|
195
|
-
silent: true,
|
|
196
|
-
continue: true,
|
|
197
|
-
squash: true,
|
|
198
|
-
emit: "feature-v2",
|
|
199
|
-
task: "New task",
|
|
200
|
-
}),
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
// Verify we're on the new branch
|
|
204
|
-
const newBranch = await getCurrentBranch(tempDir)
|
|
205
|
-
expect(newBranch).toBe("agency--feature-v2")
|
|
206
|
-
|
|
207
|
-
// Verify agency files exist
|
|
208
|
-
expect(await fileExists(join(tempDir, "agency.json"))).toBe(true)
|
|
209
|
-
expect(await fileExists(join(tempDir, "TASK.md"))).toBe(true)
|
|
210
|
-
|
|
211
|
-
// Verify fresh TASK.md
|
|
212
|
-
const taskContent = await readFile(join(tempDir, "TASK.md"))
|
|
213
|
-
expect(taskContent).toContain("New task")
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
test("preserves other agency files while creating fresh TASK.md", async () => {
|
|
217
|
-
await initGitRepo(tempDir)
|
|
218
|
-
process.chdir(tempDir)
|
|
219
|
-
|
|
220
|
-
await initAgency(tempDir, "test")
|
|
221
|
-
|
|
222
|
-
// Create a task branch
|
|
223
|
-
await runTestEffect(
|
|
224
|
-
task({ silent: true, emit: "feature-v1", task: "Old task" }),
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
// Read the original AGENCY.md content
|
|
228
|
-
const originalAgencyMd = await readFile(join(tempDir, "AGENCY.md"))
|
|
229
|
-
|
|
230
|
-
// Add a code commit
|
|
231
|
-
await Bun.write(join(tempDir, "code.txt"), "code")
|
|
232
|
-
await Bun.spawn(["git", "add", "code.txt"], {
|
|
233
|
-
cwd: tempDir,
|
|
234
|
-
stdout: "pipe",
|
|
235
|
-
stderr: "pipe",
|
|
236
|
-
}).exited
|
|
237
|
-
await Bun.spawn(["git", "commit", "--no-verify", "-m", "Add code"], {
|
|
238
|
-
cwd: tempDir,
|
|
239
|
-
stdout: "pipe",
|
|
240
|
-
stderr: "pipe",
|
|
241
|
-
}).exited
|
|
242
|
-
|
|
243
|
-
// Continue with squash
|
|
244
|
-
await runTestEffect(
|
|
245
|
-
task({
|
|
246
|
-
silent: true,
|
|
247
|
-
continue: true,
|
|
248
|
-
squash: true,
|
|
249
|
-
emit: "feature-v2",
|
|
250
|
-
task: "New task",
|
|
251
|
-
}),
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
// AGENCY.md should be carried forward (not fresh)
|
|
255
|
-
const newAgencyMd = await readFile(join(tempDir, "AGENCY.md"))
|
|
256
|
-
expect(newAgencyMd).toBe(originalAgencyMd)
|
|
257
|
-
|
|
258
|
-
// TASK.md should be fresh
|
|
259
|
-
const taskContent = await readFile(join(tempDir, "TASK.md"))
|
|
260
|
-
expect(taskContent).toContain("New task")
|
|
261
|
-
expect(taskContent).not.toContain("Old task")
|
|
262
|
-
|
|
263
|
-
// opencode.json should be carried forward
|
|
264
|
-
expect(await fileExists(join(tempDir, "opencode.json"))).toBe(true)
|
|
265
|
-
})
|
|
266
|
-
})
|
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
import { test, expect, describe, beforeEach, afterEach } from "bun:test"
|
|
2
|
-
import { join } from "path"
|
|
3
|
-
import { tasks } from "./tasks"
|
|
4
|
-
import {
|
|
5
|
-
createTempDir,
|
|
6
|
-
cleanupTempDir,
|
|
7
|
-
initGitRepo,
|
|
8
|
-
getCurrentBranch,
|
|
9
|
-
createCommit,
|
|
10
|
-
initAgency,
|
|
11
|
-
runTestEffect,
|
|
12
|
-
} from "../test-utils"
|
|
13
|
-
import { writeAgencyMetadata } from "../types"
|
|
14
|
-
|
|
15
|
-
async function createBranch(cwd: string, branchName: string): Promise<void> {
|
|
16
|
-
await Bun.spawn(["git", "checkout", "-b", branchName], {
|
|
17
|
-
cwd,
|
|
18
|
-
stdout: "pipe",
|
|
19
|
-
stderr: "pipe",
|
|
20
|
-
}).exited
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function checkoutBranch(cwd: string, branchName: string): Promise<void> {
|
|
24
|
-
await Bun.spawn(["git", "checkout", branchName], {
|
|
25
|
-
cwd,
|
|
26
|
-
stdout: "pipe",
|
|
27
|
-
stderr: "pipe",
|
|
28
|
-
}).exited
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
describe("tasks command", () => {
|
|
32
|
-
let tempDir: string
|
|
33
|
-
let originalCwd: string
|
|
34
|
-
|
|
35
|
-
beforeEach(async () => {
|
|
36
|
-
tempDir = await createTempDir()
|
|
37
|
-
originalCwd = process.cwd()
|
|
38
|
-
process.chdir(tempDir)
|
|
39
|
-
|
|
40
|
-
// Set config path to non-existent file to use defaults
|
|
41
|
-
process.env.AGENCY_CONFIG_PATH = join(tempDir, "non-existent-config.json")
|
|
42
|
-
|
|
43
|
-
// Initialize git repo
|
|
44
|
-
await initGitRepo(tempDir)
|
|
45
|
-
await createCommit(tempDir, "Initial commit")
|
|
46
|
-
|
|
47
|
-
// Rename to main if needed
|
|
48
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
49
|
-
if (currentBranch === "master") {
|
|
50
|
-
await Bun.spawn(["git", "branch", "-m", "main"], {
|
|
51
|
-
cwd: tempDir,
|
|
52
|
-
stdout: "pipe",
|
|
53
|
-
stderr: "pipe",
|
|
54
|
-
}).exited
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
afterEach(async () => {
|
|
59
|
-
process.chdir(originalCwd)
|
|
60
|
-
delete process.env.AGENCY_CONFIG_PATH
|
|
61
|
-
await cleanupTempDir(tempDir)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
describe("basic functionality", () => {
|
|
65
|
-
test("shows no task branches when none exist", async () => {
|
|
66
|
-
let output = ""
|
|
67
|
-
const originalLog = console.log
|
|
68
|
-
console.log = (msg: string) => {
|
|
69
|
-
output += msg + "\n"
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
await runTestEffect(tasks({}))
|
|
73
|
-
|
|
74
|
-
console.log = originalLog
|
|
75
|
-
expect(output).toContain("No task branches found")
|
|
76
|
-
expect(output).toContain("agency task")
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
test("lists single task branch", async () => {
|
|
80
|
-
// Initialize agency
|
|
81
|
-
await initAgency(tempDir, "test-template")
|
|
82
|
-
|
|
83
|
-
// Create feature branch with agency-- prefix and agency.json
|
|
84
|
-
await createBranch(tempDir, "agency--feature")
|
|
85
|
-
await writeAgencyMetadata(tempDir, {
|
|
86
|
-
version: 1,
|
|
87
|
-
injectedFiles: ["AGENTS.md"],
|
|
88
|
-
template: "test-template",
|
|
89
|
-
baseBranch: "main",
|
|
90
|
-
createdAt: new Date().toISOString(),
|
|
91
|
-
} as any)
|
|
92
|
-
|
|
93
|
-
// Commit agency.json
|
|
94
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
95
|
-
cwd: tempDir,
|
|
96
|
-
stdout: "pipe",
|
|
97
|
-
stderr: "pipe",
|
|
98
|
-
}).exited
|
|
99
|
-
await createCommit(tempDir, "Add agency.json")
|
|
100
|
-
|
|
101
|
-
let output = ""
|
|
102
|
-
const originalLog = console.log
|
|
103
|
-
console.log = (msg: string) => {
|
|
104
|
-
output += msg + "\n"
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
await runTestEffect(tasks({}))
|
|
108
|
-
|
|
109
|
-
console.log = originalLog
|
|
110
|
-
expect(output).toContain("agency--feature")
|
|
111
|
-
expect(output.trim()).toBe("agency--feature")
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
test("lists multiple task branches", async () => {
|
|
115
|
-
await initAgency(tempDir, "test-template")
|
|
116
|
-
|
|
117
|
-
// Create first feature branch
|
|
118
|
-
await createBranch(tempDir, "agency--feature-1")
|
|
119
|
-
await writeAgencyMetadata(tempDir, {
|
|
120
|
-
version: 1,
|
|
121
|
-
injectedFiles: ["AGENTS.md"],
|
|
122
|
-
template: "test-template",
|
|
123
|
-
baseBranch: "main",
|
|
124
|
-
createdAt: new Date().toISOString(),
|
|
125
|
-
} as any)
|
|
126
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
127
|
-
cwd: tempDir,
|
|
128
|
-
stdout: "pipe",
|
|
129
|
-
stderr: "pipe",
|
|
130
|
-
}).exited
|
|
131
|
-
await createCommit(tempDir, "Add agency.json")
|
|
132
|
-
|
|
133
|
-
// Go back to main and create second feature branch
|
|
134
|
-
await checkoutBranch(tempDir, "main")
|
|
135
|
-
await createBranch(tempDir, "agency--feature-2")
|
|
136
|
-
await writeAgencyMetadata(tempDir, {
|
|
137
|
-
version: 1,
|
|
138
|
-
injectedFiles: ["opencode.json"],
|
|
139
|
-
template: "another-template",
|
|
140
|
-
baseBranch: "main",
|
|
141
|
-
createdAt: new Date().toISOString(),
|
|
142
|
-
} as any)
|
|
143
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
144
|
-
cwd: tempDir,
|
|
145
|
-
stdout: "pipe",
|
|
146
|
-
stderr: "pipe",
|
|
147
|
-
}).exited
|
|
148
|
-
await createCommit(tempDir, "Add agency.json")
|
|
149
|
-
|
|
150
|
-
let output = ""
|
|
151
|
-
const originalLog = console.log
|
|
152
|
-
console.log = (msg: string) => {
|
|
153
|
-
output += msg + "\n"
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
await runTestEffect(tasks({}))
|
|
157
|
-
|
|
158
|
-
console.log = originalLog
|
|
159
|
-
expect(output).toContain("agency--feature-1")
|
|
160
|
-
expect(output).toContain("agency--feature-2")
|
|
161
|
-
const lines = output.trim().split("\n")
|
|
162
|
-
expect(lines.length).toBe(2)
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
test("ignores branches without agency-- prefix", async () => {
|
|
166
|
-
await initAgency(tempDir, "test-template")
|
|
167
|
-
|
|
168
|
-
// Create a branch without agency-- prefix (even with agency.json)
|
|
169
|
-
await createBranch(tempDir, "no-prefix")
|
|
170
|
-
await writeAgencyMetadata(tempDir, {
|
|
171
|
-
version: 1,
|
|
172
|
-
injectedFiles: [],
|
|
173
|
-
template: "test-template",
|
|
174
|
-
createdAt: new Date().toISOString(),
|
|
175
|
-
} as any)
|
|
176
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
177
|
-
cwd: tempDir,
|
|
178
|
-
stdout: "pipe",
|
|
179
|
-
stderr: "pipe",
|
|
180
|
-
}).exited
|
|
181
|
-
await createCommit(tempDir, "Add agency.json")
|
|
182
|
-
|
|
183
|
-
// Go back to main and create a branch with agency-- prefix
|
|
184
|
-
await checkoutBranch(tempDir, "main")
|
|
185
|
-
await createBranch(tempDir, "agency--with-prefix")
|
|
186
|
-
await writeAgencyMetadata(tempDir, {
|
|
187
|
-
version: 1,
|
|
188
|
-
injectedFiles: [],
|
|
189
|
-
template: "test-template",
|
|
190
|
-
createdAt: new Date().toISOString(),
|
|
191
|
-
} as any)
|
|
192
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
193
|
-
cwd: tempDir,
|
|
194
|
-
stdout: "pipe",
|
|
195
|
-
stderr: "pipe",
|
|
196
|
-
}).exited
|
|
197
|
-
await createCommit(tempDir, "Add agency.json")
|
|
198
|
-
|
|
199
|
-
let output = ""
|
|
200
|
-
const originalLog = console.log
|
|
201
|
-
console.log = (msg: string) => {
|
|
202
|
-
output += msg + "\n"
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
await runTestEffect(tasks({}))
|
|
206
|
-
|
|
207
|
-
console.log = originalLog
|
|
208
|
-
expect(output).toContain("agency--with-prefix")
|
|
209
|
-
expect(output).not.toContain("no-prefix")
|
|
210
|
-
expect(output.trim()).toBe("agency--with-prefix")
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
test("lists agency-- branches even without agency.json", async () => {
|
|
214
|
-
// Create a branch with agency-- prefix but no agency.json
|
|
215
|
-
await createBranch(tempDir, "agency--no-metadata")
|
|
216
|
-
await createCommit(tempDir, "Some work")
|
|
217
|
-
|
|
218
|
-
let output = ""
|
|
219
|
-
const originalLog = console.log
|
|
220
|
-
console.log = (msg: string) => {
|
|
221
|
-
output += msg + "\n"
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
await runTestEffect(tasks({}))
|
|
225
|
-
|
|
226
|
-
console.log = originalLog
|
|
227
|
-
expect(output).toContain("agency--no-metadata")
|
|
228
|
-
expect(output.trim()).toBe("agency--no-metadata")
|
|
229
|
-
})
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
describe("JSON output", () => {
|
|
233
|
-
test("outputs JSON format when --json is provided", async () => {
|
|
234
|
-
await initAgency(tempDir, "test-template")
|
|
235
|
-
|
|
236
|
-
await createBranch(tempDir, "agency--feature")
|
|
237
|
-
const createdAt = new Date().toISOString()
|
|
238
|
-
await writeAgencyMetadata(tempDir, {
|
|
239
|
-
version: 1,
|
|
240
|
-
injectedFiles: ["AGENTS.md"],
|
|
241
|
-
template: "test-template",
|
|
242
|
-
baseBranch: "main",
|
|
243
|
-
createdAt,
|
|
244
|
-
} as any)
|
|
245
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
246
|
-
cwd: tempDir,
|
|
247
|
-
stdout: "pipe",
|
|
248
|
-
stderr: "pipe",
|
|
249
|
-
}).exited
|
|
250
|
-
await createCommit(tempDir, "Add agency.json")
|
|
251
|
-
|
|
252
|
-
let output = ""
|
|
253
|
-
const originalLog = console.log
|
|
254
|
-
console.log = (msg: string) => {
|
|
255
|
-
output += msg + "\n"
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
await runTestEffect(tasks({ json: true }))
|
|
259
|
-
|
|
260
|
-
console.log = originalLog
|
|
261
|
-
|
|
262
|
-
// Parse JSON output
|
|
263
|
-
const data = JSON.parse(output.trim())
|
|
264
|
-
expect(Array.isArray(data)).toBe(true)
|
|
265
|
-
expect(data.length).toBe(1)
|
|
266
|
-
expect(data[0].branch).toBe("agency--feature")
|
|
267
|
-
expect(data[0].template).toBe("test-template")
|
|
268
|
-
expect(data[0].baseBranch).toBe("main")
|
|
269
|
-
expect(data[0].createdAt).toBeDefined()
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
test("outputs empty array in JSON format when no task branches exist", async () => {
|
|
273
|
-
let output = ""
|
|
274
|
-
const originalLog = console.log
|
|
275
|
-
console.log = (msg: string) => {
|
|
276
|
-
output += msg + "\n"
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
await runTestEffect(tasks({ json: true }))
|
|
280
|
-
|
|
281
|
-
console.log = originalLog
|
|
282
|
-
|
|
283
|
-
const data = JSON.parse(output.trim())
|
|
284
|
-
expect(Array.isArray(data)).toBe(true)
|
|
285
|
-
expect(data.length).toBe(0)
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
test("JSON output shows null metadata for branches without agency.json", async () => {
|
|
289
|
-
// Create a branch with agency-- prefix but no agency.json
|
|
290
|
-
await createBranch(tempDir, "agency--no-metadata")
|
|
291
|
-
await createCommit(tempDir, "Some work")
|
|
292
|
-
|
|
293
|
-
let output = ""
|
|
294
|
-
const originalLog = console.log
|
|
295
|
-
console.log = (msg: string) => {
|
|
296
|
-
output += msg + "\n"
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
await runTestEffect(tasks({ json: true }))
|
|
300
|
-
|
|
301
|
-
console.log = originalLog
|
|
302
|
-
|
|
303
|
-
const data = JSON.parse(output.trim())
|
|
304
|
-
expect(Array.isArray(data)).toBe(true)
|
|
305
|
-
expect(data.length).toBe(1)
|
|
306
|
-
expect(data[0].branch).toBe("agency--no-metadata")
|
|
307
|
-
expect(data[0].template).toBeNull()
|
|
308
|
-
expect(data[0].baseBranch).toBeNull()
|
|
309
|
-
expect(data[0].createdAt).toBeNull()
|
|
310
|
-
})
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
describe("edge cases", () => {
|
|
314
|
-
test("handles branches with invalid agency.json gracefully", async () => {
|
|
315
|
-
await initAgency(tempDir, "test-template")
|
|
316
|
-
|
|
317
|
-
// Create branch with invalid agency.json but agency-- prefix
|
|
318
|
-
await createBranch(tempDir, "agency--invalid")
|
|
319
|
-
await Bun.write(join(tempDir, "agency.json"), "{ invalid json }")
|
|
320
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
321
|
-
cwd: tempDir,
|
|
322
|
-
stdout: "pipe",
|
|
323
|
-
stderr: "pipe",
|
|
324
|
-
}).exited
|
|
325
|
-
await createCommit(tempDir, "Add invalid agency.json")
|
|
326
|
-
|
|
327
|
-
// Create branch with valid agency.json and agency-- prefix
|
|
328
|
-
await checkoutBranch(tempDir, "main")
|
|
329
|
-
await createBranch(tempDir, "agency--valid")
|
|
330
|
-
await writeAgencyMetadata(tempDir, {
|
|
331
|
-
version: 1,
|
|
332
|
-
injectedFiles: [],
|
|
333
|
-
template: "test-template",
|
|
334
|
-
createdAt: new Date().toISOString(),
|
|
335
|
-
} as any)
|
|
336
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
337
|
-
cwd: tempDir,
|
|
338
|
-
stdout: "pipe",
|
|
339
|
-
stderr: "pipe",
|
|
340
|
-
}).exited
|
|
341
|
-
await createCommit(tempDir, "Add valid agency.json")
|
|
342
|
-
|
|
343
|
-
let output = ""
|
|
344
|
-
const originalLog = console.log
|
|
345
|
-
console.log = (msg: string) => {
|
|
346
|
-
output += msg + "\n"
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
await runTestEffect(tasks({}))
|
|
350
|
-
|
|
351
|
-
console.log = originalLog
|
|
352
|
-
// Both branches should be listed since they match the prefix
|
|
353
|
-
expect(output).toContain("agency--valid")
|
|
354
|
-
expect(output).toContain("agency--invalid")
|
|
355
|
-
const lines = output.trim().split("\n")
|
|
356
|
-
expect(lines.length).toBe(2)
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
test("handles branches with old version agency.json", async () => {
|
|
360
|
-
await initAgency(tempDir, "test-template")
|
|
361
|
-
|
|
362
|
-
// Create branch with version 0 agency.json (future or old version)
|
|
363
|
-
await createBranch(tempDir, "agency--old-version")
|
|
364
|
-
await Bun.write(
|
|
365
|
-
join(tempDir, "agency.json"),
|
|
366
|
-
JSON.stringify({
|
|
367
|
-
version: 0,
|
|
368
|
-
template: "old-template",
|
|
369
|
-
}),
|
|
370
|
-
)
|
|
371
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
372
|
-
cwd: tempDir,
|
|
373
|
-
stdout: "pipe",
|
|
374
|
-
stderr: "pipe",
|
|
375
|
-
}).exited
|
|
376
|
-
await createCommit(tempDir, "Add old version agency.json")
|
|
377
|
-
|
|
378
|
-
let output = ""
|
|
379
|
-
const originalLog = console.log
|
|
380
|
-
console.log = (msg: string) => {
|
|
381
|
-
output += msg + "\n"
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
await runTestEffect(tasks({}))
|
|
385
|
-
|
|
386
|
-
console.log = originalLog
|
|
387
|
-
// Branch should still be listed since it matches the prefix
|
|
388
|
-
expect(output).toContain("agency--old-version")
|
|
389
|
-
})
|
|
390
|
-
})
|
|
391
|
-
})
|