@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mark Jaquith
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# @markjaquith/agency
|
|
2
|
+
|
|
3
|
+
Smuggle project-level LLM instruction into any Git repo. Plan your tasks. Commit your plans. Execute your plans using Opencode. Filter those plans out out your PRs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun install -g @markjaquith/agency
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Primary Commands
|
|
12
|
+
|
|
13
|
+
### `agency task [branch-name]`
|
|
14
|
+
|
|
15
|
+
Initialize `AGENTS.md` and `TASK.md` files using the template you've set for this repo. Commits smuggled files and lands you on that branch.
|
|
16
|
+
|
|
17
|
+
### `agency task edit`
|
|
18
|
+
|
|
19
|
+
Open `TASK.md` in the system editor for editing. Nice if you have to paste in large amounts of context.
|
|
20
|
+
|
|
21
|
+
### `agency work`
|
|
22
|
+
|
|
23
|
+
Launch Opencode to work on the current task defined in `TASK.md`. All your context will be loaded.
|
|
24
|
+
|
|
25
|
+
### `agency pr [base-branch]`
|
|
26
|
+
|
|
27
|
+
Create an emit branch with smuggled files reverted to their merge-base state (removes additions/modifications to those files made on feature branch). Default branch name is current branch with `--PR` suffix.
|
|
28
|
+
|
|
29
|
+
### `agency push`
|
|
30
|
+
|
|
31
|
+
Runs `agency pr`, pushes the branch, and then switches back to the source branch.
|
|
32
|
+
|
|
33
|
+
### `agency merge`
|
|
34
|
+
|
|
35
|
+
Runs `agency pr`, and then merges the PR back into the base branch locally.
|
|
36
|
+
|
|
37
|
+
## Other Commands
|
|
38
|
+
|
|
39
|
+
### `agency template use [template]`
|
|
40
|
+
|
|
41
|
+
Set which template to use for this repository. Shows interactive selection if no template name provided. Saves to `.git/config`.
|
|
42
|
+
|
|
43
|
+
### `agency template save <files...>`
|
|
44
|
+
|
|
45
|
+
Save the specified files back to the configured template directory (so they will be used for future `agency task` commands).
|
|
46
|
+
|
|
47
|
+
### `agency base get`
|
|
48
|
+
|
|
49
|
+
Get the base branch for the current feature branch.
|
|
50
|
+
|
|
51
|
+
### `agency base set <branch>`
|
|
52
|
+
|
|
53
|
+
Set the base branch for the current feature branch.
|
|
54
|
+
|
|
55
|
+
### `agency switch`
|
|
56
|
+
|
|
57
|
+
Toggle between source branch and emit branch. If on an emit branch (e.g., `foo--PR`), switches to source branch (e.g., `foo`). If on source branch and emit branch exists, switches to emit branch.
|
|
58
|
+
|
|
59
|
+
### `agency source`
|
|
60
|
+
|
|
61
|
+
Switch to the source branch for the current emit branch.
|
|
62
|
+
|
|
63
|
+
## Requirements
|
|
64
|
+
|
|
65
|
+
- [Bun](https://bun.sh) >= 1.0.0 (recommended)
|
|
66
|
+
- TypeScript ^5
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
To install dependencies:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
bun install
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
To run:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
bun run index.ts
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Git Hooks
|
|
83
|
+
|
|
84
|
+
This project uses [hk](https://github.com/jdx/hk) for git hook management. The configuration is in `hk.pkl`.
|
|
85
|
+
|
|
86
|
+
To install the git hooks:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
hk install
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Pre-commit hook runs:**
|
|
93
|
+
|
|
94
|
+
- Prettier formatting
|
|
95
|
+
- Knip (unused code detection)
|
|
96
|
+
- TypeScript type checking
|
|
97
|
+
|
|
98
|
+
**Commit-msg hook validates:**
|
|
99
|
+
|
|
100
|
+
- Conventional commits format
|
|
101
|
+
- Commit message history
|
|
102
|
+
|
|
103
|
+
**Pre-push hook runs the same checks as pre-commit.**
|
|
104
|
+
|
|
105
|
+
Note: Tests are intentionally excluded from git hooks as they are slow. Run them manually with `bun test`.
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
MIT
|
package/cli.ts
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { parseArgs } from "util"
|
|
4
|
+
import { Effect, Layer } from "effect"
|
|
5
|
+
import { clean, help as cleanHelp } from "./src/commands/clean"
|
|
6
|
+
import { init, help as initHelp } from "./src/commands/init"
|
|
7
|
+
import { task, taskEdit, help as taskHelp } from "./src/commands/task"
|
|
8
|
+
import { tasks, help as tasksHelp } from "./src/commands/tasks"
|
|
9
|
+
import { emit, help as emitHelp } from "./src/commands/emit"
|
|
10
|
+
import { emitted, help as emittedHelp } from "./src/commands/emitted"
|
|
11
|
+
import { push, help as pushHelp } from "./src/commands/push"
|
|
12
|
+
import { pull, help as pullHelp } from "./src/commands/pull"
|
|
13
|
+
import { base, help as baseHelp } from "./src/commands/base"
|
|
14
|
+
import { switchBranch, help as switchHelp } from "./src/commands/switch"
|
|
15
|
+
import { source, help as sourceHelp } from "./src/commands/source"
|
|
16
|
+
import { merge, help as mergeHelp } from "./src/commands/merge"
|
|
17
|
+
import { template, help as templateHelp } from "./src/commands/template"
|
|
18
|
+
import { work, help as workHelp } from "./src/commands/work"
|
|
19
|
+
import { status, help as statusHelp } from "./src/commands/status"
|
|
20
|
+
import type { Command } from "./src/types"
|
|
21
|
+
import { setColorsEnabled } from "./src/utils/colors"
|
|
22
|
+
import { GitService } from "./src/services/GitService"
|
|
23
|
+
import { ConfigService } from "./src/services/ConfigService"
|
|
24
|
+
import { FileSystemService } from "./src/services/FileSystemService"
|
|
25
|
+
import { PromptService } from "./src/services/PromptService"
|
|
26
|
+
import { TemplateService } from "./src/services/TemplateService"
|
|
27
|
+
import { OpencodeService } from "./src/services/OpencodeService"
|
|
28
|
+
import { ClaudeService } from "./src/services/ClaudeService"
|
|
29
|
+
|
|
30
|
+
// Create CLI layer with all services
|
|
31
|
+
const CliLayer = Layer.mergeAll(
|
|
32
|
+
GitService.Default,
|
|
33
|
+
ConfigService.Default,
|
|
34
|
+
FileSystemService.Default,
|
|
35
|
+
PromptService.Default,
|
|
36
|
+
TemplateService.Default,
|
|
37
|
+
OpencodeService.Default,
|
|
38
|
+
ClaudeService.Default,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Run a command Effect with all services provided
|
|
43
|
+
*/
|
|
44
|
+
async function runCommand<E>(
|
|
45
|
+
effect: Effect.Effect<void, E, any>,
|
|
46
|
+
): Promise<void> {
|
|
47
|
+
const providedEffect = Effect.provide(effect, CliLayer) as Effect.Effect<
|
|
48
|
+
void,
|
|
49
|
+
E,
|
|
50
|
+
never
|
|
51
|
+
>
|
|
52
|
+
const program = Effect.catchAllDefect(providedEffect, (defect) =>
|
|
53
|
+
Effect.fail(defect instanceof Error ? defect : new Error(String(defect))),
|
|
54
|
+
) as Effect.Effect<void, E | Error, never>
|
|
55
|
+
|
|
56
|
+
await Effect.runPromise(program)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Read version from package.json
|
|
60
|
+
const packageJson = await Bun.file(
|
|
61
|
+
new URL("./package.json", import.meta.url),
|
|
62
|
+
).json()
|
|
63
|
+
const VERSION = packageJson.version
|
|
64
|
+
|
|
65
|
+
// Define commands
|
|
66
|
+
const commands: Record<string, Command> = {
|
|
67
|
+
init: {
|
|
68
|
+
name: "init",
|
|
69
|
+
description: "Initialize agency with template selection",
|
|
70
|
+
run: async (_args: string[], options: Record<string, any>) => {
|
|
71
|
+
if (options.help) {
|
|
72
|
+
console.log(initHelp)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
await runCommand(
|
|
76
|
+
init({
|
|
77
|
+
template: options.template,
|
|
78
|
+
silent: options.silent,
|
|
79
|
+
verbose: options.verbose,
|
|
80
|
+
}),
|
|
81
|
+
)
|
|
82
|
+
},
|
|
83
|
+
help: initHelp,
|
|
84
|
+
},
|
|
85
|
+
emit: {
|
|
86
|
+
name: "emit",
|
|
87
|
+
description: "Emit a branch without backpack files",
|
|
88
|
+
run: async (args: string[], options: Record<string, any>) => {
|
|
89
|
+
if (options.help) {
|
|
90
|
+
console.log(emitHelp)
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
await runCommand(
|
|
94
|
+
emit({
|
|
95
|
+
baseBranch: args[0],
|
|
96
|
+
branch: options.branch,
|
|
97
|
+
silent: options.silent,
|
|
98
|
+
force: options.verbose,
|
|
99
|
+
verbose: options.verbose,
|
|
100
|
+
}),
|
|
101
|
+
)
|
|
102
|
+
},
|
|
103
|
+
help: emitHelp,
|
|
104
|
+
},
|
|
105
|
+
emitted: {
|
|
106
|
+
name: "emitted",
|
|
107
|
+
description: "Get the name of the emitted branch",
|
|
108
|
+
run: async (_args: string[], options: Record<string, any>) => {
|
|
109
|
+
if (options.help) {
|
|
110
|
+
console.log(emittedHelp)
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
await runCommand(
|
|
114
|
+
emitted({ silent: options.silent, verbose: options.verbose }),
|
|
115
|
+
)
|
|
116
|
+
},
|
|
117
|
+
help: emittedHelp,
|
|
118
|
+
},
|
|
119
|
+
push: {
|
|
120
|
+
name: "push",
|
|
121
|
+
description: "Emit, push to remote, return to source",
|
|
122
|
+
run: async (args: string[], options: Record<string, any>) => {
|
|
123
|
+
if (options.help) {
|
|
124
|
+
console.log(pushHelp)
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
await runCommand(
|
|
128
|
+
push({
|
|
129
|
+
baseBranch: args[0],
|
|
130
|
+
branch: options.branch,
|
|
131
|
+
silent: options.silent,
|
|
132
|
+
force: options.force,
|
|
133
|
+
verbose: options.verbose,
|
|
134
|
+
pr: options.pr,
|
|
135
|
+
}),
|
|
136
|
+
)
|
|
137
|
+
},
|
|
138
|
+
help: pushHelp,
|
|
139
|
+
},
|
|
140
|
+
pull: {
|
|
141
|
+
name: "pull",
|
|
142
|
+
description: "Pull commits from remote emit branch to source",
|
|
143
|
+
run: async (_args: string[], options: Record<string, any>) => {
|
|
144
|
+
if (options.help) {
|
|
145
|
+
console.log(pullHelp)
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
await runCommand(
|
|
149
|
+
pull({
|
|
150
|
+
remote: options.remote,
|
|
151
|
+
silent: options.silent,
|
|
152
|
+
verbose: options.verbose,
|
|
153
|
+
}),
|
|
154
|
+
)
|
|
155
|
+
},
|
|
156
|
+
help: pullHelp,
|
|
157
|
+
},
|
|
158
|
+
template: {
|
|
159
|
+
name: "template",
|
|
160
|
+
description: "Template management commands",
|
|
161
|
+
run: async (args: string[], options: Record<string, any>) => {
|
|
162
|
+
if (options.help) {
|
|
163
|
+
console.log(templateHelp)
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
await runCommand(
|
|
167
|
+
template({
|
|
168
|
+
subcommand: args[0],
|
|
169
|
+
args: args.slice(1),
|
|
170
|
+
silent: options.silent,
|
|
171
|
+
verbose: options.verbose,
|
|
172
|
+
template: options.template,
|
|
173
|
+
}),
|
|
174
|
+
)
|
|
175
|
+
},
|
|
176
|
+
help: templateHelp,
|
|
177
|
+
},
|
|
178
|
+
base: {
|
|
179
|
+
name: "base",
|
|
180
|
+
description: "Get or set the base branch",
|
|
181
|
+
run: async (args: string[], options: Record<string, any>) => {
|
|
182
|
+
if (options.help) {
|
|
183
|
+
console.log(baseHelp)
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
await runCommand(
|
|
187
|
+
base({
|
|
188
|
+
subcommand: args[0],
|
|
189
|
+
args: args.slice(1),
|
|
190
|
+
repo: options.repo,
|
|
191
|
+
silent: options.silent,
|
|
192
|
+
verbose: options.verbose,
|
|
193
|
+
}),
|
|
194
|
+
)
|
|
195
|
+
},
|
|
196
|
+
help: baseHelp,
|
|
197
|
+
},
|
|
198
|
+
switch: {
|
|
199
|
+
name: "switch",
|
|
200
|
+
description: "Toggle between source and emitted branch",
|
|
201
|
+
run: async (_args: string[], options: Record<string, any>) => {
|
|
202
|
+
if (options.help) {
|
|
203
|
+
console.log(switchHelp)
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
await runCommand(
|
|
207
|
+
switchBranch({ silent: options.silent, verbose: options.verbose }),
|
|
208
|
+
)
|
|
209
|
+
},
|
|
210
|
+
help: switchHelp,
|
|
211
|
+
},
|
|
212
|
+
source: {
|
|
213
|
+
name: "source",
|
|
214
|
+
description: "Switch to source branch from emitted branch",
|
|
215
|
+
run: async (_args: string[], options: Record<string, any>) => {
|
|
216
|
+
if (options.help) {
|
|
217
|
+
console.log(sourceHelp)
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
await runCommand(
|
|
221
|
+
source({ silent: options.silent, verbose: options.verbose }),
|
|
222
|
+
)
|
|
223
|
+
},
|
|
224
|
+
help: sourceHelp,
|
|
225
|
+
},
|
|
226
|
+
merge: {
|
|
227
|
+
name: "merge",
|
|
228
|
+
description: "Merge emitted branch into base branch",
|
|
229
|
+
run: async (_args: string[], options: Record<string, any>) => {
|
|
230
|
+
if (options.help) {
|
|
231
|
+
console.log(mergeHelp)
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
await runCommand(
|
|
235
|
+
merge({
|
|
236
|
+
silent: options.silent,
|
|
237
|
+
verbose: options.verbose,
|
|
238
|
+
squash: options.squash,
|
|
239
|
+
push: options.push,
|
|
240
|
+
}),
|
|
241
|
+
)
|
|
242
|
+
},
|
|
243
|
+
help: mergeHelp,
|
|
244
|
+
},
|
|
245
|
+
task: {
|
|
246
|
+
name: "task",
|
|
247
|
+
description: "Task management commands",
|
|
248
|
+
run: async (args: string[], options: Record<string, any>) => {
|
|
249
|
+
if (options.help) {
|
|
250
|
+
console.log(taskHelp)
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
// Initialize with optional branch name
|
|
254
|
+
const branch = args[0] || options.branch
|
|
255
|
+
await runCommand(
|
|
256
|
+
task({
|
|
257
|
+
branch,
|
|
258
|
+
silent: options.silent,
|
|
259
|
+
verbose: options.verbose,
|
|
260
|
+
task: options.task,
|
|
261
|
+
from: options.from,
|
|
262
|
+
fromCurrent: options["from-current"],
|
|
263
|
+
}),
|
|
264
|
+
)
|
|
265
|
+
},
|
|
266
|
+
help: taskHelp,
|
|
267
|
+
},
|
|
268
|
+
tasks: {
|
|
269
|
+
name: "tasks",
|
|
270
|
+
description: "List all task branches",
|
|
271
|
+
run: async (_args: string[], options: Record<string, any>) => {
|
|
272
|
+
if (options.help) {
|
|
273
|
+
console.log(tasksHelp)
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
await runCommand(
|
|
277
|
+
tasks({
|
|
278
|
+
silent: options.silent,
|
|
279
|
+
verbose: options.verbose,
|
|
280
|
+
json: options.json,
|
|
281
|
+
}),
|
|
282
|
+
)
|
|
283
|
+
},
|
|
284
|
+
help: tasksHelp,
|
|
285
|
+
},
|
|
286
|
+
edit: {
|
|
287
|
+
name: "edit",
|
|
288
|
+
description: "Open TASK.md in system editor",
|
|
289
|
+
run: async (_args: string[], options: Record<string, any>) => {
|
|
290
|
+
if (options.help) {
|
|
291
|
+
console.log(`
|
|
292
|
+
Usage: agency edit [options]
|
|
293
|
+
|
|
294
|
+
Open TASK.md in the system editor for editing.
|
|
295
|
+
|
|
296
|
+
Notes:
|
|
297
|
+
- Requires TASK.md to exist (run 'agency task' first)
|
|
298
|
+
- Respects VISUAL and EDITOR environment variables
|
|
299
|
+
- On macOS, defaults to 'open' which uses the default app for .md files
|
|
300
|
+
- On other platforms, defaults to 'vim'
|
|
301
|
+
- The command waits for the editor to close before returning
|
|
302
|
+
|
|
303
|
+
Example:
|
|
304
|
+
agency edit # Open TASK.md in default editor
|
|
305
|
+
`)
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
await runCommand(
|
|
309
|
+
taskEdit({
|
|
310
|
+
silent: options.silent,
|
|
311
|
+
verbose: options.verbose,
|
|
312
|
+
}),
|
|
313
|
+
)
|
|
314
|
+
},
|
|
315
|
+
help: `Open TASK.md in system editor`,
|
|
316
|
+
},
|
|
317
|
+
work: {
|
|
318
|
+
name: "work",
|
|
319
|
+
description: "Start working on TASK.md with OpenCode",
|
|
320
|
+
run: async (args: string[], options: Record<string, any>) => {
|
|
321
|
+
if (options.help) {
|
|
322
|
+
console.log(workHelp)
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Extract extra args (anything after --)
|
|
327
|
+
// Note: parseArgs with strict:false and allowPositionals:true will put
|
|
328
|
+
// args after -- in the positionals array
|
|
329
|
+
const extraArgs = args.length > 0 ? args : undefined
|
|
330
|
+
|
|
331
|
+
await runCommand(
|
|
332
|
+
work({
|
|
333
|
+
silent: options.silent,
|
|
334
|
+
verbose: options.verbose,
|
|
335
|
+
opencode: options.opencode,
|
|
336
|
+
claude: options.claude,
|
|
337
|
+
extraArgs,
|
|
338
|
+
}),
|
|
339
|
+
)
|
|
340
|
+
},
|
|
341
|
+
help: workHelp,
|
|
342
|
+
},
|
|
343
|
+
status: {
|
|
344
|
+
name: "status",
|
|
345
|
+
description: "Show agency status for this repository",
|
|
346
|
+
run: async (_args: string[], options: Record<string, any>) => {
|
|
347
|
+
if (options.help) {
|
|
348
|
+
console.log(statusHelp)
|
|
349
|
+
return
|
|
350
|
+
}
|
|
351
|
+
await runCommand(
|
|
352
|
+
status({
|
|
353
|
+
silent: options.silent,
|
|
354
|
+
verbose: options.verbose,
|
|
355
|
+
json: options.json,
|
|
356
|
+
}),
|
|
357
|
+
)
|
|
358
|
+
},
|
|
359
|
+
help: statusHelp,
|
|
360
|
+
},
|
|
361
|
+
clean: {
|
|
362
|
+
name: "clean",
|
|
363
|
+
description: "Delete branches merged into a specified branch",
|
|
364
|
+
run: async (_args: string[], options: Record<string, any>) => {
|
|
365
|
+
if (options.help) {
|
|
366
|
+
console.log(cleanHelp)
|
|
367
|
+
return
|
|
368
|
+
}
|
|
369
|
+
await runCommand(
|
|
370
|
+
clean({
|
|
371
|
+
silent: options.silent,
|
|
372
|
+
verbose: options.verbose,
|
|
373
|
+
dryRun: options["dry-run"],
|
|
374
|
+
mergedInto: options["merged-into"],
|
|
375
|
+
}),
|
|
376
|
+
)
|
|
377
|
+
},
|
|
378
|
+
help: cleanHelp,
|
|
379
|
+
},
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function showMainHelp() {
|
|
383
|
+
console.log(`
|
|
384
|
+
agency v${VERSION}
|
|
385
|
+
|
|
386
|
+
Usage: agency <command> [options]
|
|
387
|
+
|
|
388
|
+
Commands:
|
|
389
|
+
init Initialize agency with template selection (run first)
|
|
390
|
+
task [branch] Initialize template files on a feature branch
|
|
391
|
+
tasks List all task branches
|
|
392
|
+
edit Open TASK.md in system editor
|
|
393
|
+
work Start working on TASK.md with OpenCode
|
|
394
|
+
template Template management commands
|
|
395
|
+
use [template] Set template for this repository
|
|
396
|
+
save <file|dir> ... Save files/dirs to configured template
|
|
397
|
+
list List all files in configured template
|
|
398
|
+
view <file> View contents of a file in template
|
|
399
|
+
delete <file> ... Delete files from configured template
|
|
400
|
+
emit [base-branch] Emit a branch with backpack files reverted
|
|
401
|
+
emitted Get the name of the emitted branch
|
|
402
|
+
push [base-branch] Emit, push to remote, return to source
|
|
403
|
+
pull Pull commits from remote emit branch to source
|
|
404
|
+
base Get or set the base branch
|
|
405
|
+
set <branch> Set the base branch for the current feature branch
|
|
406
|
+
get Get the configured base branch
|
|
407
|
+
switch Toggle between source and emitted branch
|
|
408
|
+
source Switch to source branch from emitted branch
|
|
409
|
+
merge Merge emitted branch into base branch
|
|
410
|
+
status Show agency status for this repository
|
|
411
|
+
|
|
412
|
+
Global Options:
|
|
413
|
+
-h, --help Show help for a command
|
|
414
|
+
-v, --version Show version number
|
|
415
|
+
--no-color Disable color output
|
|
416
|
+
-s, --silent Suppress output messages
|
|
417
|
+
-v, --verbose Show verbose output including detailed debugging info
|
|
418
|
+
|
|
419
|
+
Examples:
|
|
420
|
+
agency init # Initialize with template (run first)
|
|
421
|
+
agency task # Initialize on current feature branch
|
|
422
|
+
agency task my-feature # Create 'my-feature' branch and initialize
|
|
423
|
+
agency emit # Emit a branch (prompts for base branch)
|
|
424
|
+
agency switch # Toggle between source and emitted branch
|
|
425
|
+
|
|
426
|
+
For more information about a command, run:
|
|
427
|
+
agency <command> --help
|
|
428
|
+
`)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Parse global arguments
|
|
432
|
+
try {
|
|
433
|
+
const { values, positionals } = parseArgs({
|
|
434
|
+
args: process.argv.slice(2),
|
|
435
|
+
options: {
|
|
436
|
+
help: {
|
|
437
|
+
type: "boolean",
|
|
438
|
+
short: "h",
|
|
439
|
+
},
|
|
440
|
+
version: {
|
|
441
|
+
type: "boolean",
|
|
442
|
+
short: "v",
|
|
443
|
+
},
|
|
444
|
+
"no-color": {
|
|
445
|
+
type: "boolean",
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
strict: false,
|
|
449
|
+
allowPositionals: true,
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
// Handle --no-color flag
|
|
453
|
+
if (values["no-color"]) {
|
|
454
|
+
setColorsEnabled(false)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Handle global flags
|
|
458
|
+
if (values.version) {
|
|
459
|
+
console.log(`v${VERSION}`)
|
|
460
|
+
process.exit(0)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Get command
|
|
464
|
+
const commandName = positionals[0]
|
|
465
|
+
|
|
466
|
+
// Show help if no command
|
|
467
|
+
if (!commandName) {
|
|
468
|
+
showMainHelp()
|
|
469
|
+
process.exit(1)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Show main help if --help with no command
|
|
473
|
+
if (values.help && !commandName) {
|
|
474
|
+
showMainHelp()
|
|
475
|
+
process.exit(0)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Check if command exists
|
|
479
|
+
const command = commands[commandName]
|
|
480
|
+
if (!command) {
|
|
481
|
+
console.error(`Error: Unknown command '${commandName}'`)
|
|
482
|
+
console.error("\nRun 'agency --help' for usage information.")
|
|
483
|
+
process.exit(1)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Parse command-specific arguments
|
|
487
|
+
const commandArgs = process.argv.slice(3)
|
|
488
|
+
const { values: cmdValues, positionals: cmdPositionals } = parseArgs({
|
|
489
|
+
args: commandArgs,
|
|
490
|
+
options: {
|
|
491
|
+
help: {
|
|
492
|
+
type: "boolean",
|
|
493
|
+
short: "h",
|
|
494
|
+
},
|
|
495
|
+
silent: {
|
|
496
|
+
type: "boolean",
|
|
497
|
+
short: "s",
|
|
498
|
+
},
|
|
499
|
+
force: {
|
|
500
|
+
type: "boolean",
|
|
501
|
+
short: "f",
|
|
502
|
+
},
|
|
503
|
+
verbose: {
|
|
504
|
+
type: "boolean",
|
|
505
|
+
short: "v",
|
|
506
|
+
},
|
|
507
|
+
template: {
|
|
508
|
+
type: "string",
|
|
509
|
+
short: "t",
|
|
510
|
+
},
|
|
511
|
+
branch: {
|
|
512
|
+
type: "string",
|
|
513
|
+
short: "b",
|
|
514
|
+
},
|
|
515
|
+
task: {
|
|
516
|
+
type: "string",
|
|
517
|
+
},
|
|
518
|
+
from: {
|
|
519
|
+
type: "string",
|
|
520
|
+
},
|
|
521
|
+
"from-current": {
|
|
522
|
+
type: "boolean",
|
|
523
|
+
},
|
|
524
|
+
json: {
|
|
525
|
+
type: "boolean",
|
|
526
|
+
},
|
|
527
|
+
repo: {
|
|
528
|
+
type: "boolean",
|
|
529
|
+
},
|
|
530
|
+
pr: {
|
|
531
|
+
type: "boolean",
|
|
532
|
+
},
|
|
533
|
+
squash: {
|
|
534
|
+
type: "boolean",
|
|
535
|
+
},
|
|
536
|
+
push: {
|
|
537
|
+
type: "boolean",
|
|
538
|
+
},
|
|
539
|
+
remote: {
|
|
540
|
+
type: "string",
|
|
541
|
+
short: "r",
|
|
542
|
+
},
|
|
543
|
+
"merged-into": {
|
|
544
|
+
type: "string",
|
|
545
|
+
},
|
|
546
|
+
"dry-run": {
|
|
547
|
+
type: "boolean",
|
|
548
|
+
},
|
|
549
|
+
opencode: {
|
|
550
|
+
type: "boolean",
|
|
551
|
+
},
|
|
552
|
+
claude: {
|
|
553
|
+
type: "boolean",
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
strict: false,
|
|
557
|
+
allowPositionals: true,
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
// Run the command
|
|
561
|
+
await command.run(cmdPositionals, cmdValues)
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if (error instanceof Error) {
|
|
564
|
+
console.error(`ⓘ ${error.message}`)
|
|
565
|
+
} else {
|
|
566
|
+
console.error("An unexpected error occurred:", error)
|
|
567
|
+
}
|
|
568
|
+
process.exit(1)
|
|
569
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log("Hello via Bun!")
|