@markjaquith/agency 2.7.0 → 2.7.1
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 +4 -2
- package/package.json +1 -1
- package/src/commands/work.test.ts +38 -4
- package/src/commands/work.ts +20 -10
package/cli.ts
CHANGED
|
@@ -240,7 +240,9 @@ const commands: Record<string, Command> = {
|
|
|
240
240
|
return
|
|
241
241
|
}
|
|
242
242
|
if (args.length > 1) {
|
|
243
|
-
throw new Error(
|
|
243
|
+
throw new Error(
|
|
244
|
+
"Usage: agency work [<directory-or-task-id> | --epic <epic-id>]",
|
|
245
|
+
)
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
await runCommand(
|
|
@@ -301,7 +303,7 @@ Commands:
|
|
|
301
303
|
phase <subcommand> Manage task phases
|
|
302
304
|
archive <type> Archive a work item
|
|
303
305
|
task <subcommand> Manage tasks
|
|
304
|
-
work [directory]
|
|
306
|
+
work [directory|task] Work on an epic, task, or phase
|
|
305
307
|
pr create Create a pull request for an execution unit
|
|
306
308
|
repo <subcommand> Manage workbase repositories
|
|
307
309
|
status Show status for the current workbase
|
package/package.json
CHANGED
|
@@ -46,12 +46,14 @@ interface HarnessOptions {
|
|
|
46
46
|
readonly phaseRecords?: readonly any[]
|
|
47
47
|
readonly outsideWorkbase?: boolean
|
|
48
48
|
readonly registeredWorkbases?: readonly string[]
|
|
49
|
+
readonly existingDirectories?: readonly string[]
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
const createHarness = (options: HarnessOptions = {}) => {
|
|
52
53
|
const events: string[] = []
|
|
53
54
|
const probes: string[] = []
|
|
54
55
|
const statusUpdates: string[] = []
|
|
56
|
+
const shownTasks: string[] = []
|
|
55
57
|
const progressUpdates: string[] = []
|
|
56
58
|
const launches: Array<{
|
|
57
59
|
cli: string
|
|
@@ -95,14 +97,16 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
95
97
|
list: () => Effect.succeed(options.epicRecords ?? []),
|
|
96
98
|
}
|
|
97
99
|
const tasks = {
|
|
98
|
-
show: (id: string) =>
|
|
99
|
-
|
|
100
|
+
show: (id: string) => {
|
|
101
|
+
shownTasks.push(id)
|
|
102
|
+
return Effect.succeed({
|
|
100
103
|
id,
|
|
101
104
|
path: `/workbase/tasks/${id}/TASK.md`,
|
|
102
105
|
data: options.multiPhaseTasks?.includes(id)
|
|
103
106
|
? { phases: [] }
|
|
104
107
|
: { repo: "agency", branch: `task/${id}`, base: "main" },
|
|
105
|
-
})
|
|
108
|
+
})
|
|
109
|
+
},
|
|
106
110
|
list: () => Effect.succeed(options.taskRecords ?? []),
|
|
107
111
|
setStatus: (id: string, status: string) => {
|
|
108
112
|
statusUpdates.push(`task:${id}:${status}`)
|
|
@@ -124,7 +128,8 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
124
128
|
},
|
|
125
129
|
}
|
|
126
130
|
const fs = {
|
|
127
|
-
isDirectory: () =>
|
|
131
|
+
isDirectory: (path: string) =>
|
|
132
|
+
Effect.succeed(options.existingDirectories?.includes(path) ?? true),
|
|
128
133
|
runCommand: (args: readonly string[]) => {
|
|
129
134
|
const cli = args[1] as "opencode" | "claude"
|
|
130
135
|
events.push(`probe:${cli}`)
|
|
@@ -169,6 +174,7 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
169
174
|
launches,
|
|
170
175
|
materializeOptions,
|
|
171
176
|
statusUpdates,
|
|
177
|
+
shownTasks,
|
|
172
178
|
progressUpdates,
|
|
173
179
|
run,
|
|
174
180
|
}
|
|
@@ -192,6 +198,34 @@ describe("work command", () => {
|
|
|
192
198
|
})
|
|
193
199
|
})
|
|
194
200
|
|
|
201
|
+
test("resolves an existing positional path before treating it as a task ID", async () => {
|
|
202
|
+
const harness = createHarness({
|
|
203
|
+
existingDirectories: ["/workbase/tasks/delivery"],
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
await harness.run({
|
|
207
|
+
cwd: "/workbase/tasks/delivery",
|
|
208
|
+
directory: ".",
|
|
209
|
+
opencode: true,
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
expect(harness.shownTasks).toEqual(["delivery"])
|
|
213
|
+
expect(harness.launches[0]?.cwd).toBe("/workbase/tasks/delivery")
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test("treats a positional value as a task ID when it is not a directory", async () => {
|
|
217
|
+
const harness = createHarness({ existingDirectories: [] })
|
|
218
|
+
|
|
219
|
+
await harness.run({
|
|
220
|
+
cwd: "/workbase",
|
|
221
|
+
directory: "delivery",
|
|
222
|
+
opencode: true,
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
expect(harness.shownTasks).toEqual(["delivery"])
|
|
226
|
+
expect(harness.launches[0]?.cwd).toBe("/workbase/tasks/delivery")
|
|
227
|
+
})
|
|
228
|
+
|
|
195
229
|
test("launches a multi-phase task agent without materializing", async () => {
|
|
196
230
|
const harness = createHarness({ multiPhaseTasks: ["delivery"] })
|
|
197
231
|
|
package/src/commands/work.ts
CHANGED
|
@@ -77,12 +77,13 @@ export const work = (
|
|
|
77
77
|
const phases = yield* PhaseService
|
|
78
78
|
const { log, verboseLog } = createLoggers(options)
|
|
79
79
|
const cwd = options.cwd ?? process.cwd()
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
85
|
-
|
|
80
|
+
const directoryPath = options.directory
|
|
81
|
+
? resolve(cwd, options.directory)
|
|
82
|
+
: undefined
|
|
83
|
+
const isDirectory = directoryPath
|
|
84
|
+
? yield* fs.isDirectory(directoryPath)
|
|
85
|
+
: false
|
|
86
|
+
const startPath = isDirectory && directoryPath ? directoryPath : cwd
|
|
86
87
|
const root = yield* resolveWorkbase(startPath, log, pickBase)
|
|
87
88
|
if (!root) return
|
|
88
89
|
|
|
@@ -108,7 +109,15 @@ export const work = (
|
|
|
108
109
|
multiPhase: "phases" in task.data,
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
|
-
} else if (options.directory) {
|
|
112
|
+
} else if (options.directory && !isDirectory) {
|
|
113
|
+
const task = yield* tasks.show(options.directory, root)
|
|
114
|
+
target = {
|
|
115
|
+
kind: "task",
|
|
116
|
+
taskId: task.id,
|
|
117
|
+
path: task.path,
|
|
118
|
+
multiPhase: "phases" in task.data,
|
|
119
|
+
}
|
|
120
|
+
} else if (directoryPath) {
|
|
112
121
|
const path = relative(root, startPath)
|
|
113
122
|
const parts =
|
|
114
123
|
!path || isAbsolute(path) || path.startsWith(`..${sep}`)
|
|
@@ -226,11 +235,12 @@ export const work = (
|
|
|
226
235
|
})
|
|
227
236
|
|
|
228
237
|
export const help = `
|
|
229
|
-
Usage: agency work [<directory> | --epic <epic-id>]
|
|
238
|
+
Usage: agency work [<directory-or-task-id> | --epic <epic-id>]
|
|
230
239
|
|
|
231
240
|
Launch an agent for an epic, task, or phase. With no directory, select one
|
|
232
|
-
with fzf.
|
|
233
|
-
|
|
241
|
+
with fzf. A positional argument resolves as a directory first, then as a task
|
|
242
|
+
ID. Use '.' for the current directory. Outside a workbase, select a registered
|
|
243
|
+
workbase first.
|
|
234
244
|
|
|
235
245
|
Options:
|
|
236
246
|
--epic <id> Work on an epic
|