@markjaquith/agency 1.8.1 → 1.8.2
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/package.json +1 -1
- package/src/commands/tasks.test.ts +84 -28
- package/src/commands/tasks.ts +31 -8
- package/src/services/GitService.ts +32 -0
package/package.json
CHANGED
|
@@ -80,8 +80,8 @@ describe("tasks command", () => {
|
|
|
80
80
|
// Initialize agency
|
|
81
81
|
await initAgency(tempDir, "test-template")
|
|
82
82
|
|
|
83
|
-
// Create feature branch with agency.json
|
|
84
|
-
await createBranch(tempDir, "feature")
|
|
83
|
+
// Create feature branch with agency-- prefix and agency.json
|
|
84
|
+
await createBranch(tempDir, "agency--feature")
|
|
85
85
|
await writeAgencyMetadata(tempDir, {
|
|
86
86
|
version: 1,
|
|
87
87
|
injectedFiles: ["AGENTS.md"],
|
|
@@ -107,15 +107,15 @@ describe("tasks command", () => {
|
|
|
107
107
|
await runTestEffect(tasks({}))
|
|
108
108
|
|
|
109
109
|
console.log = originalLog
|
|
110
|
-
expect(output).toContain("feature")
|
|
111
|
-
expect(output.trim()).toBe("feature")
|
|
110
|
+
expect(output).toContain("agency--feature")
|
|
111
|
+
expect(output.trim()).toBe("agency--feature")
|
|
112
112
|
})
|
|
113
113
|
|
|
114
114
|
test("lists multiple task branches", async () => {
|
|
115
115
|
await initAgency(tempDir, "test-template")
|
|
116
116
|
|
|
117
117
|
// Create first feature branch
|
|
118
|
-
await createBranch(tempDir, "feature-1")
|
|
118
|
+
await createBranch(tempDir, "agency--feature-1")
|
|
119
119
|
await writeAgencyMetadata(tempDir, {
|
|
120
120
|
version: 1,
|
|
121
121
|
injectedFiles: ["AGENTS.md"],
|
|
@@ -132,7 +132,7 @@ describe("tasks command", () => {
|
|
|
132
132
|
|
|
133
133
|
// Go back to main and create second feature branch
|
|
134
134
|
await checkoutBranch(tempDir, "main")
|
|
135
|
-
await createBranch(tempDir, "feature-2")
|
|
135
|
+
await createBranch(tempDir, "agency--feature-2")
|
|
136
136
|
await writeAgencyMetadata(tempDir, {
|
|
137
137
|
version: 1,
|
|
138
138
|
injectedFiles: ["opencode.json"],
|
|
@@ -156,22 +156,33 @@ describe("tasks command", () => {
|
|
|
156
156
|
await runTestEffect(tasks({}))
|
|
157
157
|
|
|
158
158
|
console.log = originalLog
|
|
159
|
-
expect(output).toContain("feature-1")
|
|
160
|
-
expect(output).toContain("feature-2")
|
|
159
|
+
expect(output).toContain("agency--feature-1")
|
|
160
|
+
expect(output).toContain("agency--feature-2")
|
|
161
161
|
const lines = output.trim().split("\n")
|
|
162
162
|
expect(lines.length).toBe(2)
|
|
163
163
|
})
|
|
164
164
|
|
|
165
|
-
test("ignores branches without agency
|
|
165
|
+
test("ignores branches without agency-- prefix", async () => {
|
|
166
166
|
await initAgency(tempDir, "test-template")
|
|
167
167
|
|
|
168
|
-
// Create a branch without agency.json
|
|
169
|
-
await createBranch(tempDir, "no-
|
|
170
|
-
await
|
|
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")
|
|
171
182
|
|
|
172
|
-
// Go back to main and create a branch with agency
|
|
183
|
+
// Go back to main and create a branch with agency-- prefix
|
|
173
184
|
await checkoutBranch(tempDir, "main")
|
|
174
|
-
await createBranch(tempDir, "with-
|
|
185
|
+
await createBranch(tempDir, "agency--with-prefix")
|
|
175
186
|
await writeAgencyMetadata(tempDir, {
|
|
176
187
|
version: 1,
|
|
177
188
|
injectedFiles: [],
|
|
@@ -194,9 +205,27 @@ describe("tasks command", () => {
|
|
|
194
205
|
await runTestEffect(tasks({}))
|
|
195
206
|
|
|
196
207
|
console.log = originalLog
|
|
197
|
-
expect(output).toContain("with-
|
|
198
|
-
expect(output).not.toContain("no-
|
|
199
|
-
expect(output.trim()).toBe("with-
|
|
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")
|
|
200
229
|
})
|
|
201
230
|
})
|
|
202
231
|
|
|
@@ -204,7 +233,7 @@ describe("tasks command", () => {
|
|
|
204
233
|
test("outputs JSON format when --json is provided", async () => {
|
|
205
234
|
await initAgency(tempDir, "test-template")
|
|
206
235
|
|
|
207
|
-
await createBranch(tempDir, "feature")
|
|
236
|
+
await createBranch(tempDir, "agency--feature")
|
|
208
237
|
const createdAt = new Date().toISOString()
|
|
209
238
|
await writeAgencyMetadata(tempDir, {
|
|
210
239
|
version: 1,
|
|
@@ -234,7 +263,7 @@ describe("tasks command", () => {
|
|
|
234
263
|
const data = JSON.parse(output.trim())
|
|
235
264
|
expect(Array.isArray(data)).toBe(true)
|
|
236
265
|
expect(data.length).toBe(1)
|
|
237
|
-
expect(data[0].branch).toBe("feature")
|
|
266
|
+
expect(data[0].branch).toBe("agency--feature")
|
|
238
267
|
expect(data[0].template).toBe("test-template")
|
|
239
268
|
expect(data[0].baseBranch).toBe("main")
|
|
240
269
|
expect(data[0].createdAt).toBeDefined()
|
|
@@ -255,14 +284,38 @@ describe("tasks command", () => {
|
|
|
255
284
|
expect(Array.isArray(data)).toBe(true)
|
|
256
285
|
expect(data.length).toBe(0)
|
|
257
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
|
+
})
|
|
258
311
|
})
|
|
259
312
|
|
|
260
313
|
describe("edge cases", () => {
|
|
261
314
|
test("handles branches with invalid agency.json gracefully", async () => {
|
|
262
315
|
await initAgency(tempDir, "test-template")
|
|
263
316
|
|
|
264
|
-
// Create branch with invalid agency.json
|
|
265
|
-
await createBranch(tempDir, "invalid")
|
|
317
|
+
// Create branch with invalid agency.json but agency-- prefix
|
|
318
|
+
await createBranch(tempDir, "agency--invalid")
|
|
266
319
|
await Bun.write(join(tempDir, "agency.json"), "{ invalid json }")
|
|
267
320
|
await Bun.spawn(["git", "add", "agency.json"], {
|
|
268
321
|
cwd: tempDir,
|
|
@@ -271,9 +324,9 @@ describe("tasks command", () => {
|
|
|
271
324
|
}).exited
|
|
272
325
|
await createCommit(tempDir, "Add invalid agency.json")
|
|
273
326
|
|
|
274
|
-
// Create branch with valid agency.json
|
|
327
|
+
// Create branch with valid agency.json and agency-- prefix
|
|
275
328
|
await checkoutBranch(tempDir, "main")
|
|
276
|
-
await createBranch(tempDir, "valid")
|
|
329
|
+
await createBranch(tempDir, "agency--valid")
|
|
277
330
|
await writeAgencyMetadata(tempDir, {
|
|
278
331
|
version: 1,
|
|
279
332
|
injectedFiles: [],
|
|
@@ -296,16 +349,18 @@ describe("tasks command", () => {
|
|
|
296
349
|
await runTestEffect(tasks({}))
|
|
297
350
|
|
|
298
351
|
console.log = originalLog
|
|
299
|
-
|
|
300
|
-
expect(output).
|
|
301
|
-
expect(output
|
|
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)
|
|
302
357
|
})
|
|
303
358
|
|
|
304
359
|
test("handles branches with old version agency.json", async () => {
|
|
305
360
|
await initAgency(tempDir, "test-template")
|
|
306
361
|
|
|
307
362
|
// Create branch with version 0 agency.json (future or old version)
|
|
308
|
-
await createBranch(tempDir, "old-version")
|
|
363
|
+
await createBranch(tempDir, "agency--old-version")
|
|
309
364
|
await Bun.write(
|
|
310
365
|
join(tempDir, "agency.json"),
|
|
311
366
|
JSON.stringify({
|
|
@@ -329,7 +384,8 @@ describe("tasks command", () => {
|
|
|
329
384
|
await runTestEffect(tasks({}))
|
|
330
385
|
|
|
331
386
|
console.log = originalLog
|
|
332
|
-
|
|
387
|
+
// Branch should still be listed since it matches the prefix
|
|
388
|
+
expect(output).toContain("agency--old-version")
|
|
333
389
|
})
|
|
334
390
|
})
|
|
335
391
|
})
|
package/src/commands/tasks.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Effect, DateTime } from "effect"
|
|
|
2
2
|
import { Schema } from "@effect/schema"
|
|
3
3
|
import type { BaseCommandOptions } from "../utils/command"
|
|
4
4
|
import { GitService } from "../services/GitService"
|
|
5
|
+
import { ConfigService } from "../services/ConfigService"
|
|
5
6
|
import { AgencyMetadata } from "../schemas"
|
|
6
7
|
import highlight from "../utils/colors"
|
|
7
8
|
import { createLoggers, ensureGitRepo } from "../utils/effect"
|
|
@@ -62,16 +63,32 @@ const parseAgencyMetadata = (content: string) =>
|
|
|
62
63
|
}).pipe(Effect.catchAll(() => Effect.succeed(null)))
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
|
-
*
|
|
66
|
+
* Extract the prefix from a source branch pattern (everything before %branch%).
|
|
67
|
+
*/
|
|
68
|
+
const getSourceBranchPrefix = (pattern: string): string => {
|
|
69
|
+
if (pattern.includes("%branch%")) {
|
|
70
|
+
return pattern.split("%branch%")[0]!
|
|
71
|
+
}
|
|
72
|
+
// If no %branch% placeholder, the whole pattern is the prefix
|
|
73
|
+
return pattern
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Find all branches that match the source branch pattern prefix
|
|
66
78
|
*/
|
|
67
79
|
const findAllTaskBranches = (gitRoot: string) =>
|
|
68
80
|
Effect.gen(function* () {
|
|
69
81
|
const git = yield* GitService
|
|
82
|
+
const configService = yield* ConfigService
|
|
83
|
+
|
|
84
|
+
// Get source branch pattern from config
|
|
85
|
+
const config = yield* configService.loadConfig()
|
|
86
|
+
const prefix = getSourceBranchPrefix(config.sourceBranchPattern)
|
|
70
87
|
|
|
71
|
-
// Get
|
|
72
|
-
const branches = yield* git.
|
|
88
|
+
// Get only branches matching the prefix (fast - single git command)
|
|
89
|
+
const branches = yield* git.getBranchesByPrefix(gitRoot, prefix)
|
|
73
90
|
|
|
74
|
-
// For each branch, try to read agency.json
|
|
91
|
+
// For each matching branch, try to read agency.json for metadata
|
|
75
92
|
const taskBranches: TaskBranchInfo[] = []
|
|
76
93
|
for (const branch of branches) {
|
|
77
94
|
const metadata = yield* readAgencyMetadataFromBranch(gitRoot, branch)
|
|
@@ -85,6 +102,14 @@ const findAllTaskBranches = (gitRoot: string) =>
|
|
|
85
102
|
? DateTime.toDateUtc(metadata.createdAt).toISOString()
|
|
86
103
|
: null,
|
|
87
104
|
})
|
|
105
|
+
} else {
|
|
106
|
+
// Branch matches prefix but has no valid agency.json - still list it
|
|
107
|
+
taskBranches.push({
|
|
108
|
+
branch,
|
|
109
|
+
template: null,
|
|
110
|
+
baseBranch: null,
|
|
111
|
+
createdAt: null,
|
|
112
|
+
})
|
|
88
113
|
}
|
|
89
114
|
}
|
|
90
115
|
|
|
@@ -124,10 +149,8 @@ export const tasks = (options: TasksOptions = {}) =>
|
|
|
124
149
|
export const help = `
|
|
125
150
|
Usage: agency tasks [options]
|
|
126
151
|
|
|
127
|
-
List all source branches that have agency tasks (branches
|
|
128
|
-
|
|
129
|
-
This command searches through all local branches and displays those that have
|
|
130
|
-
been initialized with 'agency task'.
|
|
152
|
+
List all source branches that have agency tasks (branches matching the source
|
|
153
|
+
branch pattern, e.g. "agency--*").
|
|
131
154
|
|
|
132
155
|
Options:
|
|
133
156
|
--json Output as JSON (includes metadata: template, base branch, created date)
|
|
@@ -967,6 +967,38 @@ export class GitService extends Effect.Service<GitService>()("GitService", {
|
|
|
967
967
|
),
|
|
968
968
|
),
|
|
969
969
|
|
|
970
|
+
/**
|
|
971
|
+
* Get local branch names matching a prefix.
|
|
972
|
+
* @param gitRoot - The git repository root
|
|
973
|
+
* @param prefix - The prefix to match (e.g., "agency--")
|
|
974
|
+
* @returns Array of matching local branch names
|
|
975
|
+
*/
|
|
976
|
+
getBranchesByPrefix: (gitRoot: string, prefix: string) =>
|
|
977
|
+
pipe(
|
|
978
|
+
runGitCommand(
|
|
979
|
+
[
|
|
980
|
+
"git",
|
|
981
|
+
"branch",
|
|
982
|
+
"--list",
|
|
983
|
+
`${prefix}*`,
|
|
984
|
+
"--format=%(refname:short)",
|
|
985
|
+
],
|
|
986
|
+
gitRoot,
|
|
987
|
+
),
|
|
988
|
+
Effect.map((result) => {
|
|
989
|
+
if (result.exitCode === 0 && result.stdout.trim()) {
|
|
990
|
+
return result.stdout.trim().split("\n") as readonly string[]
|
|
991
|
+
}
|
|
992
|
+
return [] as readonly string[]
|
|
993
|
+
}),
|
|
994
|
+
Effect.mapError(
|
|
995
|
+
() =>
|
|
996
|
+
new GitError({
|
|
997
|
+
message: `Failed to get branches with prefix "${prefix}"`,
|
|
998
|
+
}),
|
|
999
|
+
),
|
|
1000
|
+
),
|
|
1001
|
+
|
|
970
1002
|
/**
|
|
971
1003
|
* Get branches that have been merged into a target branch.
|
|
972
1004
|
* @param gitRoot - The git repository root
|