@markjaquith/agency 1.11.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +302 -113
- package/cli.ts +123 -420
- package/index.ts +1 -1
- package/package.json +12 -17
- package/skills/agency/SKILL.md +258 -0
- package/src/cli.test.ts +290 -0
- package/src/commands/description.test.ts +103 -0
- package/src/commands/epic.test.ts +103 -0
- package/src/commands/epic.ts +101 -0
- package/src/commands/init.test.ts +42 -290
- package/src/commands/init.ts +15 -123
- package/src/commands/phase.ts +120 -0
- package/src/commands/pr.test.ts +19 -170
- package/src/commands/pr.ts +23 -179
- package/src/commands/repo.test.ts +80 -0
- package/src/commands/repo.ts +83 -0
- package/src/commands/status.test.ts +35 -477
- package/src/commands/status.ts +24 -213
- package/src/commands/task-phase.test.ts +206 -0
- package/src/commands/task.ts +83 -1285
- package/src/commands/validate.test.ts +91 -0
- package/src/commands/validate.ts +50 -0
- package/src/commands/work.test.ts +172 -419
- package/src/commands/work.ts +47 -166
- package/src/services/EpicService.test.ts +71 -0
- package/src/services/EpicService.ts +135 -0
- package/src/services/FileSystemService.ts +61 -92
- package/src/services/PhaseService.ts +323 -0
- package/src/services/PullRequestService.test.ts +386 -0
- package/src/services/PullRequestService.ts +143 -0
- package/src/services/RepositoryService.test.ts +106 -0
- package/src/services/RepositoryService.ts +159 -0
- package/src/services/TaskPhaseService.test.ts +217 -0
- package/src/services/TaskService.ts +192 -0
- package/src/services/WorkbaseService.test.ts +446 -0
- package/src/services/WorkbaseService.ts +493 -0
- package/src/services/WorktreeService.test.ts +599 -0
- package/src/services/WorktreeService.ts +357 -0
- package/src/test-utils.ts +41 -438
- package/src/types.ts +1 -215
- package/src/utils/command.ts +1 -5
- package/src/utils/effect.test.ts +24 -0
- package/src/utils/effect.ts +3 -273
- package/src/utils/process.test.ts +0 -15
- package/src/utils/process.ts +1 -60
- package/src/workbase/frontmatter.test.ts +50 -0
- package/src/workbase/frontmatter.ts +77 -0
- package/src/workbase/repository-reference.test.ts +25 -0
- package/src/workbase/repository-reference.ts +18 -0
- package/src/workbase/schemas.test.ts +146 -0
- package/src/workbase/schemas.ts +78 -0
- package/src/workbase/worktree-command.test.ts +61 -0
- package/src/workbase/worktree-command.ts +52 -0
- package/src/commands/base.test.ts +0 -198
- package/src/commands/base.ts +0 -198
- package/src/commands/clean.test.ts +0 -299
- package/src/commands/clean.ts +0 -274
- package/src/commands/completions.test.ts +0 -51
- package/src/commands/completions.ts +0 -308
- package/src/commands/emit.integration.test.ts +0 -549
- package/src/commands/emit.test.ts +0 -576
- package/src/commands/emit.ts +0 -700
- package/src/commands/emitted.test.ts +0 -226
- package/src/commands/emitted.ts +0 -57
- package/src/commands/loop.test.ts +0 -621
- package/src/commands/loop.ts +0 -406
- package/src/commands/merge.integration.test.ts +0 -195
- package/src/commands/merge.test.ts +0 -246
- package/src/commands/merge.ts +0 -244
- package/src/commands/pull.test.ts +0 -395
- package/src/commands/pull.ts +0 -205
- package/src/commands/push.test.ts +0 -641
- package/src/commands/push.ts +0 -482
- package/src/commands/rebase.test.ts +0 -521
- package/src/commands/rebase.ts +0 -232
- package/src/commands/save.test.ts +0 -247
- package/src/commands/save.ts +0 -162
- package/src/commands/source.test.ts +0 -195
- package/src/commands/source.ts +0 -72
- package/src/commands/switch.test.ts +0 -194
- package/src/commands/switch.ts +0 -84
- package/src/commands/task-branching.test.ts +0 -633
- package/src/commands/task-continue.test.ts +0 -311
- package/src/commands/task-edit.test.ts +0 -235
- package/src/commands/task-main.test.ts +0 -1066
- package/src/commands/task-squash.test.ts +0 -266
- package/src/commands/tasks.test.ts +0 -391
- package/src/commands/tasks.ts +0 -161
- package/src/commands/template-delete.test.ts +0 -178
- package/src/commands/template-delete.ts +0 -98
- package/src/commands/template-list.test.ts +0 -135
- package/src/commands/template-list.ts +0 -87
- package/src/commands/template-view.test.ts +0 -158
- package/src/commands/template-view.ts +0 -86
- package/src/commands/template.test.ts +0 -32
- package/src/commands/template.ts +0 -96
- package/src/commands/use.test.ts +0 -87
- package/src/commands/use.ts +0 -97
- package/src/constants.ts +0 -10
- package/src/errors.ts +0 -17
- package/src/schemas.ts +0 -33
- package/src/services/AgencyMetadataService.ts +0 -311
- package/src/services/ClaudeService.test.ts +0 -184
- package/src/services/ClaudeService.ts +0 -91
- package/src/services/ConfigService.ts +0 -115
- package/src/services/FilterRepoService.ts +0 -164
- package/src/services/FormatterService.test.ts +0 -432
- package/src/services/FormatterService.ts +0 -219
- package/src/services/GitService.ts +0 -1187
- package/src/services/MockFilterRepoService.ts +0 -140
- package/src/services/OpencodeService.ts +0 -263
- package/src/services/PromptService.ts +0 -183
- package/src/services/TemplateService.ts +0 -81
- package/src/types/native-exec.d.ts +0 -8
- package/src/utils/colors.ts +0 -178
- package/src/utils/git-path.ts +0 -6
- package/src/utils/glob.test.ts +0 -161
- package/src/utils/glob.ts +0 -78
- package/src/utils/paths.ts +0 -51
- package/src/utils/pr-branch.test.ts +0 -376
- package/src/utils/pr-branch.ts +0 -508
- package/src/utils/spinner.test.ts +0 -45
- package/src/utils/spinner.ts +0 -96
- package/src/utils/task-parser.test.ts +0 -172
- package/src/utils/task-parser.ts +0 -93
- package/templates/AGENCY.md +0 -22
- package/templates/AGENTS.md +0 -11
- package/templates/CLAUDE.md +0 -3
- package/templates/TASK.md +0 -5
- package/templates/opencode.json +0 -4
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test, beforeEach, afterEach } from "bun:test"
|
|
2
|
-
import { templateView } from "./template-view"
|
|
3
|
-
import { mkdir, writeFile, rm } from "node:fs/promises"
|
|
4
|
-
import { join } from "node:path"
|
|
5
|
-
import { tmpdir } from "node:os"
|
|
6
|
-
import { runTestEffect } from "../test-utils"
|
|
7
|
-
|
|
8
|
-
describe("template view command", () => {
|
|
9
|
-
let testDir: string
|
|
10
|
-
let gitRoot: string
|
|
11
|
-
let templateDir: string
|
|
12
|
-
|
|
13
|
-
beforeEach(async () => {
|
|
14
|
-
// Create temporary test directory
|
|
15
|
-
testDir = join(tmpdir(), `agency-test-view-${Date.now()}`)
|
|
16
|
-
await mkdir(testDir, { recursive: true })
|
|
17
|
-
gitRoot = join(testDir, "repo")
|
|
18
|
-
await mkdir(gitRoot, { recursive: true })
|
|
19
|
-
|
|
20
|
-
// Initialize git repo
|
|
21
|
-
await Bun.spawn(["git", "init"], {
|
|
22
|
-
cwd: gitRoot,
|
|
23
|
-
stdout: "ignore",
|
|
24
|
-
stderr: "ignore",
|
|
25
|
-
}).exited
|
|
26
|
-
|
|
27
|
-
// Set up config dir
|
|
28
|
-
const configDir = join(testDir, "config")
|
|
29
|
-
templateDir = join(configDir, "templates", "test-template")
|
|
30
|
-
await mkdir(templateDir, { recursive: true })
|
|
31
|
-
|
|
32
|
-
// Set environment variable for config
|
|
33
|
-
process.env.AGENCY_CONFIG_DIR = configDir
|
|
34
|
-
|
|
35
|
-
// Set git config
|
|
36
|
-
await Bun.spawn(["git", "config", "agency.template", "test-template"], {
|
|
37
|
-
cwd: gitRoot,
|
|
38
|
-
stdout: "ignore",
|
|
39
|
-
stderr: "ignore",
|
|
40
|
-
}).exited
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
afterEach(async () => {
|
|
44
|
-
// Clean up
|
|
45
|
-
await rm(testDir, { recursive: true, force: true })
|
|
46
|
-
delete process.env.AGENCY_CONFIG_DIR
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
test("views file from template directory", async () => {
|
|
50
|
-
// Create test file in template
|
|
51
|
-
const content = "# Test File\n\nThis is a test."
|
|
52
|
-
const testFile = join(templateDir, "test.md")
|
|
53
|
-
await writeFile(testFile, content)
|
|
54
|
-
|
|
55
|
-
const originalCwd = process.cwd()
|
|
56
|
-
process.chdir(gitRoot)
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
// Just verify the command doesn't throw - output goes to stdout
|
|
60
|
-
await runTestEffect(templateView({ file: "test.md", silent: true }))
|
|
61
|
-
|
|
62
|
-
// Verify the file exists and has expected content
|
|
63
|
-
const fileContent = await Bun.file(testFile).text()
|
|
64
|
-
expect(fileContent).toContain("# Test File")
|
|
65
|
-
expect(fileContent).toContain("This is a test.")
|
|
66
|
-
} finally {
|
|
67
|
-
process.chdir(originalCwd)
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
test("views file in subdirectory", async () => {
|
|
72
|
-
// Create test file in subdirectory
|
|
73
|
-
const docsDir = join(templateDir, "docs")
|
|
74
|
-
await mkdir(docsDir, { recursive: true })
|
|
75
|
-
const content = "# Documentation\n\nDocs content."
|
|
76
|
-
const testFile = join(docsDir, "README.md")
|
|
77
|
-
await writeFile(testFile, content)
|
|
78
|
-
|
|
79
|
-
const originalCwd = process.cwd()
|
|
80
|
-
process.chdir(gitRoot)
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
// Just verify the command doesn't throw - output goes to stdout
|
|
84
|
-
await runTestEffect(
|
|
85
|
-
templateView({ file: "docs/README.md", silent: true }),
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
// Verify the file exists and has expected content
|
|
89
|
-
const fileContent = await Bun.file(testFile).text()
|
|
90
|
-
expect(fileContent).toContain("# Documentation")
|
|
91
|
-
expect(fileContent).toContain("Docs content.")
|
|
92
|
-
} finally {
|
|
93
|
-
process.chdir(originalCwd)
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
test("throws error when file does not exist", async () => {
|
|
98
|
-
const originalCwd = process.cwd()
|
|
99
|
-
process.chdir(gitRoot)
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
await expect(
|
|
103
|
-
runTestEffect(templateView({ file: "nonexistent.md", silent: true })),
|
|
104
|
-
).rejects.toThrow("does not exist in template")
|
|
105
|
-
} finally {
|
|
106
|
-
process.chdir(originalCwd)
|
|
107
|
-
}
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
test("throws error when no file specified", async () => {
|
|
111
|
-
const originalCwd = process.cwd()
|
|
112
|
-
process.chdir(gitRoot)
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
await expect(
|
|
116
|
-
runTestEffect(templateView({ silent: true })),
|
|
117
|
-
).rejects.toThrow("File path is required")
|
|
118
|
-
} finally {
|
|
119
|
-
process.chdir(originalCwd)
|
|
120
|
-
}
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
test("throws error when not in git repository", async () => {
|
|
124
|
-
const nonGitDir = join(testDir, "non-git")
|
|
125
|
-
await mkdir(nonGitDir, { recursive: true })
|
|
126
|
-
|
|
127
|
-
const originalCwd = process.cwd()
|
|
128
|
-
process.chdir(nonGitDir)
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
await expect(
|
|
132
|
-
runTestEffect(templateView({ file: "test.md", silent: true })),
|
|
133
|
-
).rejects.toThrow("Not in a git repository")
|
|
134
|
-
} finally {
|
|
135
|
-
process.chdir(originalCwd)
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
test("throws error when template not configured", async () => {
|
|
140
|
-
// Remove git config
|
|
141
|
-
await Bun.spawn(["git", "config", "--unset", "agency.template"], {
|
|
142
|
-
cwd: gitRoot,
|
|
143
|
-
stdout: "ignore",
|
|
144
|
-
stderr: "ignore",
|
|
145
|
-
}).exited
|
|
146
|
-
|
|
147
|
-
const originalCwd = process.cwd()
|
|
148
|
-
process.chdir(gitRoot)
|
|
149
|
-
|
|
150
|
-
try {
|
|
151
|
-
await expect(
|
|
152
|
-
runTestEffect(templateView({ file: "test.md", silent: true })),
|
|
153
|
-
).rejects.toThrow("Repository not initialized")
|
|
154
|
-
} finally {
|
|
155
|
-
process.chdir(originalCwd)
|
|
156
|
-
}
|
|
157
|
-
})
|
|
158
|
-
})
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { resolve } from "path"
|
|
2
|
-
import { Effect } from "effect"
|
|
3
|
-
import type { BaseCommandOptions } from "../utils/command"
|
|
4
|
-
import { TemplateService } from "../services/TemplateService"
|
|
5
|
-
import { FileSystemService } from "../services/FileSystemService"
|
|
6
|
-
import { RepositoryNotInitializedError } from "../errors"
|
|
7
|
-
import highlight from "../utils/colors"
|
|
8
|
-
import { createLoggers, ensureGitRepo, getTemplateName } from "../utils/effect"
|
|
9
|
-
|
|
10
|
-
interface ViewOptions extends BaseCommandOptions {
|
|
11
|
-
file?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const templateView = (options: ViewOptions = {}) =>
|
|
15
|
-
Effect.gen(function* () {
|
|
16
|
-
const { file: fileToView, silent = false } = options
|
|
17
|
-
const { verboseLog } = createLoggers(options)
|
|
18
|
-
|
|
19
|
-
const templateService = yield* TemplateService
|
|
20
|
-
const fs = yield* FileSystemService
|
|
21
|
-
|
|
22
|
-
const gitRoot = yield* ensureGitRepo()
|
|
23
|
-
|
|
24
|
-
// Get template name from git config
|
|
25
|
-
const templateName = yield* getTemplateName(gitRoot)
|
|
26
|
-
if (!templateName) {
|
|
27
|
-
return yield* Effect.fail(new RepositoryNotInitializedError())
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!fileToView) {
|
|
31
|
-
return yield* Effect.fail(
|
|
32
|
-
new Error("File path is required. Usage: agency template view <file>"),
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
verboseLog(
|
|
37
|
-
`Viewing ${highlight.file(fileToView)} from ${highlight.template(templateName)} template`,
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
// Get template directory
|
|
41
|
-
const templateDir = yield* templateService.getTemplateDir(templateName)
|
|
42
|
-
const templateFilePath = resolve(templateDir, fileToView)
|
|
43
|
-
|
|
44
|
-
// Check if file exists
|
|
45
|
-
const exists = yield* fs.exists(templateFilePath)
|
|
46
|
-
if (!exists) {
|
|
47
|
-
return yield* Effect.fail(
|
|
48
|
-
new Error(
|
|
49
|
-
`File ${highlight.file(fileToView)} does not exist in template ${highlight.template(templateName)}`,
|
|
50
|
-
),
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Read and display the file
|
|
55
|
-
const content = yield* fs.readFile(templateFilePath)
|
|
56
|
-
if (!silent) {
|
|
57
|
-
console.log(content)
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
// Help text for reference (not exported as it's handled by template command)
|
|
62
|
-
const help = `
|
|
63
|
-
Usage: agency template view <file> [options]
|
|
64
|
-
|
|
65
|
-
View the contents of a file in the configured template directory.
|
|
66
|
-
|
|
67
|
-
This command displays the contents of a file stored in the template
|
|
68
|
-
configured for the current git repository.
|
|
69
|
-
|
|
70
|
-
Arguments:
|
|
71
|
-
<file> File path to view (relative to template root)
|
|
72
|
-
|
|
73
|
-
Options:
|
|
74
|
-
-h, --help Show this help message
|
|
75
|
-
-s, --silent Suppress verbose messages (file content still shown)
|
|
76
|
-
-v, --verbose Show verbose output
|
|
77
|
-
|
|
78
|
-
Examples:
|
|
79
|
-
agency template view AGENTS.md # View AGENTS.md from template
|
|
80
|
-
agency template view docs/README.md # View file in subdirectory
|
|
81
|
-
|
|
82
|
-
Notes:
|
|
83
|
-
- Requires agency.template to be set (run 'agency init' first)
|
|
84
|
-
- File path is relative to template root directory
|
|
85
|
-
- File content is displayed directly to stdout
|
|
86
|
-
`
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test"
|
|
2
|
-
import { template } from "./template"
|
|
3
|
-
import { runTestEffect } from "../test-utils"
|
|
4
|
-
|
|
5
|
-
describe("template command", () => {
|
|
6
|
-
test("throws error when no subcommand provided", async () => {
|
|
7
|
-
await expect(
|
|
8
|
-
runTestEffect(
|
|
9
|
-
template({
|
|
10
|
-
args: [],
|
|
11
|
-
silent: true,
|
|
12
|
-
}),
|
|
13
|
-
),
|
|
14
|
-
).rejects.toThrow(
|
|
15
|
-
"Subcommand is required. Available subcommands: use, save, list, view, delete",
|
|
16
|
-
)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
test("throws error for unknown subcommand", async () => {
|
|
20
|
-
await expect(
|
|
21
|
-
runTestEffect(
|
|
22
|
-
template({
|
|
23
|
-
subcommand: "invalid",
|
|
24
|
-
args: [],
|
|
25
|
-
silent: true,
|
|
26
|
-
}),
|
|
27
|
-
),
|
|
28
|
-
).rejects.toThrow(
|
|
29
|
-
"Unknown template subcommand 'invalid'. Available: use, save, list, view, delete",
|
|
30
|
-
)
|
|
31
|
-
})
|
|
32
|
-
})
|
package/src/commands/template.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect"
|
|
2
|
-
import { use } from "./use"
|
|
3
|
-
import { save } from "./save"
|
|
4
|
-
import { templateList } from "./template-list"
|
|
5
|
-
import { templateDelete } from "./template-delete"
|
|
6
|
-
import { templateView } from "./template-view"
|
|
7
|
-
|
|
8
|
-
export const help = `
|
|
9
|
-
Usage: agency template <subcommand> [options]
|
|
10
|
-
|
|
11
|
-
Subcommands:
|
|
12
|
-
use [template] Set template for this repository
|
|
13
|
-
save <file|dir> ... Save files/dirs to configured template
|
|
14
|
-
list List all files in configured template
|
|
15
|
-
view <file> View contents of a file in template
|
|
16
|
-
delete <file> ... Delete files from configured template
|
|
17
|
-
|
|
18
|
-
Examples:
|
|
19
|
-
agency template use # Interactively select template
|
|
20
|
-
agency template use work # Set template to 'work'
|
|
21
|
-
agency template save AGENTS.md # Save specific file to template
|
|
22
|
-
agency template list # List files in current template
|
|
23
|
-
agency template view AGENTS.md # View file from template
|
|
24
|
-
agency template delete AGENTS.md # Delete file from template
|
|
25
|
-
|
|
26
|
-
For more information about a subcommand, run:
|
|
27
|
-
agency template <subcommand> --help
|
|
28
|
-
`
|
|
29
|
-
|
|
30
|
-
export const template = (options: {
|
|
31
|
-
subcommand?: string
|
|
32
|
-
args: string[]
|
|
33
|
-
silent?: boolean
|
|
34
|
-
verbose?: boolean
|
|
35
|
-
template?: string
|
|
36
|
-
}) =>
|
|
37
|
-
Effect.gen(function* () {
|
|
38
|
-
const {
|
|
39
|
-
subcommand,
|
|
40
|
-
args,
|
|
41
|
-
silent,
|
|
42
|
-
verbose,
|
|
43
|
-
template: templateName,
|
|
44
|
-
} = options
|
|
45
|
-
|
|
46
|
-
if (!subcommand) {
|
|
47
|
-
return yield* Effect.fail(
|
|
48
|
-
new Error(
|
|
49
|
-
"Subcommand is required. Available subcommands: use, save, list, view, delete\n\nRun 'agency template --help' for usage information.",
|
|
50
|
-
),
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
switch (subcommand) {
|
|
55
|
-
case "use":
|
|
56
|
-
return yield* use({
|
|
57
|
-
template: args[0] || templateName,
|
|
58
|
-
silent,
|
|
59
|
-
verbose,
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
case "save":
|
|
63
|
-
return yield* save({
|
|
64
|
-
files: args,
|
|
65
|
-
silent,
|
|
66
|
-
verbose,
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
case "list":
|
|
70
|
-
return yield* templateList({
|
|
71
|
-
silent,
|
|
72
|
-
verbose,
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
case "view":
|
|
76
|
-
return yield* templateView({
|
|
77
|
-
file: args[0],
|
|
78
|
-
silent,
|
|
79
|
-
verbose,
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
case "delete":
|
|
83
|
-
return yield* templateDelete({
|
|
84
|
-
files: args,
|
|
85
|
-
silent,
|
|
86
|
-
verbose,
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
default:
|
|
90
|
-
return yield* Effect.fail(
|
|
91
|
-
new Error(
|
|
92
|
-
`Unknown template subcommand '${subcommand}'. Available: use, save, list, view, delete`,
|
|
93
|
-
),
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
})
|
package/src/commands/use.test.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { test, expect, describe, beforeEach, afterEach } from "bun:test"
|
|
2
|
-
import { use } from "./use"
|
|
3
|
-
import {
|
|
4
|
-
createTempDir,
|
|
5
|
-
cleanupTempDir,
|
|
6
|
-
initGitRepo,
|
|
7
|
-
getGitConfig,
|
|
8
|
-
runTestEffect,
|
|
9
|
-
} from "../test-utils"
|
|
10
|
-
|
|
11
|
-
describe("use command", () => {
|
|
12
|
-
let tempDir: string
|
|
13
|
-
let originalCwd: string
|
|
14
|
-
let originalConfigDir: string | undefined
|
|
15
|
-
|
|
16
|
-
beforeEach(async () => {
|
|
17
|
-
tempDir = await createTempDir()
|
|
18
|
-
originalCwd = process.cwd()
|
|
19
|
-
originalConfigDir = process.env.AGENCY_CONFIG_DIR
|
|
20
|
-
// Use a temp config dir
|
|
21
|
-
process.env.AGENCY_CONFIG_DIR = await createTempDir()
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
afterEach(async () => {
|
|
25
|
-
process.chdir(originalCwd)
|
|
26
|
-
if (originalConfigDir !== undefined) {
|
|
27
|
-
process.env.AGENCY_CONFIG_DIR = originalConfigDir
|
|
28
|
-
} else {
|
|
29
|
-
delete process.env.AGENCY_CONFIG_DIR
|
|
30
|
-
}
|
|
31
|
-
if (
|
|
32
|
-
process.env.AGENCY_CONFIG_DIR &&
|
|
33
|
-
process.env.AGENCY_CONFIG_DIR !== originalConfigDir
|
|
34
|
-
) {
|
|
35
|
-
await cleanupTempDir(process.env.AGENCY_CONFIG_DIR)
|
|
36
|
-
}
|
|
37
|
-
await cleanupTempDir(tempDir)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
test("sets template name in git config", async () => {
|
|
41
|
-
await initGitRepo(tempDir)
|
|
42
|
-
process.chdir(tempDir)
|
|
43
|
-
|
|
44
|
-
await runTestEffect(use({ template: "work", silent: true }))
|
|
45
|
-
|
|
46
|
-
const templateName = await getGitConfig("agency.template", tempDir)
|
|
47
|
-
expect(templateName).toBe("work")
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test("updates existing template name", async () => {
|
|
51
|
-
await initGitRepo(tempDir)
|
|
52
|
-
process.chdir(tempDir)
|
|
53
|
-
|
|
54
|
-
await runTestEffect(use({ template: "work", silent: true }))
|
|
55
|
-
await runTestEffect(use({ template: "personal", silent: true }))
|
|
56
|
-
|
|
57
|
-
const templateName = await getGitConfig("agency.template", tempDir)
|
|
58
|
-
expect(templateName).toBe("personal")
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
test("throws error when not in git repo", async () => {
|
|
62
|
-
process.chdir(tempDir)
|
|
63
|
-
|
|
64
|
-
await expect(
|
|
65
|
-
runTestEffect(use({ template: "work", silent: true })),
|
|
66
|
-
).rejects.toThrow("Not in a git repository")
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test("throws error when no template provided in silent mode", async () => {
|
|
70
|
-
await initGitRepo(tempDir)
|
|
71
|
-
process.chdir(tempDir)
|
|
72
|
-
|
|
73
|
-
await expect(runTestEffect(use({ silent: true }))).rejects.toThrow(
|
|
74
|
-
"Template name required",
|
|
75
|
-
)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
test("works with --template flag", async () => {
|
|
79
|
-
await initGitRepo(tempDir)
|
|
80
|
-
process.chdir(tempDir)
|
|
81
|
-
|
|
82
|
-
await runTestEffect(use({ template: "client", silent: true }))
|
|
83
|
-
|
|
84
|
-
const templateName = await getGitConfig("agency.template", tempDir)
|
|
85
|
-
expect(templateName).toBe("client")
|
|
86
|
-
})
|
|
87
|
-
})
|
package/src/commands/use.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect"
|
|
2
|
-
import type { BaseCommandOptions } from "../utils/command"
|
|
3
|
-
import { GitService } from "../services/GitService"
|
|
4
|
-
import { TemplateService } from "../services/TemplateService"
|
|
5
|
-
import { PromptService } from "../services/PromptService"
|
|
6
|
-
import highlight from "../utils/colors"
|
|
7
|
-
import { createLoggers, ensureGitRepo } from "../utils/effect"
|
|
8
|
-
|
|
9
|
-
interface UseOptions extends BaseCommandOptions {
|
|
10
|
-
template?: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const use = (options: UseOptions = {}) =>
|
|
14
|
-
Effect.gen(function* () {
|
|
15
|
-
const { silent = false } = options
|
|
16
|
-
const { log, verboseLog } = createLoggers(options)
|
|
17
|
-
|
|
18
|
-
const git = yield* GitService
|
|
19
|
-
const templateService = yield* TemplateService
|
|
20
|
-
const promptService = yield* PromptService
|
|
21
|
-
|
|
22
|
-
const gitRoot = yield* ensureGitRepo()
|
|
23
|
-
|
|
24
|
-
let templateName = options.template
|
|
25
|
-
|
|
26
|
-
// If no template name provided, show interactive selection
|
|
27
|
-
if (!templateName) {
|
|
28
|
-
if (silent) {
|
|
29
|
-
return yield* Effect.fail(
|
|
30
|
-
new Error(
|
|
31
|
-
"Template name required. Use --template flag in silent mode.",
|
|
32
|
-
),
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const templates = yield* templateService.listTemplates()
|
|
37
|
-
|
|
38
|
-
if (templates.length === 0) {
|
|
39
|
-
log("No templates found in ~/.config/agency/templates/")
|
|
40
|
-
log("Run 'agency task' to create a template.")
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Show current template if set
|
|
45
|
-
const currentTemplate = yield* git.getGitConfig(
|
|
46
|
-
"agency.template",
|
|
47
|
-
gitRoot,
|
|
48
|
-
)
|
|
49
|
-
if (currentTemplate) {
|
|
50
|
-
log(`Current template: ${highlight.template(currentTemplate)}`)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
templateName = yield* promptService.promptForTemplate(templates, {
|
|
54
|
-
currentTemplate: currentTemplate ?? undefined,
|
|
55
|
-
allowNew: false,
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
verboseLog(`Setting template to: ${templateName}`)
|
|
60
|
-
|
|
61
|
-
// Set the template in git config
|
|
62
|
-
yield* git.setGitConfig("agency.template", templateName, gitRoot)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
// Help text for reference (not exported as it's handled by template command)
|
|
66
|
-
const help = `
|
|
67
|
-
Usage: agency template use [template] [options]
|
|
68
|
-
|
|
69
|
-
Set the template to use for this repository.
|
|
70
|
-
|
|
71
|
-
NOTE: This command is equivalent to 'agency init'. Use 'agency init' for
|
|
72
|
-
initial setup, and 'agency template use' to change templates later.
|
|
73
|
-
|
|
74
|
-
When no template name is provided, shows an interactive list of available
|
|
75
|
-
templates to choose from. The template name is saved to .git/config
|
|
76
|
-
(agency.template) and will be used by subsequent 'agency task' commands.
|
|
77
|
-
|
|
78
|
-
Arguments:
|
|
79
|
-
template Template name to use (optional, prompts if not provided)
|
|
80
|
-
|
|
81
|
-
Options:
|
|
82
|
-
-h, --help Show this help message
|
|
83
|
-
-s, --silent Suppress output messages
|
|
84
|
-
-v, --verbose Show verbose output
|
|
85
|
-
-t, --template Specify template name (same as positional argument)
|
|
86
|
-
|
|
87
|
-
Examples:
|
|
88
|
-
agency template use # Interactive template selection
|
|
89
|
-
agency template use work # Set template to 'work'
|
|
90
|
-
agency template use --template=client # Set template to 'client'
|
|
91
|
-
|
|
92
|
-
Notes:
|
|
93
|
-
- Template must exist in ~/.config/agency/templates/{name}/
|
|
94
|
-
- Template name is saved to .git/config (not committed)
|
|
95
|
-
- Use 'agency task' after changing template to create/update files
|
|
96
|
-
- Template directory is created when you save files to it
|
|
97
|
-
`
|
package/src/constants.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared constants for the agency CLI tool.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Marker used in commit messages to indicate commits that should be
|
|
7
|
-
* dropped entirely during the emit process. When this marker is found
|
|
8
|
-
* in a commit message, emit will remove that commit from history.
|
|
9
|
-
*/
|
|
10
|
-
export const AGENCY_REMOVE_COMMIT = "AGENCY_REMOVE_COMMIT"
|
package/src/errors.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Data } from "effect"
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Error thrown when a command requires the repository to be initialized
|
|
5
|
-
* but agency.template is not set in git config
|
|
6
|
-
*/
|
|
7
|
-
export class RepositoryNotInitializedError extends Data.TaggedError(
|
|
8
|
-
"RepositoryNotInitializedError",
|
|
9
|
-
)<{
|
|
10
|
-
readonly message: string
|
|
11
|
-
}> {
|
|
12
|
-
constructor(
|
|
13
|
-
message: string = "Repository not initialized. Run 'agency init' first to select a template.",
|
|
14
|
-
) {
|
|
15
|
-
super({ message })
|
|
16
|
-
}
|
|
17
|
-
}
|
package/src/schemas.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { Schema } from "@effect/schema"
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Schema for managed files in the agency system
|
|
5
|
-
*/
|
|
6
|
-
export class ManagedFile extends Schema.Class<ManagedFile>("ManagedFile")({
|
|
7
|
-
name: Schema.String,
|
|
8
|
-
defaultContent: Schema.optional(Schema.String),
|
|
9
|
-
}) {}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Schema for agency metadata stored in agency.json
|
|
13
|
-
*/
|
|
14
|
-
export class AgencyMetadata extends Schema.Class<AgencyMetadata>(
|
|
15
|
-
"AgencyMetadata",
|
|
16
|
-
)({
|
|
17
|
-
version: Schema.Literal(1),
|
|
18
|
-
injectedFiles: Schema.Array(Schema.String),
|
|
19
|
-
baseBranch: Schema.optional(Schema.String),
|
|
20
|
-
template: Schema.String,
|
|
21
|
-
createdAt: Schema.DateTimeUtc,
|
|
22
|
-
emitBranch: Schema.optional(Schema.String),
|
|
23
|
-
}) {}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Schema for agency configuration stored in ~/.config/agency/agency.json
|
|
27
|
-
*/
|
|
28
|
-
export class AgencyConfig extends Schema.Class<AgencyConfig>("AgencyConfig")({
|
|
29
|
-
sourceBranchPattern: Schema.String.pipe(
|
|
30
|
-
Schema.annotations({ default: "agency--%branch%" }),
|
|
31
|
-
),
|
|
32
|
-
emitBranch: Schema.String.pipe(Schema.annotations({ default: "%branch%" })),
|
|
33
|
-
}) {}
|