@markjaquith/agency 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +109 -0
- package/cli.ts +569 -0
- package/index.ts +1 -0
- package/package.json +65 -0
- package/src/commands/base.test.ts +198 -0
- package/src/commands/base.ts +198 -0
- package/src/commands/clean.test.ts +299 -0
- package/src/commands/clean.ts +320 -0
- package/src/commands/emit.test.ts +412 -0
- package/src/commands/emit.ts +521 -0
- package/src/commands/emitted.test.ts +226 -0
- package/src/commands/emitted.ts +57 -0
- package/src/commands/init.test.ts +311 -0
- package/src/commands/init.ts +140 -0
- package/src/commands/merge.test.ts +365 -0
- package/src/commands/merge.ts +253 -0
- package/src/commands/pull.test.ts +385 -0
- package/src/commands/pull.ts +205 -0
- package/src/commands/push.test.ts +394 -0
- package/src/commands/push.ts +346 -0
- package/src/commands/save.test.ts +247 -0
- package/src/commands/save.ts +162 -0
- package/src/commands/source.test.ts +195 -0
- package/src/commands/source.ts +72 -0
- package/src/commands/status.test.ts +489 -0
- package/src/commands/status.ts +258 -0
- package/src/commands/switch.test.ts +194 -0
- package/src/commands/switch.ts +84 -0
- package/src/commands/task-branching.test.ts +334 -0
- package/src/commands/task-edit.test.ts +141 -0
- package/src/commands/task-main.test.ts +872 -0
- package/src/commands/task.ts +712 -0
- package/src/commands/tasks.test.ts +335 -0
- package/src/commands/tasks.ts +155 -0
- package/src/commands/template-delete.test.ts +178 -0
- package/src/commands/template-delete.ts +98 -0
- package/src/commands/template-list.test.ts +135 -0
- package/src/commands/template-list.ts +87 -0
- package/src/commands/template-view.test.ts +158 -0
- package/src/commands/template-view.ts +86 -0
- package/src/commands/template.test.ts +32 -0
- package/src/commands/template.ts +96 -0
- package/src/commands/use.test.ts +87 -0
- package/src/commands/use.ts +97 -0
- package/src/commands/work.test.ts +462 -0
- package/src/commands/work.ts +193 -0
- package/src/errors.ts +17 -0
- package/src/schemas.ts +33 -0
- package/src/services/AgencyMetadataService.ts +287 -0
- package/src/services/ClaudeService.test.ts +184 -0
- package/src/services/ClaudeService.ts +91 -0
- package/src/services/ConfigService.ts +115 -0
- package/src/services/FileSystemService.ts +222 -0
- package/src/services/GitService.ts +751 -0
- package/src/services/OpencodeService.ts +263 -0
- package/src/services/PromptService.ts +183 -0
- package/src/services/TemplateService.ts +75 -0
- package/src/test-utils.ts +362 -0
- package/src/types/native-exec.d.ts +8 -0
- package/src/types.ts +216 -0
- package/src/utils/colors.ts +178 -0
- package/src/utils/command.ts +17 -0
- package/src/utils/effect.ts +281 -0
- package/src/utils/exec.ts +48 -0
- package/src/utils/paths.ts +51 -0
- package/src/utils/pr-branch.test.ts +372 -0
- package/src/utils/pr-branch.ts +473 -0
- package/src/utils/process.ts +110 -0
- package/src/utils/spinner.ts +82 -0
- package/templates/AGENCY.md +20 -0
- package/templates/AGENTS.md +11 -0
- package/templates/CLAUDE.md +3 -0
- package/templates/TASK.md +5 -0
- package/templates/opencode.json +4 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { test, expect, describe, beforeEach, afterEach } from "bun:test"
|
|
2
|
+
import { join } from "path"
|
|
3
|
+
import { emitted } from "./emitted"
|
|
4
|
+
import {
|
|
5
|
+
createTempDir,
|
|
6
|
+
cleanupTempDir,
|
|
7
|
+
initGitRepo,
|
|
8
|
+
getCurrentBranch,
|
|
9
|
+
createCommit,
|
|
10
|
+
checkoutBranch,
|
|
11
|
+
runTestEffect,
|
|
12
|
+
} from "../test-utils"
|
|
13
|
+
|
|
14
|
+
async function createBranch(cwd: string, branchName: string): Promise<void> {
|
|
15
|
+
await Bun.spawn(["git", "checkout", "-b", branchName], {
|
|
16
|
+
cwd,
|
|
17
|
+
stdout: "pipe",
|
|
18
|
+
stderr: "pipe",
|
|
19
|
+
}).exited
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("emitted command", () => {
|
|
23
|
+
let tempDir: string
|
|
24
|
+
let originalCwd: string
|
|
25
|
+
|
|
26
|
+
beforeEach(async () => {
|
|
27
|
+
tempDir = await createTempDir()
|
|
28
|
+
originalCwd = process.cwd()
|
|
29
|
+
process.chdir(tempDir)
|
|
30
|
+
|
|
31
|
+
// Set config path to non-existent file to use defaults
|
|
32
|
+
process.env.AGENCY_CONFIG_PATH = join(tempDir, "non-existent-config.json")
|
|
33
|
+
|
|
34
|
+
// Initialize git repo
|
|
35
|
+
await initGitRepo(tempDir)
|
|
36
|
+
await createCommit(tempDir, "Initial commit")
|
|
37
|
+
|
|
38
|
+
// Rename to main if needed
|
|
39
|
+
const currentBranch = await getCurrentBranch(tempDir)
|
|
40
|
+
if (currentBranch === "master") {
|
|
41
|
+
await Bun.spawn(["git", "branch", "-m", "main"], {
|
|
42
|
+
cwd: tempDir,
|
|
43
|
+
stdout: "pipe",
|
|
44
|
+
stderr: "pipe",
|
|
45
|
+
}).exited
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
afterEach(async () => {
|
|
50
|
+
process.chdir(originalCwd)
|
|
51
|
+
delete process.env.AGENCY_CONFIG_PATH
|
|
52
|
+
await cleanupTempDir(tempDir)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe("basic functionality", () => {
|
|
56
|
+
test("returns emit branch name when on source branch", async () => {
|
|
57
|
+
// Create source branch (agency/main)
|
|
58
|
+
await createBranch(tempDir, "agency/main")
|
|
59
|
+
await createCommit(tempDir, "Work on source")
|
|
60
|
+
|
|
61
|
+
// Capture output
|
|
62
|
+
const originalLog = console.log
|
|
63
|
+
let capturedOutput = ""
|
|
64
|
+
console.log = (msg: string) => {
|
|
65
|
+
capturedOutput = msg
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await runTestEffect(emitted({ silent: false }))
|
|
69
|
+
|
|
70
|
+
console.log = originalLog
|
|
71
|
+
expect(capturedOutput).toBe("main")
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test("returns emit branch name when on emit branch", async () => {
|
|
75
|
+
// Create source branch
|
|
76
|
+
await createBranch(tempDir, "agency/main")
|
|
77
|
+
await createCommit(tempDir, "Work on source")
|
|
78
|
+
|
|
79
|
+
// Switch to emit branch (main)
|
|
80
|
+
await checkoutBranch(tempDir, "main")
|
|
81
|
+
|
|
82
|
+
// Capture output
|
|
83
|
+
const originalLog = console.log
|
|
84
|
+
let capturedOutput = ""
|
|
85
|
+
console.log = (msg: string) => {
|
|
86
|
+
capturedOutput = msg
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await runTestEffect(emitted({ silent: false }))
|
|
90
|
+
|
|
91
|
+
console.log = originalLog
|
|
92
|
+
expect(capturedOutput).toBe("main")
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test("returns emit branch with custom pattern", async () => {
|
|
96
|
+
// Create custom config
|
|
97
|
+
const configPath = join(tempDir, "custom-config.json")
|
|
98
|
+
await Bun.write(
|
|
99
|
+
configPath,
|
|
100
|
+
JSON.stringify({
|
|
101
|
+
sourceBranchPattern: "agency/%branch%",
|
|
102
|
+
emitBranch: "%branch%--PR",
|
|
103
|
+
}),
|
|
104
|
+
)
|
|
105
|
+
process.env.AGENCY_CONFIG_PATH = configPath
|
|
106
|
+
|
|
107
|
+
// Create source branch
|
|
108
|
+
await createBranch(tempDir, "agency/feature")
|
|
109
|
+
await createCommit(tempDir, "Feature work")
|
|
110
|
+
|
|
111
|
+
// Capture output
|
|
112
|
+
const originalLog = console.log
|
|
113
|
+
let capturedOutput = ""
|
|
114
|
+
console.log = (msg: string) => {
|
|
115
|
+
capturedOutput = msg
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await runTestEffect(emitted({ silent: false }))
|
|
119
|
+
|
|
120
|
+
console.log = originalLog
|
|
121
|
+
expect(capturedOutput).toBe("feature--PR")
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test("returns emit branch from agency.json when present", async () => {
|
|
125
|
+
// Create source branch
|
|
126
|
+
await createBranch(tempDir, "agency/feature")
|
|
127
|
+
|
|
128
|
+
// Create agency.json with custom emitBranch
|
|
129
|
+
const agencyJsonPath = join(tempDir, "agency.json")
|
|
130
|
+
await Bun.write(
|
|
131
|
+
agencyJsonPath,
|
|
132
|
+
JSON.stringify({
|
|
133
|
+
version: 1,
|
|
134
|
+
injectedFiles: [],
|
|
135
|
+
template: "test-template",
|
|
136
|
+
createdAt: new Date().toISOString(),
|
|
137
|
+
emitBranch: "custom-emit-name",
|
|
138
|
+
}),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
// Commit the agency.json file so it's available on the branch
|
|
142
|
+
await Bun.spawn(["git", "add", "agency.json"], {
|
|
143
|
+
cwd: tempDir,
|
|
144
|
+
stdout: "pipe",
|
|
145
|
+
stderr: "pipe",
|
|
146
|
+
}).exited
|
|
147
|
+
await createCommit(tempDir, "Add agency.json")
|
|
148
|
+
|
|
149
|
+
// Capture output
|
|
150
|
+
const originalLog = console.log
|
|
151
|
+
let capturedOutput = ""
|
|
152
|
+
console.log = (msg: string) => {
|
|
153
|
+
capturedOutput = msg
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await runTestEffect(emitted({ silent: false }))
|
|
157
|
+
|
|
158
|
+
console.log = originalLog
|
|
159
|
+
expect(capturedOutput).toBe("custom-emit-name")
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test("works with legacy branch names", async () => {
|
|
163
|
+
// On a branch that doesn't match the source pattern
|
|
164
|
+
// Should treat it as a legacy branch and apply emit pattern
|
|
165
|
+
await createBranch(tempDir, "feature-foo")
|
|
166
|
+
|
|
167
|
+
// Capture output
|
|
168
|
+
const originalLog = console.log
|
|
169
|
+
let capturedOutput = ""
|
|
170
|
+
console.log = (msg: string) => {
|
|
171
|
+
capturedOutput = msg
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await runTestEffect(emitted({ silent: false }))
|
|
175
|
+
|
|
176
|
+
console.log = originalLog
|
|
177
|
+
// With default pattern "%branch%", emit should be the same as clean branch
|
|
178
|
+
expect(capturedOutput).toBe("feature-foo")
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
describe("silent mode", () => {
|
|
183
|
+
test("still outputs when not in silent mode", async () => {
|
|
184
|
+
await createBranch(tempDir, "agency/main")
|
|
185
|
+
|
|
186
|
+
const originalLog = console.log
|
|
187
|
+
let logCalled = false
|
|
188
|
+
console.log = () => {
|
|
189
|
+
logCalled = true
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
await runTestEffect(emitted({ silent: false }))
|
|
193
|
+
|
|
194
|
+
console.log = originalLog
|
|
195
|
+
expect(logCalled).toBe(true)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test("silent flag suppresses output", async () => {
|
|
199
|
+
await createBranch(tempDir, "agency/main")
|
|
200
|
+
|
|
201
|
+
const originalLog = console.log
|
|
202
|
+
let logCalled = false
|
|
203
|
+
console.log = () => {
|
|
204
|
+
logCalled = true
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
await runTestEffect(emitted({ silent: true }))
|
|
208
|
+
|
|
209
|
+
console.log = originalLog
|
|
210
|
+
expect(logCalled).toBe(false)
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
describe("error handling", () => {
|
|
215
|
+
test("throws error when not in a git repository", async () => {
|
|
216
|
+
const nonGitDir = await createTempDir()
|
|
217
|
+
process.chdir(nonGitDir)
|
|
218
|
+
|
|
219
|
+
await expect(runTestEffect(emitted({ silent: true }))).rejects.toThrow(
|
|
220
|
+
"Not in a git repository",
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
await cleanupTempDir(nonGitDir)
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
})
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Effect } from "effect"
|
|
2
|
+
import type { BaseCommandOptions } from "../utils/command"
|
|
3
|
+
import { GitService } from "../services/GitService"
|
|
4
|
+
import { ConfigService } from "../services/ConfigService"
|
|
5
|
+
import {
|
|
6
|
+
resolveBranchPairWithAgencyJson,
|
|
7
|
+
type BranchPair,
|
|
8
|
+
} from "../utils/pr-branch"
|
|
9
|
+
import { createLoggers, ensureGitRepo } from "../utils/effect"
|
|
10
|
+
|
|
11
|
+
interface EmittedOptions extends BaseCommandOptions {}
|
|
12
|
+
|
|
13
|
+
export const emitted = (options: EmittedOptions = {}) =>
|
|
14
|
+
Effect.gen(function* () {
|
|
15
|
+
const { log } = createLoggers(options)
|
|
16
|
+
|
|
17
|
+
const git = yield* GitService
|
|
18
|
+
const configService = yield* ConfigService
|
|
19
|
+
|
|
20
|
+
const gitRoot = yield* ensureGitRepo()
|
|
21
|
+
|
|
22
|
+
// Load config
|
|
23
|
+
const config = yield* configService.loadConfig()
|
|
24
|
+
|
|
25
|
+
// Get current branch and resolve the branch pair
|
|
26
|
+
const currentBranch = yield* git.getCurrentBranch(gitRoot)
|
|
27
|
+
const branches: BranchPair = yield* resolveBranchPairWithAgencyJson(
|
|
28
|
+
gitRoot,
|
|
29
|
+
currentBranch,
|
|
30
|
+
config.sourceBranchPattern,
|
|
31
|
+
config.emitBranch,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
// Return the emit branch name (whether we're on it or not)
|
|
35
|
+
log(branches.emitBranch)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export const help = `
|
|
39
|
+
Usage: agency emitted [options]
|
|
40
|
+
|
|
41
|
+
Get the name of the emitted branch (or what it would be).
|
|
42
|
+
|
|
43
|
+
This command shows the emit branch name corresponding to your current branch:
|
|
44
|
+
- If on a source branch (e.g., agency/main), shows the emit branch (e.g., main)
|
|
45
|
+
- If on an emit branch (e.g., main), shows the current branch name
|
|
46
|
+
|
|
47
|
+
This is useful for scripting and automation where you need to know
|
|
48
|
+
the emit branch name without actually creating or switching to it.
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
agency emitted # Show the emit branch name
|
|
52
|
+
|
|
53
|
+
Notes:
|
|
54
|
+
- Does not require the emit branch to exist
|
|
55
|
+
- Uses source and emit patterns from ~/.config/agency/agency.json
|
|
56
|
+
- Respects emitBranch field in agency.json when present
|
|
57
|
+
`
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { test, expect, describe } from "bun:test"
|
|
2
|
+
import { mkdtemp, rm } from "fs/promises"
|
|
3
|
+
import { tmpdir } from "os"
|
|
4
|
+
import { join } from "path"
|
|
5
|
+
import { init } from "./init"
|
|
6
|
+
import { initGitRepo, getGitConfig, runTestEffect } from "../test-utils"
|
|
7
|
+
|
|
8
|
+
describe("init command", () => {
|
|
9
|
+
test("initializes with template flag", async () => {
|
|
10
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
11
|
+
try {
|
|
12
|
+
await initGitRepo(tempDir)
|
|
13
|
+
await runTestEffect(
|
|
14
|
+
init({ template: "test-template", silent: true, cwd: tempDir }),
|
|
15
|
+
)
|
|
16
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe(
|
|
17
|
+
"test-template",
|
|
18
|
+
)
|
|
19
|
+
} finally {
|
|
20
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test("saves template name to git config", async () => {
|
|
25
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
26
|
+
try {
|
|
27
|
+
await initGitRepo(tempDir)
|
|
28
|
+
await runTestEffect(
|
|
29
|
+
init({ template: "my-template", silent: true, cwd: tempDir }),
|
|
30
|
+
)
|
|
31
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe("my-template")
|
|
32
|
+
} finally {
|
|
33
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test("accepts template names with hyphens", async () => {
|
|
38
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
39
|
+
try {
|
|
40
|
+
await initGitRepo(tempDir)
|
|
41
|
+
await runTestEffect(
|
|
42
|
+
init({ template: "my-work-template", silent: true, cwd: tempDir }),
|
|
43
|
+
)
|
|
44
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe(
|
|
45
|
+
"my-work-template",
|
|
46
|
+
)
|
|
47
|
+
} finally {
|
|
48
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test("accepts template names with underscores", async () => {
|
|
53
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
54
|
+
try {
|
|
55
|
+
await initGitRepo(tempDir)
|
|
56
|
+
await runTestEffect(
|
|
57
|
+
init({ template: "my_work_template", silent: true, cwd: tempDir }),
|
|
58
|
+
)
|
|
59
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe(
|
|
60
|
+
"my_work_template",
|
|
61
|
+
)
|
|
62
|
+
} finally {
|
|
63
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test("throws error when not in git repository", async () => {
|
|
68
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
69
|
+
try {
|
|
70
|
+
await expect(
|
|
71
|
+
runTestEffect(init({ template: "test", silent: true, cwd: tempDir })),
|
|
72
|
+
).rejects.toThrow("Not in a git repository")
|
|
73
|
+
} finally {
|
|
74
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test("throws error when already initialized without template flag", async () => {
|
|
79
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
80
|
+
try {
|
|
81
|
+
await initGitRepo(tempDir)
|
|
82
|
+
await runTestEffect(
|
|
83
|
+
init({ template: "first-template", silent: true, cwd: tempDir }),
|
|
84
|
+
)
|
|
85
|
+
await expect(
|
|
86
|
+
runTestEffect(init({ silent: true, cwd: tempDir })),
|
|
87
|
+
).rejects.toThrow("Already initialized")
|
|
88
|
+
} finally {
|
|
89
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test("allows re-initialization with different template", async () => {
|
|
94
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
95
|
+
try {
|
|
96
|
+
await initGitRepo(tempDir)
|
|
97
|
+
await runTestEffect(
|
|
98
|
+
init({ template: "first-template", silent: true, cwd: tempDir }),
|
|
99
|
+
)
|
|
100
|
+
await runTestEffect(
|
|
101
|
+
init({ template: "second-template", silent: true, cwd: tempDir }),
|
|
102
|
+
)
|
|
103
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe(
|
|
104
|
+
"second-template",
|
|
105
|
+
)
|
|
106
|
+
} finally {
|
|
107
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test("requires template name in silent mode", async () => {
|
|
112
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
113
|
+
try {
|
|
114
|
+
await initGitRepo(tempDir)
|
|
115
|
+
await expect(
|
|
116
|
+
runTestEffect(init({ silent: true, cwd: tempDir })),
|
|
117
|
+
).rejects.toThrow("Template name required")
|
|
118
|
+
} finally {
|
|
119
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test("sets config even if template directory does not exist", async () => {
|
|
124
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
125
|
+
try {
|
|
126
|
+
await initGitRepo(tempDir)
|
|
127
|
+
await runTestEffect(
|
|
128
|
+
init({ template: "nonexistent", silent: true, cwd: tempDir }),
|
|
129
|
+
)
|
|
130
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe("nonexistent")
|
|
131
|
+
} finally {
|
|
132
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
test("works without output in silent mode", async () => {
|
|
137
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
138
|
+
try {
|
|
139
|
+
await initGitRepo(tempDir)
|
|
140
|
+
await runTestEffect(
|
|
141
|
+
init({ template: "silent-test", silent: true, cwd: tempDir }),
|
|
142
|
+
)
|
|
143
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe("silent-test")
|
|
144
|
+
} finally {
|
|
145
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test("fails in silent mode without template", async () => {
|
|
150
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
151
|
+
try {
|
|
152
|
+
await initGitRepo(tempDir)
|
|
153
|
+
await expect(
|
|
154
|
+
runTestEffect(init({ silent: true, cwd: tempDir })),
|
|
155
|
+
).rejects.toThrow("Template name required")
|
|
156
|
+
} finally {
|
|
157
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test("works in verbose mode", async () => {
|
|
162
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
163
|
+
try {
|
|
164
|
+
await initGitRepo(tempDir)
|
|
165
|
+
const originalLog = console.log
|
|
166
|
+
console.log = () => {}
|
|
167
|
+
try {
|
|
168
|
+
await runTestEffect(
|
|
169
|
+
init({
|
|
170
|
+
template: "verbose-test",
|
|
171
|
+
verbose: true,
|
|
172
|
+
silent: false,
|
|
173
|
+
cwd: tempDir,
|
|
174
|
+
}),
|
|
175
|
+
)
|
|
176
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe(
|
|
177
|
+
"verbose-test",
|
|
178
|
+
)
|
|
179
|
+
} finally {
|
|
180
|
+
console.log = originalLog
|
|
181
|
+
}
|
|
182
|
+
} finally {
|
|
183
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
test("handles empty template name", async () => {
|
|
188
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
189
|
+
try {
|
|
190
|
+
await initGitRepo(tempDir)
|
|
191
|
+
await expect(
|
|
192
|
+
runTestEffect(init({ template: "", silent: true, cwd: tempDir })),
|
|
193
|
+
).rejects.toThrow("Template name required")
|
|
194
|
+
} finally {
|
|
195
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
test("handles template name with special characters", async () => {
|
|
200
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
201
|
+
try {
|
|
202
|
+
await initGitRepo(tempDir)
|
|
203
|
+
await runTestEffect(
|
|
204
|
+
init({ template: "test.template", silent: true, cwd: tempDir }),
|
|
205
|
+
)
|
|
206
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe(
|
|
207
|
+
"test.template",
|
|
208
|
+
)
|
|
209
|
+
} finally {
|
|
210
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
test("handles very long template names", async () => {
|
|
215
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
216
|
+
try {
|
|
217
|
+
await initGitRepo(tempDir)
|
|
218
|
+
const longName = "a".repeat(100)
|
|
219
|
+
await runTestEffect(
|
|
220
|
+
init({ template: longName, silent: true, cwd: tempDir }),
|
|
221
|
+
)
|
|
222
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe(longName)
|
|
223
|
+
} finally {
|
|
224
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test("changing template updates git config", async () => {
|
|
229
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
230
|
+
try {
|
|
231
|
+
await initGitRepo(tempDir)
|
|
232
|
+
await runTestEffect(
|
|
233
|
+
init({ template: "template-a", silent: true, cwd: tempDir }),
|
|
234
|
+
)
|
|
235
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe("template-a")
|
|
236
|
+
await runTestEffect(
|
|
237
|
+
init({ template: "template-b", silent: true, cwd: tempDir }),
|
|
238
|
+
)
|
|
239
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe("template-b")
|
|
240
|
+
} finally {
|
|
241
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
test("preserves template name across command runs", async () => {
|
|
246
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
247
|
+
try {
|
|
248
|
+
await initGitRepo(tempDir)
|
|
249
|
+
await runTestEffect(
|
|
250
|
+
init({ template: "persistent", silent: true, cwd: tempDir }),
|
|
251
|
+
)
|
|
252
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe("persistent")
|
|
253
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe("persistent")
|
|
254
|
+
} finally {
|
|
255
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
test("does not create template directory during init", async () => {
|
|
260
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
261
|
+
const configDir = await mkdtemp(join(tmpdir(), "agency-config-"))
|
|
262
|
+
const originalConfigDir = process.env.AGENCY_CONFIG_DIR
|
|
263
|
+
try {
|
|
264
|
+
await initGitRepo(tempDir)
|
|
265
|
+
process.env.AGENCY_CONFIG_DIR = configDir
|
|
266
|
+
await runTestEffect(
|
|
267
|
+
init({ template: "test-template", silent: true, cwd: tempDir }),
|
|
268
|
+
)
|
|
269
|
+
expect(
|
|
270
|
+
await Bun.file(
|
|
271
|
+
`${configDir}/templates/test-template/AGENTS.md`,
|
|
272
|
+
).exists(),
|
|
273
|
+
).toBe(false)
|
|
274
|
+
} finally {
|
|
275
|
+
if (originalConfigDir !== undefined) {
|
|
276
|
+
process.env.AGENCY_CONFIG_DIR = originalConfigDir
|
|
277
|
+
} else {
|
|
278
|
+
delete process.env.AGENCY_CONFIG_DIR
|
|
279
|
+
}
|
|
280
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
281
|
+
await rm(configDir, { recursive: true, force: true })
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
test("works when templates directory does not exist", async () => {
|
|
286
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agency-test-"))
|
|
287
|
+
const originalConfigDir = process.env.AGENCY_CONFIG_DIR
|
|
288
|
+
try {
|
|
289
|
+
await initGitRepo(tempDir)
|
|
290
|
+
// Point to a non-existent config directory
|
|
291
|
+
const configDir = join(tmpdir(), `agency-config-${Date.now()}`)
|
|
292
|
+
process.env.AGENCY_CONFIG_DIR = configDir
|
|
293
|
+
// Verify templates directory doesn't exist
|
|
294
|
+
expect(await Bun.file(join(configDir, "templates")).exists()).toBe(false)
|
|
295
|
+
// Init should work even without templates directory
|
|
296
|
+
await runTestEffect(
|
|
297
|
+
init({ template: "new-template", silent: true, cwd: tempDir }),
|
|
298
|
+
)
|
|
299
|
+
expect(await getGitConfig("agency.template", tempDir)).toBe(
|
|
300
|
+
"new-template",
|
|
301
|
+
)
|
|
302
|
+
} finally {
|
|
303
|
+
if (originalConfigDir !== undefined) {
|
|
304
|
+
process.env.AGENCY_CONFIG_DIR = originalConfigDir
|
|
305
|
+
} else {
|
|
306
|
+
delete process.env.AGENCY_CONFIG_DIR
|
|
307
|
+
}
|
|
308
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
})
|