@markjaquith/agency 1.6.3 → 1.8.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/cli.ts +24 -2
- package/package.json +1 -1
- package/src/commands/clean.test.ts +2 -2
- package/src/commands/clean.ts +3 -20
- package/src/commands/emit.integration.test.ts +17 -17
- package/src/commands/emit.test.ts +22 -22
- package/src/commands/emit.ts +9 -8
- package/src/commands/emitted.test.ts +8 -8
- package/src/commands/emitted.ts +1 -1
- package/src/commands/merge.integration.test.ts +5 -5
- package/src/commands/merge.test.ts +7 -7
- package/src/commands/pull.test.ts +15 -15
- package/src/commands/push.test.ts +30 -30
- package/src/commands/rebase.test.ts +19 -19
- package/src/commands/rebase.ts +5 -8
- package/src/commands/source.test.ts +13 -13
- package/src/commands/status.test.ts +8 -8
- package/src/commands/status.ts +4 -26
- package/src/commands/switch.test.ts +18 -18
- package/src/commands/switch.ts +3 -3
- package/src/commands/task-branching.test.ts +19 -19
- package/src/commands/task-continue.test.ts +3 -3
- package/src/commands/task-main.test.ts +7 -7
- package/src/commands/task-squash.test.ts +266 -0
- package/src/commands/task.ts +174 -10
- package/src/constants.ts +10 -0
- package/src/schemas.ts +1 -1
- package/src/services/AgencyMetadataService.ts +217 -242
- package/src/services/ConfigService.ts +1 -1
- package/src/services/FormatterService.test.ts +432 -0
- package/src/services/FormatterService.ts +219 -0
- package/src/services/TemplateService.ts +9 -3
- package/src/test-utils.ts +3 -0
- package/src/types.ts +6 -9
- package/src/utils/pr-branch.test.ts +36 -32
- package/src/utils/pr-branch.ts +12 -15
package/cli.ts
CHANGED
|
@@ -54,9 +54,30 @@ async function runCommand<E>(
|
|
|
54
54
|
E,
|
|
55
55
|
never
|
|
56
56
|
>
|
|
57
|
-
|
|
57
|
+
|
|
58
|
+
// Catch typed errors and convert to standard Error objects
|
|
59
|
+
const programWithErrorHandling = Effect.catchAll(providedEffect, (error) => {
|
|
60
|
+
// Convert typed errors to standard Error objects with clear messages
|
|
61
|
+
if (error instanceof Error) {
|
|
62
|
+
return Effect.fail(error)
|
|
63
|
+
}
|
|
64
|
+
// Handle objects with message property (common pattern for tagged errors)
|
|
65
|
+
if (
|
|
66
|
+
typeof error === "object" &&
|
|
67
|
+
error !== null &&
|
|
68
|
+
"message" in error &&
|
|
69
|
+
typeof error.message === "string"
|
|
70
|
+
) {
|
|
71
|
+
return Effect.fail(new Error(error.message))
|
|
72
|
+
}
|
|
73
|
+
// Fallback: convert to string
|
|
74
|
+
return Effect.fail(new Error(String(error)))
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Catch defects (unexpected crashes) and convert to errors
|
|
78
|
+
const program = Effect.catchAllDefect(programWithErrorHandling, (defect) =>
|
|
58
79
|
Effect.fail(defect instanceof Error ? defect : new Error(String(defect))),
|
|
59
|
-
)
|
|
80
|
+
)
|
|
60
81
|
|
|
61
82
|
await Effect.runPromise(program)
|
|
62
83
|
}
|
|
@@ -313,6 +334,7 @@ const commands: Record<string, Command> = {
|
|
|
313
334
|
from: options.from,
|
|
314
335
|
fromCurrent: options["from-current"],
|
|
315
336
|
continue: options.continue,
|
|
337
|
+
squash: options.squash,
|
|
316
338
|
}),
|
|
317
339
|
)
|
|
318
340
|
},
|
package/package.json
CHANGED
|
@@ -87,7 +87,7 @@ describe("clean command", () => {
|
|
|
87
87
|
test("finds source branches for merged emit branches", async () => {
|
|
88
88
|
// Create source branch with agency pattern
|
|
89
89
|
await checkoutBranch(tempDir, "main")
|
|
90
|
-
await Bun.spawn(["git", "checkout", "-b", "agency
|
|
90
|
+
await Bun.spawn(["git", "checkout", "-b", "agency--feature-2"], {
|
|
91
91
|
cwd: tempDir,
|
|
92
92
|
stdout: "pipe",
|
|
93
93
|
stderr: "pipe",
|
|
@@ -132,7 +132,7 @@ describe("clean command", () => {
|
|
|
132
132
|
// Verify both emit and source branches were deleted
|
|
133
133
|
const branches = await getGitOutput(tempDir, ["branch", "--list"])
|
|
134
134
|
expect(branches).not.toContain("feature-2")
|
|
135
|
-
expect(branches).not.toContain("agency
|
|
135
|
+
expect(branches).not.toContain("agency--feature-2")
|
|
136
136
|
expect(branches).toContain("main")
|
|
137
137
|
})
|
|
138
138
|
|
package/src/commands/clean.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Effect } from "effect"
|
|
2
|
-
import { Schema } from "@effect/schema"
|
|
3
2
|
import type { BaseCommandOptions } from "../utils/command"
|
|
4
3
|
import { GitService } from "../services/GitService"
|
|
5
4
|
import { ConfigService } from "../services/ConfigService"
|
|
6
|
-
import {
|
|
5
|
+
import { parseAgencyMetadata } from "../services/AgencyMetadataService"
|
|
7
6
|
import highlight, { done } from "../utils/colors"
|
|
8
7
|
import { createLoggers, ensureGitRepo } from "../utils/effect"
|
|
9
8
|
import { extractCleanBranch, makeSourceBranchName } from "../utils/pr-branch"
|
|
@@ -28,23 +27,7 @@ const readAgencyMetadata = (gitRoot: string, branch: string) =>
|
|
|
28
27
|
return null
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
try: () => JSON.parse(content),
|
|
33
|
-
catch: () => new Error("Failed to parse agency.json"),
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
// Validate version
|
|
37
|
-
if (typeof data.version !== "number" || data.version !== 1) {
|
|
38
|
-
return null
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Parse and validate using Effect schema
|
|
42
|
-
const metadata: AgencyMetadata | null = yield* Effect.try({
|
|
43
|
-
try: () => Schema.decodeUnknownSync(AgencyMetadata)(data),
|
|
44
|
-
catch: () => new Error("Invalid agency.json format"),
|
|
45
|
-
}).pipe(Effect.catchAll(() => Effect.succeed(null)))
|
|
46
|
-
|
|
47
|
-
return metadata
|
|
30
|
+
return yield* parseAgencyMetadata(content)
|
|
48
31
|
}).pipe(Effect.catchAll(() => Effect.succeed(null)))
|
|
49
32
|
|
|
50
33
|
/**
|
|
@@ -179,7 +162,7 @@ export const clean = (options: CleanOptions = {}) =>
|
|
|
179
162
|
|
|
180
163
|
// Load config to get source and emit patterns
|
|
181
164
|
const config = yield* configService.loadConfig()
|
|
182
|
-
const sourcePattern = config.sourceBranchPattern || "agency
|
|
165
|
+
const sourcePattern = config.sourceBranchPattern || "agency--%branch%"
|
|
183
166
|
const emitPattern = config.emitBranch || "%branch%"
|
|
184
167
|
|
|
185
168
|
verboseLog(
|
|
@@ -52,8 +52,8 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
52
52
|
// Initialize git repo with main branch (already includes initial commit)
|
|
53
53
|
await initGitRepo(tempDir)
|
|
54
54
|
|
|
55
|
-
// Create a source branch (with agency
|
|
56
|
-
await createBranch(tempDir, "agency
|
|
55
|
+
// Create a source branch (with agency-- prefix)
|
|
56
|
+
await createBranch(tempDir, "agency--test-feature")
|
|
57
57
|
|
|
58
58
|
// Initialize AGENTS.md and commit in one go
|
|
59
59
|
await initAgency(tempDir, "test")
|
|
@@ -83,7 +83,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
83
83
|
|
|
84
84
|
// Go back to main and create a fresh source branch
|
|
85
85
|
await checkoutBranch(tempDir, "main")
|
|
86
|
-
await createBranch(tempDir, "agency
|
|
86
|
+
await createBranch(tempDir, "agency--feature")
|
|
87
87
|
// Create agency.json with AGENTS.md as managed file
|
|
88
88
|
await Bun.write(
|
|
89
89
|
join(tempDir, "agency.json"),
|
|
@@ -104,7 +104,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
104
104
|
|
|
105
105
|
// Should still be on feature branch
|
|
106
106
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
107
|
-
expect(currentBranch).toBe("agency
|
|
107
|
+
expect(currentBranch).toBe("agency--feature")
|
|
108
108
|
|
|
109
109
|
// Switch to emit branch to verify files
|
|
110
110
|
await checkoutBranch(tempDir, "feature")
|
|
@@ -132,7 +132,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
132
132
|
// Store merge-base before advancing main
|
|
133
133
|
const initialMergeBase = await getGitOutput(tempDir, [
|
|
134
134
|
"merge-base",
|
|
135
|
-
"agency
|
|
135
|
+
"agency--test-feature",
|
|
136
136
|
"main",
|
|
137
137
|
])
|
|
138
138
|
|
|
@@ -141,7 +141,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
141
141
|
|
|
142
142
|
// Should still be on test-feature branch
|
|
143
143
|
let currentBranch = await getCurrentBranch(tempDir)
|
|
144
|
-
expect(currentBranch).toBe("agency
|
|
144
|
+
expect(currentBranch).toBe("agency--test-feature")
|
|
145
145
|
|
|
146
146
|
// Switch to emit branch to verify AGENTS.md is filtered
|
|
147
147
|
await checkoutBranch(tempDir, "test-feature")
|
|
@@ -151,7 +151,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
151
151
|
expect(files).toContain("feature.txt")
|
|
152
152
|
|
|
153
153
|
// Switch back to source branch
|
|
154
|
-
await checkoutBranch(tempDir, "agency
|
|
154
|
+
await checkoutBranch(tempDir, "agency--test-feature")
|
|
155
155
|
|
|
156
156
|
// Simulate advancing main branch with a different file
|
|
157
157
|
await checkoutBranch(tempDir, "main")
|
|
@@ -159,7 +159,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
159
159
|
await addAndCommit(tempDir, "main-file.txt", "Main branch advancement")
|
|
160
160
|
|
|
161
161
|
// Rebase test-feature onto new main
|
|
162
|
-
await checkoutBranch(tempDir, "agency
|
|
162
|
+
await checkoutBranch(tempDir, "agency--test-feature")
|
|
163
163
|
const rebaseProc = Bun.spawn(["git", "rebase", "main"], {
|
|
164
164
|
cwd: tempDir,
|
|
165
165
|
stdout: "pipe",
|
|
@@ -174,7 +174,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
174
174
|
// Verify merge-base has changed after rebase
|
|
175
175
|
const newMergeBase = await getGitOutput(tempDir, [
|
|
176
176
|
"merge-base",
|
|
177
|
-
"agency
|
|
177
|
+
"agency--test-feature",
|
|
178
178
|
"main",
|
|
179
179
|
])
|
|
180
180
|
expect(newMergeBase.trim()).not.toBe(initialMergeBase.trim())
|
|
@@ -184,7 +184,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
184
184
|
|
|
185
185
|
// Should still be on test-feature branch
|
|
186
186
|
currentBranch = await getCurrentBranch(tempDir)
|
|
187
|
-
expect(currentBranch).toBe("agency
|
|
187
|
+
expect(currentBranch).toBe("agency--test-feature")
|
|
188
188
|
|
|
189
189
|
// Switch to emit branch to verify files
|
|
190
190
|
await checkoutBranch(tempDir, "test-feature")
|
|
@@ -222,7 +222,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
222
222
|
await addAndCommit(tempDir, "CLAUDE.md", "Add CLAUDE.md")
|
|
223
223
|
|
|
224
224
|
// Create a new feature branch
|
|
225
|
-
await createBranch(tempDir, "agency
|
|
225
|
+
await createBranch(tempDir, "agency--claude-test")
|
|
226
226
|
|
|
227
227
|
// Initialize agency on this branch - first commit has agency files (NOT including CLAUDE.md)
|
|
228
228
|
await Bun.write(
|
|
@@ -260,7 +260,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
260
260
|
|
|
261
261
|
// Should still be on source branch
|
|
262
262
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
263
|
-
expect(currentBranch).toBe("agency
|
|
263
|
+
expect(currentBranch).toBe("agency--claude-test")
|
|
264
264
|
|
|
265
265
|
// Switch to emit branch to verify CLAUDE.md is reverted to main's version
|
|
266
266
|
await checkoutBranch(tempDir, "claude-test")
|
|
@@ -294,7 +294,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
294
294
|
await Bun.write(join(tempDir, "FILE2.md"), "Original content 2\n")
|
|
295
295
|
await addAndCommit(tempDir, "FILE1.md FILE2.md", "Add files")
|
|
296
296
|
|
|
297
|
-
await createBranch(tempDir, "agency
|
|
297
|
+
await createBranch(tempDir, "agency--contiguous-test")
|
|
298
298
|
|
|
299
299
|
// Create agency.json
|
|
300
300
|
await Bun.write(
|
|
@@ -352,7 +352,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
352
352
|
await Bun.write(join(tempDir, "FILE2.md"), "Original content 2\n")
|
|
353
353
|
await addAndCommit(tempDir, "FILE1.md FILE2.md", "Add files")
|
|
354
354
|
|
|
355
|
-
await createBranch(tempDir, "agency
|
|
355
|
+
await createBranch(tempDir, "agency--non-contiguous-test")
|
|
356
356
|
|
|
357
357
|
await Bun.write(
|
|
358
358
|
join(tempDir, "agency.json"),
|
|
@@ -415,7 +415,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
415
415
|
await Bun.write(join(tempDir, "EXISTING.md"), "Original content\n")
|
|
416
416
|
await addAndCommit(tempDir, "EXISTING.md", "Add existing file")
|
|
417
417
|
|
|
418
|
-
await createBranch(tempDir, "agency
|
|
418
|
+
await createBranch(tempDir, "agency--first-commit-test")
|
|
419
419
|
|
|
420
420
|
// First commit is AGENCY_REMOVE_COMMIT (modifying existing file)
|
|
421
421
|
await Bun.write(join(tempDir, "EXISTING.md"), "Modified content\n")
|
|
@@ -463,7 +463,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
463
463
|
await Bun.write(join(tempDir, "EXISTING.md"), "Original content\n")
|
|
464
464
|
await addAndCommit(tempDir, "EXISTING.md", "Add existing file")
|
|
465
465
|
|
|
466
|
-
await createBranch(tempDir, "agency
|
|
466
|
+
await createBranch(tempDir, "agency--last-commit-test")
|
|
467
467
|
|
|
468
468
|
// agency.json first
|
|
469
469
|
await Bun.write(
|
|
@@ -512,7 +512,7 @@ describe("emit command - integration tests (requires git-filter-repo)", () => {
|
|
|
512
512
|
await Bun.write(join(tempDir, "EXISTING.md"), "Original content\n")
|
|
513
513
|
await addAndCommit(tempDir, "EXISTING.md", "Add existing file")
|
|
514
514
|
|
|
515
|
-
await createBranch(tempDir, "agency
|
|
515
|
+
await createBranch(tempDir, "agency--only-commit-test")
|
|
516
516
|
|
|
517
517
|
// agency.json
|
|
518
518
|
await Bun.write(
|
|
@@ -53,8 +53,8 @@ describe("emit command", () => {
|
|
|
53
53
|
// Initialize git repo with main branch (already includes initial commit)
|
|
54
54
|
await initGitRepo(tempDir)
|
|
55
55
|
|
|
56
|
-
// Create a source branch (with agency
|
|
57
|
-
await createBranch(tempDir, "agency
|
|
56
|
+
// Create a source branch (with agency-- prefix)
|
|
57
|
+
await createBranch(tempDir, "agency--test-feature")
|
|
58
58
|
|
|
59
59
|
// Initialize AGENTS.md and commit in one go
|
|
60
60
|
await initAgency(tempDir, "test")
|
|
@@ -84,7 +84,7 @@ describe("emit command", () => {
|
|
|
84
84
|
return
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
await createBranch(tempDir, "agency
|
|
87
|
+
await createBranch(tempDir, "agency--feature")
|
|
88
88
|
await createCommit(tempDir, "Feature commit")
|
|
89
89
|
|
|
90
90
|
expect(runTestEffect(emit({ silent: true }))).rejects.toThrow(
|
|
@@ -95,7 +95,7 @@ describe("emit command", () => {
|
|
|
95
95
|
test("creates emit branch with default name", async () => {
|
|
96
96
|
// Go back to main and create a fresh source branch (no inherited agency.json)
|
|
97
97
|
await checkoutBranch(tempDir, "main")
|
|
98
|
-
await createBranch(tempDir, "agency
|
|
98
|
+
await createBranch(tempDir, "agency--feature")
|
|
99
99
|
// Create agency.json for this branch
|
|
100
100
|
await Bun.write(
|
|
101
101
|
join(tempDir, "agency.json"),
|
|
@@ -121,11 +121,11 @@ describe("emit command", () => {
|
|
|
121
121
|
|
|
122
122
|
// Check we're still on the source branch
|
|
123
123
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
124
|
-
expect(currentBranch).toBe("agency
|
|
124
|
+
expect(currentBranch).toBe("agency--feature")
|
|
125
125
|
})
|
|
126
126
|
|
|
127
127
|
test("creates emit branch with custom name", async () => {
|
|
128
|
-
await createBranch(tempDir, "agency
|
|
128
|
+
await createBranch(tempDir, "agency--feature")
|
|
129
129
|
await createCommit(tempDir, "Feature commit")
|
|
130
130
|
|
|
131
131
|
// Skip filter for speed - we're just testing branch creation
|
|
@@ -142,11 +142,11 @@ describe("emit command", () => {
|
|
|
142
142
|
|
|
143
143
|
// Check we're still on the source branch
|
|
144
144
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
145
|
-
expect(currentBranch).toBe("agency
|
|
145
|
+
expect(currentBranch).toBe("agency--feature")
|
|
146
146
|
})
|
|
147
147
|
|
|
148
148
|
test("completes emit workflow successfully", async () => {
|
|
149
|
-
await createBranch(tempDir, "agency
|
|
149
|
+
await createBranch(tempDir, "agency--feature")
|
|
150
150
|
await createCommit(tempDir, "Feature commit")
|
|
151
151
|
|
|
152
152
|
// Should complete without throwing (skip filter for speed)
|
|
@@ -154,11 +154,11 @@ describe("emit command", () => {
|
|
|
154
154
|
|
|
155
155
|
// Should still be on source branch
|
|
156
156
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
157
|
-
expect(currentBranch).toBe("agency
|
|
157
|
+
expect(currentBranch).toBe("agency--feature")
|
|
158
158
|
})
|
|
159
159
|
|
|
160
160
|
test("preserves files on source branch after emit", async () => {
|
|
161
|
-
await createBranch(tempDir, "agency
|
|
161
|
+
await createBranch(tempDir, "agency--feature")
|
|
162
162
|
await createCommit(tempDir, "Feature commit")
|
|
163
163
|
|
|
164
164
|
// Skip filter for speed - we're testing source branch preservation
|
|
@@ -166,7 +166,7 @@ describe("emit command", () => {
|
|
|
166
166
|
|
|
167
167
|
// Should still be on source branch
|
|
168
168
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
169
|
-
expect(currentBranch).toBe("agency
|
|
169
|
+
expect(currentBranch).toBe("agency--feature")
|
|
170
170
|
|
|
171
171
|
// Check that test file still exists on source branch
|
|
172
172
|
expect(await fileExists(join(tempDir, "test.txt"))).toBe(true)
|
|
@@ -176,7 +176,7 @@ describe("emit command", () => {
|
|
|
176
176
|
})
|
|
177
177
|
|
|
178
178
|
test("original branch remains untouched", async () => {
|
|
179
|
-
await createBranch(tempDir, "agency
|
|
179
|
+
await createBranch(tempDir, "agency--feature")
|
|
180
180
|
await createCommit(tempDir, "Feature commit")
|
|
181
181
|
|
|
182
182
|
// Create emit branch (skip filter for speed)
|
|
@@ -184,7 +184,7 @@ describe("emit command", () => {
|
|
|
184
184
|
|
|
185
185
|
// Should still be on feature branch
|
|
186
186
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
187
|
-
expect(currentBranch).toBe("agency
|
|
187
|
+
expect(currentBranch).toBe("agency--feature")
|
|
188
188
|
|
|
189
189
|
// Check that AGENTS.md still exist on original branch
|
|
190
190
|
const files = await getGitOutput(tempDir, ["ls-files"])
|
|
@@ -192,7 +192,7 @@ describe("emit command", () => {
|
|
|
192
192
|
})
|
|
193
193
|
|
|
194
194
|
test("works correctly when run multiple times (recreates emit branch)", async () => {
|
|
195
|
-
await createBranch(tempDir, "agency
|
|
195
|
+
await createBranch(tempDir, "agency--feature")
|
|
196
196
|
|
|
197
197
|
// Modify AGENTS.md on feature branch
|
|
198
198
|
await Bun.write(
|
|
@@ -205,7 +205,7 @@ describe("emit command", () => {
|
|
|
205
205
|
await runTestEffect(emit({ silent: true, skipFilter: true }))
|
|
206
206
|
|
|
207
207
|
// Switch back to feature branch
|
|
208
|
-
await checkoutBranch(tempDir, "agency
|
|
208
|
+
await checkoutBranch(tempDir, "agency--feature")
|
|
209
209
|
|
|
210
210
|
// Make another commit
|
|
211
211
|
await createCommit(tempDir, "Another feature commit")
|
|
@@ -215,11 +215,11 @@ describe("emit command", () => {
|
|
|
215
215
|
|
|
216
216
|
// Should complete successfully without interactive prompts and stay on source branch
|
|
217
217
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
218
|
-
expect(currentBranch).toBe("agency
|
|
218
|
+
expect(currentBranch).toBe("agency--feature")
|
|
219
219
|
})
|
|
220
220
|
|
|
221
221
|
test("accepts explicit base branch argument", async () => {
|
|
222
|
-
await createBranch(tempDir, "agency
|
|
222
|
+
await createBranch(tempDir, "agency--feature")
|
|
223
223
|
await createCommit(tempDir, "Feature commit")
|
|
224
224
|
|
|
225
225
|
// Create emit branch with explicit base branch (skip filter for speed)
|
|
@@ -229,11 +229,11 @@ describe("emit command", () => {
|
|
|
229
229
|
|
|
230
230
|
// Should stay on source branch
|
|
231
231
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
232
|
-
expect(currentBranch).toBe("agency
|
|
232
|
+
expect(currentBranch).toBe("agency--feature")
|
|
233
233
|
})
|
|
234
234
|
|
|
235
235
|
test("throws error if provided base branch does not exist", async () => {
|
|
236
|
-
await createBranch(tempDir, "agency
|
|
236
|
+
await createBranch(tempDir, "agency--feature")
|
|
237
237
|
await createCommit(tempDir, "Feature commit")
|
|
238
238
|
|
|
239
239
|
// This should fail even with skipFilter since base branch validation happens first
|
|
@@ -260,7 +260,7 @@ describe("emit command", () => {
|
|
|
260
260
|
|
|
261
261
|
describe("silent mode", () => {
|
|
262
262
|
test("silent flag suppresses output", async () => {
|
|
263
|
-
await createBranch(tempDir, "agency
|
|
263
|
+
await createBranch(tempDir, "agency--feature")
|
|
264
264
|
await createCommit(tempDir, "Feature commit")
|
|
265
265
|
|
|
266
266
|
const logs: string[] = []
|
|
@@ -284,7 +284,7 @@ describe("emit command", () => {
|
|
|
284
284
|
test("constructs correct filter-repo arguments", async () => {
|
|
285
285
|
// Set up fresh branch with agency.json
|
|
286
286
|
await checkoutBranch(tempDir, "main")
|
|
287
|
-
await createBranch(tempDir, "agency
|
|
287
|
+
await createBranch(tempDir, "agency--filter-test")
|
|
288
288
|
|
|
289
289
|
// Create agency.json with injected files
|
|
290
290
|
await Bun.write(
|
|
@@ -329,7 +329,7 @@ describe("emit command", () => {
|
|
|
329
329
|
test("includes symlink targets in files to filter", async () => {
|
|
330
330
|
// Set up fresh branch
|
|
331
331
|
await checkoutBranch(tempDir, "main")
|
|
332
|
-
await createBranch(tempDir, "agency
|
|
332
|
+
await createBranch(tempDir, "agency--symlink-test")
|
|
333
333
|
|
|
334
334
|
// Create AGENTS.md as the real file
|
|
335
335
|
await Bun.write(join(tempDir, "AGENTS.md"), "# Real file\n")
|
package/src/commands/emit.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
withBranchProtection,
|
|
24
24
|
} from "../utils/effect"
|
|
25
25
|
import { withSpinner } from "../utils/spinner"
|
|
26
|
+
import { AGENCY_REMOVE_COMMIT } from "../constants"
|
|
26
27
|
|
|
27
28
|
interface EmitOptions extends BaseCommandOptions {
|
|
28
29
|
emit?: string
|
|
@@ -42,7 +43,7 @@ export const emit = (options: EmitOptions = {}) =>
|
|
|
42
43
|
yield* withBranchProtection(gitRoot, emitCore(gitRoot, options))
|
|
43
44
|
})
|
|
44
45
|
|
|
45
|
-
const emitCore = (gitRoot: string, options: EmitOptions) =>
|
|
46
|
+
export const emitCore = (gitRoot: string, options: EmitOptions) =>
|
|
46
47
|
Effect.gen(function* () {
|
|
47
48
|
const { force = false, verbose = false, skipFilter = false } = options
|
|
48
49
|
const { log, verboseLog } = createLoggers(options)
|
|
@@ -207,7 +208,7 @@ const emitCore = (gitRoot: string, options: EmitOptions) =>
|
|
|
207
208
|
"--commit-callback",
|
|
208
209
|
// Clear file changes from commits with AGENCY_REMOVE_COMMIT marker
|
|
209
210
|
// This makes the commit empty (which gets pruned) while preserving the tree state
|
|
210
|
-
`if b"AGENCY_REMOVE_COMMIT" in commit.message: commit.file_changes = []`,
|
|
211
|
+
`if b"${AGENCY_REMOVE_COMMIT}" in commit.message: commit.file_changes = []`,
|
|
211
212
|
"--force",
|
|
212
213
|
"--refs",
|
|
213
214
|
`${forkPoint}..${emitBranchName}`,
|
|
@@ -433,7 +434,7 @@ Create an emit branch from the current source branch with backpack files (AGENTS
|
|
|
433
434
|
reverted to their state on the base branch.
|
|
434
435
|
|
|
435
436
|
Source and Emit Branches:
|
|
436
|
-
- Source branches: Your working branches with agency-specific files (e.g., agency
|
|
437
|
+
- Source branches: Your working branches with agency-specific files (e.g., agency--feature-foo)
|
|
437
438
|
- Emit branches: Clean branches suitable for PRs without agency files (e.g., feature-foo)
|
|
438
439
|
|
|
439
440
|
This command creates a clean emit branch from your source branch by filtering out
|
|
@@ -472,20 +473,20 @@ Options:
|
|
|
472
473
|
Configuration:
|
|
473
474
|
~/.config/agency/agency.json can contain:
|
|
474
475
|
{
|
|
475
|
-
"sourceBranchPattern": "agency
|
|
476
|
+
"sourceBranchPattern": "agency--%branch%", // Pattern for source branch names
|
|
476
477
|
"emitBranch": "%branch%" // Pattern for emit branch names
|
|
477
478
|
}
|
|
478
479
|
|
|
479
480
|
Use %branch% as placeholder for the clean branch name.
|
|
480
481
|
|
|
481
482
|
Source Pattern Examples:
|
|
482
|
-
"agency
|
|
483
|
+
"agency--%branch%" -> main becomes agency--main (default)
|
|
483
484
|
"wip/%branch%" -> feature becomes wip/feature
|
|
484
485
|
|
|
485
486
|
Emit Pattern Examples:
|
|
486
|
-
"%branch%" -> agency
|
|
487
|
-
"%branch%--PR" -> agency
|
|
488
|
-
"PR/%branch%" -> agency
|
|
487
|
+
"%branch%" -> agency--main emits to main (default)
|
|
488
|
+
"%branch%--PR" -> agency--feature emits to feature--PR
|
|
489
|
+
"PR/%branch%" -> agency--feature emits to PR/feature
|
|
489
490
|
|
|
490
491
|
Examples:
|
|
491
492
|
agency emit # Prompt for base branch (first time) or use saved
|
|
@@ -54,8 +54,8 @@ describe("emitted command", () => {
|
|
|
54
54
|
|
|
55
55
|
describe("basic functionality", () => {
|
|
56
56
|
test("returns emit branch name when on source branch", async () => {
|
|
57
|
-
// Create source branch (agency
|
|
58
|
-
await createBranch(tempDir, "agency
|
|
57
|
+
// Create source branch (agency--main)
|
|
58
|
+
await createBranch(tempDir, "agency--main")
|
|
59
59
|
await createCommit(tempDir, "Work on source")
|
|
60
60
|
|
|
61
61
|
// Capture output
|
|
@@ -73,7 +73,7 @@ describe("emitted command", () => {
|
|
|
73
73
|
|
|
74
74
|
test("returns emit branch name when on emit branch", async () => {
|
|
75
75
|
// Create source branch
|
|
76
|
-
await createBranch(tempDir, "agency
|
|
76
|
+
await createBranch(tempDir, "agency--main")
|
|
77
77
|
await createCommit(tempDir, "Work on source")
|
|
78
78
|
|
|
79
79
|
// Switch to emit branch (main)
|
|
@@ -98,14 +98,14 @@ describe("emitted command", () => {
|
|
|
98
98
|
await Bun.write(
|
|
99
99
|
configPath,
|
|
100
100
|
JSON.stringify({
|
|
101
|
-
sourceBranchPattern: "agency
|
|
101
|
+
sourceBranchPattern: "agency--%branch%",
|
|
102
102
|
emitBranch: "%branch%--PR",
|
|
103
103
|
}),
|
|
104
104
|
)
|
|
105
105
|
process.env.AGENCY_CONFIG_PATH = configPath
|
|
106
106
|
|
|
107
107
|
// Create source branch
|
|
108
|
-
await createBranch(tempDir, "agency
|
|
108
|
+
await createBranch(tempDir, "agency--feature")
|
|
109
109
|
await createCommit(tempDir, "Feature work")
|
|
110
110
|
|
|
111
111
|
// Capture output
|
|
@@ -123,7 +123,7 @@ describe("emitted command", () => {
|
|
|
123
123
|
|
|
124
124
|
test("returns emit branch from agency.json when present", async () => {
|
|
125
125
|
// Create source branch
|
|
126
|
-
await createBranch(tempDir, "agency
|
|
126
|
+
await createBranch(tempDir, "agency--feature")
|
|
127
127
|
|
|
128
128
|
// Create agency.json with custom emitBranch
|
|
129
129
|
const agencyJsonPath = join(tempDir, "agency.json")
|
|
@@ -181,7 +181,7 @@ describe("emitted command", () => {
|
|
|
181
181
|
|
|
182
182
|
describe("silent mode", () => {
|
|
183
183
|
test("still outputs when not in silent mode", async () => {
|
|
184
|
-
await createBranch(tempDir, "agency
|
|
184
|
+
await createBranch(tempDir, "agency--main")
|
|
185
185
|
|
|
186
186
|
const originalLog = console.log
|
|
187
187
|
let logCalled = false
|
|
@@ -196,7 +196,7 @@ describe("emitted command", () => {
|
|
|
196
196
|
})
|
|
197
197
|
|
|
198
198
|
test("silent flag suppresses output", async () => {
|
|
199
|
-
await createBranch(tempDir, "agency
|
|
199
|
+
await createBranch(tempDir, "agency--main")
|
|
200
200
|
|
|
201
201
|
const originalLog = console.log
|
|
202
202
|
let logCalled = false
|
package/src/commands/emitted.ts
CHANGED
|
@@ -41,7 +41,7 @@ Usage: agency emitted [options]
|
|
|
41
41
|
Get the name of the emitted branch (or what it would be).
|
|
42
42
|
|
|
43
43
|
This command shows the emit branch name corresponding to your current branch:
|
|
44
|
-
- If on a source branch (e.g., agency
|
|
44
|
+
- If on a source branch (e.g., agency--main), shows the emit branch (e.g., main)
|
|
45
45
|
- If on an emit branch (e.g., main), shows the current branch name
|
|
46
46
|
|
|
47
47
|
This is useful for scripting and automation where you need to know
|
|
@@ -58,8 +58,8 @@ describe("merge command - integration tests (requires git-filter-repo)", () => {
|
|
|
58
58
|
// Set up origin for git-filter-repo
|
|
59
59
|
await setupRemote(tempDir, "origin", tempDir)
|
|
60
60
|
|
|
61
|
-
// Create a source branch (with agency
|
|
62
|
-
await createBranch(tempDir, "agency
|
|
61
|
+
// Create a source branch (with agency-- prefix per new default config)
|
|
62
|
+
await createBranch(tempDir, "agency--feature")
|
|
63
63
|
|
|
64
64
|
// Initialize AGENTS.md on feature branch
|
|
65
65
|
await initAgency(tempDir, "test")
|
|
@@ -102,7 +102,7 @@ describe("merge command - integration tests (requires git-filter-repo)", () => {
|
|
|
102
102
|
|
|
103
103
|
// We're on feature branch (source)
|
|
104
104
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
105
|
-
expect(currentBranch).toBe("agency
|
|
105
|
+
expect(currentBranch).toBe("agency--feature")
|
|
106
106
|
|
|
107
107
|
// Run merge - should create feature--PR and merge it to main
|
|
108
108
|
await runTestEffect(merge({ silent: true }))
|
|
@@ -130,7 +130,7 @@ describe("merge command - integration tests (requires git-filter-repo)", () => {
|
|
|
130
130
|
await runTestEffect(emit({ silent: true }))
|
|
131
131
|
|
|
132
132
|
// Go back to feature branch
|
|
133
|
-
await checkoutBranch(tempDir, "agency
|
|
133
|
+
await checkoutBranch(tempDir, "agency--feature")
|
|
134
134
|
|
|
135
135
|
// Make additional changes
|
|
136
136
|
await createCommit(tempDir, "More feature work")
|
|
@@ -186,7 +186,7 @@ describe("merge command - integration tests (requires git-filter-repo)", () => {
|
|
|
186
186
|
await checkoutBranch(tempDir, "feature")
|
|
187
187
|
|
|
188
188
|
// Delete the source branch
|
|
189
|
-
await deleteBranch(tempDir, "agency
|
|
189
|
+
await deleteBranch(tempDir, "agency--feature", true)
|
|
190
190
|
|
|
191
191
|
// Try to merge - should fail (error message may vary since source branch is deleted)
|
|
192
192
|
await expect(runTestEffect(merge({ silent: true }))).rejects.toThrow()
|
|
@@ -39,8 +39,8 @@ describe("merge command", () => {
|
|
|
39
39
|
// Set up origin for git-filter-repo
|
|
40
40
|
await setupRemote(tempDir, "origin", tempDir)
|
|
41
41
|
|
|
42
|
-
// Create a source branch (with agency
|
|
43
|
-
await createBranch(tempDir, "agency
|
|
42
|
+
// Create a source branch (with agency-- prefix per new default config)
|
|
43
|
+
await createBranch(tempDir, "agency--feature")
|
|
44
44
|
|
|
45
45
|
// Initialize AGENTS.md on feature branch
|
|
46
46
|
await initAgency(tempDir, "test")
|
|
@@ -119,7 +119,7 @@ describe("merge command", () => {
|
|
|
119
119
|
test("performs squash merge when --squash flag is set", async () => {
|
|
120
120
|
// We're on feature branch (source)
|
|
121
121
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
122
|
-
expect(currentBranch).toBe("agency
|
|
122
|
+
expect(currentBranch).toBe("agency--feature")
|
|
123
123
|
|
|
124
124
|
// Run merge with squash flag (skipFilter for speed, we're testing squash behavior)
|
|
125
125
|
await runTestEffect(
|
|
@@ -146,7 +146,7 @@ describe("merge command", () => {
|
|
|
146
146
|
test("performs regular merge when --squash flag is not set", async () => {
|
|
147
147
|
// We're on feature branch (source)
|
|
148
148
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
149
|
-
expect(currentBranch).toBe("agency
|
|
149
|
+
expect(currentBranch).toBe("agency--feature")
|
|
150
150
|
|
|
151
151
|
// Run merge without squash flag (skipFilter for speed, we're testing merge behavior)
|
|
152
152
|
await runTestEffect(
|
|
@@ -175,7 +175,7 @@ describe("merge command", () => {
|
|
|
175
175
|
test("pushes base branch to origin when --push flag is set", async () => {
|
|
176
176
|
// We're on feature branch (source)
|
|
177
177
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
178
|
-
expect(currentBranch).toBe("agency
|
|
178
|
+
expect(currentBranch).toBe("agency--feature")
|
|
179
179
|
|
|
180
180
|
// Get the current commit on main before merge
|
|
181
181
|
await checkoutBranch(tempDir, "main")
|
|
@@ -184,7 +184,7 @@ describe("merge command", () => {
|
|
|
184
184
|
).trim()
|
|
185
185
|
|
|
186
186
|
// Go back to feature branch
|
|
187
|
-
await checkoutBranch(tempDir, "agency
|
|
187
|
+
await checkoutBranch(tempDir, "agency--feature")
|
|
188
188
|
|
|
189
189
|
// Run merge with push flag (skipFilter for speed, we're testing push behavior)
|
|
190
190
|
await runTestEffect(merge({ silent: true, push: true, skipFilter: true }))
|
|
@@ -213,7 +213,7 @@ describe("merge command", () => {
|
|
|
213
213
|
test("does not push when --push flag is not set", async () => {
|
|
214
214
|
// We're on feature branch (source)
|
|
215
215
|
const currentBranch = await getCurrentBranch(tempDir)
|
|
216
|
-
expect(currentBranch).toBe("agency
|
|
216
|
+
expect(currentBranch).toBe("agency--feature")
|
|
217
217
|
|
|
218
218
|
// Get the current commit on origin/main before merge
|
|
219
219
|
const beforeOriginCommit = (
|