cbox-plan

star 1

Create a structured feature plan. Reads the codebase, drafts a plan locally, refines it through conversation, then creates a GitHub issue on approval. Use when starting a new feature.

richardvanbergen By richardvanbergen schedule Updated 4/2/2026

name: cbox-plan description: Create a structured feature plan. Reads the codebase, drafts a plan locally, refines it through conversation, then creates a GitHub issue on approval. Use when starting a new feature.

You are a planning assistant. Your job is to create a structured feature plan, refine it through conversation, and create a GitHub issue once the plan is approved.

Running Commands

ALWAYS use cbox run <branch> <command> to execute any project command. Never invoke bun, npm, playwright, go, cargo, or any other tool directly.

Available commands are defined in cbox.toml under [commands]. The branch comes from ## Branch in .cbox/plan.md, or from git branch --show-current if no box has been created yet.

Examples:

  • Tests: cbox run <branch> test
  • E2E: cbox run <branch> test-e2e
  • Build: cbox run <branch> build

If a command you need is not in cbox.toml, run /cbox-init first to configure it.

The Anchor

.cbox/plan.md is the single source of truth. Always read it first if it exists. Always write back to it after any change. This file is gitignored — it's your local working copy.

Step 1: Load or Initialize

Check if .cbox/plan.md exists.

  • If it exists: Read it. You are in refinement mode. Show the current plan and ask what the user wants to change.
  • If it doesn't exist: You are in creation mode. Proceed to Step 2.

Step 2: Read the Codebase

Before drafting anything, explore the project to understand:

  1. Project type — CLI tool, web app, library, monorepo? Check for cbox.toml, package.json, go.mod, Cargo.toml, etc.
  2. Existing architecture — key packages, patterns, naming conventions
  3. Available cbox commands — read cbox.toml [commands]. These are the commands you must use to interact with the project (e.g. cbox run <branch> test-e2e). Never invoke the underlying package manager or test runner directly.
  4. Existing tests — look at test file locations and naming. If you need to run tests to understand the current state, use cbox run <branch> <command>.
  5. Serve config — does cbox.toml have a [serve] section? If so, note the URL pattern: http://<branch>.<project>.dev.localhost

Do not ask the user for this information. Infer it from the codebase.

Step 3: Draft the Plan

Write a draft to .cbox/plan.md using this exact structure:

# <Feature Title>

## Status
draft

## GitHub Issue
(pending)

## Branch
(pending)

## Feature Overview
<Plain English description of what this feature does and why>

## Architecture
<Key design decisions, packages to create or modify, interfaces to define, patterns to follow>

## Acceptance Criteria
- [ ] <specific, verifiable criterion> → `<verification command>`
- [ ] <specific, verifiable criterion> → `<verification command>`

## E2E Test Spec
<Only include this section if the project has a serve config or an e2e test command. List specific test cases that cover the acceptance criteria. For web apps: user-facing flows. For CLI tools: command invocations and expected output. Omit this section entirely for pure library/infrastructure work.>

- <test case: what action, what expected result>
- <test case: what action, what expected result>

## Execution Plan

### Phase 1: E2E Tests
<Only include if E2E Test Spec section exists above>
- [ ] <specific test file or test case to write>

### Phase 2: Infrastructure
- [ ] <types, interfaces, packages to define>
- [ ] <any scaffolding needed before implementation>

### Phase 3: Implementation
- [ ] <specific implementation task>
- [ ] <specific implementation task>

Keep tasks in the execution plan atomic — one agent, one task, one acceptance check.

Acceptance Criteria Verification Format

Every acceptance criterion must include an inline → \command`` showing how to verify it. Choose based on task type:

  • Infrastructure / refactor (no new behaviour): → \cbox_build``
  • Logic / unit-testable: → \cbox_test``
  • User-facing feature: → \cbox_test-e2e args="--grep ''"`or→ `cbox_test-e2e args="apps/web/e2e/.spec.ts"``

