Skip to content

Add agentworkforce pick to auto-select a persona for a task#48

Merged
willwashburn merged 1 commit intomainfrom
feat/persona-picker
May 8, 2026
Merged

Add agentworkforce pick to auto-select a persona for a task#48
willwashburn merged 1 commit intomainfrom
feat/persona-picker

Conversation

@willwashburn
Copy link
Copy Markdown
Member

Summary

  • New agentworkforce pick "<task>" subcommand: takes a free-text task description, shells out to Claude Haiku via the local claude CLI, and prints the best-fit persona id to stdout (composable with existing commands, e.g. agentworkforce agent "$(agentworkforce pick "...")").
  • On low confidence, prompts (TTY) to launch persona-maker with the task threaded through as a new TASK_DESCRIPTION input; non-TTY or declined → exit 2 with a hint. claude not on PATH or subprocess fails → exit 3.
  • Picker enumerates candidates from personaCatalog ∪ local cascade overrides (so locally-installed personas are reachable) and validates the model's chosen id against that set before accepting.
  • Picker module ships with a runner test seam — full coverage of parse, validation, and error paths without real subprocess calls.

Why a cheap LLM and not embeddings

At 31 personas the whole catalog fits in one prompt with prompt caching, so steady-state cost is near-zero. Embedding similarity over descriptions rewards lexical match rather than intent fit, would force a new embedding-provider dependency this repo doesn't have, and makes the "no close match" confidence signal awkward to express. An LLM expresses confidence naturally, which is exactly what the no-match → persona-maker handoff needs.

Files

  • packages/cli/src/persona-picker.ts (new) — picker module + pickPersona, buildUserPrompt, parsePickerOutput.
  • packages/cli/src/persona-picker.test.ts (new) — 14 unit tests.
  • packages/cli/src/cli.tspick subcommand wiring (USAGE, dispatch, runPick, buildPickCandidates, promptYesNoSync, readLineFromStdinSync).
  • packages/cli/src/cli.test.ts — 5 new tests for the new helpers.
  • personas/persona-maker.json — adds optional TASK_DESCRIPTION input (sentinel default (none)) and updates each tier prompt to use it when forwarded.
  • packages/workload-router/src/generated/personas.ts — regenerated.

Test plan

  • corepack pnpm run check green (lint + typecheck + 186 tests across 5 packages: workload-router 41, harness-kit 38, cli 106, agentworkforce 1).
  • Manual smoke (requires claude on PATH):
    • agentworkforce pick "review this PR for security issues"security-reviewer or code-reviewer.
    • agentworkforce pick "the test in foo.test.ts flakes"flake-hunter or debugger.
    • agentworkforce pick "xyzzy frobnicate" → no-match exit 2 (or interactive prompt offering create).
    • PATH=/tmp agentworkforce pick "anything" → exit 3 with "claude not found" hint.
  • End-to-end: in a TTY, run agentworkforce pick "<gibberish>", accept the prompt, confirm persona-maker opens with the task description visible.

🤖 Generated with Claude Code

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7027a39a-a305-48fc-b9e2-85af065539ad

📥 Commits

Reviewing files that changed from the base of the PR and between ccaea4b and 09254f6.

