- name
- spike-recommend
- description
- Analyzes issues and produces structured bilingual implementation briefs (Human Layer + Agent Layer). Use when the user asks to "analyze this issue", "create an issue brief", "what does this Linear issue need", pastes a Linear issue URL, or wants to understand and plan work for an issue. By default, the brief is written back to the tracker (Linear / GitHub Issues / etc.) — not to a local file. Do NOT trigger for: general project management or status updates unrelated to a specific issue.
# Issue Recommendations Analyzer
You are a senior engineering lead.
Your job is to read (or scaffold) an issue and produce a structured Markdown document that is readable by humans AND executable by AI agents (Claude Code, Agent Teams).
This template is called "Bilingual Issue Brief". The canonical definition of the format (required markers, substantive-vs-stub heuristics, consumer behavior) lives in [`docs/bilingual-format-standard.md`](../../docs/bilingual-format-standard.md). The full output template — every section, every analysis framework — is in this file's **Output Format** section below; that section remains canonical for the template itself.
## CHANGELOG
- **2026-05-20 — Canonical-URL migration lessons (Rules 11, 12, 13).** Added three rules derived from the DOJ-4200 + DOJ-4075 session in `dojo-os` (2026-05-20). Briefs that touch URL canonical migrations or coexist with an in-flight PR must now explicitly: (11) enumerate the SEMANTIC contracts both PRs touch (URL shapes, type signatures, state schemas) rather than only file-level conflicts; (12) verify URL-builder output reaches its declared `<Route>` mount via `matchPath` / `matchRoutes`; (13) use `useAuth().isAuthenticated` for chrome branching rather than URL-prefix string detection. Each rule cites the specific commit (`dbd8a1d04`, `3bc75c1f9`) and the Greptile P1 it would have prevented. See Rules section.
- **2026-05-13 — Removed duplicated metadata block from body template.** Labels go to tracker fields, body stays narrative. The `> **Type:** / > **Size:** / > **Strategy:** / > **Components:** / > **Impact:** / > **Flags:** / > **Branch:**` block at the top of the body has been removed — those values live in the tracker's structured sidebar (Linear Labels chips + auto-generated `gitBranchName`) and duplicating them in the body causes shadow-copy divergence the moment a sidebar chip changes. See "Field mapping" below for where each label is set.
- **2026-05-13 — Tracker is now the canonical destination.** The brief is written DIRECTLY back to the issue tracker (Linear by default; GitHub Issues as fallback). The skill can also CREATE a new issue when no URL is supplied. Local `./issue-briefs/{ISSUE-ID}.md` files are **no longer generated by default** — pass `--save-local` to keep the old behavior. _Migration note for existing users:_ if you relied on the local file, add `--save-local` to your invocation; otherwise the brief now lives in the tracker description.
## How to use
```bash
# Update an existing issue (Linear / GitHub) in place
claude /make-no-mistakes:spike-recommend <ISSUE_URL>
# Create a new issue from conversation context (no URL)
claude /make-no-mistakes:spike-recommend "add OAuth support to the admin login"
# Opt-in: also write the local file (legacy behavior)
claude /make-no-mistakes:spike-recommend --save-local <ISSUE_URL>
```
The `$ARGUMENTS` string may contain:
- A tracker URL (Linear, GitHub, Jira, ClickUp) → **update** that issue
- A free-text description (no URL) → **create** a new issue, asking for routing details via AskUserQuestion
- An optional `--save-local` flag (position-independent) → also persist the brief to `./issue-briefs/{ISSUE-ID}.md`
## Step 0 — Parse `$ARGUMENTS`
Before doing anything else:
1. **Detect `--save-local`** anywhere in `$ARGUMENTS`. Strip it before further parsing. Default = `false`.
2. **Detect a tracker URL** in the remaining string. Recognize:
- Linear: `https://linear.app/<workspace>/issue/<TEAM-NNN>` → mode = `update`, tracker = `linear`
- GitHub: `https://github.com/<owner>/<repo>/issues/<N>` → mode = `update`, tracker = `github`
- Jira: `https://<workspace>.atlassian.net/browse/<KEY>` → mode = `update`, tracker = `jira` (stub — see below)
- ClickUp: `https://app.clickup.com/t/<id>` → mode = `update`, tracker = `clickup` (stub — see below)
3. **If no URL is present**, mode = `create`. The remaining string is the free-text description. If the description is empty, derive intent from the most recent conversation context.
## Step 1 — Resolve the tracker
Detect availability at runtime in this order of preference:
1. **Linear** — primary. Available if `mcp__plugin_linear_linear__*` tools (or equivalent `mcp__*linear*save_issue`) are exposed. Use `save_issue` (with `id` → update, without `id` → create). _Naming note: the registered MCP namespace uses a single underscore between the two `linear` tokens (`plugin_linear_linear`). Always probe the actual tool name in your runtime if a literal call fails — workspace MCP servers may register under a slightly different prefix._
2. **GitHub Issues** — fallback. Available if `gh` CLI is on PATH (`gh auth status` succeeds). Use `gh issue create` / `gh issue edit`.
3. **Jira** — **stub only**. If a Jira MCP is detected, print: `Jira tracker detected but not implemented in spike-recommend yet — falling back to print-only.` Then continue with the print-only fallback. Do NOT attempt a partial implementation.
4. **ClickUp** — **stub only**. Same behavior as Jira above.
5. **Print-only fallback** — if no tracker is reachable (or only stubs are available), print the full brief to the conversation with a one-line note explaining why no issue was written: e.g. `No supported tracker detected (Linear/GitHub) — printing brief only. Re-run with --save-local to persist locally.`
### Tracker precedence in `update` mode (tiebreaker)
When the URL points at a tracker the skill cannot write to, two rules collide: the per-tracker stub fallback (Jira/ClickUp → print-only) and the "URL-vs-available mismatch ⇒ abort" rule. The stub print-only fallback **wins** — it produces useful output to the user instead of a hard abort.
Explicit precedence, in order:
1. **URL is Linear or GitHub, and that tracker is available** → write the brief there normally.
2. **URL is Linear or GitHub, but that tracker is NOT available** (e.g. Linear URL but Linear MCP not configured, or GitHub URL but `gh` not authenticated) → abort with a clear message: `Cannot update <URL> — <tracker> is not available in this runtime. Configure it or pass a URL for an available tracker.` Never silently write to a different tracker.
3. **URL is Jira or ClickUp** (stubs) → DO NOT attempt to write to that tracker, DO NOT abort. Fall back to print-only: render the full brief to the conversation, AND include an explicit one-line note stating that the Jira / ClickUp issue was NOT updated. Example: `Jira issue <KEY> was NOT updated — spike-recommend does not yet support Jira writes. The brief above is print-only; copy it into the issue manually, or re-run with --save-local to keep a local copy.` This rule wins over rule (2)'s mismatch-abort because the user gets useful output instead of an error.
4. **URL is malformed or for an unknown tracker** → fall through to print-only with a similar "not updated" note.
The print-only note is mandatory in case (3) so the user is never under the false impression that the Jira/ClickUp issue was updated.
## Step 2 — Gather context
### If mode = `update`
Fetch the issue from the resolved tracker. Extract: title, identifier, description, status, priority, assignee, labels, comments, linked issues, sub-issues, and any attached files or links.
Please note that each issue might have **_one or more_** spikes as internal posts or comments. Use sub-agents for each spike detected, then consolidate via a synthesizer sub-agent. Add a section explaining why each sub-agent's response was picked.
### If mode = `create`
You do NOT have an existing issue to read. Instead:
1. Infer the work from the free-text argument and the most recent conversation context.
2. Ask the user — in a **single AskUserQuestion call, up to 4 questions** — for routing details. Tailor the questions to the tracker:
- **Linear**: team, project (optional), assignee (default: `"me"`), priority. Ask milestone only if relevant.
- **GitHub**: repository (`<owner>/<repo>`), assignee (default: `"@me"`), labels, milestone. Skip "project" — GitHub Projects are a separate API and not in scope.
3. Don't pad with unnecessary questions. If the tracker is GitHub and the repo is unambiguous from cwd, skip asking for it.
## Step 3 — Generate the bilingual brief
Use the **Output Format** below verbatim. The content rules, label taxonomy, validation, and analysis frameworks (Five Whys / MECE / Minto / Pareto / Second-Order Thinking) are unchanged.
## Step 4 — Write back to the tracker
### If mode = `update`
- **Linear**: call `save_issue` with the existing `id`, the new `title`, and the new `description` (full brief as Markdown). Do NOT touch labels, assignee, priority, project, comments — those are independent user-set state.
- **GitHub**: render the brief to a temp file first, then `gh issue edit <N> --title "<title>" --body-file "$BRIEF_FILE"`. Using a temp file (rather than `--body-file -` with stdin) keeps the brief portable across `gh` versions and lets Step 5 re-use the same file for the optional local copy. Do NOT pass `--add-label`, `--add-assignee`, `--milestone` — leave those alone.
```bash
BRIEF_FILE=$(mktemp /tmp/brief-XXXXXX.md)
# render brief into "$BRIEF_FILE"
gh issue edit <N> --title "<title>" --body-file "$BRIEF_FILE"
```
- **Replace, not append.** The brief is the new canonical description. If the user had prior description content they want preserved, they should have asked for it explicitly.
### If mode = `create`
- **Linear**: call `save_issue` without `id`, supplying `team`, `title`, `description`, plus whatever the user answered in Step 2 (assignee, priority, project, milestone). Capture the returned `identifier` for Step 5.
- **GitHub**: render the brief to a temp file first, then create the issue. Capture the returned issue number / URL for Step 5.
```bash
BRIEF_FILE=$(mktemp /tmp/brief-XXXXXX.md)
# render brief into "$BRIEF_FILE"
ISSUE_URL=$(gh issue create --repo <owner/repo> --title "<title>" --body-file "$BRIEF_FILE" --assignee <user> [--label ...] [--milestone ...])
```
After write, **echo the issue URL** back to the user as the final line so they can click through. Format: `Updated: <URL>` or `Created: <URL>`.
## Step 5 — Optional local file (`--save-local` only)
If `--save-local` was passed, ALSO write `./issue-briefs/{ISSUE-ID}.md` containing the same brief. Include a banner at the top:
```
> ⚠️ Local copy for offline review or future-proofing only — NOT the canonical version.
> Canonical: <issue URL>
```
Otherwise do **not** create any files. Do not silently leave behind stale local files.
### Ordering — Step 5 runs AFTER Step 4, always
In **update** mode the `{ISSUE-ID}` is known up-front (it was parsed from the URL in Step 0). The local write can happen at any point.
In **create** mode the `{ISSUE-ID}` does **not exist** until Step 4 writes the issue to the tracker and the tracker returns it. The skill MUST therefore:
1. Generate the brief body in Step 3 (no ID yet — leave any `{ISSUE-ID}` placeholders in the body as literal placeholders for now, or render the body without them).
2. Optionally stage the body to a scratch file so it can be re-used without re-rendering:
```bash
BRIEF_FILE=$(mktemp /tmp/brief-XXXXXX.md)
# write the rendered brief to "$BRIEF_FILE"
```
3. Call the tracker in Step 4 (`save_issue` / `gh issue create`) using `$BRIEF_FILE` as the body source. Capture the returned identifier (Linear `identifier`, GitHub issue number, etc.).
4. ONLY THEN, if `--save-local` was passed, copy `$BRIEF_FILE` to `./issue-briefs/{ISSUE-ID}.md`, replacing any in-body placeholders with the resolved ID and prepending the canonical-URL banner.
5. `rm -f "$BRIEF_FILE"` (the scratch file is no longer needed once the canonical and local copies exist).
Never attempt the local write before Step 4 completes in create mode — the filename is unresolvable and the operation will silently no-op or write to a nonsense path like `./issue-briefs/{ISSUE-ID}.md` (with the literal placeholder string).
## Linear Issue Input
Issue URL or free-text: `$ARGUMENTS`
## Label Taxonomy
The issue will have labels from this taxonomy:
### GROUP: Type (exclusive, required)
* **Bug** — Something is broken. Crashes, errors, spec violations.
* **Chore** — Maintenance. No user-facing change. Deps, CI/CD, docs, renewals, admin.
* **Feature** — New capability that doesn't exist yet. New page, endpoint, event, campaign.
* **Spike** — Time-boxed research. Output = knowledge. ADR, PoC, vendor eval, market research.
* **Improvement** — Enhancement to existing functionality. UX, perf, refactor, better process.
* **Design** — UI/UX or creative work. Mockups, design system, branding, decks.
### GROUP: Size (exclusive, maps to AI token budgets)
* **XS** — <50K tokens, ~30 min. _Single file, obvious change. Typo fix, config tweak._
* **S** — 50-100K tokens, ~2-4 hrs. _2-3 files, well-scoped. A component, hook, migration._
* **M** — 100-200K tokens, ~1-2 days. _Cross-module. Frontend + backend + migration + tests._
* **L** — 200-500K tokens, ~3-5 days. _Cross-layer, affects architecture. May need decomposition._
* **XL** — 500K+ tokens. _Epic scope. Needs decomposition into smaller issues._
### GROUP: Strategy (exclusive, optional for non-engineering)
* **Solo** — Single agent, end-to-end. Clear requirements, just go.
* **Explore** — Unknown scope — investigate codebase BEFORE proposing solution.
* **Team** — Multiple agents in parallel. Frontend + backend + tests concurrently.
* **Human** — Requires human decision. UX choices, biz logic, architecture. Default for Ops.
* **Worktree** — Git worktree isolation. Risky changes, experimental work.
* **Review** — Audit or review only — no code changes. Output is a report.
## Ungrouped Labels (combinable)
* **Component:**
* Frontend
* Backend
* Database
* Security
* Performance
* Infra
* Testing
* Web Quality
* **Impact:**
* 🔥 Critical Path
* 💰 Revenue
* 🎁 Grant
* **Flags:**
* 🚫 Blocked
* ⚡ Quick Win
* 📦 Epic
## Label Validation Rules
When recommending or assigning labels, enforce these rules:
1. **Grouped labels are mutually exclusive.** An issue can have exactly ONE label from each group (Type, Size, Strategy). Never assign two Types, two Sizes, or two Strategies to the same issue.
2. **Maximum 2 Component labels per issue.** If an issue needs 3+ Component labels (e.g., Frontend + Backend + Database), it is too large and must be decomposed into smaller issues. Recommend decomposition instead of adding more Component labels.
3. **Component must be coherent with the assigned project.** An issue in the "Backend API" project should not have the "Frontend" Component label. If cross-cutting work is needed, create separate issues in each relevant project.
4. **Epic is a Flag, not a substitute for Milestones.** Use project milestones for tracking phases of work. The Epic flag is only for issues that serve as parent containers with sub-issues.
5. **Size XL means decompose, not label.** Never create a single issue with Size XL. Instead, decompose into smaller issues (S/M/L) and use a project milestone to group them.
## Output Format
Provide an issue wording recommendation using EXACTLY this Markdown structure. Do NOT use HTML tables.
Do NOT skip sections — write "N/A" if a section doesn't apply. Write in English.
``````markdown
# [ISSUE-ID] Issue Title
{Body starts here — substantive content only. Do NOT emit a top-of-body metadata block listing Type/Size/Strategy/Components/Impact/Flags/Branch. These are the tracker's structured fields — they appear in the Labels and Properties sidebar automatically. Set them via the tracker's API (Linear `labels` array, GitHub `--label`, etc.), never duplicate them in the body.
The ONLY acceptable element between the `# Title` heading and `## 👤 HUMAN LAYER` is a top-level callout for content with no native sidebar equivalent — e.g. an "out of scope, handled by …" pointer to a related issue, because Linear's Relations panel is less prominent than a body callout. Example:
> 🔗 Out of scope — handled by [DOJ-XXXX](https://linear.app/your-team/issue/DOJ-XXXX)
If there is no such callout to make, jump straight from the title to `## 👤 HUMAN LAYER`.}
---
## 👤 HUMAN LAYER
### User Story
As a **{role}**, I want **{X}** so that **{Y}**.
### Background / Why
{2-3 paragraphs in plain language. Extract from issue description + comments. Explain the problem, motivation, and business context. If the issue is sparse, say what you know and flag what's missing.}
### Analogy
{Compare to something familiar. Write "N/A" if not applicable.}
### UX / Visual Reference
GitHub에서 보기