Rules:

  • Propose the grep pattern or spec path based on the feature — it may not exist yet; the inner agent will adapt
  • Use $Args patterns only (do not embed full playwright commands — those are in cbox.toml)
  • Full suite (cbox_test-e2e with no args) is reserved for the final phase; never use it per-task

Step 4: Refinement Loop

Show the user the drafted plan (or the changed section after an edit). Then wait.

The user will give freeform feedback. Update .cbox/plan.md accordingly and show what changed. Repeat until the user approves.

You may bring in other skills during this loop (e.g. /grill-me for architecture stress-testing). When you return to planning, re-read .cbox/plan.md to re-anchor yourself.

Watch for approval signals: "ship it", "looks good", "go ahead", "do it".

Step 5: Create the GitHub Issue

Once approved:

  1. Create the GitHub issue using gh. The issue body contains only the static spec — no checkboxes:

    gh issue create --title "<Feature Title>" --body "## Feature Overview
    <...>
    
    ## Architecture
    <...>
    
    ## E2E Test Spec
    <...if present...>"
    

    Capture the issue number from the output.

  2. Derive the branch name: feature/<issue-number>-<slugified-title>

    • Slugify: lowercase, spaces to hyphens, strip special chars, max 40 chars
  3. Update .cbox/plan.md:

    • Set ## GitHub Issue to #<number>
    • Set ## Branch to the branch name
    • Set ## Status to ready
  4. End with:

    Issue #<number> created. Branch will be: <branch>
    
    Ready to implement? Type "yes" to begin the swarm, or give feedback to refine the plan first.
    

If the user says yes: invoke /cbox-swarm directly. If the user gives feedback: loop back to Step 4.

Resuming an Existing Plan

