docs: Plan spec for agent CLI ergonomics (bulk ops, output, sync)#176
docs: Plan spec for agent CLI ergonomics (bulk ops, output, sync)#176jlevy wants to merge 16 commits into
Conversation
Plan to reduce agent bash contortions (for-loops over issues, output truncation, the --no-sync/sync ritual) via bulk/multi-target verbs, a trustworthy output contract, and an honest stage-then-publish sync model. Phase 1 is backward-compatible quick wins for the current release; Phase 2 maps query-driven mutation and a tbd apply transaction file. https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| Secrets | Jun 14, 2026 11:21p.m. | Review ↗ |
Important
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
Coverage Report for packages/tbd
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Senior Engineering ReviewI reviewed PR #176 ( Findings
Phase 1 Scope NotesI would tighten Phase 1 to the smallest ergonomic slice that is both safe and immediately useful:
I would defer bulk Phase 2 NotesThe Phase 2 ideas are directionally sound, especially Relevant code/spec:
For Documentation NotesThe spec correctly identifies stale/incorrect sync docs, and implementation should update them in the same PR as Phase 1. There are multiple existing user-facing references that still describe
The Phase 1 implementation plan mentions the manual and Testing NotesThe testing strategy is mostly right. I would add explicit tests for:
CI StatusCurrent PR checks are green: Ubuntu, macOS, Windows, Coverage & Lint, Benchmark, and DeepSource Secrets. I did not run local tests; this worktree has no installed Bottom LineThe design is worth pursuing, but Phase 1 needs a few decisions made explicit before implementation. The highest-risk pieces are sync flag semantics, visible unsynced hints, |
Incorporate senior review feedback tightening Phase 1: tri-state sync intent with an explicit lock boundary, a visible unsynced hint via output.notice() (not verbose-only info()), preserved single-ID close/reopen behavior (skips are bulk-only), bulk show split into a separate read-only design, update --status closed excluded from bulk closure, an aligned --quiet contract (silent on success), and sync doc fixes extended to tbd-design.md. https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
|
Thanks — this is a genuinely useful review. I verified each finding against the code and adopted all of them in
Also folded in: Phase 2 Implementing the tightened slice next: variadic Generated by Claude Code |
tbd close now accepts multiple IDs and processes them under one lock: validate-all-then-apply (fail-closed on unknown IDs, --ignore-missing to skip), a one-line summary, a structured --json results array with sync:{pending,hint}, and a visible unsynced-changes notice. Single-ID behavior is preserved byte-for-byte. Adds shared cli/lib/bulk.ts helpers and a cli-bulk-mutation golden; updates the close help-text golden.
Part of Phase 1 of the agent CLI ergonomics spec (tbd-38ov).
https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Covers resolveAllIds (order preservation, missing collection, empty input), summarizeBulk (changed/skipped/missing tallies including reopened/updated), and toJsonResult (skippedReason omission). These pure helpers back the variadic close path, which is otherwise exercised only as a tryscript subprocess and thus invisible to vitest coverage. Implements tbd-xxe4 (part of Phase 1, tbd-38ov). https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
reopen now accepts <ids...>. A single ID preserves the legacy output exactly, including the hard error when the issue is not closed (cli-crud golden, exit 1). Two or more IDs are a bulk op: validate-all-then-apply with fail-closed on unknown IDs (--ignore-missing to override), already-open reported as a skip (bulk-only), one-line summary + --json results array + visible unsynced-changes hint. Consistency fix in close: a single-ID read failure now throws the legacy NotFoundError instead of silently returning, matching reopen and pre-PR close. Adds bulk reopen goldens; cli-crud single-ID contract unchanged (76 pass). Implements tbd-n6eo (part of Phase 1, tbd-38ov). https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
update now accepts <ids...>. A single ID dispatches to the unchanged legacy path, preserving all structural side effects (re-parenting, spec propagation, child ordering). Two or more IDs apply only shared, side-effect-free fields: --priority, --assignee, --type, --add-label, --remove-label, --due, --defer. Per-ID-only and lifecycle flags are rejected in the bulk path (--from-file, --title, --description, --notes, --notes-file, --status, --parent, --spec, --child-order). Bulk closure stays on close so closed_at/close_reason are never left inconsistent, per the PR #176 review. validate-all-then-apply with --ignore-missing; one-line summary + --json results + visible unsynced hint; --quiet silent on success. Also updates the tbd --help golden (cli-setup) for the new reopen/update <ids...> signatures. That golden was the Coverage & Lint failure CI flagged on 03ab2d6: the pre-push hook runs vitest only, not the tryscript suite, so the help-text drift slipped through. Adds bulk update goldens. Implements tbd-jud0 (part of Phase 1, tbd-38ov). https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
New cli/lib/body-input.ts: resolveBodyInput resolves a free-text body from inline text, a companion *-file flag, or stdin via the '-' convention. Rejects inline+file together and two stdin readers in one command, so agents can pass shell-sensitive text ($, backticks, quotes) without escaping. Wired in: close/reopen gain --reason-file and '-' stdin for --reason; create's -d/--description and -f/--file accept '-'; update's --description/--notes accept '-' and --notes-file is supported. close/reopen/create resolve bodies before the data context; update now does too via resolveBodyOptions (the context changes cwd, so relative file paths and stdin must be read up front). Tests: body-input unit tests + cli-body-input goldens round-tripping shell-sensitive text through file and stdin; cli-help-all updated for --reason-file. Filed tbd-649r for a pre-existing quirk found while testing: working notes persist to disk but show --json returns null on the first notes write (independent of this change). Implements tbd-hfk0 (part of Phase 1). https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Per the PR #176 review (Documentation Notes): issue mutations stage to the local tbd-sync worktree and 'tbd sync' publishes them. There is no per-command auto-sync, so --no-sync has no effect on issue writes and auto_sync (default false) is not applied to them. Corrected the stale claims in tbd-docs.md (global options, config example, deletion-recovery note) and tbd-design.md (config schema, command synopses, write-flow step, bd-compat table). Also documented Phase 1 in the tbd-design.md synopses: variadic close/reopen/update <ids...>, --ignore-missing, --reason-file and the '-'/stdin convention for --reason/-d/--description/--notes, and the constrained bulk update (shared fields only; lifecycle via close/reopen). Implements tbd-6zqx / tbd-dc49 (Phase 1 docs). https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
The global --no-sync help text claimed 'Skip automatic sync after write operations', but nothing auto-syncs after issue writes — they stage to the local tbd-sync worktree and 'tbd sync' publishes. Updated to 'Accepted; issue writes stage locally', consistent with the doc corrections in 35b8c73 (PR #176 review). Propagated to the 11 --help golden snapshots across 7 tryscript files; full suite green (950). https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
`--no-sync` set a `ctx.sync` flag that no command ever read, so it was a complete no-op for issue writes — which always stage to the local tbd-sync worktree and are published by `tbd sync`. Its only effect was encouraging the `--no-sync … ; tbd sync` cargo-cult among agents. Removed: the global option, the dead `ctx.sync` context field and its test mock, all served-doc references (manual example + option list; tbd-design.md embedded --help snapshots and the bd-compat table row, now marked Dropped), and the no-op flag from 8 test invocations. `tbd <verb> --no-sync` now errors as an unknown option — the honest signal. tbd is not moving to an always-auto-sync model; an opt-in `--sync` may come later but is non-essential. Updates the agent-cli-ergonomics plan's sync decision (a → c). Validated: typecheck clean, vitest 1339 passed, tryscript 950 passed. https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
The emitBulkSummary method was copy-pasted verbatim in close, reopen, and update. Extract it into a single emitBulkSummary(output, results, {verb, skippedNote}) in cli/lib/bulk.ts. The only per-verb differences were the past-tense verb word and the skipped-clause phrase, now parameters; update never produces a skipped result, so its output is unchanged. Behavior-preserving (cli-bulk-mutation golden + bulk unit tests green). Implements tbd-ojl1.
https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
The worktree auto-heal (#135) and config-format-migration stderr notices printed even under --quiet, so agents still had to wrap commands in '2>&1 | tail -1'. getCommandContext now records the active --quiet process-wide (context.ts) and both notify functions early-return when it is set, covering all callers (loadDataContext, create, and the deep migration path) without threading the flag through withDataSyncContext's ~18 call sites. Adds quiet-notices.test.ts. Implements tbd-29k3. https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
The bulk close/reopen/update contract, atomicity, --ignore-missing, quiet/json output, single-ID backward-compat, and file/stdin bodies were already covered by cli-bulk-mutation, cli-crud, cli-body-input, and the bulk unit tests. The one missing lock was the removed global --no-sync: add goldens asserting close and update now reject it as an unknown option (exit 1). Implements tbd-xc4f; tbd-05mw (reopen/update bulk goldens) was already satisfied by cli-bulk-mutation. https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Document the Phase 1 agent contract in the manual: multi-ID bulk close/reopen/update under one lock, the one-line summary, the --json {results,summary,sync} shape, what --quiet guarantees, --reason-file plus the shared - stdin convention, fail-closed validation with --ignore-missing, and stage-then-publish (with --no-sync removed). Adds a CHANGELOG entry and ticks the Phase 1 checklist in the spec. Implements tbd-dc49.
https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Restores bulk.ts unit coverage after the tbd-ojl1 extraction moved emitBulkSummary out of the command files. Covers the text summary line (changed/skipped/missing clauses + id list + visible sync hint), the no-skip verb path (update), and the --json results/summary/sync.pending contract including the no-change case. https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
withLockfile's mkdir acquire path threw on any non-EEXIST error, so a transient Windows EPERM/EBUSY/EACCES (raised when mkdir races a concurrent rmdir of the same lock directory) surfaced as a hard failure -- the flaky lockfile.test.ts 'serializes concurrent access' on windows-latest. Mirror the acquire side on removeLockDir's existing release-side tolerance: retry the same transient codes a small bounded number of times (5), resetting the budget whenever a real EEXIST shows the lock is genuinely held. A persistent failure (e.g. a genuinely unwritable parent dir, which tbd doctor reports) still propagates after the budget, and non-transient codes (ENOSPC, ENOENT) throw immediately, so the deliberate unwritable-lock detection is preserved. Adds lockfile-acquire-eperm.test.ts (mocked fs, platform-agnostic). Separate from the agent-CLI-ergonomics work on this branch. https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Summary
Adds a Draft planning spec,
docs/project/specs/active/plan-2026-06-13-agent-cli-ergonomics.md, for improving tbd's CLI ergonomics for agents.It grew out of observing agents drive tbd through brittle bash:
forloops over issue IDs,2>&1 | tail -1on every command, hand-rolledecho "=== ... ==="progress headers, and a--no-sync ... ; tbd syncritual. Each contortion maps to a missing tbd primitive.What's in the spec
<ids...>onclose/reopen/update/show; a bulk summary line +--jsonresults array + accurate "unsynced changes" hint;--reason-file/stdin bodies; and an honest opt-in--sync.--wheremutation (reusing thelistfilter grammar, with mandatory preview) and atbd applytransaction file generalizingimport.Grounded throughout with
file:linecites from the current command sources. One design decision is already resolved in the spec: keep stage-then-publish syncing, make it self-revealing via an unsynced-changes hint, and add opt-in--sync(no per-command auto-sync).Notes
plan-2026-06-03-tbd-agent-cli-guideline-improvements.md(that one improves the guideline about writing agent skills; this one changes tbd's runtime CLI behavior).--spec.https://claude.ai/code/session_01P9jYZ7T6MCMCzLL2iJjn9b
Generated by Claude Code