Loading...
Loading...
Add a milestone to an existing project, starting a new milestone cycle, creating the first milestone after project init, or defining what's next after completing work. Triggers include "add milestone", "new milestone", "start milestone", "create milestone", "first milestone", "next milestone", and "milestone cycle".
npx skill4agent add gannonh/kata-skills kata-add-milestone.planning/PROJECT.md.planning/research/.planning/REQUIREMENTS.md.planning/ROADMAP.md.planning/STATE.md/kata-plan-phase [N]## Current Milestone: v[X.Y] [Name]
**Goal:** [One sentence describing milestone focus]
**Target features:**
- [Feature 1]
- [Feature 2]
- [Feature 3]## Current Position
Phase: Not started (defining requirements)
Plan: —
Status: Defining requirements
Last activity: [today] — Milestone v[X.Y] startedGITHUB_ENABLED=$(cat .planning/config.json 2>/dev/null | grep -o '"enabled"[[:space:]]*:[[:space:]]*[^,}]*' | grep -o 'true\|false' || echo "false")GITHUB_ENABLED=trueHAS_GITHUB_REMOTE=$(git remote -v 2>/dev/null | grep -q 'github\.com' && echo "true" || echo "false")HAS_GITHUB_REMOTE=falsegh repo create --source=. --private --pushHAS_GITHUB_REMOTE=truegh repo create --source=. --public --pushHAS_GITHUB_REMOTE=trueContinuing without GitHub integration. Run `gh repo create` later to enable.HAS_GITHUB_REMOTE=trueif ! gh auth status &>/dev/null; then
echo "Warning: GitHub CLI not authenticated. Run 'gh auth login' to enable GitHub integration."
# Continue without GitHub operations - local milestone still created
else
# Proceed with milestone creation
fiMILESTONE_EXISTS=$(gh api /repos/:owner/:repo/milestones 2>/dev/null | jq -r ".[] | select(.title==\"v${VERSION}\") | .number" 2>/dev/null)if [ -z "$MILESTONE_EXISTS" ]; then
# Extract milestone description (first paragraph of goal, truncated to 500 chars)
MILESTONE_DESC=$(echo "$MILESTONE_GOALS" | head -1 | cut -c1-500)
gh api \
--method POST \
-H "Accept: application/vnd.github.v3+json" \
/repos/:owner/:repo/milestones \
-f title="v${VERSION}" \
-f state='open' \
-f description="${MILESTONE_DESC}" \
2>/dev/null && echo "GitHub Milestone v${VERSION} created" || echo "Warning: Failed to create GitHub Milestone (continuing)"
else
echo "GitHub Milestone v${VERSION} already exists (#${MILESTONE_EXISTS})"
fiGITHUB_ENABLED=falseCOMMIT_PLANNING_DOCS=$(cat .planning/config.json 2>/dev/null | grep -o '"commit_docs"[[:space:]]*:[[:space:]]*[^,}]*' | grep -o 'true\|false' || echo "true")
git check-ignore -q .planning 2>/dev/null && COMMIT_PLANNING_DOCS=falseCOMMIT_PLANNING_DOCS=falseCOMMIT_PLANNING_DOCS=truegit add .planning/PROJECT.md .planning/STATE.md
git commit -m "docs: start milestone v[X.Y] [Name]"MODEL_PROFILE=$(cat .planning/config.json 2>/dev/null | grep -o '"model_profile"[[:space:]]*:[[:space:]]*"[^"]*"' | grep -o '"[^"]*"$' | tr -d '"' || echo "balanced")| Agent | quality | balanced | budget |
|---|---|---|---|
| kata-project-researcher | opus | sonnet | haiku |
| kata-research-synthesizer | sonnet | sonnet | haiku |
| kata-roadmapper | opus | sonnet | sonnet |
mkdir -p .planning/research◆ Spawning 4 researchers in parallel...
→ Stack research (for new features)
→ Features research
→ Architecture research (integration)
→ Pitfalls researchproject_researcher_instructions_content=$(cat ${SKILL_BASE_DIR}/references/project-researcher-instructions.md)
research_synthesizer_instructions_content=$(cat ${SKILL_BASE_DIR}/references/research-synthesizer-instructions.md)
roadmapper_instructions_content=$(cat ${SKILL_BASE_DIR}/references/roadmapper-instructions.md)Task(prompt="
<agent-instructions>
{project_researcher_instructions_content}
</agent-instructions>
<research_type>
Project Research — Stack dimension for [new features].
</research_type>
<milestone_context>
SUBSEQUENT MILESTONE — Adding [target features] to existing app.
Existing validated capabilities (DO NOT re-research):
[List from PROJECT.md Validated requirements]
Focus ONLY on what's needed for the NEW features.
</milestone_context>
<question>
What stack additions/changes are needed for [new features]?
</question>
<project_context>
[PROJECT.md summary - current state, new milestone goals]
</project_context>
<downstream_consumer>
Your STACK.md feeds into roadmap creation. Be prescriptive:
- Specific libraries with versions for NEW capabilities
- Integration points with existing stack
- What NOT to add and why
</downstream_consumer>
<quality_gate>
- [ ] Versions are current (verify with Context7/official docs, not training data)
- [ ] Rationale explains WHY, not just WHAT
- [ ] Integration with existing stack considered
</quality_gate>
<output>
Write to: .planning/research/STACK.md
Format: Standard research output forSTACK.md
</output>
", subagent_type="general-purpose", model="{researcher_model}", description="Stack research")
Task(prompt="
<agent-instructions>
{project_researcher_instructions_content}
</agent-instructions>
<research_type>
Project Research — Features dimension for [new features].
</research_type>
<milestone_context>
SUBSEQUENT MILESTONE — Adding [target features] to existing app.
Existing features (already built):
[List from PROJECT.md Validated requirements]
Focus on how [new features] typically work, expected behavior.
</milestone_context>
<question>
How do [target features] typically work? What's expected behavior?
</question>
<project_context>
[PROJECT.md summary - new milestone goals]
</project_context>
<downstream_consumer>
Your FEATURES.md feeds into requirements definition. Categorize clearly:
- Table stakes (must have for these features)
- Differentiators (competitive advantage)
- Anti-features (things to deliberately NOT build)
</downstream_consumer>
<quality_gate>
- [ ] Categories are clear (table stakes vs differentiators vs anti-features)
- [ ] Complexity noted for each feature
- [ ] Dependencies on existing features identified
</quality_gate>
<output>
Write to: .planning/research/FEATURES.md
Format: Standard research output forFEATURES.md
</output>
", subagent_type="general-purpose", model="{researcher_model}", description="Features research")
Task(prompt="
<agent-instructions>
{project_researcher_instructions_content}
</agent-instructions>
<research_type>
Project Research — Architecture dimension for [new features].
</research_type>
<milestone_context>
SUBSEQUENT MILESTONE — Adding [target features] to existing app.
Existing architecture:
[Summary from PROJECT.md or codebase map]
Focus on how [new features] integrate with existing architecture.
</milestone_context>
<question>
How do [target features] integrate with existing [domain] architecture?
</question>
<project_context>
[PROJECT.md summary - current architecture, new features]
</project_context>
<downstream_consumer>
Your ARCHITECTURE.md informs phase structure in roadmap. Include:
- Integration points with existing components
- New components needed
- Data flow changes
- Suggested build order
</downstream_consumer>
<quality_gate>
- [ ] Integration points clearly identified
- [ ] New vs modified components explicit
- [ ] Build order considers existing dependencies
</quality_gate>
<output>
Write to: .planning/research/ARCHITECTURE.md
Format: Standard research output forARCHITECTURE.md
</output>
", subagent_type="general-purpose", model="{researcher_model}", description="Architecture research")
Task(prompt="
<agent-instructions>
{project_researcher_instructions_content}
</agent-instructions>
<research_type>
Project Research — Pitfalls dimension for [new features].
</research_type>
<milestone_context>
SUBSEQUENT MILESTONE — Adding [target features] to existing app.
Focus on common mistakes when ADDING these features to an existing system.
</milestone_context>
<question>
What are common mistakes when adding [target features] to [domain]?
</question>
<project_context>
[PROJECT.md summary - current state, new features]
</project_context>
<downstream_consumer>
Your PITFALLS.md prevents mistakes in roadmap/planning. For each pitfall:
- Warning signs (how to detect early)
- Prevention strategy (how to avoid)
- Which phase should address it
</downstream_consumer>
<quality_gate>
- [ ] Pitfalls are specific to adding these features (not generic)
- [ ] Integration pitfalls with existing system covered
- [ ] Prevention strategies are actionable
</quality_gate>
<output>
Write to: .planning/research/PITFALLS.md
Format: Standard research output forPITFALLS.md
</output>
", subagent_type="general-purpose", model="{researcher_model}", description="Pitfalls research")Task(prompt="
<agent-instructions>
{research_synthesizer_instructions_content}
</agent-instructions>
<task>
Synthesize research outputs into SUMMARY.md.
</task>
<research_files>
Read these files:
- .planning/research/STACK.md
- .planning/research/FEATURES.md
- .planning/research/ARCHITECTURE.md
- .planning/research/PITFALLS.md
</research_files>
<output>
Write to: .planning/research/SUMMARY.md
Format: Standard research output forSUMMARY.md
Commit after writing.
</output>
", subagent_type="general-purpose", model="{synthesizer_model}", description="Synthesize research").planning/research/BACKLOG_COUNT=$(find .planning/issues/open -maxdepth 1 -name "*.md" 2>/dev/null | wc -l | tr -d ' ')ISSUE_OPTIONS=""
for file in .planning/issues/open/*.md; do
[ -f "$file" ] || continue
created=$(grep "^created:" "$file" | cut -d' ' -f2)
title=$(grep "^title:" "$file" | cut -d':' -f2- | xargs)
area=$(grep "^area:" "$file" | cut -d' ' -f2)
provenance=$(grep "^provenance:" "$file" | cut -d' ' -f2)
# Calculate age
created_date=$(echo "$created" | cut -dT -f1)
days_ago=$(( ($(date +%s) - $(date -j -f "%Y-%m-%d" "$created_date" +%s 2>/dev/null || echo $(date +%s))) / 86400 ))
if [ "$days_ago" -eq 0 ]; then
age="today"
elif [ "$days_ago" -eq 1 ]; then
age="1d ago"
else
age="${days_ago}d ago"
fi
# Extract GitHub issue number if linked
github_ref=""
if echo "$provenance" | grep -q "^github:"; then
github_ref=" (GitHub $(echo "$provenance" | grep -oE '#[0-9]+')"
fi
echo "$file|$title|$area|$age$github_ref"
done"[title]" — [area], [age]### Milestone Scope Issues
Issues pulled into current milestone scope:
- "[issue-title]" (from: [issue-file-path], GitHub: #N if linked)Here are the features for [new capabilities]:
## [Category 1]
**Table stakes:**
- Feature A
- Feature B
**Differentiators:**
- Feature C
- Feature D
**Research notes:** [any relevant notes]
---
## [Next Category]
....planning/REQUIREMENTS.md[CATEGORY]-[NUMBER]## Milestone v[X.Y] Requirements
### [Category 1]
- [ ] **CAT1-01**: User can do X
- [ ] **CAT1-02**: User can do Y
### [Category 2]
- [ ] **CAT2-01**: User can do Z
[... full list ...]
---
Does this capture what you're building? (yes / adjust)git add .planning/REQUIREMENTS.md
git commit -m "$(cat <<'EOF'
docs: define milestone v[X.Y] requirements
[X] requirements across [N] categories
EOF
)"find ... -name "01-*" | head -1DUPES=$(for state in active pending completed; do
ls .planning/phases/${state}/ 2>/dev/null
done | grep -oE '^[0-9]+' | sort -n | uniq -d)
# Include flat directories (unmigrated projects)
FLAT_DUPES=$(ls .planning/phases/ 2>/dev/null | grep -E '^[0-9]' | grep -oE '^[0-9]+' | sort -n | uniq -d)
ALL_DUPES=$(echo -e "${DUPES}\n${FLAT_DUPES}" | sort -nu | grep -v '^$')ALL_DUPES⚠ Duplicate phase prefixes detected: [list]
Phase directories share numeric prefixes across milestones. This causes
phase lookup commands to return wrong directories./kata-migrate-phases<details>NEXT_PHASE⚠ Skipping migration. Run `/kata-migrate-phases` to fix collisions later.ALL_PHASE_DIRS=""
for state in active pending completed; do
[ -d ".planning/phases/${state}" ] && ALL_PHASE_DIRS="${ALL_PHASE_DIRS} $(find .planning/phases/${state} -maxdepth 1 -type d -not -name "${state}" 2>/dev/null)"
done
HIGHEST=$(echo "$ALL_PHASE_DIRS" | tr ' ' '\n' | grep -oE '/[0-9]+' | grep -oE '[0-9]+' | sort -n | tail -1)
NEXT_PHASE=$((HIGHEST + 1))NEXT_PHASETask(prompt="
<agent-instructions>
{roadmapper_instructions_content}
</agent-instructions>
<planning_context>
**Project:**
@.planning/PROJECT.md
**Requirements:**
@.planning/REQUIREMENTS.md
**Research (if exists):**
@.planning/research/SUMMARY.md
**Config:**
@.planning/config.json
**Previous milestones (shipped context):**
@.planning/MILESTONES.md
</planning_context>
<instructions>
Create roadmap for milestone v[X.Y]:
1. Continue phase numbering from the highest existing phase number + 1 (globally sequential across milestones). The starting phase number is provided as NEXT_PHASE.
2. Derive phases from THIS MILESTONE's requirements (don't include validated/existing)
3. Map every requirement to exactly one phase
4. Derive 2-5 success criteria per phase (observable user behaviors)
5. Validate 100% coverage of new requirements
6. Write files immediately (ROADMAP.md, STATE.md, update REQUIREMENTS.md traceability)
7. Include "Planned Milestones" section in ROADMAP.md if user mentioned future milestone ideas
8. Use ○ symbol for planned milestones in overview, 🔄 for current, ✅ for completed
9. Include planned milestones in Progress Summary table with "Planned" status
10. Return ROADMAP CREATED with summary
Write files first, then return. This ensures artifacts persist even if context is lost.
</instructions>
<format_conventions>
Milestones overview uses these symbols:
- ✅ for shipped milestones
- 🔄 for current/in-progress milestone
- ○ for planned milestones
Completed milestone details blocks MUST include:
- Summary line: ✅ v[X.Y] [Name] — SHIPPED [DATE]
- **Goal:** line
- Phase checkboxes with plan counts and dates
- [Full archive](milestones/v[X.Y]-ROADMAP.md) link
Progress Summary table includes planned milestones with "Planned" status and "—" for metrics.
</format_conventions>
", subagent_type="general-purpose", model="{roadmapper_model}", description="Create roadmap")## ROADMAP BLOCKED## ROADMAP CREATED---
## Proposed Roadmap
**[N] phases** | **[X] requirements mapped** | All milestone requirements covered ✓
| # | Phase | Goal | Requirements | Success Criteria |
| ----- | ------ | ------ | ------------ | ---------------- |
| [N] | [Name] | [Goal] | [REQ-IDs] | [count] |
| [N+1] | [Name] | [Goal] | [REQ-IDs] | [count] |
...
### Phase Details
**Phase [N]: [Name]**
Goal: [goal]
Requirements: [REQ-IDs]
Success criteria:
1. [criterion]
2. [criterion]
[... continue for all phases ...]
---Task(prompt="
<agent-instructions>
{roadmapper_instructions_content}
</agent-instructions>
<revision>
User feedback on roadmap:
[user's notes]
Current ROADMAP.md: @.planning/ROADMAP.md
Update the roadmap based on feedback. Edit files in place.
Return ROADMAP REVISED with changes made.
</revision>
", subagent_type="general-purpose", model="{roadmapper_model}", description="Revise roadmap")cat .planning/ROADMAP.mdgit add .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md
git commit -m "$(cat <<'EOF'
docs: create milestone v[X.Y] roadmap ([N] phases)
Phases:
[N]. [phase-name]: [requirements covered]
[N+1]. [phase-name]: [requirements covered]
...
All milestone requirements mapped to phases.
EOF
)"GITHUB_ENABLED=falseISSUE_MODE=$(cat .planning/config.json 2>/dev/null | grep -o '"issueMode"[[:space:]]*:[[:space:]]*"[^"]*"' | grep -o '"[^"]*"$' | tr -d '"' || echo "auto")gh label create "phase" --color "0E8A16" --description "Kata phase tracking" --force 2>/dev/null || true# Extract VERSION from current milestone (the one marked "In Progress")
VERSION=$(grep -E "^### v[0-9]+\.[0-9]+.*\(In Progress\)" .planning/ROADMAP.md | grep -oE "v[0-9]+\.[0-9]+(\.[0-9]+)?" | head -1 | tr -d 'v')
if [ -z "$VERSION" ]; then
echo "Warning: Could not determine milestone version from ROADMAP.md. Skipping phase issue creation."
exit 0
fi
MILESTONE_NUM=$(gh api /repos/:owner/:repo/milestones --jq ".[] | select(.title==\"v${VERSION}\") | .number" 2>/dev/null)
if [ -z "$MILESTONE_NUM" ]; then
echo "Warning: Could not find GitHub Milestone v${VERSION}. Skipping phase issue creation."
# Continue without phase issues - skip to Phase 10
fi### v1.1.0 GitHub Integration (Planned)### v{VERSION} {Name} (Status)#### Phase N: Phase Name**Goal**: description text**Requirements**: REQ-01, REQ-02**Success Criteria** (what must be TRUE):# Find the milestone section and extract phases
ROADMAP_FILE=".planning/ROADMAP.md"
# Extract VERSION from current milestone (the one marked "In Progress")
VERSION=$(grep -E "^### v[0-9]+\.[0-9]+.*\(In Progress\)" "$ROADMAP_FILE" | grep -oE "v[0-9]+\.[0-9]+(\.[0-9]+)?" | head -1 | tr -d 'v')
if [ -z "$VERSION" ]; then
echo "Warning: Could not determine milestone version. Skipping phase issue creation."
exit 0
fi
# Get line numbers for milestone section boundaries
MILESTONE_START=$(grep -n "^### v${VERSION}" "$ROADMAP_FILE" | head -1 | cut -d: -f1)
NEXT_MILESTONE=$(awk -v start="$MILESTONE_START" 'NR > start && /^### v[0-9]/ {print NR; exit}' "$ROADMAP_FILE")
# If no next milestone, use end of file
if [ -z "$NEXT_MILESTONE" ]; then
NEXT_MILESTONE=$(wc -l < "$ROADMAP_FILE")
fi
# Extract phase blocks within this milestone section
# Each phase starts with "#### Phase N:" and ends at next "#### Phase" or section boundary
PHASE_HEADERS=$(sed -n "${MILESTONE_START},${NEXT_MILESTONE}p" "$ROADMAP_FILE" | grep -n "^#### Phase [0-9]")
# Process each phase
echo "$PHASE_HEADERS" | while IFS= read -r phase_line; do
# Extract phase number and name from "#### Phase N: Name"
PHASE_NUM=$(echo "$phase_line" | sed -E 's/.*Phase ([0-9.]+):.*/\1/')
PHASE_NAME=$(echo "$phase_line" | sed -E 's/.*Phase [0-9.]+: (.*)/\1/')
# Get relative line number within milestone section
PHASE_REL_LINE=$(echo "$phase_line" | cut -d: -f1)
PHASE_ABS_LINE=$((MILESTONE_START + PHASE_REL_LINE - 1))
# Find next phase or section end
NEXT_PHASE_LINE=$(sed -n "$((PHASE_ABS_LINE+1)),${NEXT_MILESTONE}p" "$ROADMAP_FILE" | grep -n "^#### Phase\|^### \|^## " | head -1 | cut -d: -f1)
if [ -z "$NEXT_PHASE_LINE" ]; then
PHASE_END=$NEXT_MILESTONE
else
PHASE_END=$((PHASE_ABS_LINE + NEXT_PHASE_LINE - 1))
fi
# Extract phase block
PHASE_BLOCK=$(sed -n "${PHASE_ABS_LINE},${PHASE_END}p" "$ROADMAP_FILE")
# Extract goal (line starting with **Goal**:)
PHASE_GOAL=$(echo "$PHASE_BLOCK" | grep "^\*\*Goal\*\*:" | sed 's/\*\*Goal\*\*: *//')
# Extract requirements (line starting with **Requirements**:) - may not exist
REQUIREMENT_IDS=$(echo "$PHASE_BLOCK" | grep "^\*\*Requirements\*\*:" | sed 's/\*\*Requirements\*\*: *//')
# Extract success criteria (numbered list after **Success Criteria**)
# Convert to checklist format: " 1. item" -> "- [ ] item"
SUCCESS_CRITERIA_AS_CHECKLIST=$(echo "$PHASE_BLOCK" | \
awk '/^\*\*Success Criteria\*\*/{found=1; next} found && /^ [0-9]+\./{print} found && /^\*\*/{exit}' | \
sed -E 's/^ [0-9]+\. /- [ ] /')
# --- Issue creation code (step 6) ---
# Check if issue already exists (idempotent)
EXISTING=$(gh issue list --label "phase" --milestone "v${VERSION}" --json number,title --jq ".[] | select(.title | startswith(\"Phase ${PHASE_NUM}:\")) | .number" 2>/dev/null)
if [ -n "$EXISTING" ]; then
echo "Phase ${PHASE_NUM} issue already exists: #${EXISTING}"
else
# Build issue body using temp file (handles special characters safely)
cat > /tmp/phase-issue-body.md << PHASE_EOF
## Goal
${PHASE_GOAL}
## Success Criteria
${SUCCESS_CRITERIA_AS_CHECKLIST}
$([ -n "$REQUIREMENT_IDS" ] && echo "
## Requirements
${REQUIREMENT_IDS}")
## Plans
<!-- Checklist added by /kata-plan-phase (Phase 4) -->
_Plans will be added after phase planning completes._
---
<sub>Created by Kata | Phase ${PHASE_NUM} of milestone v${VERSION}</sub>
PHASE_EOF
# Create issue
gh issue create \
--title "Phase ${PHASE_NUM}: ${PHASE_NAME}" \
--body-file /tmp/phase-issue-body.md \
--label "phase" \
--milestone "v${VERSION}" \
2>/dev/null && echo "Created issue: Phase ${PHASE_NUM}: ${PHASE_NAME}" || echo "Warning: Failed to create issue for Phase ${PHASE_NUM}"
fi
done◆ GitHub Phase Issues: [N] created for milestone v${VERSION}| Artifact | Location |
|---|---|
| Project | |
| Research | |
| Requirements | |
| Roadmap | |
GITHUB_ENABLED=true/kata-discuss-phase [N]/clear/kata-plan-phase [N]/kata-discuss-phase [N]