@markjaquith/agency 2.65.2 → 2.66.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 +11 -8
- package/package.json +1 -1
- package/src/commands/work.test.ts +34 -7
- package/src/commands/work.ts +13 -6
- package/src/workbase/runner-command.test.ts +12 -0
- package/src/workbase/runner-command.ts +6 -0
package/README.md
CHANGED
|
@@ -347,8 +347,10 @@ external effects the failed invocation may have completed.
|
|
|
347
347
|
|
|
348
348
|
### Agent Runners
|
|
349
349
|
|
|
350
|
-
OpenCode and Claude Code are built-in runner presets.
|
|
351
|
-
|
|
350
|
+
OpenCode (`opencode2` and `opencode`) and Claude Code are built-in runner presets.
|
|
351
|
+
Without an explicit runner, Agency uses the first available executable in this
|
|
352
|
+
order: `opencode2`, `opencode`, then `claude`. Select a preset or configured
|
|
353
|
+
runner with `agency work --runner <name>`. A launch is fresh unless
|
|
352
354
|
`AGENCY_SESSION_ID` is already set; resumed launches use the runner's
|
|
353
355
|
`resumeCommand` when configured. The built-in presets use `--continue` only for
|
|
354
356
|
resumed launches. By default Agency opens the runner without a prompt. `--auto`
|
|
@@ -399,8 +401,9 @@ to the receiving OpenCode session, injects an explicit active-worker system
|
|
|
399
401
|
instruction, and restores Agency identity for that session's shell environment.
|
|
400
402
|
This session bridge is necessary because an OpenCode client can attach to a
|
|
401
403
|
long-lived server process that did not inherit the client's launch environment.
|
|
402
|
-
The `opencode`
|
|
403
|
-
the workbase `AGENTS.md` and managed OpenCode config are
|
|
404
|
+
The `opencode2` and `opencode` runners remain rooted in their task or epic
|
|
405
|
+
working directory so the workbase `AGENTS.md` and managed OpenCode config are
|
|
406
|
+
discovered normally.
|
|
404
407
|
Agency's managed OpenCode plugin grants the active workbase external-directory
|
|
405
408
|
access and adds existing checkout-local `.claude/skills`, `.agents/skills`, and
|
|
406
409
|
`.opencode/{skill,skills}` directories to `skills.paths`.
|
|
@@ -1063,10 +1066,10 @@ configured external chooser. Pass a directory, including `.` for the current
|
|
|
1063
1066
|
directory, to infer its epic, task, or phase. Outside a workbase, Agency first
|
|
1064
1067
|
presents the registered workbases, then the selected workbase's hierarchy.
|
|
1065
1068
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
their built-in presets. Launches are interactive and
|
|
1069
|
-
`--auto` to send Agency's generated context prompt.
|
|
1069
|
+
Agency automatically uses the first available runner in this order: `opencode2`,
|
|
1070
|
+
`opencode`, then `claude`. `--opencode` and `--claude` remain aliases for
|
|
1071
|
+
requiring their corresponding built-in presets. Launches are interactive and
|
|
1072
|
+
promptless by default; use `--auto` to send Agency's generated context prompt.
|
|
1070
1073
|
|
|
1071
1074
|
`agency work prepare` resolves an execution unit and creates or reuses its
|
|
1072
1075
|
writable and reference worktrees, or its single pinned review checkout, without
|
package/package.json
CHANGED
|
@@ -1050,18 +1050,45 @@ describe("work command", () => {
|
|
|
1050
1050
|
).toBeUndefined()
|
|
1051
1051
|
})
|
|
1052
1052
|
|
|
1053
|
-
test("automatically
|
|
1054
|
-
const harness = createHarness(
|
|
1053
|
+
test("automatically prefers opencode2", async () => {
|
|
1054
|
+
const harness = createHarness()
|
|
1055
|
+
|
|
1056
|
+
await harness.run({ taskId: "example" })
|
|
1057
|
+
|
|
1058
|
+
expect(harness.probes).toEqual(["opencode2"])
|
|
1059
|
+
expect(harness.launches[0]).toEqual({
|
|
1060
|
+
cli: "opencode2",
|
|
1061
|
+
args: ["opencode2"],
|
|
1062
|
+
cwd: taskDirectory,
|
|
1063
|
+
})
|
|
1064
|
+
})
|
|
1065
|
+
|
|
1066
|
+
test("automatically falls back from opencode2 to opencode", async () => {
|
|
1067
|
+
const harness = createHarness({ available: { opencode2: false } })
|
|
1055
1068
|
|
|
1056
1069
|
await harness.run({ taskId: "example" })
|
|
1057
1070
|
|
|
1058
|
-
expect(harness.probes).toEqual(["
|
|
1071
|
+
expect(harness.probes).toEqual(["opencode2", "opencode"])
|
|
1072
|
+
expect(harness.launches[0]).toEqual({
|
|
1073
|
+
cli: "opencode",
|
|
1074
|
+
args: ["opencode"],
|
|
1075
|
+
cwd: taskDirectory,
|
|
1076
|
+
})
|
|
1077
|
+
})
|
|
1078
|
+
|
|
1079
|
+
test("automatically falls back from OpenCode to Claude", async () => {
|
|
1080
|
+
const harness = createHarness({
|
|
1081
|
+
available: { opencode2: false, opencode: false },
|
|
1082
|
+
})
|
|
1083
|
+
|
|
1084
|
+
await harness.run({ taskId: "example" })
|
|
1085
|
+
|
|
1086
|
+
expect(harness.probes).toEqual(["opencode2", "opencode", "claude"])
|
|
1059
1087
|
expect(harness.launches[0]).toEqual({
|
|
1060
1088
|
cli: "claude",
|
|
1061
1089
|
args: ["claude"],
|
|
1062
1090
|
cwd: taskDirectory,
|
|
1063
1091
|
})
|
|
1064
|
-
expect(harness.launchEnvironments[0]?.OPENCODE_CONFIG).toBeUndefined()
|
|
1065
1092
|
})
|
|
1066
1093
|
|
|
1067
1094
|
test("does not fall back when OpenCode is explicitly required", async () => {
|
|
@@ -1090,13 +1117,13 @@ describe("work command", () => {
|
|
|
1090
1117
|
|
|
1091
1118
|
test("fails when neither agent tool is available", async () => {
|
|
1092
1119
|
const harness = createHarness({
|
|
1093
|
-
available: { opencode: false, claude: false },
|
|
1120
|
+
available: { opencode2: false, opencode: false, claude: false },
|
|
1094
1121
|
})
|
|
1095
1122
|
|
|
1096
1123
|
await expect(harness.run({ taskId: "example" })).rejects.toThrow(
|
|
1097
1124
|
"claude CLI tool not found",
|
|
1098
1125
|
)
|
|
1099
|
-
expect(harness.probes).toEqual(["opencode", "claude"])
|
|
1126
|
+
expect(harness.probes).toEqual(["opencode2", "opencode", "claude"])
|
|
1100
1127
|
expect(harness.launches).toEqual([])
|
|
1101
1128
|
})
|
|
1102
1129
|
|
|
@@ -1150,7 +1177,7 @@ describe("work command", () => {
|
|
|
1150
1177
|
verboseHarness.run({ taskId: "example", verbose: true }),
|
|
1151
1178
|
)
|
|
1152
1179
|
expect(verboseLogs).toEqual([
|
|
1153
|
-
"Launching command:
|
|
1180
|
+
"Launching command: opencode2 (cwd: /workbase/tasks/example)",
|
|
1154
1181
|
])
|
|
1155
1182
|
expect(verboseHarness.materializeOptions[0]?.verbose).toBe(true)
|
|
1156
1183
|
|
package/src/commands/work.ts
CHANGED
|
@@ -280,10 +280,16 @@ export const work = (
|
|
|
280
280
|
options.claude ||
|
|
281
281
|
process.env.AGENCY_RUNNER,
|
|
282
282
|
)
|
|
283
|
-
|
|
283
|
+
const defaultRunners = ["opencode2", "opencode", "claude"] as const
|
|
284
|
+
let defaultRunnerIndex = 0
|
|
285
|
+
let runner: string =
|
|
284
286
|
options.runner ??
|
|
285
287
|
process.env.AGENCY_RUNNER ??
|
|
286
|
-
(options.claude
|
|
288
|
+
(options.claude
|
|
289
|
+
? "claude"
|
|
290
|
+
: options.opencode
|
|
291
|
+
? "opencode"
|
|
292
|
+
: defaultRunners[defaultRunnerIndex]!)
|
|
287
293
|
const claimant = process.env.AGENCY_CLAIMANT ?? process.env.USER ?? "agency"
|
|
288
294
|
const sessionId =
|
|
289
295
|
process.env.AGENCY_SESSION_ID ?? `${process.pid}-${Date.now()}`
|
|
@@ -311,12 +317,12 @@ export const work = (
|
|
|
311
317
|
let available = yield* fs.runCommand(["which", cli], {
|
|
312
318
|
captureOutput: true,
|
|
313
319
|
})
|
|
314
|
-
|
|
320
|
+
while (
|
|
315
321
|
available.exitCode !== 0 &&
|
|
316
322
|
!explicitlyRequested &&
|
|
317
|
-
|
|
323
|
+
defaultRunnerIndex < defaultRunners.length - 1
|
|
318
324
|
) {
|
|
319
|
-
runner =
|
|
325
|
+
runner = defaultRunners[++defaultRunnerIndex]!
|
|
320
326
|
resolved = resolveRunnerCommand(
|
|
321
327
|
runner,
|
|
322
328
|
config.runners,
|
|
@@ -580,7 +586,8 @@ Launch an agent for an epic, task, or phase. With no directory, select one
|
|
|
580
586
|
interactively. A positional argument resolves as a directory first, then as a task
|
|
581
587
|
ID. Use '.' for the current directory. Outside a workbase, select a registered
|
|
582
588
|
workbase first. Managed OpenCode launches receive whole-workbase access through
|
|
583
|
-
Agency's project plugin; Agency context remains authoritative for writes.
|
|
589
|
+
Agency's project plugin; Agency context remains authoritative for writes. Automatic
|
|
590
|
+
runner discovery checks opencode2, opencode, then claude.
|
|
584
591
|
|
|
585
592
|
The prepare subcommand resolves and materializes an execution workspace without
|
|
586
593
|
launching an agent or changing lifecycle status. --dry-run reports planned Git
|
|
@@ -19,6 +19,12 @@ const variables = {
|
|
|
19
19
|
|
|
20
20
|
describe("runner commands", () => {
|
|
21
21
|
test("uses promptless interactive commands for built-in presets", () => {
|
|
22
|
+
expect(
|
|
23
|
+
resolveRunnerCommand("opencode2", undefined, variables, false).argv,
|
|
24
|
+
).toEqual(["opencode2"])
|
|
25
|
+
expect(
|
|
26
|
+
resolveRunnerCommand("opencode2", undefined, variables, true).argv,
|
|
27
|
+
).toEqual(["opencode2", "--continue"])
|
|
22
28
|
expect(
|
|
23
29
|
resolveRunnerCommand("opencode", undefined, variables, false).argv,
|
|
24
30
|
).toEqual(["opencode"])
|
|
@@ -31,6 +37,12 @@ describe("runner commands", () => {
|
|
|
31
37
|
})
|
|
32
38
|
|
|
33
39
|
test("uses autonomous commands when a prompt is requested", () => {
|
|
40
|
+
expect(
|
|
41
|
+
resolveRunnerCommand("opencode2", undefined, variables, false, true).argv,
|
|
42
|
+
).toEqual(["opencode2", "--prompt", "Read the task."])
|
|
43
|
+
expect(
|
|
44
|
+
resolveRunnerCommand("opencode2", undefined, variables, true, true).argv,
|
|
45
|
+
).toEqual(["opencode2", "--continue", "--prompt", "Read the task."])
|
|
34
46
|
expect(
|
|
35
47
|
resolveRunnerCommand("opencode", undefined, variables, false, true).argv,
|
|
36
48
|
).toEqual(["opencode", "--prompt", "Read the task."])
|
|
@@ -31,6 +31,12 @@ const PLACEHOLDERS = new Set<keyof RunnerCommandVariables>([
|
|
|
31
31
|
])
|
|
32
32
|
|
|
33
33
|
const BUILTIN_RUNNERS: Readonly<Record<string, RunnerDefinition>> = {
|
|
34
|
+
opencode2: {
|
|
35
|
+
command: ["opencode2"],
|
|
36
|
+
autoCommand: ["opencode2", "--prompt", "{prompt}"],
|
|
37
|
+
resumeCommand: ["opencode2", "--continue"],
|
|
38
|
+
autoResumeCommand: ["opencode2", "--continue", "--prompt", "{prompt}"],
|
|
39
|
+
},
|
|
34
40
|
opencode: {
|
|
35
41
|
command: ["opencode"],
|
|
36
42
|
autoCommand: ["opencode", "--prompt", "{prompt}"],
|