If the user invokes /cbox-plan without arguments and .cbox/plan.md exists with ## Status: ready, ask: "You have an existing plan for (#<issue>). Do you want to refine it or start a new one?"</p> </article> </div> <!-- Right: Metadata & Command Sidebar --> <div class="w-full lg:w-80 shrink-0 flex flex-col gap-6" data-astro-cid-7zzsworf> <!-- Install Card --> <div class="p-6 rounded-xl bg-surface-container border border-border/80 flex flex-col gap-4 shadow-sm" data-astro-cid-7zzsworf> <span class="text-xs font-bold uppercase tracking-widest text-on-surface-variant/60 font-mono" data-astro-cid-7zzsworf>Install via CLI</span> <div class="flex flex-col gap-2" data-astro-cid-7zzsworf> <div id="detail-install-cmd" class="font-mono text-[11px] p-3 rounded-lg bg-black/40 border border-border select-all break-all text-primary font-bold leading-relaxed" data-astro-cid-7zzsworf> npx skills add https://github.com/richardvanbergen/cbox --skill cbox-plan </div> <button id="detail-copy-btn" class="w-full py-2.5 rounded-lg bg-primary hover:bg-primary-hover text-on-primary font-sans font-bold text-sm shadow transition-all active:scale-95 flex items-center justify-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px]" data-astro-cid-7zzsworf>content_copy</span> <span data-astro-cid-7zzsworf>Copy Command</span> </button> </div> </div> <!-- Details & Stats Card --> <div class="p-6 rounded-xl bg-surface-container border border-border/80 flex flex-col gap-4 shadow-sm text-on-surface" data-astro-cid-7zzsworf> <span class="text-xs font-bold uppercase tracking-widest text-on-surface-variant/60 font-sans" data-astro-cid-7zzsworf>Repository Details</span> <div class="flex flex-col gap-3.5" data-astro-cid-7zzsworf> <div class="flex justify-between items-center text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>star</span> Stars </span> <span class="font-mono font-bold text-on-surface" data-astro-cid-7zzsworf>1</span> </div> <div class="flex justify-between items-center text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>call_split</span> Forks </span> <span class="font-mono font-bold text-on-surface" data-astro-cid-7zzsworf>0</span> </div> <div class="flex justify-between items-center text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>navigation</span> Branch </span> <span class="font-mono bg-surface border border-border px-2 py-0.5 rounded text-[11px] text-on-surface-variant" data-astro-cid-7zzsworf>main</span> </div> <div class="flex justify-between items-start text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5 mt-0.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>article</span> Path </span> <span class="font-mono bg-surface border border-border px-2 py-0.5 rounded text-[11px] text-on-surface-variant truncate max-w-[150px]" title="SKILL.md" data-astro-cid-7zzsworf>SKILL.md</span> </div> </div> </div> <!-- Occupations Tag Card --> <!-- Related Creators Card --> <div class="p-6 rounded-xl bg-surface-container border border-border/80 flex flex-col gap-3 shadow-sm" data-astro-cid-7zzsworf> <span class="text-xs font-bold uppercase tracking-widest text-on-surface-variant/60 font-sans" data-astro-cid-7zzsworf>More from Creator</span> <div class="flex items-center gap-2" data-astro-cid-7zzsworf> <img class="w-8 h-8 rounded-full border border-border" src="https://avatars.githubusercontent.com/u/3854592?u=ac30a4a90849abe8a90b211b228ee0005c2f714d&v=4" alt="richardvanbergen" onerror="this.src='https://avatars.githubusercontent.com/u/9919?v=4'" data-astro-cid-7zzsworf> <div class="flex flex-col min-w-0" data-astro-cid-7zzsworf> <span class="font-bold text-sm truncate text-on-surface" data-astro-cid-7zzsworf>richardvanbergen</span> <a href="/?creator=richardvanbergen" class="text-xs text-primary hover:underline font-semibold transition-all" data-astro-cid-7zzsworf>Explore all skills →</a> </div> </div> </div> </div> </div> </div> </div> <script> const copyBtn = document.getElementById("detail-copy-btn"); const installCmd = document.getElementById("detail-install-cmd"); if (copyBtn && installCmd) { copyBtn.addEventListener("click", () => { const cmd = installCmd.textContent.trim(); navigator.clipboard.writeText(cmd).then(() => { const originalText = copyBtn.innerHTML; copyBtn.innerHTML = ` <span class="material-symbols-outlined text-[16px]">check</span> <span>Copied!</span> `; copyBtn.style.background = "#10b981"; copyBtn.style.borderColor = "#10b981"; setTimeout(() => { copyBtn.innerHTML = originalText; copyBtn.style.background = ""; copyBtn.style.borderColor = ""; }, 1500); }); }); } </script> </div> <!-- Footer --> <footer class="border-t border-border bg-surface-container-low text-on-surface-variant py-8 px-gutter mt-16 rounded-xl"> <div class="max-w-container-max mx-auto flex flex-col md:flex-row justify-between items-center gap-6"> <div class="flex items-center gap-2"> <div class="w-6 h-6 rounded bg-primary bg-opacity-20 flex items-center justify-center"> <span class="material-symbols-outlined text-primary text-sm">code_blocks</span> </div> <span class="font-bold text-on-surface text-sm">SkillMD</span> </div> <div class="flex flex-wrap justify-center gap-6 text-sm"> <a href="/about" class="hover:text-primary transition-colors">About Us</a> <a href="/contact" class="hover:text-primary transition-colors">Contact Us</a> <a href="/privacy" class="hover:text-primary transition-colors">Privacy Policy</a> <a href="/terms" class="hover:text-primary transition-colors">Terms of Service</a> <a href="/support" class="hover:text-primary transition-colors">Support</a> </div> <div class="text-xs text-on-surface-variant/80"> © 2026 SkillMD. All rights reserved. </div> </div> </footer> </main> <!-- Script for Theme Toggle, Mobile Menu, and Sidebar Filter Redirection --> <script> // Theme setup const savedTheme = localStorage.getItem("theme") || "dark"; function applyTheme(theme) { document.documentElement.classList.remove("dark", "green", "dracula", "nord"); if (theme === "dark") { document.documentElement.classList.add("dark"); } else if (theme === "green") { document.documentElement.classList.add("dark", "green"); } else if (theme === "dracula") { document.documentElement.classList.add("dark", "dracula"); } else if (theme === "nord") { document.documentElement.classList.add("dark", "nord"); } document.documentElement.setAttribute("data-theme", theme); const themeMoon = document.getElementById("theme-moon"); const themeSun = document.getElementById("theme-sun"); const themeLeaf = document.getElementById("theme-leaf"); const themeDracula = document.getElementById("theme-dracula"); const themeNord = document.getElementById("theme-nord"); if (themeMoon && themeSun && themeLeaf && themeDracula && themeNord) { themeMoon.style.display = theme === "dark" ? "inline" : "none"; themeSun.style.display = theme === "light" ? "inline" : "none"; themeLeaf.style.display = theme === "green" ? "inline" : "none"; themeDracula.style.display = theme === "dracula" ? "inline" : "none"; themeNord.style.display = theme === "nord" ? "inline" : "none"; } } applyTheme(savedTheme); const themeToggleBtn = document.getElementById("theme-toggle-btn"); if (themeToggleBtn) { themeToggleBtn.addEventListener("click", () => { const currentTheme = document.documentElement.getAttribute("data-theme") || "dark"; let newTheme = "dark"; if (currentTheme === "dark") { newTheme = "light"; } else if (currentTheme === "light") { newTheme = "green"; } else if (currentTheme === "green") { newTheme = "dracula"; } else if (currentTheme === "dracula") { newTheme = "nord"; } else { newTheme = "dark"; } applyTheme(newTheme); localStorage.setItem("theme", newTheme); }); } // Mobile menu toggle and sidebar logic const mobileMenuToggle = document.getElementById("mobile-menu-toggle"); const sidebarMenu = document.getElementById("sidebar-menu"); const sidebarOverlay = document.getElementById("sidebar-overlay"); function isMobile() { return window.innerWidth < 768; // 768px is the 'md' breakpoint in Tailwind } function openSidebar() { if (sidebarMenu) { sidebarMenu.classList.remove("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.remove("hidden"); } } function closeSidebar() { if (sidebarMenu && isMobile()) { sidebarMenu.classList.add("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.add("hidden"); } } if (mobileMenuToggle && sidebarMenu) { mobileMenuToggle.addEventListener("click", (e) => { e.stopPropagation(); if (isMobile()) { const isClosed = sidebarMenu.classList.contains("-translate-x-full"); if (isClosed) { openSidebar(); } else { closeSidebar(); } } }); document.addEventListener("click", (e) => { if (isMobile()) { if (!sidebarMenu.contains(e.target) && !mobileMenuToggle.contains(e.target)) { closeSidebar(); } } }); if (sidebarOverlay) { sidebarOverlay.addEventListener("click", () => { if (isMobile()) { closeSidebar(); } }); } // Collapse sidebar when clicking a filter button, creator button, or nav item inside it sidebarMenu.addEventListener("click", (e) => { if (isMobile()) { const clickTarget = e.target.closest("button, a"); if (clickTarget) { closeSidebar(); } } }); // Sync sidebar state on window resize window.addEventListener("resize", () => { if (!isMobile()) { // Desktop: sidebar should be visible, no overlay if (sidebarMenu) { sidebarMenu.classList.remove("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.add("hidden"); } } else { // Mobile: start collapsed if (sidebarMenu) { sidebarMenu.classList.add("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.add("hidden"); } } }); } // If not on homepage, redirect on sidebar filter click const isHomepage = window.location.pathname === "/"; document.querySelectorAll("#occupation-filters .filter-btn").forEach(btn => { btn.addEventListener("click", (e) => { const occ = e.currentTarget.getAttribute("data-occupation"); if (!isHomepage) { window.location.href = occ ? `/?occupation=${encodeURIComponent(occ)}` : "/"; } }); }); document.querySelectorAll("#creator-filters .creator-btn").forEach(btn => { btn.addEventListener("click", (e) => { const creator = e.currentTarget.getAttribute("data-creator"); if (!isHomepage) { window.location.href = `/?creator=${encodeURIComponent(creator)}`; } }); }); </script> </body> </html>