Help us improve
Share bugs, ideas, or general feedback.
From hoyeon
Transforms vague goals into structured requirements via systematic interview. Three phases: Interview, Extract, Cross-check. Outputs requirements.md consumed by /blueprint.
npx claudepluginhub team-attention/hoyeon --plugin hoyeonHow this skill is triggered — by the user, by Claude, or both
Slash command
/hoyeon:specifyThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Transform a vague goal into structured, traceable requirements through:
Clarifies vague requests via Socratic questioning, focusing on one key uncertainty (goals, scope, constraints, completion criteria) at a time to produce actionable requirements. Activates on deep-interview requests or unclear specs.
Conducts structured requirements-gathering interviews for software features/systems. Reads SPEC.md and context, asks numbered questions on purpose, technical design, UI/UX, edge cases, security, rollout. For deep requirement elicitation before specs/plans.
Transforms vague ideas into implementation-ready specifications via two-phase structured interviewing. Use for new features, problems, or requirements documentation.
Share bugs, ideas, or general feedback.
Transform a vague goal into structured, traceable requirements through: 0. WHERE Grounding — establish project type, situation, ambition, and risk modifiers 0.5. Context Research (brownfield only) — scan existing codebase before asking the user
requirements.md committed in cli formatThe final deliverable is <spec_dir>/requirements.md in the format that /blueprint consumes:
type (greenfield|feature|refactor|bugfix), goal, non_goals[]## R-X<num>: parent requirements, each with nested #### R-X<num>.Y: sub-requirements carrying given/when/thenX in the ID is axis code: B=Business, U=Interaction (user), T=Tech## Open Decisions section with ### OD-N: blocksAll intermediate files (qa-log.md, reqs-business.md, reqs-interaction.md, reqs-tech.md) stay in <spec_dir>/ for traceability but are NOT read by /blueprint.
AskUserQuestion for the structured interview forms described below.Agent(subagent_type="...") for brownfield research and extractor
subagents.<spec_dir>/requirements.md.hoyeon-cli req init.requirements.md.The WHERE is the combination of current situation and intended scope. It calibrates how deep the interview goes on each axis — without it, every project gets the same heavyweight treatment, which over-engineers toys and under-specs production systems.
Before asking for goal/non-goals, present your understanding of the user's request using this template:
**Mirror — Here's what I understood**
**Understanding:**
<1–2 sentences paraphrasing the user's request in your own words. Not a verbatim echo.>
**Goal:**
- <bullet 1: concrete outcome>
**Non-Goal (explicitly out of scope):**
- <bullet 1: exclusion — at least one must be inferred by you, not stated by user>
**Ambiguous (scope-level unknowns):**
- <ambiguity about what "done" means, what's included, or who the user is>
Then confirm via AskUserQuestion:
AskUserQuestion(
question: "Does this match your intent?",
options: [
{ label: "Approve", description: "Proceed to WHERE grounding" },
{ label: "Revise", description: "Fix goal/non-goal/scope" }
]
)
Rules:
goal and non_goals from the mirror (no need to re-ask in free-text)Default behavior: infer from the working directory and the user's goal. Only ask the user about dimensions you genuinely cannot determine from evidence.
Don't burn 4 cold-start questions on facts the codebase already answers. Most of PROJECT_TYPE, SITUATION, and risk factors are visible to you. Only AMBITION is reliably user-intent-driven.
Inspect the repo. Use evidence — file paths, not vibes:
package.json + frontend deps (react, vue, svelte, vite) / app/, src/components/ → user-facingpackage.json + backend deps (express, fastify, nest) / routes/, api/, controllers/ → api-servicebin/, cli.ts, pyproject.toml with [project.scripts], single-binary Cargo project → dev-toolterraform/, k8s/, helm/, Dockerfile + no app code → infrastructureauth/, session, passport, oauth, cookie, jwt references → sensitive-data candidateprisma/schema.prisma / migrations/ with PII columns (email, phone, ssn) → sensitive-data/api/public → external-exposuremigrations/, destructive scripts, public SDK exports → irreversibleIf brownfield, you may dispatch a quick code-explorer here — but a cheap ls + Read README.md + Glob is often enough for inference. Don't over-spend on Phase 0.
Build a single block in the Mirror (extend Step 0.1's mirror, or send as follow-up):
**Inferred Context** (confirm or override)
- **PROJECT_TYPE**: <inferred value>
- Evidence: <file:line or path that supports this>
- **SITUATION**: <inferred value>
- Evidence: <...>
- **AMBITION**: <best guess, mark confidence>
- Confidence: low/medium/high — <why>
- **RISK factors**: [<inferred list, or "none detected">]
- Evidence: <...>
Mark each dimension with confidence:
package.json with react = user-facing)If ALL dimensions are medium/high confidence:
AskUserQuestion(
question: "Confirm inferred context?",
options: [
{ label: "Approve all", description: "Use inferred WHERE as-is" },
{ label: "Override some", description: "Correct one or more dimensions" }
]
)
On Override: ask follow-up only for the dimensions the user flags. Use the original option lists below as the menu for those targeted questions.
If ANY dimension is low confidence (typically AMBITION on truly new projects), skip the confirm-or-drill choice and ask only the low-confidence dimensions directly, batched in one AskUserQuestion. Do not re-ask high-confidence dimensions.
Use these when the user picks "Override some" or when a dimension is low-confidence:
Definitions match the originals: Toy = days, failure ok; Feature/MVP = 1-2 weeks, core only; Product = long-term, reliability + security matter. Risk factors escalate specific nodes to deep in Step 0.4.
user-dashboard)spec_dir: default .hoyeon/specs/{spec-name}/requirements.md stub with the correct frontmatter so /blueprint can read it later:
hoyeon-cli req init <spec_dir> --type <greenfield|feature|refactor|bugfix> --goal "<one-line goal>"
Map WHERE.SITUATION → --type:
greenfield → greenfieldbrownfield-extension → featurebrownfield-refactor → refactorhybrid → feature (or refactor if structural churn dominates)
The stub is overwritten at Phase 4.3 once the interview is complete. If <spec_dir>/requirements.md already exists from a prior run, skip req init and proceed (the user is re-running specify on the same spec).${baseDir}/templates/qa-log.md<spec_dir>/qa-log.md with spec name, goal, non-goals, and the WHERE context filled inCombine SITUATION × AMBITION × RISK_MODIFIERS to assign each taxonomy node a depth level (light, standard, or deep). Apply rules in order — later rules escalate, never downgrade.
Step A — SITUATION base:
Step B — AMBITION modulation:
Step C — RISK_MODIFIERS escalation (override Step B downgrades):
Examples:
Project-type notes — PROJECT_TYPE doesn't change calibration numbers, but it changes what each INTERACTION node means (the interaction-extractor reads project_type for lens selection).
Write the derived calibration into qa-log.md frontmatter as depth_calibration: so Phase 1 and the gap-auditor can read it.
Skip this phase entirely if where.situation == greenfield. Run it for brownfield-extension, brownfield-refactor, and hybrid.
Why: brownfield work depends on existing code that the user may not fully remember. Asking the user "what's the architecture?" when the codebase is right there is wasteful and unreliable. Scan the code first, then interview them on decisions — not facts.
Task(subagent_type="code-explorer",
prompt="Goal: {goal}. Find: existing patterns, modules, or files relevant to this change. Report as file:line format with brief summary.")
Task(subagent_type="code-explorer",
prompt="Find project structure and toolchain: package manifests, build/test/lint commands, entry points, deployment config. Report as file:line format.")
Task(subagent_type="docs-researcher",
prompt="Goal: {goal}. Search ADRs, READMEs, docs/, CLAUDE.md, config files for conventions, architecture decisions, and constraints relevant to this work. Report as file:line format.")
For brownfield-refactor specifically, add:
Task(subagent_type="code-explorer",
prompt="Find all call sites and dependents of {the area being refactored}. Report impact surface as file:line format.")
qa-log.md → research: sectionWrite findings into qa-log.md under a new top-level heading ## Research (before the axis sections). Include:
Also add research_done: true to the where: frontmatter block so later phases can rely on it.
During Phase 1, when asking Tech axis questions:
You are the interviewer. Ask questions one axis at a time, following this taxonomy:
Axis 1: BUSINESS — WHO, WHY, WHAT, SUCCESS, SCOPE, RISK
Axis 2: INTERACTION — JOURNEY, HAPPY, EDGE, STATE, FEEDBACK, ACCESS
Axis 3: TECH — ARCH, DATA, INFRA, DEPEND, COMPAT, SECURITY
The INTERACTION axis is consumer-generic. Reinterpret nodes based on where.project_type:
| project_type | JOURNEY | HAPPY | FEEDBACK | ACCESS |
|---|---|---|---|---|
| user-facing | User entry → outcome | Core UI flow | Visual/audio reactions | Permissions, a11y |
| api-service | Consumer integration flow | Canonical API call | HTTP responses, errors | Auth, rate limits |
| dev-tool | Install → invoke → result | --help / canonical use | stdout+exit codes | Install, platform |
| infrastructure | Operator procedure | Green deploy path | Dashboards, alerts | RBAC, IAM |
EDGE/STATE are universal: failures & conditional behavior apply everywhere.
PRIMARY: Use AskUserQuestion tool for all interview questions. Free-text prompting should only be a fallback when options genuinely cannot be enumerated.
description field show consequences/trade-offs per choicemultiSelect: true for non-exclusive choicesBatch when:
Do NOT batch when:
Max 4 per call (tool limit). Default: batch 2-3 related questions per turn.
Each AskUserQuestion option must have:
label: 1-5 words (what the user picks)description: the consequence/implication of this choiceExample (batched):
questions: [
{
question: "Who is the primary user?",
header: "Primary user",
options: [
{ label: "Senior developers", description: "Power users; expect depth + customization" },
{ label: "Junior developers", description: "Learning users; expect guidance + safe defaults" },
{ label: "Both equally", description: "Dual-mode UX; complexity to serve both" }
]
},
{
question: "What's the success signal?",
header: "Success metric",
options: [
{ label: "Team-wide adoption", description: "Qualitative; hard to measure" },
{ label: "Daily active use", description: "Quantitative DAU; needs tracking" },
{ label: "Time saved per task", description: "Efficiency metric; baseline needed" }
]
}
]
Drills happen at two distinct moments, with different judges. Both are required.
When: Immediately after an AskUserQuestion answer arrives.
How: Scan the selected option + any "Other" free-text for these signals. If present, the NEXT AskUserQuestion is a drill on the same node.
| Signal | Example answer | Drill question |
|---|---|---|
| Vague qualifier | "fast", "easy", "simple", "good UX" | AskUserQuestion with concrete thresholds as options (e.g., "<1s", "<3s", "<10s") |
| Hidden assumption | "obviously X", "of course Y" | AskUserQuestion surfacing the assumption ("does X always hold? What if not?") |
| Multiple interpretations | A term that could mean 2+ things (e.g., "admin") | AskUserQuestion listing each interpretation as an option |
| New stakeholder | Mentions a role not yet covered | Add a new node under the current axis, AskUserQuestion about their perspective |
Inline drills are fast and subjective — you catch the obvious ones on the spot.
When: After an axis ends, gap-auditor returns verdict=CONTINUE with an AMBIGUOUS list.
How: The auditor's AMBIGUOUS list tells you exactly which nodes still need drilling. Convert each AMBIGUOUS item into an AskUserQuestion targeting that specific ambiguity, then continue until gap-auditor returns SUFFICIENT.
Post-audit drills are systematic — they catch what inline judgment missed.
Type A is a fast first-pass filter; Type B is the safety net. Relying on only Type A means subjective blind spots slip through. Relying on only Type B means needlessly long axis rounds because trivially fixable ambiguities aren't caught early.
Only use free-text Q&A (no AskUserQuestion) when:
Users will sometimes not know the answer (especially on Tech axis, or when the PM doesn't know implementation details). Don't let the interview stall.
When the user's answer is "I don't know / not sure / up to you / whatever works" (either by Other free-text or by tone):
qa-log.md with status: assumption and include the reasoning in > blockquote.## Open Items in qa-log.md with:
Don't re-ask the same question. Move on. The Phase 4 Confirmation will let the user review and override any tentative judgment.
Example:
Q: What authentication method?
User: "Dunno, whatever works"
→ Tentative: "Given brownfield-extension + sensitive-data, I'll assume existing SSO integration"
→ Log as assumption with status: assumption
→ Add to Open Items: "OD: auth method (tentative: SSO based on existing system)"
→ Continue to next question
Update qa-log.md after each exchange using the template format:
#### Q: for the question, > blockquote for the answer (include the selected option label + any free-text)##### Drill: for depth follow-upsstatus: resolved | ambiguous | assumptionDispatch the gap-auditor agent at these specific moments:
Do NOT call gap-auditor after every AskUserQuestion turn — that's wasteful. Call it at boundaries.
Each call:
qa-log.md firstqa-log.md contentAll 3 axes must receive SUFFICIENT verdict, AND the final audit must also return SUFFICIENT.
Update qa-log.md frontmatter: status: complete with final coverage scores.
Run 3 agents in parallel:
${baseDir}/templates/reqs-axis.md template.hoyeon/specs/{spec-name}/reqs-business.md.hoyeon/specs/{spec-name}/reqs-interaction.md.hoyeon/specs/{spec-name}/reqs-tech.mdconfidence: low and open_questions items from extractor outputsBefore writing the final requirements.md, surface everything to the user for explicit acceptance. This prevents assumptions from silently becoming "requirements."
Show the user a concise summary grouped into:
## Final Confirmation
### Confirmed Requirements
{count by axis: Business N, Interaction N, Tech N}
### Conflicts to Resolve ({count})
- {ID pair}: {conflict description}
→ Options to resolve
### Open Questions ({count})
- {ID}: {question} (axis: {axis})
### Assumptions to Accept ({count})
- {ID}: {assumption the extractor made} — accept / reject / replace
### Out of Scope (Non-Goals)
- {items from where.non_goals}
For each CONFLICT and ASSUMPTION, use AskUserQuestion with options (typically: accept / reject / modify / defer).
For OPEN QUESTIONS: either answer them now (free-text or AskUserQuestion) or explicitly defer them to the open_decisions list.
After all conflicts and assumptions are resolved, show the full requirements list before writing to disk:
[specify] Final Requirements Preview
Type: greenfield | Goal: "<goal>"
Non-goals: <list>
## R-B1: <title>
- R-B1.1: <sub title>
given: ... | when: ... | then: ...
- R-B1.2: ...
## R-U1: <title>
- R-U1.1: ...
## R-T1: <title>
- R-T1.1: ...
Summary: {N} parent reqs, {M} sub-reqs (B:{b} U:{u} T:{t})
Open Decisions: {count or "none"}
Then ask:
AskUserQuestion(
question: "Finalize these requirements?",
options: [
{ label: "Approve", description: "Write requirements.md and finish" },
{ label: "Edit", description: "Modify specific requirements before writing" },
{ label: "Re-interview", description: "Go back to interview for missing coverage" }
]
)
If Edit: ask which requirements to change, apply edits, re-show preview. Max 3 rounds. If Re-interview: return to Phase 1 with the gap identified.
requirements.mdOnly after user has explicitly approved the preview:
${baseDir}/templates/requirements.md template (cli format)<spec_dir>/requirements.md (replacing the stub created by hoyeon-cli req init at Phase 0.3). Final shape:
---
type: greenfield | feature | refactor | bugfix
goal: "<one-line goal>"
non_goals:
- "<item>"
---
# Requirements
## R-B1: <parent title>
- behavior: <one-sentence system behavior>
#### R-B1.1: <sub title>
- given: <precondition>
- when: <trigger>
- then: <expected outcome>
#### R-B1.2: ...
## R-U1: <Interaction requirement parent>
...
## R-T1: <Tech requirement parent>
...
## Pre-work
- [ ] <action> (blocking)
- [ ] <action> (non-blocking)
## Open Decisions
### OD-1: <title>
- context: <why undecided>
- options: [<A>, <B>]
- impact: <what is blocked>
/blueprint's expectations):
## R-X<num>: at H2, where X is axis code (B=Business, U=Interaction, T=Tech)#### R-X<num>.Y: at H4 with given/when/then linestype, goal, non_goals[]. Do NOT add extra keys like spec, phase, date, total_requirements — those broke with cli's frontmatter format.(blocking) or (non-blocking). execute will gate on blocking items./blueprint <spec_dir>/All outputs go to <spec_dir>/ (default .hoyeon/specs/{spec-name}/):
| File | Phase | Description | Consumed by |
|---|---|---|---|
requirements.md | 0.3 (stub) / 4.3 (final) | Requirements in cli format (frontmatter + flat ## R-X / #### R-X.Y with GWT) | /blueprint |
qa-log.md | 1 | Full interview transcript | audit/traceability only |
reqs-business.md | 2 | Axis extraction scratch | merged into requirements.md |
reqs-interaction.md | 2 | Axis extraction scratch | merged into requirements.md |
reqs-tech.md | 2 | Axis extraction scratch | merged into requirements.md |
Only requirements.md is load-bearing for downstream skills. The other files are internal scratch/audit — /blueprint does not read them.
hoyeon-cli req init <spec_dir> --type <t> --goal "<g>" (Phase 0.3) — creates dir + requirements.md stubrequirements.md directly via Write tool.| Agent | Phase | Purpose |
|---|---|---|
gap-auditor | 1 | Interview coverage validation |
business-extractor | 2 | Business req extraction |
interaction-extractor | 2 | Interaction req extraction (project-type-aware) |
tech-extractor | 2 | Tech req extraction |