diff --git a/README.md b/README.md
index 76149512f6..6f5ed83cd1 100644
--- a/README.md
+++ b/README.md
@@ -267,6 +267,7 @@ Additional commands for enhanced quality and validation:
| `/speckit.clarify` | Clarify underspecified areas (recommended before `/speckit.plan`; formerly `/quizme`) |
| `/speckit.analyze` | Cross-artifact consistency & coverage analysis (run after `/speckit.tasks`, before `/speckit.implement`) |
| `/speckit.checklist` | Generate custom quality checklists that validate requirements completeness, clarity, and consistency (like "unit tests for English") |
+| `/speckit.feedback` | Extract and store lessons learned from PR review comments for future reference |
### Environment Variables
@@ -274,6 +275,15 @@ Additional commands for enhanced quality and validation:
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `SPECIFY_FEATURE` | Override feature detection for non-Git repositories. Set to the feature directory name (e.g., `001-photo-albums`) to work on a specific feature when not using Git branches.
\*\*Must be set in the context of the agent you're working with prior to using `/speckit.plan` or follow-up commands. |
+### Lessons Learned
+
+Spec-kit implements a continuous improvement system that captures lessons from PR reviews and applies them to future development work. The `/speckit.feedback` command extracts insights from code review comments and stores them in two places:
+
+- **`memory/lessons.md`** - Central lessons database organized by category (architecture, code-quality, testing, security, performance, documentation)
+- **`memory/feedback/pr--lessons.md`** - Per-PR detailed feedback summaries that preserve the full context of each review
+
+When you run `/speckit.specify`, `/speckit.plan`, or `/speckit.implement`, these commands automatically load relevant lessons from the central database for their phase and apply them proactively. This creates a compounding knowledge effect where your team's collective wisdom from past reviews continuously improves future implementations, helping avoid repeated mistakes and reinforcing best practices without manual intervention.
+
## đ Core Philosophy
Spec-Driven Development is a structured process that emphasizes:
@@ -401,7 +411,7 @@ The first step should be establishing your project's governing principles using
/speckit.constitution Create principles focused on code quality, testing standards, user experience consistency, and performance requirements. Include governance for how these principles should guide technical decisions and implementation choices.
```
-This step creates or updates the `.specify/memory/constitution.md` file with your project's foundational guidelines that the AI agent will reference during specification, planning, and implementation phases.
+This step creates or updates the `memory/constitution.md` file with your project's foundational guidelines that the AI agent will reference during specification, planning, and implementation phases.
### **STEP 2:** Create project specifications
@@ -431,6 +441,11 @@ see yours. You can edit any comments that you make, but you can't edit comments
delete any comments that you made, but you can't delete comments anybody else made.
```
+You can mention a number of issue in your description and it will be used to generate the branch number, such as
+```text
+For GH issue #123, Develop BugZapper, a new fuzzy test framework".
+```
+
After this prompt is entered, you should see Claude Code kick off the planning and spec drafting process. Claude Code will also trigger some of the built-in scripts to set up the repository.
Once this step is completed, you should have a new branch created (e.g., `001-create-taskify`), as well as a new specification in the `specs/001-create-taskify` directory.
diff --git a/scripts/bash/fetch-pr-feedback.sh b/scripts/bash/fetch-pr-feedback.sh
new file mode 100755
index 0000000000..9c15dbda92
--- /dev/null
+++ b/scripts/bash/fetch-pr-feedback.sh
@@ -0,0 +1,220 @@
+#!/usr/bin/env bash
+#
+# fetch-pr-feedback.sh - Extract PR data for the speckit.feedback agent
+#
+# Usage:
+# ./fetch-pr-feedback.sh
+# ./fetch-pr-feedback.sh --find-current # Find open PR for current branch (preferred)
+# ./fetch-pr-feedback.sh --find-recent # Find most recently merged PR
+#
+# Output: JSON with PR metadata, review comments, and discussion comments
+#
+
+set -euo pipefail
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+error() {
+ echo -e "${RED}ERROR: $1${NC}" >&2
+ exit 1
+}
+
+warn() {
+ echo -e "${YELLOW}WARNING: $1${NC}" >&2
+}
+
+info() {
+ echo -e "${GREEN}$1${NC}" >&2
+}
+
+# Check for gh CLI
+command -v gh >/dev/null 2>&1 || error "GitHub CLI (gh) is required but not installed."
+
+# Check for jq
+command -v jq >/dev/null 2>&1 || error "jq is required but not installed. Install with: brew install jq"
+
+# Verify authentication
+gh auth status >/dev/null 2>&1 || error "Not authenticated with GitHub. Run 'gh auth login' first."
+
+# Get repository info
+get_repo_info() {
+ gh repo view --json owner,name -q '"\(.owner.login)/\(.name)"' 2>/dev/null || error "Not in a GitHub repository."
+}
+
+# Find most recently merged PR for current branch
+find_recent_merged_pr() {
+ local current_branch
+ current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || error "Not in a git repository."
+
+ # First try to find a merged PR for the current branch
+ local pr_number
+ pr_number=$(gh pr list --state merged --head "$current_branch" --json number -q '.[0].number' 2>/dev/null)
+
+ if [[ -z "$pr_number" || "$pr_number" == "null" ]]; then
+ # Fallback: get the most recently merged PR in the repo
+ pr_number=$(gh pr list --state merged --json number,mergedAt --limit 10 -q 'sort_by(.mergedAt) | reverse | .[0].number' 2>/dev/null)
+ fi
+
+ if [[ -z "$pr_number" || "$pr_number" == "null" ]]; then
+ error "No merged PRs found for branch '$current_branch' or in recent history."
+ fi
+
+ echo "$pr_number"
+}
+
+# Find open PR for current branch (preferred for pre-merge workflow)
+find_current_pr() {
+ local current_branch
+ current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || error "Not in a git repository."
+
+ # First try to find an open PR for the current branch
+ local pr_number
+ pr_number=$(gh pr list --state open --head "$current_branch" --json number -q '.[0].number' 2>/dev/null)
+
+ if [[ -n "$pr_number" && "$pr_number" != "null" ]]; then
+ echo "$pr_number"
+ return
+ fi
+
+ # Fallback: try to find a merged PR for the current branch
+ pr_number=$(gh pr list --state merged --head "$current_branch" --json number -q '.[0].number' 2>/dev/null)
+
+ if [[ -n "$pr_number" && "$pr_number" != "null" ]]; then
+ warn "No open PR found, using most recently merged PR for this branch."
+ echo "$pr_number"
+ return
+ fi
+
+ error "No open or merged PRs found for branch '$current_branch'."
+}
+
+# Fetch PR basic info
+fetch_pr_info() {
+ local pr_number=$1
+ gh pr view "$pr_number" --json number,title,body,state,mergedAt,headRefName,url,author 2>/dev/null || error "Failed to fetch PR #$pr_number"
+}
+
+# Fetch review comments via GraphQL
+fetch_review_comments() {
+ local pr_number=$1
+ local repo_info
+ repo_info=$(get_repo_info)
+ local owner="${repo_info%/*}"
+ local repo="${repo_info#*/}"
+
+ local result
+ result=$(gh api graphql -f query='
+ query($owner: String!, $repo: String!, $number: Int!) {
+ repository(owner: $owner, name: $repo) {
+ pullRequest(number: $number) {
+ reviews(first: 100) {
+ nodes {
+ body
+ author { login }
+ state
+ submittedAt
+ comments(first: 50) {
+ nodes {
+ body
+ path
+ line
+ author { login }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ' -f owner="$owner" -f repo="$repo" -F number="$pr_number" 2>/dev/null)
+
+ if [[ -z "$result" ]]; then
+ warn "Failed to fetch review comments for PR #$pr_number"
+ echo "null"
+ else
+ echo "$result"
+ fi
+}
+
+# Fetch PR discussion comments
+fetch_discussion_comments() {
+ local pr_number=$1
+ local result
+ result=$(gh pr view "$pr_number" --json comments 2>/dev/null)
+
+ if [[ -z "$result" ]]; then
+ warn "Failed to fetch discussion comments for PR #$pr_number"
+ echo "null"
+ else
+ echo "$result"
+ fi
+}
+
+# Main function to fetch all PR feedback data
+fetch_all_feedback() {
+ local pr_number=$1
+
+ info "Fetching PR #$pr_number data..."
+
+ local pr_info
+ local review_comments
+ local discussion_comments
+
+ pr_info=$(fetch_pr_info "$pr_number")
+ review_comments=$(fetch_review_comments "$pr_number")
+ discussion_comments=$(fetch_discussion_comments "$pr_number")
+
+ # Normalize empty strings to "null" for jq
+ [[ -z "$review_comments" || "$review_comments" == "null" ]] && review_comments="null"
+ [[ -z "$discussion_comments" || "$discussion_comments" == "null" ]] && discussion_comments="null"
+
+ # Combine all data into a single JSON object
+ jq -n \
+ --argjson pr_info "$pr_info" \
+ --argjson review_comments "$review_comments" \
+ --argjson discussion_comments "$discussion_comments" \
+ '{
+ pr: $pr_info,
+ reviews: $review_comments,
+ discussion: $discussion_comments
+ }'
+}
+
+# Parse arguments
+case "${1:-}" in
+ --find-current)
+ pr_number=$(find_current_pr)
+ info "Found PR for current branch: #$pr_number"
+ fetch_all_feedback "$pr_number"
+ ;;
+ --find-recent)
+ pr_number=$(find_recent_merged_pr)
+ info "Found most recent merged PR: #$pr_number"
+ fetch_all_feedback "$pr_number"
+ ;;
+ --help|-h)
+ echo "Usage: $0 | --find-current | --find-recent"
+ echo ""
+ echo "Arguments:"
+ echo " Specific PR number to fetch feedback from"
+ echo " --find-current Find and fetch the open PR for current branch (falls back to merged)"
+ echo " --find-recent Find and fetch the most recently merged PR for current branch"
+ echo ""
+ echo "Output: JSON with PR metadata, review comments, and discussion comments"
+ exit 0
+ ;;
+ "")
+ error "No PR number provided. Use '$0 ', '$0 --find-current', or '$0 --find-recent'"
+ ;;
+ *)
+ if [[ "$1" =~ ^[0-9]+$ ]]; then
+ fetch_all_feedback "$1"
+ else
+ error "Invalid PR number: $1"
+ fi
+ ;;
+esac
diff --git a/scripts/powershell/fetch-pr-feedback.ps1 b/scripts/powershell/fetch-pr-feedback.ps1
new file mode 100644
index 0000000000..56dac9d3b2
--- /dev/null
+++ b/scripts/powershell/fetch-pr-feedback.ps1
@@ -0,0 +1,277 @@
+#!/usr/bin/env pwsh
+#
+# fetch-pr-feedback.ps1 - Extract PR data for the speckit.feedback agent
+#
+# Usage:
+# .\fetch-pr-feedback.ps1
+# .\fetch-pr-feedback.ps1 -FindCurrent # Find open PR for current branch (preferred)
+# .\fetch-pr-feedback.ps1 -FindRecent # Find most recently merged PR
+#
+# Output: JSON with PR metadata, review comments, and discussion comments
+#
+
+[CmdletBinding()]
+param(
+ [Parameter(Position = 0)]
+ [string]$PRNumber,
+
+ [Parameter()]
+ [switch]$FindCurrent,
+
+ [Parameter()]
+ [switch]$FindRecent,
+
+ [Parameter()]
+ [switch]$Help
+)
+
+$ErrorActionPreference = "Stop"
+
+# Helper functions for colored output
+function Write-Error-Message {
+ param([string]$Message)
+ Write-Host "ERROR: $Message" -ForegroundColor Red
+ exit 1
+}
+
+function Write-Warning-Message {
+ param([string]$Message)
+ Write-Host "WARNING: $Message" -ForegroundColor Yellow
+}
+
+function Write-Info-Message {
+ param([string]$Message)
+ Write-Host $Message -ForegroundColor Green
+}
+
+# Check for gh CLI
+function Test-GitHubCLI {
+ if (-not (Get-Command gh -ErrorAction SilentlyContinue)) {
+ Write-Error-Message "GitHub CLI (gh) is required but not installed."
+ }
+}
+
+# Check for git
+function Test-Git {
+ if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
+ Write-Error-Message "Git is required but not installed."
+ }
+}
+
+# Verify authentication
+function Test-GitHubAuth {
+ $null = gh auth status 2>&1
+ if ($LASTEXITCODE -ne 0) {
+ Write-Error-Message "Not authenticated with GitHub. Run 'gh auth login' first."
+ }
+}
+
+# Get repository info
+function Get-RepoInfo {
+ $repoInfo = gh repo view --json owner,name | ConvertFrom-Json
+ if ($LASTEXITCODE -ne 0) {
+ Write-Error-Message "Not in a GitHub repository."
+ }
+ return "$($repoInfo.owner.login)/$($repoInfo.name)"
+}
+
+# Find most recently merged PR for current branch
+function Find-RecentMergedPR {
+ $currentBranch = git rev-parse --abbrev-ref HEAD 2>&1
+ if ($LASTEXITCODE -ne 0) {
+ Write-Error-Message "Not in a git repository."
+ }
+
+ # First try to find a merged PR for the current branch
+ $prList = gh pr list --state merged --head $currentBranch --json number --limit 1 | ConvertFrom-Json
+ $prNumber = $null
+
+ if ($prList -and $prList.Count -gt 0) {
+ $prNumber = $prList[0].number
+ }
+
+ if (-not $prNumber) {
+ # Fallback: get the most recently merged PR in the repo
+ $prList = gh pr list --state merged --json number,mergedAt --limit 10 | ConvertFrom-Json
+ if ($prList -and $prList.Count -gt 0) {
+ $sorted = $prList | Sort-Object -Property mergedAt -Descending
+ $prNumber = $sorted[0].number
+ }
+ }
+
+ if (-not $prNumber) {
+ Write-Error-Message "No merged PRs found for branch '$currentBranch' or in recent history."
+ }
+
+ return $prNumber
+}
+
+# Find open PR for current branch (preferred for pre-merge workflow)
+function Find-CurrentPR {
+ $currentBranch = git rev-parse --abbrev-ref HEAD 2>&1
+ if ($LASTEXITCODE -ne 0) {
+ Write-Error-Message "Not in a git repository."
+ }
+
+ # First try to find an open PR for the current branch
+ $prList = gh pr list --state open --head $currentBranch --json number --limit 1 | ConvertFrom-Json
+ $prNumber = $null
+
+ if ($prList -and $prList.Count -gt 0) {
+ $prNumber = $prList[0].number
+ return $prNumber
+ }
+
+ # Fallback: try to find a merged PR for the current branch
+ $prList = gh pr list --state merged --head $currentBranch --json number --limit 1 | ConvertFrom-Json
+
+ if ($prList -and $prList.Count -gt 0) {
+ $prNumber = $prList[0].number
+ Write-Warning-Message "No open PR found, using most recently merged PR for this branch."
+ return $prNumber
+ }
+
+ Write-Error-Message "No open or merged PRs found for branch '$currentBranch'."
+}
+
+# Fetch PR basic info
+function Get-PRInfo {
+ param([int]$PRNumber)
+
+ $prInfo = gh pr view $PRNumber --json number,title,body,state,mergedAt,headRefName,url,author 2>&1
+ if ($LASTEXITCODE -ne 0) {
+ Write-Error-Message "Failed to fetch PR #$PRNumber"
+ }
+
+ return $prInfo | ConvertFrom-Json
+}
+
+# Fetch review comments via GraphQL
+function Get-ReviewComments {
+ param([int]$PRNumber)
+
+ $repoInfo = Get-RepoInfo
+ $parts = $repoInfo -split '/'
+ $owner = $parts[0]
+ $repo = $parts[1]
+
+ $query = @"
+query(`$owner: String!, `$repo: String!, `$number: Int!) {
+ repository(owner: `$owner, name: `$repo) {
+ pullRequest(number: `$number) {
+ reviews(first: 100) {
+ nodes {
+ body
+ author { login }
+ state
+ submittedAt
+ comments(first: 50) {
+ nodes {
+ body
+ path
+ line
+ author { login }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+"@
+
+ try {
+ $result = gh api graphql -f query=$query -f owner=$owner -f repo=$repo -F number=$PRNumber 2>&1
+ if ($LASTEXITCODE -ne 0) {
+ Write-Warning-Message "Failed to fetch review comments for PR #$PRNumber"
+ return $null
+ }
+ return $result | ConvertFrom-Json
+ }
+ catch {
+ Write-Warning-Message "Failed to fetch review comments for PR #$PRNumber"
+ return $null
+ }
+}
+
+# Fetch PR discussion comments
+function Get-DiscussionComments {
+ param([int]$PRNumber)
+
+ try {
+ $result = gh pr view $PRNumber --json comments 2>&1
+ if ($LASTEXITCODE -ne 0) {
+ Write-Warning-Message "Failed to fetch discussion comments for PR #$PRNumber"
+ return $null
+ }
+ return $result | ConvertFrom-Json
+ }
+ catch {
+ Write-Warning-Message "Failed to fetch discussion comments for PR #$PRNumber"
+ return $null
+ }
+}
+
+# Main function to fetch all PR feedback data
+function Get-AllFeedback {
+ param([int]$PRNumber)
+
+ Write-Info-Message "Fetching PR #$PRNumber data..."
+
+ $prInfo = Get-PRInfo -PRNumber $PRNumber
+ $reviewComments = Get-ReviewComments -PRNumber $PRNumber
+ $discussionComments = Get-DiscussionComments -PRNumber $PRNumber
+
+ # Create combined JSON object
+ $combined = @{
+ pr = $prInfo
+ reviews = $reviewComments
+ discussion = $discussionComments
+ }
+
+ return $combined | ConvertTo-Json -Depth 100 -Compress
+}
+
+# Show help
+function Show-Help {
+ Write-Host "Usage: .\fetch-pr-feedback.ps1 | -FindCurrent | -FindRecent"
+ Write-Host ""
+ Write-Host "Arguments:"
+ Write-Host " Specific PR number to fetch feedback from"
+ Write-Host " -FindCurrent Find and fetch the open PR for current branch (falls back to merged)"
+ Write-Host " -FindRecent Find and fetch the most recently merged PR for current branch"
+ Write-Host ""
+ Write-Host "Output: JSON with PR metadata, review comments, and discussion comments"
+ exit 0
+}
+
+# Main execution
+if ($Help) {
+ Show-Help
+}
+
+Test-GitHubCLI
+Test-Git
+Test-GitHubAuth
+
+if ($FindCurrent) {
+ $prNum = Find-CurrentPR
+ Write-Info-Message "Found PR for current branch: #$prNum"
+ Get-AllFeedback -PRNumber $prNum
+}
+elseif ($FindRecent) {
+ $prNum = Find-RecentMergedPR
+ Write-Info-Message "Found most recent merged PR: #$prNum"
+ Get-AllFeedback -PRNumber $prNum
+}
+elseif ($PRNumber) {
+ if ($PRNumber -match '^\d+$') {
+ Get-AllFeedback -PRNumber ([int]$PRNumber)
+ }
+ else {
+ Write-Error-Message "Invalid PR number: $PRNumber"
+ }
+}
+else {
+ Write-Error-Message "No PR number provided. Use '.\fetch-pr-feedback.ps1 ', '-FindCurrent', or '-FindRecent'"
+}
diff --git a/spec-driven.md b/spec-driven.md
index 70b9789708..cd7f164f0b 100644
--- a/spec-driven.md
+++ b/spec-driven.md
@@ -78,7 +78,7 @@ The SDD methodology is significantly enhanced through three powerful commands th
This command transforms a simple feature description (the user-prompt) into a complete, structured specification with automatic repository management:
-1. **Automatic Feature Numbering**: Scans existing specs to determine the next feature number (e.g., 001, 002, 003)
+1. **Automatic Feature Numbering**: Scans existing specs to determine the next feature number (e.g., 001, 002, 003) or uses any mention of a number or issue number in the description.
2. **Branch Creation**: Generates a semantic branch name from your description and creates it automatically
3. **Template-Based Generation**: Copies and customizes the feature specification template with your requirements
4. **Directory Structure**: Creates the proper `specs/[branch-name]/` structure for all related documents
diff --git a/src/specify_cli/__init__.py b/src/specify_cli/__init__.py
index 1dedb31949..927c6ef7f2 100644
--- a/src/specify_cli/__init__.py
+++ b/src/specify_cli/__init__.py
@@ -635,7 +635,7 @@ def deep_merge(base: dict, update: dict) -> dict:
return merged
def download_template_from_github(ai_assistant: str, download_dir: Path, *, script_type: str = "sh", verbose: bool = True, show_progress: bool = True, client: httpx.Client = None, debug: bool = False, github_token: str = None) -> Tuple[Path, dict]:
- repo_owner = "github"
+ repo_owner = "buddhisthead"
repo_name = "spec-kit"
if client is None:
client = httpx.Client(verify=ssl_context)
@@ -1224,6 +1224,7 @@ def init(
steps_lines.append(" 2.3 [cyan]/speckit.plan[/] - Create implementation plan")
steps_lines.append(" 2.4 [cyan]/speckit.tasks[/] - Generate actionable tasks")
steps_lines.append(" 2.5 [cyan]/speckit.implement[/] - Execute implementation")
+ steps_lines.append(" 2.6 [cyan]/speckit.feedback[/] - Capture lessons from PR reviews")
steps_panel = Panel("\n".join(steps_lines), title="Next Steps", border_style="cyan", padding=(1,2))
console.print()
@@ -1307,7 +1308,7 @@ def version():
pass
# Fetch latest template release version
- repo_owner = "github"
+ repo_owner = "buddhisthead"
repo_name = "spec-kit"
api_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases/latest"
diff --git a/templates/commands/feedback.md b/templates/commands/feedback.md
new file mode 100644
index 0000000000..c420ed9756
--- /dev/null
+++ b/templates/commands/feedback.md
@@ -0,0 +1,293 @@
+---
+description: Extract lessons learned from PR review comments and discussions, storing them in per-PR lesson files and a central lessons database.
+handoffs:
+ - label: Specify Feature
+ agent: speckit.specify
+ prompt: Create a new feature specification
+ - label: Plan Feature
+ agent: speckit.plan
+ prompt: Create a technical plan for the specification
+scripts:
+ sh: scripts/bash/fetch-pr-feedback.sh
+ ps: scripts/powershell/fetch-pr-feedback.ps1
+---
+
+## User Input
+
+```text
+$ARGUMENTS
+```
+
+You **MUST** consider the user input before proceeding (if not empty).
+
+## Outline
+
+Goal: Capture lessons learned from PR feedback and store them for future reference by other speckit phases.
+
+## Execution Steps
+
+### Step 1: Parse Arguments
+
+Analyze the `$ARGUMENTS` input to determine the operation mode:
+
+**Mode A - Specific PR**: If `--pr ` is present, use that PR number.
+
+**Mode B - Auto-detect PR**: If no `--pr` flag but no inline text, find the open PR for the current branch (or most recently merged if no open PR exists).
+
+**Mode C - Manual Entry**: If inline text is provided (not a flag), treat it as a manual lesson entry.
+
+Parse these optional flags:
+- `--pr `: Specific PR to extract feedback from
+- `--category `: Category for manual lesson (code-quality, architecture, testing, documentation, security, performance, other)
+- `--commit`: Automatically run git add/commit/push after extracting lessons
+
+### Step 2: Ensure Directory Structure
+
+Check if `memory/` directory exists (it should, as it contains `constitution.md`). Create the feedback subdirectory if needed:
+- `memory/lessons.md` - Central lessons database (alongside constitution.md)
+- `memory/feedback/` - Directory for PR-specific lesson files
+- `memory/feedback/pr--lessons.md` - Per-PR lesson files
+
+### Step 3: Execute Based on Mode
+
+#### Mode A/B: PR Feedback Extraction
+
+1. **Get PR Number**:
+ - Mode A: Use the `--pr ` value
+ - Mode B: Run `{SCRIPT} --find-current` to get the open PR for current branch (falls back to most recently merged)
+
+2. **Check for Existing PR Lessons** (Incremental Mode):
+ - If `memory/feedback/pr--lessons.md` exists:
+ - Read existing lessons from the file
+ - Track existing lesson IDs for merge later
+ - Note: No prompt needed - we will merge new lessons with existing ones
+
+3. **Fetch PR Data**:
+ - Run `{SCRIPT} `
+ - Parse the JSON output containing:
+ - PR metadata (title, body, author, URL)
+ - Review comments and suggestions
+ - Discussion thread comments
+
+4. **Handle Edge Cases**:
+ - If no PR found: Prompt user "No open or merged PR found for current branch. Enter a PR number or type a lesson for manual entry."
+ - If PR has no review comments: Report "No review feedback found in PR #. Would you like to add a manual lesson instead?"
+
+5. **Extract and Categorize Lessons**:
+ - Analyze review comments, suggestions, and PR description
+ - For each substantive feedback item, create a lesson:
+ - Determine the most appropriate category (code-quality, architecture, testing, documentation, security, performance, other)
+ - Extract the core lesson content
+ - Add relevant tags based on file paths, technologies mentioned
+ - Limit to top 10 lessons if PR discussion is very long (summarize themes)
+ - **Deduplication**: Skip lessons that match existing lessons (from step 2)
+
+6. **Generate/Update PR Lessons File** (Incremental Merge):
+ - If file exists: Merge new lessons with existing ones
+ - Keep all existing lessons
+ - Add only new lessons (those not matching existing by content)
+ - Update `extracted_date` to today
+ - Update `lesson_count` to new total
+ - If file is new: Create `memory/feedback/pr--lessons.md` with YAML frontmatter:
+ ```yaml
+ ---
+ pr_number:
+ pr_title: ""
+ pr_url: ""
+ extracted_date:
+ lesson_count:
+ ---
+ ```
+ - Add each lesson in the format from data-model.md
+
+7. **Update Central Database** (proceed to Step 4)
+
+#### Mode C: Manual Lesson Entry
+
+1. **Get Lesson Content**: Extract the inline text from `$ARGUMENTS`
+
+2. **Determine Category**:
+ - If `--category ` provided, use that category
+ - Otherwise, prompt user: "Select a category for this lesson: 1) code-quality, 2) architecture, 3) testing, 4) documentation, 5) security, 6) performance, 7) other"
+
+3. **Create Lesson Object**:
+ ```yaml
+ lesson_id:
+ category:
+ tags: []
+ source: manual
+ source_ref: "Manual entry "
+ date:
+ frequency: 1
+ ```
+
+4. **Proceed to Step 4** to add to database
+
+### Step 4: Update Central Lessons Database
+
+1. **Read Existing Database**:
+ - Load `memory/lessons.md`
+ - Parse YAML frontmatter to get `total_lessons` and `last_updated`
+ - Parse existing lesson entries to build lesson ID list
+
+2. **Duplicate Detection**:
+ For each new lesson:
+ - Compare content with existing lessons (fuzzy match - look for similar phrasing, same core concept)
+ - If duplicate found (>80% similarity in meaning):
+ - Increment the `frequency` count on the existing lesson
+ - Do NOT add as new entry
+ - If not duplicate:
+ - Generate next lesson_id (L001, L002, etc. based on existing IDs)
+ - Append to database
+
+3. **Update Database Metadata**:
+ - Update `last_updated` to today's date
+ - Update `total_lessons` count
+
+4. **Write Updated Database**:
+ - Preserve the header and category documentation
+ - Append new lessons below the `` marker
+
+### Step 5: Report Summary
+
+Output a summary to the user:
+
+**For PR Extraction (first run):**
+```
+â
Extracted lessons from PR # ""
+ - code-quality lessons
+ - architecture lessons
+ - testing lessons
+ - ... (other categories with lessons)
+
+đ Created: memory/feedback/pr--lessons.md
+đ Updated: memory/lessons.md (now contains total lessons)
+
+
+âšī¸ lessons matched existing entries (frequency incremented)
+```
+
+**For PR Extraction (incremental run - file already existed):**
+```
+â
Merged new lessons into PR # lessons
+ - existing lessons preserved
+ - new lessons added
+ - duplicates skipped
+
+đ Updated: memory/feedback/pr--lessons.md (now contains lessons)
+đ Updated: memory/lessons.md (now contains total lessons)
+```
+
+**For Manual Entry:**
+```
+â
Added manual lesson to database
+ Category:
+
+đ Updated: memory/lessons.md (now contains total lessons)
+```
+
+### Step 6: Commit Changes (if --commit flag) or Prompt
+
+**IMPORTANT**: This step runs for ALL modes (PR extraction, manual entry) whenever there are changes to `memory/lessons.md` or PR lesson files. Always check for uncommitted changes and proceed with commit if `--commit` flag is present.
+
+Since feedback is captured before the PR is merged, the lessons files can be committed directly to the current feature branch.
+
+**If `--commit` flag is present**:
+
+First, check if there are any uncommitted changes to memory/ lesson files:
+```bash
+git status --short memory/lessons.md memory/feedback/
+```
+
+If there are changes, run the following git commands (get current branch name first):
+
+```bash
+git add memory/lessons.md memory/feedback/
+git commit -m "chore(feedback): "
+git push origin
+```
+
+Use appropriate commit message:
+- PR extraction: `"chore(feedback): capture lessons from PR #"`
+- Manual entry: `"chore(feedback): add manual lesson L"`
+- Mixed: `"chore(feedback): update lessons database"`
+
+If no changes are pending, skip the commit and report:
+```
+âšī¸ No uncommitted changes to memory/ lessons - nothing to commit.
+```
+
+**Output** (when changes committed):
+```
+â
Changes committed and pushed!
+ git add memory/lessons.md memory/feedback/
+ git commit -m "chore(feedback): ..."
+ git push origin
+
+Your lessons are now part of the PR - ready to merge!
+```
+
+**If no `--commit` flag** (default):
+
+**Output**:
+```
+đ Ready to commit! Run:
+ git add memory/lessons.md memory/feedback/pr--lessons.md
+ git commit -m "chore(feedback): capture lessons from PR #"
+ git push origin
+
+Then merge your PR as usual - lessons will be included!
+```
+
+This simplified workflow means:
+- No separate branch or PR needed for lessons
+- Lessons are reviewed along with the original PR
+- Merge includes both the feature changes and the captured lessons
+
+## Lesson Entry Format
+
+Each lesson in the central database uses a Markdown heading with a fenced YAML metadata block:
+
+```markdown
+### L###
+
+```yaml
+lesson_id: L###
+category:
+tags: [tag1, tag2]
+source: pr | manual
+source_ref: "PR #123" | "Manual entry YYYY-MM-DD"
+date: YYYY-MM-DD
+frequency: 1
+```
+
+
+```
+
+**Note**: Lessons are separated by blank lines. Do NOT use `---` separators between lessons to avoid ambiguous YAML parsing.
+
+## Categories Reference
+
+| Category | Use For |
+|----------|---------|
+| code-quality | Code style, idioms, language-specific best practices |
+| architecture | System design, patterns, module structure |
+| testing | Test strategies, coverage, edge case handling |
+| documentation | Comments, READMEs, API documentation |
+| security | Authentication, authorization, input validation, secrets handling |
+| performance | Optimization, efficiency, resource usage |
+| other | Miscellaneous lessons not fitting other categories |
+
+## Error Handling
+
+- **No `gh` CLI**: "GitHub CLI (gh) is required. Install with: brew install gh"
+- **Not authenticated**: "Please authenticate with GitHub: gh auth login"
+- **Not in git repo**: "This command must be run from within a git repository"
+- **PR not found**: "PR # not found. Please verify the PR number exists."
+- **Network error**: "Failed to fetch PR data. Check your network connection and try again."
+
+## Stop Conditions
+
+- Successfully created/updated lesson files
+- User cancels duplicate overwrite prompt
+- Fatal error (missing dependencies, authentication failure)
diff --git a/templates/commands/implement.md b/templates/commands/implement.md
index 39abb1e6c8..674123ba82 100644
--- a/templates/commands/implement.md
+++ b/templates/commands/implement.md
@@ -15,6 +15,12 @@ You **MUST** consider the user input before proceeding (if not empty).
## Outline
+0. **Load Lessons Context** (if available):
+ - Check if `memory/lessons.md` exists in the repository
+ - If it exists, read the file and filter for lessons with categories: `code-quality`, `security`, `testing`
+ - Keep relevant lessons in mind during implementation to avoid repeating past mistakes
+ - Apply lessons proactively (e.g., if L007 says "check CLI dependencies", do that in your code)
+
1. Run `{SCRIPT}` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\''m Groot' (or double-quote if possible: "I'm Groot").
2. **Check checklists status** (if FEATURE_DIR/checklists/ exists):
diff --git a/templates/commands/plan.md b/templates/commands/plan.md
index 147da0afa0..81842bb426 100644
--- a/templates/commands/plan.md
+++ b/templates/commands/plan.md
@@ -26,6 +26,12 @@ You **MUST** consider the user input before proceeding (if not empty).
## Outline
+0. **Load Lessons Context** (if available):
+ - Check if `memory/lessons.md` exists in the repository
+ - If it exists, read the file and filter for lessons with categories: `architecture`, `testing`, `performance`
+ - Keep relevant lessons in mind when creating the technical plan to avoid repeating past mistakes
+ - You may reference applied lessons in the plan (e.g., "Per L003: ...")
+
1. **Setup**: Run `{SCRIPT}` from repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\''m Groot' (or double-quote if possible: "I'm Groot").
2. **Load context**: Read FEATURE_SPEC and `/memory/constitution.md`. Load IMPL_PLAN template (already copied).
diff --git a/templates/commands/specify.md b/templates/commands/specify.md
index 3c952d683e..3a9607fb4d 100644
--- a/templates/commands/specify.md
+++ b/templates/commands/specify.md
@@ -27,6 +27,12 @@ The text the user typed after `/speckit.specify` in the triggering message **is*
Given that feature description, do this:
+0. **Load Lessons Context** (if available):
+ - Check if `memory/lessons.md` exists in the repository
+ - If it exists, read the file and filter for lessons with categories: `architecture`, `documentation`
+ - Keep relevant lessons in mind when writing the specification to avoid repeating past mistakes
+ - You may reference applied lessons in the spec (e.g., "Per L007: ...")
+
1. **Generate a concise short name** (2-4 words) for the branch:
- Analyze the feature description and extract the most meaningful keywords
- Create a 2-4 word short name that captures the essence of the feature
@@ -55,7 +61,8 @@ Given that feature description, do this:
c. Determine the next available number:
- Extract all numbers from all three sources
- Find the highest number N
- - Use N+1 for the new branch number
+ - Use N+1 for the default new branch number
+ - Examine User Input for any explicit number provided by the user (e.g., "number 5", "N=5", "issue number 5", or "GH issue #5", or "issue #5") and if found, use that instead of N+1 for the new branch number
d. Run the script `{SCRIPT}` with the calculated number and short-name:
- Pass `--number N+1` and `--short-name "your-short-name"` along with the feature description