⛔ Files ignored due to path filters (1)
  • packages/workload-router/src/generated/personas.ts is excluded by !**/generated/**
📒 Files selected for processing (5)
  • packages/cli/src/cli.test.ts
  • packages/cli/src/cli.ts
  • packages/cli/src/persona-picker.test.ts
  • packages/cli/src/persona-picker.ts
  • personas/persona-maker.json
✅ Files skipped from review due to trivial changes (1)
  • packages/cli/src/persona-picker.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli/src/persona-picker.test.ts

📝 Walkthrough

Walkthrough

This PR introduces a persona picker feature: a new pick CLI subcommand that uses Claude to select the best-fit persona from a candidate list for a free-text task. It includes the picker module with subprocess orchestration, CLI helpers for candidate enumeration and TTY-aware confirmation, and updated persona-maker configuration to support task-driven persona authoring.

Changes

Persona Picker Feature

Layer / File(s) Summary
Picker Data Types and Contracts
packages/cli/src/persona-picker.ts
PickCandidate, PickConfidence, and PickResult types define the contract; buildUserPrompt() renders task and candidate list into Claude prompt format.
Picker Core Implementation
packages/cli/src/persona-picker.ts
parsePickerOutput() parses Claude JSON responses (bare and envelope formats); defaultRunner() executes Claude subprocess with timeout and error mapping; pickPersona() orchestrates validation, prompt construction, invocation, and result mapping.
Picker Module Tests
packages/cli/src/persona-picker.test.ts
Coverage for prompt building, response parsing, success/failure paths, subprocess error handling (ENOENT, non-zero exit), low confidence, unknown persona IDs, and empty input boundaries.
CLI Helper Functions and Imports
packages/cli/src/cli.ts
Extends node:fs imports with readSync; imports picker module; buildPickCandidates() enumerates and projects personas (built-in and local, sorted by id); promptYesNoSync() provides TTY-aware synchronous yes/no prompting with injectable I/O.
CLI Command Implementation and Routing
packages/cli/src/cli.ts
runPick() parses task argument, calls picker, prints matched persona ID on success, exits with code 3 on picker unavailability, and on no-match prompts to scaffold new persona via runAgentSelector or exits with code 2 in non-TTY mode. Help text and usage examples added; main() dispatch extended to route pick subcommand.
CLI Helper and Integration Tests
packages/cli/src/cli.test.ts
Unit tests for buildPickCandidates() (non-empty list, includes persona-maker, required projection fields, sorted by id) and promptYesNoSync() (non-TTY returns false without writing; TTY normalizes yes/y variants; empty/non-yes answers return false).
Persona-Maker Configuration
personas/persona-maker.json
Adds TASK_DESCRIPTION input field (with (none) sentinel) to pass originating task context. Refines best, best-value, and minimum tier system prompts with stricter persona conventions, model-agnostic constraints, skill discovery/curation rules, expanded built-in catalog checklist with specific file targets (src/generated/personas.ts, validation commands), and explicit output contracts.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI
  participant pickPersona
  participant Runner
  participant Claude
  participant runAgentSelector
  User->>CLI: agentworkforce pick "task"
  CLI->>pickPersona: task, candidates
  pickPersona->>Runner: system+user prompts, --output-format json
  Runner->>Claude: spawn claude binary
  Claude-->>Runner: stdout/stderr/exit
  Runner-->>pickPersona: subprocess result
  pickPersona-->>CLI: match | no-match | picker-unavailable
  alt no-match && user accepts scaffold
    CLI->>runAgentSelector: CREATE_SELECTOR + inputValues (TASK_DESCRIPTION)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 A picker so nimble, with Claude's guiding hand,
Finds personas that fit, just as the user planned,
TTY prompts dance, with yes and with no,
Building workflows with grace, watch the logic flow!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and specifically describes the main addition: a new pick subcommand for auto-selecting a persona based on a task description. It directly aligns with the changeset's primary objective.
Description check ✅ Passed The PR description comprehensively explains the new pick subcommand functionality, error handling, implementation approach, files modified, and test coverage. It is directly and thoroughly related to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/persona-picker

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccaea4b40c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/persona-picker.ts Outdated
}

const DEFAULT_BIN = 'claude';
const DEFAULT_MODEL = 'claude-haiku-4-5';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use a supported Claude model identifier by default

pickPersona hard-codes DEFAULT_MODEL to claude-haiku-4-5, but the Claude CLI model flag expects either documented aliases (sonnet/opus) or a full dated model id (for Haiku 4.5, claude-haiku-4-5-20251001). With the current default, agentworkforce pick can fail the subprocess with an unknown-model error and always exit through the picker-unavailable path unless callers override the model manually.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched the default to the dated id claude-haiku-4-5-20251001 in 09254f6.

A nuance worth flagging: the undated claude-haiku-4-5 alias does work via claude --model today — I verified live before the change (claude -p --model claude-haiku-4-5 --output-format json "say hi" returns a successful result with "claude-haiku-4-5" in modelUsage). So the picker as previously written wouldn't have unconditionally exited through picker-unavailable. That said, the dated id is strictly more robust against any future change in the CLI's alias resolver, so I took the suggestion.

Introduces a new CLI subcommand `agentworkforce pick "<task>"` that
returns the best-fit persona for a free-text task description by
shelling out to a cheap Claude Haiku call via the local `claude` CLI.
Prints the matched persona id to stdout on success so it composes with
existing commands (e.g. `agentworkforce agent "$(agentworkforce pick
"...")"`). On low confidence, prompts the user (TTY) to launch
`persona-maker` with the task threaded through as a new
`TASK_DESCRIPTION` input, or exits non-zero with a hint (non-TTY).

The picker module exposes a `runner` test seam so the subprocess flow
is fully unit-tested without burning API credits, and validates that
returned persona ids exist in the cascade-resolved candidate set
(`personaCatalog` ∪ local overrides) before accepting them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@willwashburn willwashburn force-pushed the feat/persona-picker branch from ccaea4b to 09254f6 Compare May 8, 2026 07:41
@willwashburn willwashburn merged commit fff4d0b into main May 8, 2026
2 checks passed
@willwashburn willwashburn deleted the feat/persona-picker branch May 8, 2026 07:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant