@markjaquith/agency 3.5.2 → 3.6.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 CHANGED
@@ -7,7 +7,7 @@ read or write.
7
7
 
8
8
  ## Requirements
9
9
 
10
- - [Bun](https://bun.sh) 1.0 or newer
10
+ - [Bun](https://bun.sh) 1.3 or newer (1.4 or newer on Windows ARM64)
11
11
  - Git
12
12
  - [GitHub CLI](https://cli.github.com/) for `agency pr`
13
13
  - OpenCode, Claude Code, or a configured agent for `agency work`
@@ -558,23 +558,123 @@ agency validate
558
558
 
559
559
  ### Interactive Actions
560
560
 
561
- `agency act` opens a filterable work-item chooser followed by an action palette
562
- derived from the selected item's current state and readiness. It offers only
563
- actions that can use existing lifecycle semantics, such as working, creating a
564
- pull request, dropping, reopening, or archiving. Cancelling either chooser makes
565
- no changes, and the command refreshes the graph before dispatch so a stale
566
- selection cannot act on changed work.
561
+ `agency` with no subcommand (or `agency act`) opens on **Workstream**, a flat list
562
+ of non-archived tasks and phases; select an item to see its available actions.
563
+ Press **Tab** to cycle to **Workbase** for workbase actions. Both tabs have a blank
564
+ row above a chevron-prefixed filter with the placeholder “type to filter”.
565
+ Each tab remembers its filter and selection. The active
566
+ tab shares its background with the panel beneath it. Creation
567
+ works even in an empty workbase. The guided flow collects required inputs,
568
+ selects the sole repository automatically, and suggests IDs, base branches, and
569
+ phase branches. Suggestions remain editable; choosing a base never adds a
570
+ completion dependency.
571
+
572
+ Splitting a task / adding a phase and turning an investigation into implementation
573
+ start in **Workstream**: select the source item, then choose its action.
574
+
575
+ Workstream items occupy two rows: a muted type icon and ID on the upper left,
576
+ the main repository and colored status below, and the description wrapping across
577
+ both rows on the right. Selection highlights the entire two-row item. Other item
578
+ pickers retain their compact, flat rows with the same Nerd Font icons.
579
+ Blue is the shared focus/active accent. Type and ordinary action icons are muted;
580
+ status uses green for done, yellow for blocked, and red for dropped. Destructive
581
+ actions use red. Icons and labels convey the meaning independently of color.
582
+ Filtering matches full IDs and metadata even when a displayed name is shortened.
583
+
584
+ The built-in guided flow keeps one full-screen session across menus, text inputs,
585
+ and execution, with the tabs visible throughout. Item actions use colored icons
586
+ and return to the same item's refreshed action menu, with the latest outcome
587
+ above the prompt. Text-entry prompts support Shift-Return for a newline and
588
+ Return to submit, including multiline outcomes and completion summaries.
589
+ Wizard editors fill the available height and keep an editing hint visible below
590
+ the input; longer content scrolls with the cursor as the terminal resizes.
591
+ Submitting a blank required field retries that prompt with an explanation and
592
+ keeps earlier answers. Only input validation is retried; execution failures are
593
+ reported without automatically repeating a mutation.
594
+ Task, phase, review, and handoff ID prompts also check Agency's ID format and
595
+ currently known duplicate IDs before continuing. Native commands still perform
596
+ the final validation when executing, including concurrent changes.
597
+ In wizard inputs, Escape clears entered text first; with an
598
+ empty input it returns one prompt, retaining earlier answers and recomputing later
599
+ defaults. Escape from the first input returns to the menu; Escape from an item's
600
+ action menu returns to Workstream. Tab switches sections from any step. On
601
+ the front screen, Escape clears a filter first. With an empty filter it exits
602
+ `agency act`, but keeps the TUI open when entered through bare `agency`. Ctrl-C quits from any
603
+ screen. On exit it restores the shell and leaves a compact recap of
604
+ completed actions, affected items, and commands. Dry runs are labeled as previews;
605
+ completed steps remain in the recap if a later step is cancelled or fails. Work
606
+ handoffs restore the terminal before starting the interactive worker, then reopen
607
+ the TUI when the worker returns. A configured
608
+ external chooser continues to offer goals and **Browse items** through that chooser.
609
+
610
+ | Goal | Choose in `act` | Discovery/action ID |
611
+ | ------------------------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------- |
612
+ | Add a repository | Add from a remote, or link an existing local repository | `repo-add`, `repo-link` |
613
+ | Create a task | Create a task → standard task or investigation; describe the outcome | `task-create`, `investigation-create` |
614
+ | Split work | Add a phase / split this task; name the existing work's first phase | `split` |
615
+ | Work on a task or phase | Work on this item | `work` |
616
+ | Move from investigation to execution | Create implementation follow-up | `handoff` |
617
+ | Review someone else's work | Review a PR, or a remote branch/commit | `review`, `review-ref` |
618
+ | Close finished work | Close or reopen work → complete without a PR, drop abandoned work, refresh a merged PR, or reopen | `complete`, `drop`, `sync`, `reopen` |
619
+ | Update PR status | Refresh Agency state from the provider, create a PR, mark a GitHub PR ready, or close it | `sync`, `pr`, `pr-ready`, `pr-close` |
620
+ | Archive terminal work | Archive | `archive` |
621
+ | See current work | See current work | `current-work` |
622
+
623
+ After creating a task, phase, review, or implementation follow-up, choose **Work
624
+ on the new item now** or **Finish**. Only choosing Work prepares checkouts and
625
+ launches the configured runner; creation alone leaves the item for later.
626
+ Investigation handoff creates a distinct implementation item with source and
627
+ revision provenance. Completing a non-PR outcome requires a durable summary.
628
+ PR-backed completion comes from provider reconciliation after merge.
629
+
630
+ **Refresh Agency state** reads the provider and reconciles local records. The
631
+ explicit **GitHub PR ready/close** actions mutate the recorded GitHub URL, then
632
+ refresh Agency state. They are offered only for recorded GitHub PR URLs; actual
633
+ provider state and authorization are checked by the underlying command. Native
634
+ provider-aware PR creation remains available. Existing lifecycle services own
635
+ validation, preparation, revision guards, and archive protections.
636
+
637
+ Pass a task/phase directory, a file inside it (including `TASK.md` or `PHASE.md`),
638
+ or a positional task ID to open that item's actions immediately. Absolute and
639
+ relative paths work; a workbase-root path opens Workbase actions. Explicit
640
+ `--epic <id>`, `--task <id>`, and `--task <id> --phase <id>` also skip item selection.
641
+ Use `--action <id>` to start
642
+ at a specific scenario. `--dry-run` collects inputs and prints exact commands,
643
+ including automatic bookkeeping commands, without executing or offering Work.
644
+ Cancelling before dispatch makes no changes; cancelling after creation leaves
645
+ the newly created item. The graph is refreshed before item actions are dispatched.
567
646
 
568
- Use an existing directory, a positional task ID, `--epic <id>`, `--task <id>`,
569
- or `--task <id> --phase <id>` to skip work-item selection. For example,
570
- `agency act .` selects the current task or phase. `--dry-run` still prompts for
571
- an action but prints the exact Agency command instead of executing it. `--json`
572
- never prompts or executes; it returns matching targets, status and readiness
573
- details, document revisions, and each available action's command argv. With no
574
- selector, JSON includes every active work item.
647
+ ```bash
648
+ agency act .
649
+ agency act --action task-create --dry-run
650
+ agency act --task investigate --action handoff --json
651
+ ```
575
652
 
576
- `--auto` is included in generated or executed work commands, and `--draft` is
577
- included in generated or executed pull request commands.
653
+ For agents, `--json` never prompts or executes. Its compact, runtime-validated
654
+ result includes workbase actions and repository aliases, current working items,
655
+ and matching targets with readiness, document revisions, available `actions`,
656
+ and `blockedActions` with reasons. `--action` filters discovery to one scenario.
657
+ Targets and current working items share the same identity and metadata fields:
658
+ `id`, `kind`, `key`, `status`, `description`, `repo` (the declared main repository,
659
+ when present), `repositories`, `readiness`, and `revision`. Descriptions retain
660
+ their original line breaks rather than the compact Workstream display wrapping.
661
+ Input descriptors follow wizard collection order; narrative fields advertise
662
+ `multiline: true`. Pass a multiline value as one argv element, preserving newlines.
663
+ `command` is exact argv only when no inputs are missing and the action is
664
+ available. Otherwise substitute the required `inputs` into `commandTemplate`'s
665
+ `<input-id>` placeholders. The resulting argv is ready to run: standard task
666
+ creation has no `--purpose` flag, and investigation creation includes a fixed
667
+ `--purpose investigation`. Optional inputs are excluded from the template;
668
+ append their native `option` and value only when wanted (for example,
669
+ `--depends-on <phase-id>`). Human prompting and suggestions are not part of the
670
+ machine protocol. Run commands from the
671
+ returned workbase root. `followUpCommands` are automatic bookkeeping;
672
+ `nextActions` require a separate explicit choice and are never implied by
673
+ creation. Re-discover after mutations rather than reusing old revisions.
674
+
675
+ `--auto` applies to Work, including the post-creation choice. `--draft` applies
676
+ to PR creation. Discovery is based on local state and does not fetch GitHub or
677
+ promise that remote operations or checkout preparation will succeed.
578
678
 
579
679
  ### Target Context
580
680
 
@@ -1244,6 +1344,45 @@ bun run build
1244
1344
 
1245
1345
  Run focused tests with `bun test <test-file>`. Run formatting with `bun format`.
1246
1346
 
1347
+ The TUI uses `@opentui/core` and `@opentui/solid`, pinned together at 0.5.11,
1348
+ with the binding's required `solid-js` 1.9.12. Follow the upstream
1349
+ [Solid bindings](https://github.com/anomalyco/opentui/blob/v0.5.11/packages/web/src/content/docs/bindings/solid.mdx) and
1350
+ [lifecycle guidance](https://github.com/anomalyco/opentui/blob/v0.5.11/packages/web/src/content/docs/core-concepts/lifecycle.mdx):
1351
+
1352
+ - Use Solid signals and OpenTUI hooks for reactive state, keys, and dimensions.
1353
+ - Use declarative `focused` and component `keyBindings`/`onSubmit` for local
1354
+ input behavior; reserve `useKeyboard` for navigation and shared shortcuts.
1355
+ - The renderer creator owns cleanup. `destroy()` restores terminal modes and
1356
+ output streams and disposes the Solid root; keep it in a cleanup/finalizer path.
1357
+ - Keep the lazy Solid preload in `interactive-loader.ts` so non-TUI commands
1358
+ avoid initializing the renderer and runtime-loaded TSX uses the right transform.
1359
+ - Verify upgrades with the UI and PTY tests, including worker handoffs and
1360
+ terminal restoration.
1361
+
1362
+ The `@babel/core` override selects 7.29.6 to fix
1363
+ [GHSA-4x5r-pxfx-6jf8](https://github.com/advisories/GHSA-4x5r-pxfx-6jf8);
1364
+ OpenTUI Solid 0.5.11 otherwise pins vulnerable 7.28.0. Remove the override when
1365
+ the upstream dependency resolves to a patched version, after running `bun audit`
1366
+ and the TUI tests. Effect's minimum is 3.20.0 for
1367
+ [GHSA-38f7-945m-qr2g](https://github.com/advisories/GHSA-38f7-945m-qr2g).
1368
+
1369
+ ### Guided-flow ownership
1370
+
1371
+ The `act` implementation has four boundaries:
1372
+
1373
+ | Module | Responsibility |
1374
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------------ |
1375
+ | `src/commands/act-actions.ts` | Defines availability, discovery argv, and executable plans using existing lifecycle commands. |
1376
+ | `src/commands/act-prompts.ts` | Collects inputs and owns wizard replay. Preparation must be replayable; only `plan.run` performs operations. |
1377
+ | `src/commands/act.ts` | Coordinates navigation, checks the selected item's freshness, executes plans, and records the recap. |
1378
+ | `src/utils/interactive.tsx` | Owns rendering, focus, keyboard handling, and terminal restoration; it has no workbase lifecycle logic. |
1379
+
1380
+ The Effect scope owns the interactive session. A worker handoff explicitly closes
1381
+ that session and permits a new one afterward. External renderer destruction
1382
+ (such as SIGTERM) requests application shutdown and interrupts in-flight work;
1383
+ it must never be interpreted as Back or reopen the interface. PTY regressions
1384
+ exercise both paths against real processes and check terminal restoration.
1385
+
1247
1386
  ## License
1248
1387
 
1249
1388
  MIT
package/cli-main.ts CHANGED
@@ -179,6 +179,7 @@ const commands: Record<string, Command> = {
179
179
  if (options.help) return console.log(actHelp)
180
180
  await runCommand(
181
181
  act({
182
+ action: options.action,
182
183
  directory: args[0],
183
184
  auto: options.auto,
184
185
  draft: options.draft,
@@ -188,6 +189,7 @@ const commands: Record<string, Command> = {
188
189
  taskId: options.task,
189
190
  phaseId: options.phase,
190
191
  inputAllowed: options.inputAllowed,
192
+ exitOnEscape: options.exitOnEscape,
191
193
  silent: options.silent,
192
194
  verbose: options.verbose,
193
195
  cwd: options.cwd,
@@ -707,8 +709,10 @@ agency v${VERSION}
707
709
 
708
710
  Usage: agency <command> [options]
709
711
 
712
+ Run agency without a command to open the TUI.
713
+
710
714
  Commands:
711
- act Interactively choose and act on work
715
+ act Open the interactive workbase
712
716
  init [path] Initialize an Agency workbase
713
717
  workbase <subcommand> Manage registered workbases
714
718
  integration <command> Inspect or sync managed integration files
@@ -796,13 +800,19 @@ const pushUsageDetails = (error?: unknown) => {
796
800
  }
797
801
 
798
802
  try {
803
+ const parsed = parseCli(rawArguments)
799
804
  const {
800
805
  commandName,
801
806
  commandPath,
802
807
  args: commandArgs,
803
808
  passthrough,
804
809
  values,
805
- } = parseCli(rawArguments)
810
+ } = {
811
+ ...parsed,
812
+ ...(!parsed.commandName && !parsed.values.help && !parsed.values.version
813
+ ? { commandName: "act", commandPath: "act" }
814
+ : {}),
815
+ }
806
816
  usageCommandPath = commandPath
807
817
  usageFlagNames = Object.entries(values)
808
818
  .filter(([, value]) => value !== undefined && value !== false)
@@ -874,6 +884,7 @@ try {
874
884
  ...values,
875
885
  cwd,
876
886
  inputAllowed,
887
+ exitOnEscape: Boolean(parsed.commandName),
877
888
  passthrough,
878
889
  })
879
890
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markjaquith/agency",
3
- "version": "3.5.2",
3
+ "version": "3.6.0",
4
4
  "description": "Manage agentic work across repositories with durable workbases",
5
5
  "keywords": [
6
6
  "agents",
@@ -103,9 +103,9 @@
103
103
  },
104
104
  "dependencies": {
105
105
  "@effect/schema": "^0.75.5",
106
- "@opentui/core": "0.4.5",
107
- "@opentui/solid": "0.4.5",
108
- "effect": "^3.19.15",
106
+ "@opentui/core": "0.5.11",
107
+ "@opentui/solid": "0.5.11",
108
+ "effect": "^3.20.0",
109
109
  "solid-js": "1.9.12",
110
110
  "yaml": "^2.9.0"
111
111
  },
@@ -115,7 +115,10 @@
115
115
  "oxfmt": "^0.27.0",
116
116
  "typescript": "^7.0.2"
117
117
  },
118
+ "overrides": {
119
+ "@babel/core": "7.29.6"
120
+ },
118
121
  "engines": {
119
- "bun": ">=1.0.0"
122
+ "bun": ">=1.3.0"
120
123
  }
121
124
  }
@@ -0,0 +1,62 @@
1
+ import { Schema } from "@effect/schema"
2
+ import { GraphReadiness } from "./graph-schema"
3
+ import { WorkStatus } from "./workbase/schemas"
4
+
5
+ const Argv = Schema.Array(Schema.String)
6
+ const Action = Schema.Struct({
7
+ id: Schema.String,
8
+ label: Schema.optional(Schema.String),
9
+ available: Schema.Boolean,
10
+ blockedReason: Schema.optional(Schema.NullOr(Schema.String)),
11
+ inputs: Schema.optional(
12
+ Schema.Array(
13
+ Schema.Struct({
14
+ id: Schema.String,
15
+ label: Schema.String,
16
+ required: Schema.Boolean,
17
+ multiline: Schema.optional(Schema.Boolean),
18
+ option: Schema.optional(Schema.String),
19
+ }),
20
+ ),
21
+ ),
22
+ command: Schema.optional(Schema.NullOr(Argv)),
23
+ commandTemplate: Schema.optional(Argv),
24
+ followUpCommands: Schema.optional(Schema.Array(Argv)),
25
+ nextActions: Schema.optional(
26
+ Schema.Array(
27
+ Schema.Struct({
28
+ id: Schema.String,
29
+ requiresSelection: Schema.Literal(true),
30
+ commandTemplate: Argv,
31
+ }),
32
+ ),
33
+ ),
34
+ })
35
+ const Kind = Schema.Literal("epic", "task", "phase")
36
+ const EntityFields = {
37
+ kind: Kind,
38
+ id: Schema.String,
39
+ key: Schema.String,
40
+ status: WorkStatus,
41
+ description: Schema.optional(Schema.String),
42
+ repo: Schema.optional(Schema.String),
43
+ repositories: Argv,
44
+ readiness: GraphReadiness,
45
+ revision: Schema.String,
46
+ }
47
+
48
+ export const ActDiscovery = Schema.Struct({
49
+ workbase: Schema.Struct({
50
+ root: Schema.String,
51
+ repositories: Argv,
52
+ actions: Schema.Array(Action),
53
+ }),
54
+ currentWork: Schema.Array(Schema.Struct(EntityFields)),
55
+ targets: Schema.Array(
56
+ Schema.Struct({
57
+ ...EntityFields,
58
+ actions: Schema.Array(Action),
59
+ blockedActions: Schema.Array(Action),
60
+ }),
61
+ ),
62
+ })
package/src/cli-parser.ts CHANGED
@@ -144,20 +144,30 @@ const commands = {
144
144
  },
145
145
  act: {
146
146
  usage:
147
- "agency act [<directory-or-task-id> | --epic <id> | --task <id> [--phase <id>]] [--dry-run | --json] [--auto] [--draft]",
147
+ "agency act [<directory-or-task-id> | --epic <id> | --task <id> [--phase <id>]] [--action <id>] [--dry-run | --json] [--auto] [--draft]",
148
148
  options: {
149
149
  ...outputOptions,
150
150
  ...entitySelectorOptions,
151
151
  "dry-run": { type: "boolean" },
152
152
  auto: { type: "boolean" },
153
153
  draft: { type: "boolean" },
154
+ action: { type: "string" },
154
155
  },
155
156
  command: {
156
157
  usage:
157
- "agency act [<directory-or-task-id> | --epic <id> | --task <id> [--phase <id>]] [--dry-run | --json] [--auto] [--draft]",
158
+ "agency act [<directory-or-task-id> | --epic <id> | --task <id> [--phase <id>]] [--action <id>] [--dry-run | --json] [--auto] [--draft]",
158
159
  minArgs: 0,
159
160
  maxArgs: 1,
160
- options: ["epic", "task", "phase", "dry-run", "json", "auto", "draft"],
161
+ options: [
162
+ "epic",
163
+ "task",
164
+ "phase",
165
+ "dry-run",
166
+ "json",
167
+ "auto",
168
+ "draft",
169
+ "action",
170
+ ],
161
171
  conflicts: [
162
172
  ["dry-run", "json"],
163
173
  ["epic", "task"],