Compare commits
15 Commits
v5.0.1
...
prettier-e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c48ddf462a | ||
|
|
0c10ccd149 | ||
|
|
a0a0b1ba6c | ||
|
|
3c72d01f97 | ||
|
|
e2b72c0618 | ||
|
|
1e5dcd043a | ||
|
|
312540327f | ||
|
|
74c78d2274 | ||
|
|
e1176f337e | ||
|
|
424cea6d8f | ||
|
|
3092c9c9c2 | ||
|
|
3c7f922564 | ||
|
|
12aaaa537b | ||
|
|
faff4e06a1 | ||
|
|
5e5c7ed98f |
173
.github/workflows/manual-release.yaml
vendored
173
.github/workflows/manual-release.yaml
vendored
@@ -1,173 +0,0 @@
|
||||
name: Manual Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_bump:
|
||||
description: Version bump type
|
||||
required: true
|
||||
default: patch
|
||||
type: choice
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: npm
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests and validation
|
||||
run: |
|
||||
npm run validate
|
||||
npm run format:check
|
||||
npm run lint
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Bump version
|
||||
run: npm run version:${{ github.event.inputs.version_bump }}
|
||||
|
||||
- name: Get new version and previous tag
|
||||
id: version
|
||||
run: |
|
||||
echo "new_version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
||||
echo "previous_tag=$(git describe --tags --abbrev=0)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update installer package.json
|
||||
run: |
|
||||
sed -i 's/"version": ".*"/"version": "${{ steps.version.outputs.new_version }}"/' tools/installer/package.json
|
||||
|
||||
- name: Build project
|
||||
run: npm run build
|
||||
|
||||
- name: Commit version bump
|
||||
run: |
|
||||
git add .
|
||||
git commit -m "release: bump to v${{ steps.version.outputs.new_version }}"
|
||||
|
||||
- name: Generate release notes
|
||||
id: release_notes
|
||||
run: |
|
||||
# Get commits since last tag
|
||||
COMMITS=$(git log ${{ steps.version.outputs.previous_tag }}..HEAD --pretty=format:"- %s" --reverse)
|
||||
|
||||
# Categorize commits
|
||||
FEATURES=$(echo "$COMMITS" | grep -E "^- (feat|Feature)" || true)
|
||||
FIXES=$(echo "$COMMITS" | grep -E "^- (fix|Fix)" || true)
|
||||
CHORES=$(echo "$COMMITS" | grep -E "^- (chore|Chore)" || true)
|
||||
OTHERS=$(echo "$COMMITS" | grep -v -E "^- (feat|Feature|fix|Fix|chore|Chore|release:|Release:)" || true)
|
||||
|
||||
# Build release notes
|
||||
cat > release_notes.md << 'EOF'
|
||||
## 🚀 What's New in v${{ steps.version.outputs.new_version }}
|
||||
|
||||
EOF
|
||||
|
||||
if [ ! -z "$FEATURES" ]; then
|
||||
echo "### ✨ New Features" >> release_notes.md
|
||||
echo "$FEATURES" >> release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
fi
|
||||
|
||||
if [ ! -z "$FIXES" ]; then
|
||||
echo "### 🐛 Bug Fixes" >> release_notes.md
|
||||
echo "$FIXES" >> release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
fi
|
||||
|
||||
if [ ! -z "$OTHERS" ]; then
|
||||
echo "### 📦 Other Changes" >> release_notes.md
|
||||
echo "$OTHERS" >> release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
fi
|
||||
|
||||
if [ ! -z "$CHORES" ]; then
|
||||
echo "### 🔧 Maintenance" >> release_notes.md
|
||||
echo "$CHORES" >> release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
fi
|
||||
|
||||
cat >> release_notes.md << 'EOF'
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
```bash
|
||||
npx bmad-method install
|
||||
```
|
||||
|
||||
**Full Changelog**: https://github.com/bmadcode/BMAD-METHOD/compare/${{ steps.version.outputs.previous_tag }}...v${{ steps.version.outputs.new_version }}
|
||||
EOF
|
||||
|
||||
# Output for GitHub Actions
|
||||
echo "RELEASE_NOTES<<EOF" >> $GITHUB_OUTPUT
|
||||
cat release_notes.md >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create and push tag
|
||||
run: |
|
||||
# Check if tag already exists
|
||||
if git rev-parse "v${{ steps.version.outputs.new_version }}" >/dev/null 2>&1; then
|
||||
echo "Tag v${{ steps.version.outputs.new_version }} already exists, skipping tag creation"
|
||||
else
|
||||
git tag -a "v${{ steps.version.outputs.new_version }}" -m "Release v${{ steps.version.outputs.new_version }}"
|
||||
git push origin "v${{ steps.version.outputs.new_version }}"
|
||||
fi
|
||||
|
||||
- name: Push changes to main
|
||||
run: |
|
||||
if git push origin HEAD:main 2>/dev/null; then
|
||||
echo "✅ Successfully pushed to main branch"
|
||||
else
|
||||
echo "⚠️ Could not push to main (protected branch). This is expected."
|
||||
echo "📝 Version bump and tag were created successfully."
|
||||
fi
|
||||
|
||||
- name: Publish to NPM
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: npm publish
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: v${{ steps.version.outputs.new_version }}
|
||||
release_name: "BMad Method v${{ steps.version.outputs.new_version }}"
|
||||
body: ${{ steps.release_notes.outputs.RELEASE_NOTES }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "🎉 Successfully released v${{ steps.version.outputs.new_version }}!"
|
||||
echo "📦 Published to NPM with @latest tag"
|
||||
echo "🏷️ Git tag: v${{ steps.version.outputs.new_version }}"
|
||||
echo "✅ Users running 'npx bmad-method install' will now get version ${{ steps.version.outputs.new_version }}"
|
||||
echo ""
|
||||
echo "📝 Release notes preview:"
|
||||
cat release_notes.md
|
||||
122
.github/workflows/promote-to-stable.yaml
vendored
Normal file
122
.github/workflows/promote-to-stable.yaml
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
name: Promote to Stable
|
||||
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_bump:
|
||||
description: "Version bump type"
|
||||
required: true
|
||||
default: "minor"
|
||||
type: choice
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
|
||||
jobs:
|
||||
promote:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --global url."https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/".insteadOf "https://github.com/"
|
||||
|
||||
- name: Switch to stable branch
|
||||
run: |
|
||||
git checkout stable
|
||||
git pull origin stable
|
||||
|
||||
- name: Merge main into stable
|
||||
run: |
|
||||
git merge origin/main --no-edit
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Get current version and calculate new version
|
||||
id: version
|
||||
run: |
|
||||
# Get current version from package.json
|
||||
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
||||
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
# Remove beta suffix if present
|
||||
BASE_VERSION=$(echo $CURRENT_VERSION | sed 's/-beta\.[0-9]\+//')
|
||||
echo "base_version=$BASE_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
# Calculate new version based on bump type
|
||||
IFS='.' read -ra VERSION_PARTS <<< "$BASE_VERSION"
|
||||
MAJOR=${VERSION_PARTS[0]}
|
||||
MINOR=${VERSION_PARTS[1]}
|
||||
PATCH=${VERSION_PARTS[2]}
|
||||
|
||||
case "${{ github.event.inputs.version_bump }}" in
|
||||
"major")
|
||||
NEW_VERSION="$((MAJOR + 1)).0.0"
|
||||
;;
|
||||
"minor")
|
||||
NEW_VERSION="$MAJOR.$((MINOR + 1)).0"
|
||||
;;
|
||||
"patch")
|
||||
NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))"
|
||||
;;
|
||||
*)
|
||||
NEW_VERSION="$BASE_VERSION"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Promoting from $CURRENT_VERSION to $NEW_VERSION"
|
||||
|
||||
- name: Update package.json versions
|
||||
run: |
|
||||
# Update main package.json
|
||||
npm version ${{ steps.version.outputs.new_version }} --no-git-tag-version
|
||||
|
||||
# Update installer package.json
|
||||
sed -i 's/"version": ".*"/"version": "${{ steps.version.outputs.new_version }}"/' tools/installer/package.json
|
||||
|
||||
- name: Update package-lock.json
|
||||
run: npm install --package-lock-only
|
||||
|
||||
- name: Commit stable release
|
||||
run: |
|
||||
git add .
|
||||
git commit -m "release: promote to stable ${{ steps.version.outputs.new_version }}
|
||||
|
||||
- Promote beta features to stable release
|
||||
- Update version from ${{ steps.version.outputs.current_version }} to ${{ steps.version.outputs.new_version }}
|
||||
- Automated promotion via GitHub Actions"
|
||||
|
||||
- name: Push stable release
|
||||
run: |
|
||||
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
|
||||
git push origin stable
|
||||
|
||||
- name: Switch back to main
|
||||
run: git checkout main
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "🎉 Successfully promoted to stable!"
|
||||
echo "📦 Version: ${{ steps.version.outputs.new_version }}"
|
||||
echo "🚀 The stable release will be automatically published to NPM via semantic-release"
|
||||
echo "✅ Users running 'npx bmad-method install' will now get version ${{ steps.version.outputs.new_version }}"
|
||||
74
.github/workflows/release.yaml
vendored
Normal file
74
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
name: Release
|
||||
"on":
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- stable
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_type:
|
||||
description: Version bump type
|
||||
required: true
|
||||
default: patch
|
||||
type: choice
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
packages: write
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'push' || !contains(github.event.head_commit.message, '[skip ci]') }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Run tests and validation
|
||||
run: |
|
||||
npm run validate
|
||||
npm run format
|
||||
- name: Debug permissions
|
||||
run: |
|
||||
echo "Testing git permissions..."
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
echo "Git config set successfully"
|
||||
- name: Manual version bump
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: npm run version:${{ github.event.inputs.version_type }}
|
||||
- name: Semantic Release
|
||||
if: github.event_name == 'push'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: npm run release
|
||||
- name: Clean changelog formatting
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
# Remove any Claude Code attribution from changelog
|
||||
sed -i '/🤖 Generated with \[Claude Code\]/,+2d' CHANGELOG.md || true
|
||||
# Format and commit if changes exist
|
||||
npm run format
|
||||
if ! git diff --quiet CHANGELOG.md; then
|
||||
git add CHANGELOG.md
|
||||
git commit -m "chore: clean changelog formatting [skip ci]"
|
||||
git push
|
||||
fi
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -43,4 +43,4 @@ CLAUDE.md
|
||||
test-project-install/*
|
||||
sample-project/*
|
||||
flattened-codebase.xml
|
||||
*.stats.md
|
||||
|
||||
|
||||
27
.releaserc.json
Normal file
27
.releaserc.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"branches": [
|
||||
{
|
||||
"name": "main",
|
||||
"prerelease": "beta",
|
||||
"channel": "beta"
|
||||
},
|
||||
{
|
||||
"name": "stable",
|
||||
"channel": "latest"
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
[
|
||||
"@semantic-release/changelog",
|
||||
{
|
||||
"changelogFile": "CHANGELOG.md",
|
||||
"changelogTitle": ""
|
||||
}
|
||||
],
|
||||
"@semantic-release/npm",
|
||||
"./tools/semantic-release-sync-installer.js",
|
||||
"@semantic-release/github"
|
||||
]
|
||||
}
|
||||
@@ -75,8 +75,6 @@ This makes it easy to benefit from the latest improvements, bug fixes, and new a
|
||||
|
||||
```bash
|
||||
npx bmad-method install
|
||||
# OR explicitly use stable tag:
|
||||
npx bmad-method@stable install
|
||||
# OR if you already have BMad installed:
|
||||
git pull
|
||||
npm run install:bmad
|
||||
|
||||
234
dist/agents/analyst.txt
vendored
234
dist/agents/analyst.txt
vendored
@@ -1101,24 +1101,24 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/brief.md
|
||||
title: "Project Brief: {{project_name}}"
|
||||
title: 'Project Brief: {{project_name}}'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
elicitation: advanced-elicitation
|
||||
custom_elicitation:
|
||||
title: "Project Brief Elicitation Actions"
|
||||
title: 'Project Brief Elicitation Actions'
|
||||
options:
|
||||
- "Expand section with more specific details"
|
||||
- "Validate against similar successful products"
|
||||
- "Stress test assumptions with edge cases"
|
||||
- "Explore alternative solution approaches"
|
||||
- "Analyze resource/constraint trade-offs"
|
||||
- "Generate risk mitigation strategies"
|
||||
- "Challenge scope from MVP minimalist view"
|
||||
- "Brainstorm creative feature possibilities"
|
||||
- "If only we had [resource/capability/time]..."
|
||||
- "Proceed to next section"
|
||||
- 'Expand section with more specific details'
|
||||
- 'Validate against similar successful products'
|
||||
- 'Stress test assumptions with edge cases'
|
||||
- 'Explore alternative solution approaches'
|
||||
- 'Analyze resource/constraint trade-offs'
|
||||
- 'Generate risk mitigation strategies'
|
||||
- 'Challenge scope from MVP minimalist view'
|
||||
- 'Brainstorm creative feature possibilities'
|
||||
- 'If only we had [resource/capability/time]...'
|
||||
- 'Proceed to next section'
|
||||
|
||||
sections:
|
||||
- id: introduction
|
||||
@@ -1140,7 +1140,7 @@ sections:
|
||||
- Primary problem being solved
|
||||
- Target market identification
|
||||
- Key value proposition
|
||||
template: "{{executive_summary_content}}"
|
||||
template: '{{executive_summary_content}}'
|
||||
|
||||
- id: problem-statement
|
||||
title: Problem Statement
|
||||
@@ -1150,7 +1150,7 @@ sections:
|
||||
- Impact of the problem (quantify if possible)
|
||||
- Why existing solutions fall short
|
||||
- Urgency and importance of solving this now
|
||||
template: "{{detailed_problem_description}}"
|
||||
template: '{{detailed_problem_description}}'
|
||||
|
||||
- id: proposed-solution
|
||||
title: Proposed Solution
|
||||
@@ -1160,7 +1160,7 @@ sections:
|
||||
- Key differentiators from existing solutions
|
||||
- Why this solution will succeed where others haven't
|
||||
- High-level vision for the product
|
||||
template: "{{solution_description}}"
|
||||
template: '{{solution_description}}'
|
||||
|
||||
- id: target-users
|
||||
title: Target Users
|
||||
@@ -1172,12 +1172,12 @@ sections:
|
||||
- Goals they're trying to achieve
|
||||
sections:
|
||||
- id: primary-segment
|
||||
title: "Primary User Segment: {{segment_name}}"
|
||||
template: "{{primary_user_description}}"
|
||||
title: 'Primary User Segment: {{segment_name}}'
|
||||
template: '{{primary_user_description}}'
|
||||
- id: secondary-segment
|
||||
title: "Secondary User Segment: {{segment_name}}"
|
||||
title: 'Secondary User Segment: {{segment_name}}'
|
||||
condition: Has secondary user segment
|
||||
template: "{{secondary_user_description}}"
|
||||
template: '{{secondary_user_description}}'
|
||||
|
||||
- id: goals-metrics
|
||||
title: Goals & Success Metrics
|
||||
@@ -1186,15 +1186,15 @@ sections:
|
||||
- id: business-objectives
|
||||
title: Business Objectives
|
||||
type: bullet-list
|
||||
template: "- {{objective_with_metric}}"
|
||||
template: '- {{objective_with_metric}}'
|
||||
- id: user-success-metrics
|
||||
title: User Success Metrics
|
||||
type: bullet-list
|
||||
template: "- {{user_metric}}"
|
||||
template: '- {{user_metric}}'
|
||||
- id: kpis
|
||||
title: Key Performance Indicators (KPIs)
|
||||
type: bullet-list
|
||||
template: "- {{kpi}}: {{definition_and_target}}"
|
||||
template: '- {{kpi}}: {{definition_and_target}}'
|
||||
|
||||
- id: mvp-scope
|
||||
title: MVP Scope
|
||||
@@ -1203,14 +1203,14 @@ sections:
|
||||
- id: core-features
|
||||
title: Core Features (Must Have)
|
||||
type: bullet-list
|
||||
template: "- **{{feature}}:** {{description_and_rationale}}"
|
||||
template: '- **{{feature}}:** {{description_and_rationale}}'
|
||||
- id: out-of-scope
|
||||
title: Out of Scope for MVP
|
||||
type: bullet-list
|
||||
template: "- {{feature_or_capability}}"
|
||||
template: '- {{feature_or_capability}}'
|
||||
- id: mvp-success-criteria
|
||||
title: MVP Success Criteria
|
||||
template: "{{mvp_success_definition}}"
|
||||
template: '{{mvp_success_definition}}'
|
||||
|
||||
- id: post-mvp-vision
|
||||
title: Post-MVP Vision
|
||||
@@ -1218,13 +1218,13 @@ sections:
|
||||
sections:
|
||||
- id: phase-2-features
|
||||
title: Phase 2 Features
|
||||
template: "{{next_priority_features}}"
|
||||
template: '{{next_priority_features}}'
|
||||
- id: long-term-vision
|
||||
title: Long-term Vision
|
||||
template: "{{one_two_year_vision}}"
|
||||
template: '{{one_two_year_vision}}'
|
||||
- id: expansion-opportunities
|
||||
title: Expansion Opportunities
|
||||
template: "{{potential_expansions}}"
|
||||
template: '{{potential_expansions}}'
|
||||
|
||||
- id: technical-considerations
|
||||
title: Technical Considerations
|
||||
@@ -1265,7 +1265,7 @@ sections:
|
||||
- id: key-assumptions
|
||||
title: Key Assumptions
|
||||
type: bullet-list
|
||||
template: "- {{assumption}}"
|
||||
template: '- {{assumption}}'
|
||||
|
||||
- id: risks-questions
|
||||
title: Risks & Open Questions
|
||||
@@ -1274,15 +1274,15 @@ sections:
|
||||
- id: key-risks
|
||||
title: Key Risks
|
||||
type: bullet-list
|
||||
template: "- **{{risk}}:** {{description_and_impact}}"
|
||||
template: '- **{{risk}}:** {{description_and_impact}}'
|
||||
- id: open-questions
|
||||
title: Open Questions
|
||||
type: bullet-list
|
||||
template: "- {{question}}"
|
||||
template: '- {{question}}'
|
||||
- id: research-areas
|
||||
title: Areas Needing Further Research
|
||||
type: bullet-list
|
||||
template: "- {{research_topic}}"
|
||||
template: '- {{research_topic}}'
|
||||
|
||||
- id: appendices
|
||||
title: Appendices
|
||||
@@ -1299,10 +1299,10 @@ sections:
|
||||
- id: stakeholder-input
|
||||
title: B. Stakeholder Input
|
||||
condition: Has stakeholder feedback
|
||||
template: "{{stakeholder_feedback}}"
|
||||
template: '{{stakeholder_feedback}}'
|
||||
- id: references
|
||||
title: C. References
|
||||
template: "{{relevant_links_and_docs}}"
|
||||
template: '{{relevant_links_and_docs}}'
|
||||
|
||||
- id: next-steps
|
||||
title: Next Steps
|
||||
@@ -1310,7 +1310,7 @@ sections:
|
||||
- id: immediate-actions
|
||||
title: Immediate Actions
|
||||
type: numbered-list
|
||||
template: "{{action_item}}"
|
||||
template: '{{action_item}}'
|
||||
- id: pm-handoff
|
||||
title: PM Handoff
|
||||
content: |
|
||||
@@ -1325,24 +1325,24 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/market-research.md
|
||||
title: "Market Research Report: {{project_product_name}}"
|
||||
title: 'Market Research Report: {{project_product_name}}'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
elicitation: advanced-elicitation
|
||||
custom_elicitation:
|
||||
title: "Market Research Elicitation Actions"
|
||||
title: 'Market Research Elicitation Actions'
|
||||
options:
|
||||
- "Expand market sizing calculations with sensitivity analysis"
|
||||
- "Deep dive into a specific customer segment"
|
||||
- "Analyze an emerging market trend in detail"
|
||||
- "Compare this market to an analogous market"
|
||||
- "Stress test market assumptions"
|
||||
- "Explore adjacent market opportunities"
|
||||
- "Challenge market definition and boundaries"
|
||||
- "Generate strategic scenarios (best/base/worst case)"
|
||||
- "If only we had considered [X market factor]..."
|
||||
- "Proceed to next section"
|
||||
- 'Expand market sizing calculations with sensitivity analysis'
|
||||
- 'Deep dive into a specific customer segment'
|
||||
- 'Analyze an emerging market trend in detail'
|
||||
- 'Compare this market to an analogous market'
|
||||
- 'Stress test market assumptions'
|
||||
- 'Explore adjacent market opportunities'
|
||||
- 'Challenge market definition and boundaries'
|
||||
- 'Generate strategic scenarios (best/base/worst case)'
|
||||
- 'If only we had considered [X market factor]...'
|
||||
- 'Proceed to next section'
|
||||
|
||||
sections:
|
||||
- id: executive-summary
|
||||
@@ -1424,7 +1424,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: segment
|
||||
title: "Segment {{segment_number}}: {{segment_name}}"
|
||||
title: 'Segment {{segment_number}}: {{segment_name}}'
|
||||
template: |
|
||||
- **Description:** {{brief_overview}}
|
||||
- **Size:** {{number_of_customers_market_value}}
|
||||
@@ -1493,20 +1493,20 @@ sections:
|
||||
instruction: Analyze each force with specific evidence and implications
|
||||
sections:
|
||||
- id: supplier-power
|
||||
title: "Supplier Power: {{power_level}}"
|
||||
template: "{{analysis_and_implications}}"
|
||||
title: 'Supplier Power: {{power_level}}'
|
||||
template: '{{analysis_and_implications}}'
|
||||
- id: buyer-power
|
||||
title: "Buyer Power: {{power_level}}"
|
||||
template: "{{analysis_and_implications}}"
|
||||
title: 'Buyer Power: {{power_level}}'
|
||||
template: '{{analysis_and_implications}}'
|
||||
- id: competitive-rivalry
|
||||
title: "Competitive Rivalry: {{intensity_level}}"
|
||||
template: "{{analysis_and_implications}}"
|
||||
title: 'Competitive Rivalry: {{intensity_level}}'
|
||||
template: '{{analysis_and_implications}}'
|
||||
- id: threat-new-entry
|
||||
title: "Threat of New Entry: {{threat_level}}"
|
||||
template: "{{analysis_and_implications}}"
|
||||
title: 'Threat of New Entry: {{threat_level}}'
|
||||
template: '{{analysis_and_implications}}'
|
||||
- id: threat-substitutes
|
||||
title: "Threat of Substitutes: {{threat_level}}"
|
||||
template: "{{analysis_and_implications}}"
|
||||
title: 'Threat of Substitutes: {{threat_level}}'
|
||||
template: '{{analysis_and_implications}}'
|
||||
- id: adoption-lifecycle
|
||||
title: Technology Adoption Lifecycle Stage
|
||||
instruction: |
|
||||
@@ -1524,7 +1524,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: opportunity
|
||||
title: "Opportunity {{opportunity_number}}: {{name}}"
|
||||
title: 'Opportunity {{opportunity_number}}: {{name}}'
|
||||
template: |
|
||||
- **Description:** {{what_is_the_opportunity}}
|
||||
- **Size/Potential:** {{quantified_potential}}
|
||||
@@ -1580,24 +1580,24 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/competitor-analysis.md
|
||||
title: "Competitive Analysis Report: {{project_product_name}}"
|
||||
title: 'Competitive Analysis Report: {{project_product_name}}'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
elicitation: advanced-elicitation
|
||||
custom_elicitation:
|
||||
title: "Competitive Analysis Elicitation Actions"
|
||||
title: 'Competitive Analysis Elicitation Actions'
|
||||
options:
|
||||
- "Deep dive on a specific competitor's strategy"
|
||||
- "Analyze competitive dynamics in a specific segment"
|
||||
- "War game competitive responses to your moves"
|
||||
- "Explore partnership vs. competition scenarios"
|
||||
- "Stress test differentiation claims"
|
||||
- "Analyze disruption potential (yours or theirs)"
|
||||
- "Compare to competition in adjacent markets"
|
||||
- "Generate win/loss analysis insights"
|
||||
- 'Analyze competitive dynamics in a specific segment'
|
||||
- 'War game competitive responses to your moves'
|
||||
- 'Explore partnership vs. competition scenarios'
|
||||
- 'Stress test differentiation claims'
|
||||
- 'Analyze disruption potential (yours or theirs)'
|
||||
- 'Compare to competition in adjacent markets'
|
||||
- 'Generate win/loss analysis insights'
|
||||
- "If only we had known about [competitor X's plan]..."
|
||||
- "Proceed to next section"
|
||||
- 'Proceed to next section'
|
||||
|
||||
sections:
|
||||
- id: executive-summary
|
||||
@@ -1664,7 +1664,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: competitor
|
||||
title: "{{competitor_name}} - Priority {{priority_level}}"
|
||||
title: '{{competitor_name}} - Priority {{priority_level}}'
|
||||
sections:
|
||||
- id: company-overview
|
||||
title: Company Overview
|
||||
@@ -1696,11 +1696,11 @@ sections:
|
||||
- id: strengths
|
||||
title: Strengths
|
||||
type: bullet-list
|
||||
template: "- {{strength}}"
|
||||
template: '- {{strength}}'
|
||||
- id: weaknesses
|
||||
title: Weaknesses
|
||||
type: bullet-list
|
||||
template: "- {{weakness}}"
|
||||
template: '- {{weakness}}'
|
||||
- id: market-position
|
||||
title: Market Position & Performance
|
||||
template: |
|
||||
@@ -1718,35 +1718,35 @@ sections:
|
||||
type: table
|
||||
columns:
|
||||
[
|
||||
"Feature Category",
|
||||
"{{your_company}}",
|
||||
"{{competitor_1}}",
|
||||
"{{competitor_2}}",
|
||||
"{{competitor_3}}",
|
||||
'Feature Category',
|
||||
'{{your_company}}',
|
||||
'{{competitor_1}}',
|
||||
'{{competitor_2}}',
|
||||
'{{competitor_3}}',
|
||||
]
|
||||
rows:
|
||||
- category: "Core Functionality"
|
||||
- category: 'Core Functionality'
|
||||
items:
|
||||
- ["Feature A", "{{status}}", "{{status}}", "{{status}}", "{{status}}"]
|
||||
- ["Feature B", "{{status}}", "{{status}}", "{{status}}", "{{status}}"]
|
||||
- category: "User Experience"
|
||||
- ['Feature A', '{{status}}', '{{status}}', '{{status}}', '{{status}}']
|
||||
- ['Feature B', '{{status}}', '{{status}}', '{{status}}', '{{status}}']
|
||||
- category: 'User Experience'
|
||||
items:
|
||||
- ["Mobile App", "{{rating}}", "{{rating}}", "{{rating}}", "{{rating}}"]
|
||||
- ["Onboarding Time", "{{time}}", "{{time}}", "{{time}}", "{{time}}"]
|
||||
- category: "Integration & Ecosystem"
|
||||
- ['Mobile App', '{{rating}}', '{{rating}}', '{{rating}}', '{{rating}}']
|
||||
- ['Onboarding Time', '{{time}}', '{{time}}', '{{time}}', '{{time}}']
|
||||
- category: 'Integration & Ecosystem'
|
||||
items:
|
||||
- [
|
||||
"API Availability",
|
||||
"{{availability}}",
|
||||
"{{availability}}",
|
||||
"{{availability}}",
|
||||
"{{availability}}",
|
||||
'API Availability',
|
||||
'{{availability}}',
|
||||
'{{availability}}',
|
||||
'{{availability}}',
|
||||
'{{availability}}',
|
||||
]
|
||||
- ["Third-party Integrations", "{{number}}", "{{number}}", "{{number}}", "{{number}}"]
|
||||
- category: "Pricing & Plans"
|
||||
- ['Third-party Integrations', '{{number}}', '{{number}}', '{{number}}', '{{number}}']
|
||||
- category: 'Pricing & Plans'
|
||||
items:
|
||||
- ["Starting Price", "{{price}}", "{{price}}", "{{price}}", "{{price}}"]
|
||||
- ["Free Tier", "{{yes_no}}", "{{yes_no}}", "{{yes_no}}", "{{yes_no}}"]
|
||||
- ['Starting Price', '{{price}}', '{{price}}', '{{price}}', '{{price}}']
|
||||
- ['Free Tier', '{{yes_no}}', '{{yes_no}}', '{{yes_no}}', '{{yes_no}}']
|
||||
- id: swot-comparison
|
||||
title: SWOT Comparison
|
||||
instruction: Create SWOT analysis for your solution vs. top competitors
|
||||
@@ -1759,7 +1759,7 @@ sections:
|
||||
- **Opportunities:** {{opportunities}}
|
||||
- **Threats:** {{threats}}
|
||||
- id: vs-competitor
|
||||
title: "vs. {{main_competitor}}"
|
||||
title: 'vs. {{main_competitor}}'
|
||||
template: |
|
||||
- **Competitive Advantages:** {{your_advantages}}
|
||||
- **Competitive Disadvantages:** {{their_advantages}}
|
||||
@@ -1889,7 +1889,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/brainstorming-session-results.md
|
||||
title: "Brainstorming Session Results"
|
||||
title: 'Brainstorming Session Results'
|
||||
|
||||
workflow:
|
||||
mode: non-interactive
|
||||
@@ -1914,38 +1914,38 @@ sections:
|
||||
|
||||
**Total Ideas Generated:** {{total_ideas}}
|
||||
- id: key-themes
|
||||
title: "Key Themes Identified:"
|
||||
title: 'Key Themes Identified:'
|
||||
type: bullet-list
|
||||
template: "- {{theme}}"
|
||||
template: '- {{theme}}'
|
||||
|
||||
- id: technique-sessions
|
||||
title: Technique Sessions
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: technique
|
||||
title: "{{technique_name}} - {{duration}}"
|
||||
title: '{{technique_name}} - {{duration}}'
|
||||
sections:
|
||||
- id: description
|
||||
template: "**Description:** {{technique_description}}"
|
||||
template: '**Description:** {{technique_description}}'
|
||||
- id: ideas-generated
|
||||
title: "Ideas Generated:"
|
||||
title: 'Ideas Generated:'
|
||||
type: numbered-list
|
||||
template: "{{idea}}"
|
||||
template: '{{idea}}'
|
||||
- id: insights
|
||||
title: "Insights Discovered:"
|
||||
title: 'Insights Discovered:'
|
||||
type: bullet-list
|
||||
template: "- {{insight}}"
|
||||
template: '- {{insight}}'
|
||||
- id: connections
|
||||
title: "Notable Connections:"
|
||||
title: 'Notable Connections:'
|
||||
type: bullet-list
|
||||
template: "- {{connection}}"
|
||||
template: '- {{connection}}'
|
||||
|
||||
- id: idea-categorization
|
||||
title: Idea Categorization
|
||||
sections:
|
||||
- id: immediate-opportunities
|
||||
title: Immediate Opportunities
|
||||
content: "*Ideas ready to implement now*"
|
||||
content: '*Ideas ready to implement now*'
|
||||
repeatable: true
|
||||
type: numbered-list
|
||||
template: |
|
||||
@@ -1955,7 +1955,7 @@ sections:
|
||||
- Resources needed: {{requirements}}
|
||||
- id: future-innovations
|
||||
title: Future Innovations
|
||||
content: "*Ideas requiring development/research*"
|
||||
content: '*Ideas requiring development/research*'
|
||||
repeatable: true
|
||||
type: numbered-list
|
||||
template: |
|
||||
@@ -1965,7 +1965,7 @@ sections:
|
||||
- Timeline estimate: {{timeline}}
|
||||
- id: moonshots
|
||||
title: Moonshots
|
||||
content: "*Ambitious, transformative concepts*"
|
||||
content: '*Ambitious, transformative concepts*'
|
||||
repeatable: true
|
||||
type: numbered-list
|
||||
template: |
|
||||
@@ -1975,9 +1975,9 @@ sections:
|
||||
- Challenges to overcome: {{challenges}}
|
||||
- id: insights-learnings
|
||||
title: Insights & Learnings
|
||||
content: "*Key realizations from the session*"
|
||||
content: '*Key realizations from the session*'
|
||||
type: bullet-list
|
||||
template: "- {{insight}}: {{description_and_implications}}"
|
||||
template: '- {{insight}}: {{description_and_implications}}'
|
||||
|
||||
- id: action-planning
|
||||
title: Action Planning
|
||||
@@ -1986,21 +1986,21 @@ sections:
|
||||
title: Top 3 Priority Ideas
|
||||
sections:
|
||||
- id: priority-1
|
||||
title: "#1 Priority: {{idea_name}}"
|
||||
title: '#1 Priority: {{idea_name}}'
|
||||
template: |
|
||||
- Rationale: {{rationale}}
|
||||
- Next steps: {{next_steps}}
|
||||
- Resources needed: {{resources}}
|
||||
- Timeline: {{timeline}}
|
||||
- id: priority-2
|
||||
title: "#2 Priority: {{idea_name}}"
|
||||
title: '#2 Priority: {{idea_name}}'
|
||||
template: |
|
||||
- Rationale: {{rationale}}
|
||||
- Next steps: {{next_steps}}
|
||||
- Resources needed: {{resources}}
|
||||
- Timeline: {{timeline}}
|
||||
- id: priority-3
|
||||
title: "#3 Priority: {{idea_name}}"
|
||||
title: '#3 Priority: {{idea_name}}'
|
||||
template: |
|
||||
- Rationale: {{rationale}}
|
||||
- Next steps: {{next_steps}}
|
||||
@@ -2013,19 +2013,19 @@ sections:
|
||||
- id: what-worked
|
||||
title: What Worked Well
|
||||
type: bullet-list
|
||||
template: "- {{aspect}}"
|
||||
template: '- {{aspect}}'
|
||||
- id: areas-exploration
|
||||
title: Areas for Further Exploration
|
||||
type: bullet-list
|
||||
template: "- {{area}}: {{reason}}"
|
||||
template: '- {{area}}: {{reason}}'
|
||||
- id: recommended-techniques
|
||||
title: Recommended Follow-up Techniques
|
||||
type: bullet-list
|
||||
template: "- {{technique}}: {{reason}}"
|
||||
template: '- {{technique}}: {{reason}}'
|
||||
- id: questions-emerged
|
||||
title: Questions That Emerged
|
||||
type: bullet-list
|
||||
template: "- {{question}}"
|
||||
template: '- {{question}}'
|
||||
- id: next-session
|
||||
title: Next Session Planning
|
||||
template: |
|
||||
|
||||
286
dist/agents/architect.txt
vendored
286
dist/agents/architect.txt
vendored
@@ -933,7 +933,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/architecture.md
|
||||
title: "{{project_name}} Architecture Document"
|
||||
title: '{{project_name}} Architecture Document'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1044,11 +1044,11 @@ sections:
|
||||
- Code organization patterns (Dependency Injection, Repository, Module, Factory)
|
||||
- Data patterns (Event Sourcing, Saga, Database per Service)
|
||||
- Communication patterns (REST, GraphQL, Message Queue, Pub/Sub)
|
||||
template: "- **{{pattern_name}}:** {{pattern_description}} - _Rationale:_ {{rationale}}"
|
||||
template: '- **{{pattern_name}}:** {{pattern_description}} - _Rationale:_ {{rationale}}'
|
||||
examples:
|
||||
- "**Serverless Architecture:** Using AWS Lambda for compute - _Rationale:_ Aligns with PRD requirement for cost optimization and automatic scaling"
|
||||
- "**Repository Pattern:** Abstract data access logic - _Rationale:_ Enables testing and future database migration flexibility"
|
||||
- "**Event-Driven Communication:** Using SNS/SQS for service decoupling - _Rationale:_ Supports async processing and system resilience"
|
||||
- '**Serverless Architecture:** Using AWS Lambda for compute - _Rationale:_ Aligns with PRD requirement for cost optimization and automatic scaling'
|
||||
- '**Repository Pattern:** Abstract data access logic - _Rationale:_ Enables testing and future database migration flexibility'
|
||||
- '**Event-Driven Communication:** Using SNS/SQS for service decoupling - _Rationale:_ Supports async processing and system resilience'
|
||||
|
||||
- id: tech-stack
|
||||
title: Tech Stack
|
||||
@@ -1086,9 +1086,9 @@ sections:
|
||||
columns: [Category, Technology, Version, Purpose, Rationale]
|
||||
instruction: Populate the technology stack table with all relevant technologies
|
||||
examples:
|
||||
- "| **Language** | TypeScript | 5.3.3 | Primary development language | Strong typing, excellent tooling, team expertise |"
|
||||
- "| **Runtime** | Node.js | 20.11.0 | JavaScript runtime | LTS version, stable performance, wide ecosystem |"
|
||||
- "| **Framework** | NestJS | 10.3.2 | Backend framework | Enterprise-ready, good DI, matches team patterns |"
|
||||
- '| **Language** | TypeScript | 5.3.3 | Primary development language | Strong typing, excellent tooling, team expertise |'
|
||||
- '| **Runtime** | Node.js | 20.11.0 | JavaScript runtime | LTS version, stable performance, wide ecosystem |'
|
||||
- '| **Framework** | NestJS | 10.3.2 | Backend framework | Enterprise-ready, good DI, matches team patterns |'
|
||||
|
||||
- id: data-models
|
||||
title: Data Models
|
||||
@@ -1106,7 +1106,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: model
|
||||
title: "{{model_name}}"
|
||||
title: '{{model_name}}'
|
||||
template: |
|
||||
**Purpose:** {{model_purpose}}
|
||||
|
||||
@@ -1137,7 +1137,7 @@ sections:
|
||||
sections:
|
||||
- id: component-list
|
||||
repeatable: true
|
||||
title: "{{component_name}}"
|
||||
title: '{{component_name}}'
|
||||
template: |
|
||||
**Responsibility:** {{component_description}}
|
||||
|
||||
@@ -1175,7 +1175,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: api
|
||||
title: "{{api_name}} API"
|
||||
title: '{{api_name}} API'
|
||||
template: |
|
||||
- **Purpose:** {{api_purpose}}
|
||||
- **Documentation:** {{api_docs_url}}
|
||||
@@ -1300,12 +1300,12 @@ sections:
|
||||
- id: environments
|
||||
title: Environments
|
||||
repeatable: true
|
||||
template: "- **{{env_name}}:** {{env_purpose}} - {{env_details}}"
|
||||
template: '- **{{env_name}}:** {{env_purpose}} - {{env_details}}'
|
||||
- id: promotion-flow
|
||||
title: Environment Promotion Flow
|
||||
type: code
|
||||
language: text
|
||||
template: "{{promotion_flow_diagram}}"
|
||||
template: '{{promotion_flow_diagram}}'
|
||||
- id: rollback-strategy
|
||||
title: Rollback Strategy
|
||||
template: |
|
||||
@@ -1401,16 +1401,16 @@ sections:
|
||||
|
||||
Avoid obvious rules like "use SOLID principles" or "write clean code"
|
||||
repeatable: true
|
||||
template: "- **{{rule_name}}:** {{rule_description}}"
|
||||
template: '- **{{rule_name}}:** {{rule_description}}'
|
||||
- id: language-specifics
|
||||
title: Language-Specific Guidelines
|
||||
condition: Critical language-specific rules needed
|
||||
instruction: Add ONLY if critical for preventing AI mistakes. Most teams don't need this section.
|
||||
sections:
|
||||
- id: language-rules
|
||||
title: "{{language_name}} Specifics"
|
||||
title: '{{language_name}} Specifics'
|
||||
repeatable: true
|
||||
template: "- **{{rule_topic}}:** {{rule_detail}}"
|
||||
template: '- **{{rule_topic}}:** {{rule_detail}}'
|
||||
|
||||
- id: test-strategy
|
||||
title: Test Strategy and Standards
|
||||
@@ -1458,9 +1458,9 @@ sections:
|
||||
- **Test Infrastructure:**
|
||||
- **{{dependency_name}}:** {{test_approach}} ({{test_tool}})
|
||||
examples:
|
||||
- "**Database:** In-memory H2 for unit tests, Testcontainers PostgreSQL for integration"
|
||||
- "**Message Queue:** Embedded Kafka for tests"
|
||||
- "**External APIs:** WireMock for stubbing"
|
||||
- '**Database:** In-memory H2 for unit tests, Testcontainers PostgreSQL for integration'
|
||||
- '**Message Queue:** Embedded Kafka for tests'
|
||||
- '**External APIs:** WireMock for stubbing'
|
||||
- id: e2e-tests
|
||||
title: End-to-End Tests
|
||||
template: |
|
||||
@@ -1586,7 +1586,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/ui-architecture.md
|
||||
title: "{{project_name}} Frontend Architecture Document"
|
||||
title: '{{project_name}} Frontend Architecture Document'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1654,29 +1654,29 @@ sections:
|
||||
columns: [Category, Technology, Version, Purpose, Rationale]
|
||||
instruction: Fill in appropriate technology choices based on the selected framework and project requirements.
|
||||
rows:
|
||||
- ["Framework", "{{framework}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["UI Library", "{{ui_library}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ['Framework', '{{framework}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['UI Library', '{{ui_library}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- [
|
||||
"State Management",
|
||||
"{{state_management}}",
|
||||
"{{version}}",
|
||||
"{{purpose}}",
|
||||
"{{why_chosen}}",
|
||||
'State Management',
|
||||
'{{state_management}}',
|
||||
'{{version}}',
|
||||
'{{purpose}}',
|
||||
'{{why_chosen}}',
|
||||
]
|
||||
- ["Routing", "{{routing_library}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Build Tool", "{{build_tool}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Styling", "{{styling_solution}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Testing", "{{test_framework}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ['Routing', '{{routing_library}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Build Tool', '{{build_tool}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Styling', '{{styling_solution}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Testing', '{{test_framework}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- [
|
||||
"Component Library",
|
||||
"{{component_lib}}",
|
||||
"{{version}}",
|
||||
"{{purpose}}",
|
||||
"{{why_chosen}}",
|
||||
'Component Library',
|
||||
'{{component_lib}}',
|
||||
'{{version}}',
|
||||
'{{purpose}}',
|
||||
'{{why_chosen}}',
|
||||
]
|
||||
- ["Form Handling", "{{form_library}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Animation", "{{animation_lib}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Dev Tools", "{{dev_tools}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ['Form Handling', '{{form_library}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Animation', '{{animation_lib}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Dev Tools', '{{dev_tools}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
|
||||
- id: project-structure
|
||||
title: Project Structure
|
||||
@@ -1770,12 +1770,12 @@ sections:
|
||||
title: Testing Best Practices
|
||||
type: numbered-list
|
||||
items:
|
||||
- "**Unit Tests**: Test individual components in isolation"
|
||||
- "**Integration Tests**: Test component interactions"
|
||||
- "**E2E Tests**: Test critical user flows (using Cypress/Playwright)"
|
||||
- "**Coverage Goals**: Aim for 80% code coverage"
|
||||
- "**Test Structure**: Arrange-Act-Assert pattern"
|
||||
- "**Mock External Dependencies**: API calls, routing, state management"
|
||||
- '**Unit Tests**: Test individual components in isolation'
|
||||
- '**Integration Tests**: Test component interactions'
|
||||
- '**E2E Tests**: Test critical user flows (using Cypress/Playwright)'
|
||||
- '**Coverage Goals**: Aim for 80% code coverage'
|
||||
- '**Test Structure**: Arrange-Act-Assert pattern'
|
||||
- '**Mock External Dependencies**: API calls, routing, state management'
|
||||
|
||||
- id: environment-configuration
|
||||
title: Environment Configuration
|
||||
@@ -1807,7 +1807,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/architecture.md
|
||||
title: "{{project_name}} Fullstack Architecture Document"
|
||||
title: '{{project_name}} Fullstack Architecture Document'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1928,12 +1928,12 @@ sections:
|
||||
|
||||
For each pattern, provide recommendation and rationale.
|
||||
repeatable: true
|
||||
template: "- **{{pattern_name}}:** {{pattern_description}} - _Rationale:_ {{rationale}}"
|
||||
template: '- **{{pattern_name}}:** {{pattern_description}} - _Rationale:_ {{rationale}}'
|
||||
examples:
|
||||
- "**Jamstack Architecture:** Static site generation with serverless APIs - _Rationale:_ Optimal performance and scalability for content-heavy applications"
|
||||
- "**Component-Based UI:** Reusable React components with TypeScript - _Rationale:_ Maintainability and type safety across large codebases"
|
||||
- "**Repository Pattern:** Abstract data access logic - _Rationale:_ Enables testing and future database migration flexibility"
|
||||
- "**API Gateway Pattern:** Single entry point for all API calls - _Rationale:_ Centralized auth, rate limiting, and monitoring"
|
||||
- '**Jamstack Architecture:** Static site generation with serverless APIs - _Rationale:_ Optimal performance and scalability for content-heavy applications'
|
||||
- '**Component-Based UI:** Reusable React components with TypeScript - _Rationale:_ Maintainability and type safety across large codebases'
|
||||
- '**Repository Pattern:** Abstract data access logic - _Rationale:_ Enables testing and future database migration flexibility'
|
||||
- '**API Gateway Pattern:** Single entry point for all API calls - _Rationale:_ Centralized auth, rate limiting, and monitoring'
|
||||
|
||||
- id: tech-stack
|
||||
title: Tech Stack
|
||||
@@ -1957,45 +1957,45 @@ sections:
|
||||
type: table
|
||||
columns: [Category, Technology, Version, Purpose, Rationale]
|
||||
rows:
|
||||
- ["Frontend Language", "{{fe_language}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ['Frontend Language', '{{fe_language}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- [
|
||||
"Frontend Framework",
|
||||
"{{fe_framework}}",
|
||||
"{{version}}",
|
||||
"{{purpose}}",
|
||||
"{{why_chosen}}",
|
||||
'Frontend Framework',
|
||||
'{{fe_framework}}',
|
||||
'{{version}}',
|
||||
'{{purpose}}',
|
||||
'{{why_chosen}}',
|
||||
]
|
||||
- [
|
||||
"UI Component Library",
|
||||
"{{ui_library}}",
|
||||
"{{version}}",
|
||||
"{{purpose}}",
|
||||
"{{why_chosen}}",
|
||||
'UI Component Library',
|
||||
'{{ui_library}}',
|
||||
'{{version}}',
|
||||
'{{purpose}}',
|
||||
'{{why_chosen}}',
|
||||
]
|
||||
- ["State Management", "{{state_mgmt}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Backend Language", "{{be_language}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ['State Management', '{{state_mgmt}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Backend Language', '{{be_language}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- [
|
||||
"Backend Framework",
|
||||
"{{be_framework}}",
|
||||
"{{version}}",
|
||||
"{{purpose}}",
|
||||
"{{why_chosen}}",
|
||||
'Backend Framework',
|
||||
'{{be_framework}}',
|
||||
'{{version}}',
|
||||
'{{purpose}}',
|
||||
'{{why_chosen}}',
|
||||
]
|
||||
- ["API Style", "{{api_style}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Database", "{{database}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Cache", "{{cache}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["File Storage", "{{storage}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Authentication", "{{auth}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Frontend Testing", "{{fe_test}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Backend Testing", "{{be_test}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["E2E Testing", "{{e2e_test}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Build Tool", "{{build_tool}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Bundler", "{{bundler}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["IaC Tool", "{{iac_tool}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["CI/CD", "{{cicd}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Monitoring", "{{monitoring}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["Logging", "{{logging}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ["CSS Framework", "{{css_framework}}", "{{version}}", "{{purpose}}", "{{why_chosen}}"]
|
||||
- ['API Style', '{{api_style}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Database', '{{database}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Cache', '{{cache}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['File Storage', '{{storage}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Authentication', '{{auth}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Frontend Testing', '{{fe_test}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Backend Testing', '{{be_test}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['E2E Testing', '{{e2e_test}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Build Tool', '{{build_tool}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Bundler', '{{bundler}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['IaC Tool', '{{iac_tool}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['CI/CD', '{{cicd}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Monitoring', '{{monitoring}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['Logging', '{{logging}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
- ['CSS Framework', '{{css_framework}}', '{{version}}', '{{purpose}}', '{{why_chosen}}']
|
||||
|
||||
- id: data-models
|
||||
title: Data Models
|
||||
@@ -2014,7 +2014,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: model
|
||||
title: "{{model_name}}"
|
||||
title: '{{model_name}}'
|
||||
template: |
|
||||
**Purpose:** {{model_purpose}}
|
||||
|
||||
@@ -2026,11 +2026,11 @@ sections:
|
||||
title: TypeScript Interface
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{model_interface}}"
|
||||
template: '{{model_interface}}'
|
||||
- id: relationships
|
||||
title: Relationships
|
||||
type: bullet-list
|
||||
template: "- {{relationship}}"
|
||||
template: '- {{relationship}}'
|
||||
|
||||
- id: api-spec
|
||||
title: API Specification
|
||||
@@ -2067,13 +2067,13 @@ sections:
|
||||
condition: API style is GraphQL
|
||||
type: code
|
||||
language: graphql
|
||||
template: "{{graphql_schema}}"
|
||||
template: '{{graphql_schema}}'
|
||||
- id: trpc-api
|
||||
title: tRPC Router Definitions
|
||||
condition: API style is tRPC
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{trpc_routers}}"
|
||||
template: '{{trpc_routers}}'
|
||||
|
||||
- id: components
|
||||
title: Components
|
||||
@@ -2094,7 +2094,7 @@ sections:
|
||||
sections:
|
||||
- id: component-list
|
||||
repeatable: true
|
||||
title: "{{component_name}}"
|
||||
title: '{{component_name}}'
|
||||
template: |
|
||||
**Responsibility:** {{component_description}}
|
||||
|
||||
@@ -2132,7 +2132,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: api
|
||||
title: "{{api_name}} API"
|
||||
title: '{{api_name}} API'
|
||||
template: |
|
||||
- **Purpose:** {{api_purpose}}
|
||||
- **Documentation:** {{api_docs_url}}
|
||||
@@ -2189,12 +2189,12 @@ sections:
|
||||
title: Component Organization
|
||||
type: code
|
||||
language: text
|
||||
template: "{{component_structure}}"
|
||||
template: '{{component_structure}}'
|
||||
- id: component-template
|
||||
title: Component Template
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{component_template}}"
|
||||
template: '{{component_template}}'
|
||||
- id: state-management
|
||||
title: State Management Architecture
|
||||
instruction: Detail state management approach based on chosen solution.
|
||||
@@ -2203,11 +2203,11 @@ sections:
|
||||
title: State Structure
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{state_structure}}"
|
||||
template: '{{state_structure}}'
|
||||
- id: state-patterns
|
||||
title: State Management Patterns
|
||||
type: bullet-list
|
||||
template: "- {{pattern}}"
|
||||
template: '- {{pattern}}'
|
||||
- id: routing-architecture
|
||||
title: Routing Architecture
|
||||
instruction: Define routing structure based on framework choice.
|
||||
@@ -2216,12 +2216,12 @@ sections:
|
||||
title: Route Organization
|
||||
type: code
|
||||
language: text
|
||||
template: "{{route_structure}}"
|
||||
template: '{{route_structure}}'
|
||||
- id: protected-routes
|
||||
title: Protected Route Pattern
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{protected_route_example}}"
|
||||
template: '{{protected_route_example}}'
|
||||
- id: frontend-services
|
||||
title: Frontend Services Layer
|
||||
instruction: Define how frontend communicates with backend.
|
||||
@@ -2230,12 +2230,12 @@ sections:
|
||||
title: API Client Setup
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{api_client_setup}}"
|
||||
template: '{{api_client_setup}}'
|
||||
- id: service-example
|
||||
title: Service Example
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{service_example}}"
|
||||
template: '{{service_example}}'
|
||||
|
||||
- id: backend-architecture
|
||||
title: Backend Architecture
|
||||
@@ -2253,12 +2253,12 @@ sections:
|
||||
title: Function Organization
|
||||
type: code
|
||||
language: text
|
||||
template: "{{function_structure}}"
|
||||
template: '{{function_structure}}'
|
||||
- id: function-template
|
||||
title: Function Template
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{function_template}}"
|
||||
template: '{{function_template}}'
|
||||
- id: traditional-server
|
||||
condition: Traditional server architecture chosen
|
||||
sections:
|
||||
@@ -2266,12 +2266,12 @@ sections:
|
||||
title: Controller/Route Organization
|
||||
type: code
|
||||
language: text
|
||||
template: "{{controller_structure}}"
|
||||
template: '{{controller_structure}}'
|
||||
- id: controller-template
|
||||
title: Controller Template
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{controller_template}}"
|
||||
template: '{{controller_template}}'
|
||||
- id: database-architecture
|
||||
title: Database Architecture
|
||||
instruction: Define database schema and access patterns.
|
||||
@@ -2280,12 +2280,12 @@ sections:
|
||||
title: Schema Design
|
||||
type: code
|
||||
language: sql
|
||||
template: "{{database_schema}}"
|
||||
template: '{{database_schema}}'
|
||||
- id: data-access-layer
|
||||
title: Data Access Layer
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{repository_pattern}}"
|
||||
template: '{{repository_pattern}}'
|
||||
- id: auth-architecture
|
||||
title: Authentication and Authorization
|
||||
instruction: Define auth implementation details.
|
||||
@@ -2294,12 +2294,12 @@ sections:
|
||||
title: Auth Flow
|
||||
type: mermaid
|
||||
mermaid_type: sequence
|
||||
template: "{{auth_flow_diagram}}"
|
||||
template: '{{auth_flow_diagram}}'
|
||||
- id: auth-middleware
|
||||
title: Middleware/Guards
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{auth_middleware}}"
|
||||
template: '{{auth_middleware}}'
|
||||
|
||||
- id: unified-project-structure
|
||||
title: Unified Project Structure
|
||||
@@ -2375,12 +2375,12 @@ sections:
|
||||
title: Prerequisites
|
||||
type: code
|
||||
language: bash
|
||||
template: "{{prerequisites_commands}}"
|
||||
template: '{{prerequisites_commands}}'
|
||||
- id: initial-setup
|
||||
title: Initial Setup
|
||||
type: code
|
||||
language: bash
|
||||
template: "{{setup_commands}}"
|
||||
template: '{{setup_commands}}'
|
||||
- id: dev-commands
|
||||
title: Development Commands
|
||||
type: code
|
||||
@@ -2436,15 +2436,15 @@ sections:
|
||||
title: CI/CD Pipeline
|
||||
type: code
|
||||
language: yaml
|
||||
template: "{{cicd_pipeline_config}}"
|
||||
template: '{{cicd_pipeline_config}}'
|
||||
- id: environments
|
||||
title: Environments
|
||||
type: table
|
||||
columns: [Environment, Frontend URL, Backend URL, Purpose]
|
||||
rows:
|
||||
- ["Development", "{{dev_fe_url}}", "{{dev_be_url}}", "Local development"]
|
||||
- ["Staging", "{{staging_fe_url}}", "{{staging_be_url}}", "Pre-production testing"]
|
||||
- ["Production", "{{prod_fe_url}}", "{{prod_be_url}}", "Live environment"]
|
||||
- ['Development', '{{dev_fe_url}}', '{{dev_be_url}}', 'Local development']
|
||||
- ['Staging', '{{staging_fe_url}}', '{{staging_be_url}}', 'Pre-production testing']
|
||||
- ['Production', '{{prod_fe_url}}', '{{prod_be_url}}', 'Live environment']
|
||||
|
||||
- id: security-performance
|
||||
title: Security and Performance
|
||||
@@ -2503,17 +2503,17 @@ sections:
|
||||
title: Frontend Tests
|
||||
type: code
|
||||
language: text
|
||||
template: "{{frontend_test_structure}}"
|
||||
template: '{{frontend_test_structure}}'
|
||||
- id: backend-tests
|
||||
title: Backend Tests
|
||||
type: code
|
||||
language: text
|
||||
template: "{{backend_test_structure}}"
|
||||
template: '{{backend_test_structure}}'
|
||||
- id: e2e-tests
|
||||
title: E2E Tests
|
||||
type: code
|
||||
language: text
|
||||
template: "{{e2e_test_structure}}"
|
||||
template: '{{e2e_test_structure}}'
|
||||
- id: test-examples
|
||||
title: Test Examples
|
||||
sections:
|
||||
@@ -2521,17 +2521,17 @@ sections:
|
||||
title: Frontend Component Test
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{frontend_test_example}}"
|
||||
template: '{{frontend_test_example}}'
|
||||
- id: backend-test
|
||||
title: Backend API Test
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{backend_test_example}}"
|
||||
template: '{{backend_test_example}}'
|
||||
- id: e2e-test
|
||||
title: E2E Test
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{e2e_test_example}}"
|
||||
template: '{{e2e_test_example}}'
|
||||
|
||||
- id: coding-standards
|
||||
title: Coding Standards
|
||||
@@ -2541,22 +2541,22 @@ sections:
|
||||
- id: critical-rules
|
||||
title: Critical Fullstack Rules
|
||||
repeatable: true
|
||||
template: "- **{{rule_name}}:** {{rule_description}}"
|
||||
template: '- **{{rule_name}}:** {{rule_description}}'
|
||||
examples:
|
||||
- "**Type Sharing:** Always define types in packages/shared and import from there"
|
||||
- "**API Calls:** Never make direct HTTP calls - use the service layer"
|
||||
- "**Environment Variables:** Access only through config objects, never process.env directly"
|
||||
- "**Error Handling:** All API routes must use the standard error handler"
|
||||
- "**State Updates:** Never mutate state directly - use proper state management patterns"
|
||||
- '**Type Sharing:** Always define types in packages/shared and import from there'
|
||||
- '**API Calls:** Never make direct HTTP calls - use the service layer'
|
||||
- '**Environment Variables:** Access only through config objects, never process.env directly'
|
||||
- '**Error Handling:** All API routes must use the standard error handler'
|
||||
- '**State Updates:** Never mutate state directly - use proper state management patterns'
|
||||
- id: naming-conventions
|
||||
title: Naming Conventions
|
||||
type: table
|
||||
columns: [Element, Frontend, Backend, Example]
|
||||
rows:
|
||||
- ["Components", "PascalCase", "-", "`UserProfile.tsx`"]
|
||||
- ["Hooks", "camelCase with 'use'", "-", "`useAuth.ts`"]
|
||||
- ["API Routes", "-", "kebab-case", "`/api/user-profile`"]
|
||||
- ["Database Tables", "-", "snake_case", "`user_profiles`"]
|
||||
- ['Components', 'PascalCase', '-', '`UserProfile.tsx`']
|
||||
- ['Hooks', "camelCase with 'use'", '-', '`useAuth.ts`']
|
||||
- ['API Routes', '-', 'kebab-case', '`/api/user-profile`']
|
||||
- ['Database Tables', '-', 'snake_case', '`user_profiles`']
|
||||
|
||||
- id: error-handling
|
||||
title: Error Handling Strategy
|
||||
@@ -2567,7 +2567,7 @@ sections:
|
||||
title: Error Flow
|
||||
type: mermaid
|
||||
mermaid_type: sequence
|
||||
template: "{{error_flow_diagram}}"
|
||||
template: '{{error_flow_diagram}}'
|
||||
- id: error-format
|
||||
title: Error Response Format
|
||||
type: code
|
||||
@@ -2586,12 +2586,12 @@ sections:
|
||||
title: Frontend Error Handling
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{frontend_error_handler}}"
|
||||
template: '{{frontend_error_handler}}'
|
||||
- id: backend-error-handling
|
||||
title: Backend Error Handling
|
||||
type: code
|
||||
language: typescript
|
||||
template: "{{backend_error_handler}}"
|
||||
template: '{{backend_error_handler}}'
|
||||
|
||||
- id: monitoring
|
||||
title: Monitoring and Observability
|
||||
@@ -2633,7 +2633,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/architecture.md
|
||||
title: "{{project_name}} Brownfield Enhancement Architecture"
|
||||
title: '{{project_name}} Brownfield Enhancement Architecture'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -2691,11 +2691,11 @@ sections:
|
||||
- id: available-docs
|
||||
title: Available Documentation
|
||||
type: bullet-list
|
||||
template: "- {{existing_docs_summary}}"
|
||||
template: '- {{existing_docs_summary}}'
|
||||
- id: constraints
|
||||
title: Identified Constraints
|
||||
type: bullet-list
|
||||
template: "- {{constraint}}"
|
||||
template: '- {{constraint}}'
|
||||
- id: changelog
|
||||
title: Change Log
|
||||
type: table
|
||||
@@ -2775,7 +2775,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: model
|
||||
title: "{{model_name}}"
|
||||
title: '{{model_name}}'
|
||||
template: |
|
||||
**Purpose:** {{model_purpose}}
|
||||
**Integration:** {{integration_with_existing}}
|
||||
@@ -2818,7 +2818,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: component
|
||||
title: "{{component_name}}"
|
||||
title: '{{component_name}}'
|
||||
template: |
|
||||
**Responsibility:** {{component_description}}
|
||||
**Integration Points:** {{integration_points}}
|
||||
@@ -2861,7 +2861,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: endpoint
|
||||
title: "{{endpoint_name}}"
|
||||
title: '{{endpoint_name}}'
|
||||
template: |
|
||||
- **Method:** {{http_method}}
|
||||
- **Endpoint:** {{endpoint_path}}
|
||||
@@ -2872,12 +2872,12 @@ sections:
|
||||
title: Request
|
||||
type: code
|
||||
language: json
|
||||
template: "{{request_schema}}"
|
||||
template: '{{request_schema}}'
|
||||
- id: response
|
||||
title: Response
|
||||
type: code
|
||||
language: json
|
||||
template: "{{response_schema}}"
|
||||
template: '{{response_schema}}'
|
||||
|
||||
- id: external-api-integration
|
||||
title: External API Integration
|
||||
@@ -2886,7 +2886,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: external-api
|
||||
title: "{{api_name}} API"
|
||||
title: '{{api_name}} API'
|
||||
template: |
|
||||
- **Purpose:** {{api_purpose}}
|
||||
- **Documentation:** {{api_docs_url}}
|
||||
@@ -2915,7 +2915,7 @@ sections:
|
||||
type: code
|
||||
language: plaintext
|
||||
instruction: Document relevant parts of current structure
|
||||
template: "{{existing_structure_relevant_parts}}"
|
||||
template: '{{existing_structure_relevant_parts}}'
|
||||
- id: new-file-organization
|
||||
title: New File Organization
|
||||
type: code
|
||||
@@ -2990,7 +2990,7 @@ sections:
|
||||
title: Enhancement-Specific Standards
|
||||
condition: New patterns needed for enhancement
|
||||
repeatable: true
|
||||
template: "- **{{standard_name}}:** {{standard_description}}"
|
||||
template: '- **{{standard_name}}:** {{standard_description}}'
|
||||
- id: integration-rules
|
||||
title: Critical Integration Rules
|
||||
template: |
|
||||
|
||||
672
dist/agents/bmad-master.txt
vendored
672
dist/agents/bmad-master.txt
vendored
File diff suppressed because it is too large
Load Diff
80
dist/agents/pm.txt
vendored
80
dist/agents/pm.txt
vendored
@@ -1159,7 +1159,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/prd.md
|
||||
title: "{{project_name}} Product Requirements Document (PRD)"
|
||||
title: '{{project_name}} Product Requirements Document (PRD)'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1196,14 +1196,14 @@ sections:
|
||||
prefix: FR
|
||||
instruction: Each Requirement will be a bullet markdown and an identifier sequence starting with FR
|
||||
examples:
|
||||
- "FR6: The Todo List uses AI to detect and warn against potentially duplicate todo items that are worded differently."
|
||||
- 'FR6: The Todo List uses AI to detect and warn against potentially duplicate todo items that are worded differently.'
|
||||
- id: non-functional
|
||||
title: Non Functional
|
||||
type: numbered-list
|
||||
prefix: NFR
|
||||
instruction: Each Requirement will be a bullet markdown and an identifier sequence starting with NFR
|
||||
examples:
|
||||
- "NFR1: AWS service usage must aim to stay within free-tier limits where feasible."
|
||||
- 'NFR1: AWS service usage must aim to stay within free-tier limits where feasible.'
|
||||
|
||||
- id: ui-goals
|
||||
title: User Interface Design Goals
|
||||
@@ -1229,24 +1229,24 @@ sections:
|
||||
title: Core Screens and Views
|
||||
instruction: From a product perspective, what are the most critical screens or views necessary to deliver the the PRD values and goals? This is meant to be Conceptual High Level to Drive Rough Epic or User Stories
|
||||
examples:
|
||||
- "Login Screen"
|
||||
- "Main Dashboard"
|
||||
- "Item Detail Page"
|
||||
- "Settings Page"
|
||||
- 'Login Screen'
|
||||
- 'Main Dashboard'
|
||||
- 'Item Detail Page'
|
||||
- 'Settings Page'
|
||||
- id: accessibility
|
||||
title: "Accessibility: {None|WCAG AA|WCAG AAA|Custom Requirements}"
|
||||
title: 'Accessibility: {None|WCAG AA|WCAG AAA|Custom Requirements}'
|
||||
- id: branding
|
||||
title: Branding
|
||||
instruction: Any known branding elements or style guides that must be incorporated?
|
||||
examples:
|
||||
- "Replicate the look and feel of early 1900s black and white cinema, including animated effects replicating film damage or projector glitches during page or state transitions."
|
||||
- "Attached is the full color pallet and tokens for our corporate branding."
|
||||
- 'Replicate the look and feel of early 1900s black and white cinema, including animated effects replicating film damage or projector glitches during page or state transitions.'
|
||||
- 'Attached is the full color pallet and tokens for our corporate branding.'
|
||||
- id: target-platforms
|
||||
title: "Target Device and Platforms: {Web Responsive|Mobile Only|Desktop Only|Cross-Platform}"
|
||||
title: 'Target Device and Platforms: {Web Responsive|Mobile Only|Desktop Only|Cross-Platform}'
|
||||
examples:
|
||||
- "Web Responsive, and all mobile platforms"
|
||||
- "iPhone Only"
|
||||
- "ASCII Windows Desktop"
|
||||
- 'Web Responsive, and all mobile platforms'
|
||||
- 'iPhone Only'
|
||||
- 'ASCII Windows Desktop'
|
||||
|
||||
- id: technical-assumptions
|
||||
title: Technical Assumptions
|
||||
@@ -1265,13 +1265,13 @@ sections:
|
||||
testing: [Unit Only, Unit + Integration, Full Testing Pyramid]
|
||||
sections:
|
||||
- id: repository-structure
|
||||
title: "Repository Structure: {Monorepo|Polyrepo|Multi-repo}"
|
||||
title: 'Repository Structure: {Monorepo|Polyrepo|Multi-repo}'
|
||||
- id: service-architecture
|
||||
title: Service Architecture
|
||||
instruction: "CRITICAL DECISION - Document the high-level service architecture (e.g., Monolith, Microservices, Serverless functions within a Monorepo)."
|
||||
instruction: 'CRITICAL DECISION - Document the high-level service architecture (e.g., Monolith, Microservices, Serverless functions within a Monorepo).'
|
||||
- id: testing-requirements
|
||||
title: Testing Requirements
|
||||
instruction: "CRITICAL DECISION - Document the testing requirements, unit only, integration, e2e, manual, need for manual testing convenience methods)."
|
||||
instruction: 'CRITICAL DECISION - Document the testing requirements, unit only, integration, e2e, manual, need for manual testing convenience methods).'
|
||||
- id: additional-assumptions
|
||||
title: Additional Technical Assumptions and Requests
|
||||
instruction: Throughout the entire process of drafting this document, if any other technical assumptions are raised or discovered appropriate for the architect, add them here as additional bulleted items
|
||||
@@ -1291,10 +1291,10 @@ sections:
|
||||
- Cross Cutting Concerns should flow through epics and stories and not be final stories. For example, adding a logging framework as a last story of an epic, or at the end of a project as a final epic or story would be terrible as we would not have logging from the beginning.
|
||||
elicit: true
|
||||
examples:
|
||||
- "Epic 1: Foundation & Core Infrastructure: Establish project setup, authentication, and basic user management"
|
||||
- "Epic 2: Core Business Entities: Create and manage primary domain objects with CRUD operations"
|
||||
- "Epic 3: User Workflows & Interactions: Enable key user journeys and business processes"
|
||||
- "Epic 4: Reporting & Analytics: Provide insights and data visualization for users"
|
||||
- 'Epic 1: Foundation & Core Infrastructure: Establish project setup, authentication, and basic user management'
|
||||
- 'Epic 2: Core Business Entities: Create and manage primary domain objects with CRUD operations'
|
||||
- 'Epic 3: User Workflows & Interactions: Enable key user journeys and business processes'
|
||||
- 'Epic 4: Reporting & Analytics: Provide insights and data visualization for users'
|
||||
|
||||
- id: epic-details
|
||||
title: Epic {{epic_number}} {{epic_title}}
|
||||
@@ -1316,7 +1316,7 @@ sections:
|
||||
- Think "junior developer working for 2-4 hours" - stories must be small, focused, and self-contained
|
||||
- If a story seems complex, break it down further as long as it can deliver a vertical slice
|
||||
elicit: true
|
||||
template: "{{epic_goal}}"
|
||||
template: '{{epic_goal}}'
|
||||
sections:
|
||||
- id: story
|
||||
title: Story {{epic_number}}.{{story_number}} {{story_title}}
|
||||
@@ -1329,7 +1329,7 @@ sections:
|
||||
- id: acceptance-criteria
|
||||
title: Acceptance Criteria
|
||||
type: numbered-list
|
||||
item_template: "{{criterion_number}}: {{criteria}}"
|
||||
item_template: '{{criterion_number}}: {{criteria}}'
|
||||
repeatable: true
|
||||
instruction: |
|
||||
Define clear, comprehensive, and testable acceptance criteria that:
|
||||
@@ -1364,7 +1364,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/prd.md
|
||||
title: "{{project_name}} Brownfield Enhancement PRD"
|
||||
title: '{{project_name}} Brownfield Enhancement PRD'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1427,7 +1427,7 @@ sections:
|
||||
- External API Documentation [[LLM: If from document-project, check ✓]]
|
||||
- UX/UI Guidelines [[LLM: May not be in document-project]]
|
||||
- Technical Debt Documentation [[LLM: If from document-project, check ✓]]
|
||||
- "Other: {{other_docs}}"
|
||||
- 'Other: {{other_docs}}'
|
||||
instruction: |
|
||||
- If document-project was already run: "Using existing project analysis from document-project output."
|
||||
- If critical documentation is missing and no document-project: "I recommend running the document-project task first..."
|
||||
@@ -1447,7 +1447,7 @@ sections:
|
||||
- UI/UX Overhaul
|
||||
- Technology Stack Upgrade
|
||||
- Bug Fix and Stability Improvements
|
||||
- "Other: {{other_type}}"
|
||||
- 'Other: {{other_type}}'
|
||||
- id: enhancement-description
|
||||
title: Enhancement Description
|
||||
instruction: 2-3 sentences describing what the user wants to add or change
|
||||
@@ -1488,29 +1488,29 @@ sections:
|
||||
prefix: FR
|
||||
instruction: Each Requirement will be a bullet markdown with identifier starting with FR
|
||||
examples:
|
||||
- "FR1: The existing Todo List will integrate with the new AI duplicate detection service without breaking current functionality."
|
||||
- 'FR1: The existing Todo List will integrate with the new AI duplicate detection service without breaking current functionality.'
|
||||
- id: non-functional
|
||||
title: Non Functional
|
||||
type: numbered-list
|
||||
prefix: NFR
|
||||
instruction: Each Requirement will be a bullet markdown with identifier starting with NFR. Include constraints from existing system
|
||||
examples:
|
||||
- "NFR1: Enhancement must maintain existing performance characteristics and not exceed current memory usage by more than 20%."
|
||||
- 'NFR1: Enhancement must maintain existing performance characteristics and not exceed current memory usage by more than 20%.'
|
||||
- id: compatibility
|
||||
title: Compatibility Requirements
|
||||
instruction: Critical for brownfield - what must remain compatible
|
||||
type: numbered-list
|
||||
prefix: CR
|
||||
template: "{{requirement}}: {{description}}"
|
||||
template: '{{requirement}}: {{description}}'
|
||||
items:
|
||||
- id: cr1
|
||||
template: "CR1: {{existing_api_compatibility}}"
|
||||
template: 'CR1: {{existing_api_compatibility}}'
|
||||
- id: cr2
|
||||
template: "CR2: {{database_schema_compatibility}}"
|
||||
template: 'CR2: {{database_schema_compatibility}}'
|
||||
- id: cr3
|
||||
template: "CR3: {{ui_ux_consistency}}"
|
||||
template: 'CR3: {{ui_ux_consistency}}'
|
||||
- id: cr4
|
||||
template: "CR4: {{integration_compatibility}}"
|
||||
template: 'CR4: {{integration_compatibility}}'
|
||||
|
||||
- id: ui-enhancement-goals
|
||||
title: User Interface Enhancement Goals
|
||||
@@ -1593,10 +1593,10 @@ sections:
|
||||
- id: epic-approach
|
||||
title: Epic Approach
|
||||
instruction: Explain the rationale for epic structure - typically single epic for brownfield unless multiple unrelated features
|
||||
template: "**Epic Structure Decision**: {{epic_decision}} with rationale"
|
||||
template: '**Epic Structure Decision**: {{epic_decision}} with rationale'
|
||||
|
||||
- id: epic-details
|
||||
title: "Epic 1: {{enhancement_title}}"
|
||||
title: 'Epic 1: {{enhancement_title}}'
|
||||
instruction: |
|
||||
Comprehensive epic that delivers the brownfield enhancement while maintaining existing functionality
|
||||
|
||||
@@ -1616,7 +1616,7 @@ sections:
|
||||
**Integration Requirements**: {{integration_requirements}}
|
||||
sections:
|
||||
- id: story
|
||||
title: "Story 1.{{story_number}} {{story_title}}"
|
||||
title: 'Story 1.{{story_number}} {{story_title}}'
|
||||
repeatable: true
|
||||
template: |
|
||||
As a {{user_type}},
|
||||
@@ -1627,16 +1627,16 @@ sections:
|
||||
title: Acceptance Criteria
|
||||
type: numbered-list
|
||||
instruction: Define criteria that include both new functionality and existing system integrity
|
||||
item_template: "{{criterion_number}}: {{criteria}}"
|
||||
item_template: '{{criterion_number}}: {{criteria}}'
|
||||
- id: integration-verification
|
||||
title: Integration Verification
|
||||
instruction: Specific verification steps to ensure existing functionality remains intact
|
||||
type: numbered-list
|
||||
prefix: IV
|
||||
items:
|
||||
- template: "IV1: {{existing_functionality_verification}}"
|
||||
- template: "IV2: {{integration_point_verification}}"
|
||||
- template: "IV3: {{performance_impact_verification}}"
|
||||
- template: 'IV1: {{existing_functionality_verification}}'
|
||||
- template: 'IV2: {{integration_point_verification}}'
|
||||
- template: 'IV3: {{performance_impact_verification}}'
|
||||
==================== END: .bmad-core/templates/brownfield-prd-tmpl.yaml ====================
|
||||
|
||||
==================== START: .bmad-core/checklists/pm-checklist.md ====================
|
||||
|
||||
4
dist/agents/po.txt
vendored
4
dist/agents/po.txt
vendored
@@ -593,7 +593,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/stories/{{epic_num}}.{{story_num}}.{{story_title_short}}.md
|
||||
title: "Story {{epic_num}}.{{story_num}}: {{story_title_short}}"
|
||||
title: 'Story {{epic_num}}.{{story_num}}: {{story_title_short}}'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -695,7 +695,7 @@ sections:
|
||||
sections:
|
||||
- id: agent-model
|
||||
title: Agent Model Used
|
||||
template: "{{agent_model_name_version}}"
|
||||
template: '{{agent_model_name_version}}'
|
||||
instruction: Record the specific AI agent model and version used for development
|
||||
owner: dev-agent
|
||||
editors: [dev-agent]
|
||||
|
||||
18
dist/agents/qa.txt
vendored
18
dist/agents/qa.txt
vendored
@@ -1743,7 +1743,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/stories/{{epic_num}}.{{story_num}}.{{story_title_short}}.md
|
||||
title: "Story {{epic_num}}.{{story_num}}: {{story_title_short}}"
|
||||
title: 'Story {{epic_num}}.{{story_num}}: {{story_title_short}}'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1845,7 +1845,7 @@ sections:
|
||||
sections:
|
||||
- id: agent-model
|
||||
title: Agent Model Used
|
||||
template: "{{agent_model_name_version}}"
|
||||
template: '{{agent_model_name_version}}'
|
||||
instruction: Record the specific AI agent model and version used for development
|
||||
owner: dev-agent
|
||||
editors: [dev-agent]
|
||||
@@ -1883,16 +1883,16 @@ template:
|
||||
output:
|
||||
format: yaml
|
||||
filename: docs/qa/gates/{{epic_num}}.{{story_num}}-{{story_slug}}.yml
|
||||
title: "Quality Gate: {{epic_num}}.{{story_num}}"
|
||||
title: 'Quality Gate: {{epic_num}}.{{story_num}}'
|
||||
|
||||
# Required fields (keep these first)
|
||||
schema: 1
|
||||
story: "{{epic_num}}.{{story_num}}"
|
||||
story_title: "{{story_title}}"
|
||||
gate: "{{gate_status}}" # PASS|CONCERNS|FAIL|WAIVED
|
||||
status_reason: "{{status_reason}}" # 1-2 sentence summary of why this gate decision
|
||||
reviewer: "Quinn (Test Architect)"
|
||||
updated: "{{iso_timestamp}}"
|
||||
story: '{{epic_num}}.{{story_num}}'
|
||||
story_title: '{{story_title}}'
|
||||
gate: '{{gate_status}}' # PASS|CONCERNS|FAIL|WAIVED
|
||||
status_reason: '{{status_reason}}' # 1-2 sentence summary of why this gate decision
|
||||
reviewer: 'Quinn (Test Architect)'
|
||||
updated: '{{iso_timestamp}}'
|
||||
|
||||
# Always present but only active when WAIVED
|
||||
waiver: { active: false }
|
||||
|
||||
4
dist/agents/sm.txt
vendored
4
dist/agents/sm.txt
vendored
@@ -369,7 +369,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/stories/{{epic_num}}.{{story_num}}.{{story_title_short}}.md
|
||||
title: "Story {{epic_num}}.{{story_num}}: {{story_title_short}}"
|
||||
title: 'Story {{epic_num}}.{{story_num}}: {{story_title_short}}'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -471,7 +471,7 @@ sections:
|
||||
sections:
|
||||
- id: agent-model
|
||||
title: Agent Model Used
|
||||
template: "{{agent_model_name_version}}"
|
||||
template: '{{agent_model_name_version}}'
|
||||
instruction: Record the specific AI agent model and version used for development
|
||||
owner: dev-agent
|
||||
editors: [dev-agent]
|
||||
|
||||
114
dist/agents/ux-expert.txt
vendored
114
dist/agents/ux-expert.txt
vendored
@@ -343,7 +343,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/front-end-spec.md
|
||||
title: "{{project_name}} UI/UX Specification"
|
||||
title: '{{project_name}} UI/UX Specification'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -371,29 +371,29 @@ sections:
|
||||
sections:
|
||||
- id: user-personas
|
||||
title: Target User Personas
|
||||
template: "{{persona_descriptions}}"
|
||||
template: '{{persona_descriptions}}'
|
||||
examples:
|
||||
- "**Power User:** Technical professionals who need advanced features and efficiency"
|
||||
- "**Casual User:** Occasional users who prioritize ease of use and clear guidance"
|
||||
- "**Administrator:** System managers who need control and oversight capabilities"
|
||||
- '**Power User:** Technical professionals who need advanced features and efficiency'
|
||||
- '**Casual User:** Occasional users who prioritize ease of use and clear guidance'
|
||||
- '**Administrator:** System managers who need control and oversight capabilities'
|
||||
- id: usability-goals
|
||||
title: Usability Goals
|
||||
template: "{{usability_goals}}"
|
||||
template: '{{usability_goals}}'
|
||||
examples:
|
||||
- "Ease of learning: New users can complete core tasks within 5 minutes"
|
||||
- "Efficiency of use: Power users can complete frequent tasks with minimal clicks"
|
||||
- "Error prevention: Clear validation and confirmation for destructive actions"
|
||||
- "Memorability: Infrequent users can return without relearning"
|
||||
- 'Ease of learning: New users can complete core tasks within 5 minutes'
|
||||
- 'Efficiency of use: Power users can complete frequent tasks with minimal clicks'
|
||||
- 'Error prevention: Clear validation and confirmation for destructive actions'
|
||||
- 'Memorability: Infrequent users can return without relearning'
|
||||
- id: design-principles
|
||||
title: Design Principles
|
||||
template: "{{design_principles}}"
|
||||
template: '{{design_principles}}'
|
||||
type: numbered-list
|
||||
examples:
|
||||
- "**Clarity over cleverness** - Prioritize clear communication over aesthetic innovation"
|
||||
- '**Clarity over cleverness** - Prioritize clear communication over aesthetic innovation'
|
||||
- "**Progressive disclosure** - Show only what's needed, when it's needed"
|
||||
- "**Consistent patterns** - Use familiar UI patterns throughout the application"
|
||||
- "**Immediate feedback** - Every action should have a clear, immediate response"
|
||||
- "**Accessible by default** - Design for all users from the start"
|
||||
- '**Consistent patterns** - Use familiar UI patterns throughout the application'
|
||||
- '**Immediate feedback** - Every action should have a clear, immediate response'
|
||||
- '**Accessible by default** - Design for all users from the start'
|
||||
- id: changelog
|
||||
title: Change Log
|
||||
type: table
|
||||
@@ -415,7 +415,7 @@ sections:
|
||||
title: Site Map / Screen Inventory
|
||||
type: mermaid
|
||||
mermaid_type: graph
|
||||
template: "{{sitemap_diagram}}"
|
||||
template: '{{sitemap_diagram}}'
|
||||
examples:
|
||||
- |
|
||||
graph TD
|
||||
@@ -455,7 +455,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: flow
|
||||
title: "{{flow_name}}"
|
||||
title: '{{flow_name}}'
|
||||
template: |
|
||||
**User Goal:** {{flow_goal}}
|
||||
|
||||
@@ -467,13 +467,13 @@ sections:
|
||||
title: Flow Diagram
|
||||
type: mermaid
|
||||
mermaid_type: graph
|
||||
template: "{{flow_diagram}}"
|
||||
template: '{{flow_diagram}}'
|
||||
- id: edge-cases
|
||||
title: "Edge Cases & Error Handling:"
|
||||
title: 'Edge Cases & Error Handling:'
|
||||
type: bullet-list
|
||||
template: "- {{edge_case}}"
|
||||
template: '- {{edge_case}}'
|
||||
- id: notes
|
||||
template: "**Notes:** {{flow_notes}}"
|
||||
template: '**Notes:** {{flow_notes}}'
|
||||
|
||||
- id: wireframes-mockups
|
||||
title: Wireframes & Mockups
|
||||
@@ -482,13 +482,13 @@ sections:
|
||||
elicit: true
|
||||
sections:
|
||||
- id: design-files
|
||||
template: "**Primary Design Files:** {{design_tool_link}}"
|
||||
template: '**Primary Design Files:** {{design_tool_link}}'
|
||||
- id: key-screen-layouts
|
||||
title: Key Screen Layouts
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: screen
|
||||
title: "{{screen_name}}"
|
||||
title: '{{screen_name}}'
|
||||
template: |
|
||||
**Purpose:** {{screen_purpose}}
|
||||
|
||||
@@ -508,13 +508,13 @@ sections:
|
||||
elicit: true
|
||||
sections:
|
||||
- id: design-system-approach
|
||||
template: "**Design System Approach:** {{design_system_approach}}"
|
||||
template: '**Design System Approach:** {{design_system_approach}}'
|
||||
- id: core-components
|
||||
title: Core Components
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: component
|
||||
title: "{{component_name}}"
|
||||
title: '{{component_name}}'
|
||||
template: |
|
||||
**Purpose:** {{component_purpose}}
|
||||
|
||||
@@ -531,19 +531,19 @@ sections:
|
||||
sections:
|
||||
- id: visual-identity
|
||||
title: Visual Identity
|
||||
template: "**Brand Guidelines:** {{brand_guidelines_link}}"
|
||||
template: '**Brand Guidelines:** {{brand_guidelines_link}}'
|
||||
- id: color-palette
|
||||
title: Color Palette
|
||||
type: table
|
||||
columns: ["Color Type", "Hex Code", "Usage"]
|
||||
columns: ['Color Type', 'Hex Code', 'Usage']
|
||||
rows:
|
||||
- ["Primary", "{{primary_color}}", "{{primary_usage}}"]
|
||||
- ["Secondary", "{{secondary_color}}", "{{secondary_usage}}"]
|
||||
- ["Accent", "{{accent_color}}", "{{accent_usage}}"]
|
||||
- ["Success", "{{success_color}}", "Positive feedback, confirmations"]
|
||||
- ["Warning", "{{warning_color}}", "Cautions, important notices"]
|
||||
- ["Error", "{{error_color}}", "Errors, destructive actions"]
|
||||
- ["Neutral", "{{neutral_colors}}", "Text, borders, backgrounds"]
|
||||
- ['Primary', '{{primary_color}}', '{{primary_usage}}']
|
||||
- ['Secondary', '{{secondary_color}}', '{{secondary_usage}}']
|
||||
- ['Accent', '{{accent_color}}', '{{accent_usage}}']
|
||||
- ['Success', '{{success_color}}', 'Positive feedback, confirmations']
|
||||
- ['Warning', '{{warning_color}}', 'Cautions, important notices']
|
||||
- ['Error', '{{error_color}}', 'Errors, destructive actions']
|
||||
- ['Neutral', '{{neutral_colors}}', 'Text, borders, backgrounds']
|
||||
- id: typography
|
||||
title: Typography
|
||||
sections:
|
||||
@@ -556,13 +556,13 @@ sections:
|
||||
- id: type-scale
|
||||
title: Type Scale
|
||||
type: table
|
||||
columns: ["Element", "Size", "Weight", "Line Height"]
|
||||
columns: ['Element', 'Size', 'Weight', 'Line Height']
|
||||
rows:
|
||||
- ["H1", "{{h1_size}}", "{{h1_weight}}", "{{h1_line}}"]
|
||||
- ["H2", "{{h2_size}}", "{{h2_weight}}", "{{h2_line}}"]
|
||||
- ["H3", "{{h3_size}}", "{{h3_weight}}", "{{h3_line}}"]
|
||||
- ["Body", "{{body_size}}", "{{body_weight}}", "{{body_line}}"]
|
||||
- ["Small", "{{small_size}}", "{{small_weight}}", "{{small_line}}"]
|
||||
- ['H1', '{{h1_size}}', '{{h1_weight}}', '{{h1_line}}']
|
||||
- ['H2', '{{h2_size}}', '{{h2_weight}}', '{{h2_line}}']
|
||||
- ['H3', '{{h3_size}}', '{{h3_weight}}', '{{h3_line}}']
|
||||
- ['Body', '{{body_size}}', '{{body_weight}}', '{{body_line}}']
|
||||
- ['Small', '{{small_size}}', '{{small_weight}}', '{{small_line}}']
|
||||
- id: iconography
|
||||
title: Iconography
|
||||
template: |
|
||||
@@ -583,7 +583,7 @@ sections:
|
||||
sections:
|
||||
- id: compliance-target
|
||||
title: Compliance Target
|
||||
template: "**Standard:** {{compliance_standard}}"
|
||||
template: '**Standard:** {{compliance_standard}}'
|
||||
- id: key-requirements
|
||||
title: Key Requirements
|
||||
template: |
|
||||
@@ -603,7 +603,7 @@ sections:
|
||||
- Form labels: {{form_requirements}}
|
||||
- id: testing-strategy
|
||||
title: Testing Strategy
|
||||
template: "{{accessibility_testing}}"
|
||||
template: '{{accessibility_testing}}'
|
||||
|
||||
- id: responsiveness
|
||||
title: Responsiveness Strategy
|
||||
@@ -613,12 +613,12 @@ sections:
|
||||
- id: breakpoints
|
||||
title: Breakpoints
|
||||
type: table
|
||||
columns: ["Breakpoint", "Min Width", "Max Width", "Target Devices"]
|
||||
columns: ['Breakpoint', 'Min Width', 'Max Width', 'Target Devices']
|
||||
rows:
|
||||
- ["Mobile", "{{mobile_min}}", "{{mobile_max}}", "{{mobile_devices}}"]
|
||||
- ["Tablet", "{{tablet_min}}", "{{tablet_max}}", "{{tablet_devices}}"]
|
||||
- ["Desktop", "{{desktop_min}}", "{{desktop_max}}", "{{desktop_devices}}"]
|
||||
- ["Wide", "{{wide_min}}", "-", "{{wide_devices}}"]
|
||||
- ['Mobile', '{{mobile_min}}', '{{mobile_max}}', '{{mobile_devices}}']
|
||||
- ['Tablet', '{{tablet_min}}', '{{tablet_max}}', '{{tablet_devices}}']
|
||||
- ['Desktop', '{{desktop_min}}', '{{desktop_max}}', '{{desktop_devices}}']
|
||||
- ['Wide', '{{wide_min}}', '-', '{{wide_devices}}']
|
||||
- id: adaptation-patterns
|
||||
title: Adaptation Patterns
|
||||
template: |
|
||||
@@ -637,11 +637,11 @@ sections:
|
||||
sections:
|
||||
- id: motion-principles
|
||||
title: Motion Principles
|
||||
template: "{{motion_principles}}"
|
||||
template: '{{motion_principles}}'
|
||||
- id: key-animations
|
||||
title: Key Animations
|
||||
repeatable: true
|
||||
template: "- **{{animation_name}}:** {{animation_description}} (Duration: {{duration}}, Easing: {{easing}})"
|
||||
template: '- **{{animation_name}}:** {{animation_description}} (Duration: {{duration}}, Easing: {{easing}})'
|
||||
|
||||
- id: performance
|
||||
title: Performance Considerations
|
||||
@@ -655,7 +655,7 @@ sections:
|
||||
- **Animation FPS:** {{animation_goal}}
|
||||
- id: design-strategies
|
||||
title: Design Strategies
|
||||
template: "{{performance_strategies}}"
|
||||
template: '{{performance_strategies}}'
|
||||
|
||||
- id: next-steps
|
||||
title: Next Steps
|
||||
@@ -670,17 +670,17 @@ sections:
|
||||
- id: immediate-actions
|
||||
title: Immediate Actions
|
||||
type: numbered-list
|
||||
template: "{{action}}"
|
||||
template: '{{action}}'
|
||||
- id: design-handoff-checklist
|
||||
title: Design Handoff Checklist
|
||||
type: checklist
|
||||
items:
|
||||
- "All user flows documented"
|
||||
- "Component inventory complete"
|
||||
- "Accessibility requirements defined"
|
||||
- "Responsive strategy clear"
|
||||
- "Brand guidelines incorporated"
|
||||
- "Performance goals established"
|
||||
- 'All user flows documented'
|
||||
- 'Component inventory complete'
|
||||
- 'Accessibility requirements defined'
|
||||
- 'Responsive strategy clear'
|
||||
- 'Brand guidelines incorporated'
|
||||
- 'Performance goals established'
|
||||
|
||||
- id: checklist-results
|
||||
title: Checklist Results
|
||||
|
||||
@@ -981,8 +981,8 @@ template:
|
||||
version: 2.0
|
||||
output:
|
||||
format: markdown
|
||||
filename: "docs/{{game_name}}-game-design-document.md"
|
||||
title: "{{game_title}} Game Design Document (GDD)"
|
||||
filename: 'docs/{{game_name}}-game-design-document.md'
|
||||
title: '{{game_title}} Game Design Document (GDD)'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1019,7 +1019,7 @@ sections:
|
||||
title: Unique Selling Points
|
||||
instruction: List 3-5 key features that differentiate this game from competitors
|
||||
type: numbered-list
|
||||
template: "{{usp}}"
|
||||
template: '{{usp}}'
|
||||
|
||||
- id: core-gameplay
|
||||
title: Core Gameplay
|
||||
@@ -1064,7 +1064,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: mechanic
|
||||
title: "{{mechanic_name}}"
|
||||
title: '{{mechanic_name}}'
|
||||
template: |
|
||||
**Description:** {{detailed_description}}
|
||||
|
||||
@@ -1129,7 +1129,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: level-type
|
||||
title: "{{level_type_name}}"
|
||||
title: '{{level_type_name}}'
|
||||
template: |
|
||||
**Purpose:** {{gameplay_purpose}}
|
||||
**Duration:** {{target_time}}
|
||||
@@ -1230,10 +1230,10 @@ sections:
|
||||
instruction: Break down the development into phases that can be converted to epics
|
||||
sections:
|
||||
- id: phase-1-core-systems
|
||||
title: "Phase 1: Core Systems ({{duration}})"
|
||||
title: 'Phase 1: Core Systems ({{duration}})'
|
||||
sections:
|
||||
- id: foundation-epic
|
||||
title: "Epic: Foundation"
|
||||
title: 'Epic: Foundation'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Engine setup and configuration
|
||||
@@ -1241,41 +1241,41 @@ sections:
|
||||
- Core input handling
|
||||
- Asset loading pipeline
|
||||
- id: core-mechanics-epic
|
||||
title: "Epic: Core Mechanics"
|
||||
title: 'Epic: Core Mechanics'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- {{primary_mechanic}} implementation
|
||||
- Basic physics and collision
|
||||
- Player controller
|
||||
- id: phase-2-gameplay-features
|
||||
title: "Phase 2: Gameplay Features ({{duration}})"
|
||||
title: 'Phase 2: Gameplay Features ({{duration}})'
|
||||
sections:
|
||||
- id: game-systems-epic
|
||||
title: "Epic: Game Systems"
|
||||
title: 'Epic: Game Systems'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- {{mechanic_2}} implementation
|
||||
- {{mechanic_3}} implementation
|
||||
- Game state management
|
||||
- id: content-creation-epic
|
||||
title: "Epic: Content Creation"
|
||||
title: 'Epic: Content Creation'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Level loading system
|
||||
- First playable levels
|
||||
- Basic UI implementation
|
||||
- id: phase-3-polish-optimization
|
||||
title: "Phase 3: Polish & Optimization ({{duration}})"
|
||||
title: 'Phase 3: Polish & Optimization ({{duration}})'
|
||||
sections:
|
||||
- id: performance-epic
|
||||
title: "Epic: Performance"
|
||||
title: 'Epic: Performance'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Optimization and profiling
|
||||
- Mobile platform testing
|
||||
- Memory management
|
||||
- id: user-experience-epic
|
||||
title: "Epic: User Experience"
|
||||
title: 'Epic: User Experience'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Audio implementation
|
||||
@@ -1317,7 +1317,7 @@ sections:
|
||||
title: References
|
||||
instruction: List any competitive analysis, inspiration, or research sources
|
||||
type: bullet-list
|
||||
template: "{{reference}}"
|
||||
template: '{{reference}}'
|
||||
==================== END: .bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml ====================
|
||||
|
||||
==================== START: .bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml ====================
|
||||
@@ -1327,8 +1327,8 @@ template:
|
||||
version: 2.0
|
||||
output:
|
||||
format: markdown
|
||||
filename: "docs/{{game_name}}-level-design-document.md"
|
||||
title: "{{game_title}} Level Design Document"
|
||||
filename: 'docs/{{game_name}}-level-design-document.md'
|
||||
title: '{{game_title}} Level Design Document'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1389,7 +1389,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: level-category
|
||||
title: "{{category_name}} Levels"
|
||||
title: '{{category_name}} Levels'
|
||||
template: |
|
||||
**Purpose:** {{gameplay_purpose}}
|
||||
|
||||
@@ -1694,19 +1694,19 @@ sections:
|
||||
title: Playtesting Checklist
|
||||
type: checklist
|
||||
items:
|
||||
- "Level completes within target time range"
|
||||
- "All mechanics function correctly"
|
||||
- "Difficulty feels appropriate for level category"
|
||||
- "Player guidance is clear and effective"
|
||||
- "No exploits or sequence breaks (unless intended)"
|
||||
- 'Level completes within target time range'
|
||||
- 'All mechanics function correctly'
|
||||
- 'Difficulty feels appropriate for level category'
|
||||
- 'Player guidance is clear and effective'
|
||||
- 'No exploits or sequence breaks (unless intended)'
|
||||
- id: player-experience-testing
|
||||
title: Player Experience Testing
|
||||
type: checklist
|
||||
items:
|
||||
- "Tutorial levels teach effectively"
|
||||
- "Challenge feels fair and rewarding"
|
||||
- "Flow and pacing maintain engagement"
|
||||
- "Audio and visual feedback support gameplay"
|
||||
- 'Tutorial levels teach effectively'
|
||||
- 'Challenge feels fair and rewarding'
|
||||
- 'Flow and pacing maintain engagement'
|
||||
- 'Audio and visual feedback support gameplay'
|
||||
- id: balance-validation
|
||||
title: Balance Validation
|
||||
template: |
|
||||
@@ -1814,8 +1814,8 @@ template:
|
||||
version: 2.0
|
||||
output:
|
||||
format: markdown
|
||||
filename: "docs/{{game_name}}-game-brief.md"
|
||||
title: "{{game_title}} Game Brief"
|
||||
filename: 'docs/{{game_name}}-game-brief.md'
|
||||
title: '{{game_title}} Game Brief'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -2101,21 +2101,21 @@ sections:
|
||||
title: Development Roadmap
|
||||
sections:
|
||||
- id: phase-1-preproduction
|
||||
title: "Phase 1: Pre-Production ({{duration}})"
|
||||
title: 'Phase 1: Pre-Production ({{duration}})'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Detailed Game Design Document creation
|
||||
- Technical architecture planning
|
||||
- Art style exploration and pipeline setup
|
||||
- id: phase-2-prototype
|
||||
title: "Phase 2: Prototype ({{duration}})"
|
||||
title: 'Phase 2: Prototype ({{duration}})'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Core mechanic implementation
|
||||
- Technical proof of concept
|
||||
- Initial playtesting and iteration
|
||||
- id: phase-3-production
|
||||
title: "Phase 3: Production ({{duration}})"
|
||||
title: 'Phase 3: Production ({{duration}})'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Full feature development
|
||||
|
||||
@@ -197,8 +197,8 @@ template:
|
||||
version: 2.0
|
||||
output:
|
||||
format: markdown
|
||||
filename: "docs/{{game_name}}-game-architecture.md"
|
||||
title: "{{game_title}} Game Architecture Document"
|
||||
filename: 'docs/{{game_name}}-game-architecture.md'
|
||||
title: '{{game_title}} Game Architecture Document'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -422,7 +422,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: mechanic-system
|
||||
title: "{{mechanic_name}} System"
|
||||
title: '{{mechanic_name}} System'
|
||||
template: |
|
||||
**Purpose:** {{system_purpose}}
|
||||
|
||||
@@ -719,7 +719,7 @@ sections:
|
||||
instruction: Break down the architecture implementation into phases that align with the GDD development phases
|
||||
sections:
|
||||
- id: phase-1-foundation
|
||||
title: "Phase 1: Foundation ({{duration}})"
|
||||
title: 'Phase 1: Foundation ({{duration}})'
|
||||
sections:
|
||||
- id: phase-1-core
|
||||
title: Core Systems
|
||||
@@ -737,7 +737,7 @@ sections:
|
||||
- "Basic Scene Management System"
|
||||
- "Asset Loading Foundation"
|
||||
- id: phase-2-game-systems
|
||||
title: "Phase 2: Game Systems ({{duration}})"
|
||||
title: 'Phase 2: Game Systems ({{duration}})'
|
||||
sections:
|
||||
- id: phase-2-gameplay
|
||||
title: Gameplay Systems
|
||||
@@ -755,7 +755,7 @@ sections:
|
||||
- "Physics and Collision Framework"
|
||||
- "Game State Management System"
|
||||
- id: phase-3-content-polish
|
||||
title: "Phase 3: Content & Polish ({{duration}})"
|
||||
title: 'Phase 3: Content & Polish ({{duration}})'
|
||||
sections:
|
||||
- id: phase-3-content
|
||||
title: Content Systems
|
||||
|
||||
@@ -402,8 +402,8 @@ template:
|
||||
version: 2.0
|
||||
output:
|
||||
format: markdown
|
||||
filename: "stories/{{epic_name}}/{{story_id}}-{{story_name}}.md"
|
||||
title: "Story: {{story_title}}"
|
||||
filename: 'stories/{{epic_name}}/{{story_id}}-{{story_name}}.md'
|
||||
title: 'Story: {{story_title}}'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -432,7 +432,7 @@ sections:
|
||||
- id: description
|
||||
title: Description
|
||||
instruction: Provide a clear, concise description of what this story implements. Focus on the specific game feature or system being built. Reference the GDD section that defines this feature.
|
||||
template: "{{clear_description_of_what_needs_to_be_implemented}}"
|
||||
template: '{{clear_description_of_what_needs_to_be_implemented}}'
|
||||
|
||||
- id: acceptance-criteria
|
||||
title: Acceptance Criteria
|
||||
@@ -442,22 +442,22 @@ sections:
|
||||
title: Functional Requirements
|
||||
type: checklist
|
||||
items:
|
||||
- "{{specific_functional_requirement}}"
|
||||
- '{{specific_functional_requirement}}'
|
||||
- id: technical-requirements
|
||||
title: Technical Requirements
|
||||
type: checklist
|
||||
items:
|
||||
- "Code follows TypeScript strict mode standards"
|
||||
- "Maintains 60 FPS on target devices"
|
||||
- "No memory leaks or performance degradation"
|
||||
- "{{specific_technical_requirement}}"
|
||||
- 'Code follows TypeScript strict mode standards'
|
||||
- 'Maintains 60 FPS on target devices'
|
||||
- 'No memory leaks or performance degradation'
|
||||
- '{{specific_technical_requirement}}'
|
||||
- id: game-design-requirements
|
||||
title: Game Design Requirements
|
||||
type: checklist
|
||||
items:
|
||||
- "{{gameplay_requirement_from_gdd}}"
|
||||
- "{{balance_requirement_if_applicable}}"
|
||||
- "{{player_experience_requirement}}"
|
||||
- '{{gameplay_requirement_from_gdd}}'
|
||||
- '{{balance_requirement_if_applicable}}'
|
||||
- '{{player_experience_requirement}}'
|
||||
|
||||
- id: technical-specifications
|
||||
title: Technical Specifications
|
||||
@@ -622,14 +622,14 @@ sections:
|
||||
instruction: Checklist that must be completed before the story is considered finished
|
||||
type: checklist
|
||||
items:
|
||||
- "All acceptance criteria met"
|
||||
- "Code reviewed and approved"
|
||||
- "Unit tests written and passing"
|
||||
- "Integration tests passing"
|
||||
- "Performance targets met"
|
||||
- "No linting errors"
|
||||
- "Documentation updated"
|
||||
- "{{game_specific_dod_item}}"
|
||||
- 'All acceptance criteria met'
|
||||
- 'Code reviewed and approved'
|
||||
- 'Unit tests written and passing'
|
||||
- 'Integration tests passing'
|
||||
- 'Performance targets met'
|
||||
- 'No linting errors'
|
||||
- 'Documentation updated'
|
||||
- '{{game_specific_dod_item}}'
|
||||
|
||||
- id: notes
|
||||
title: Notes
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1231,7 +1231,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/game-architecture.md
|
||||
title: "{{project_name}} Game Architecture Document"
|
||||
title: '{{project_name}} Game Architecture Document'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1341,11 +1341,11 @@ sections:
|
||||
- Game management patterns (Singleton managers, Event systems, State machines)
|
||||
- Data patterns (ScriptableObject configuration, Save/Load systems)
|
||||
- Unity-specific patterns (Object pooling, Coroutines, Unity Events)
|
||||
template: "- **{{pattern_name}}:** {{pattern_description}} - _Rationale:_ {{rationale}}"
|
||||
template: '- **{{pattern_name}}:** {{pattern_description}} - _Rationale:_ {{rationale}}'
|
||||
examples:
|
||||
- "**Component-Based Architecture:** Using MonoBehaviour components for game logic - _Rationale:_ Aligns with Unity's design philosophy and enables reusable, testable game systems"
|
||||
- "**ScriptableObject Data:** Using ScriptableObjects for game configuration - _Rationale:_ Enables data-driven design and easy balancing without code changes"
|
||||
- "**Event-Driven Communication:** Using Unity Events and C# events for system decoupling - _Rationale:_ Supports modular architecture and easier testing"
|
||||
- '**ScriptableObject Data:** Using ScriptableObjects for game configuration - _Rationale:_ Enables data-driven design and easy balancing without code changes'
|
||||
- '**Event-Driven Communication:** Using Unity Events and C# events for system decoupling - _Rationale:_ Supports modular architecture and easier testing'
|
||||
|
||||
- id: tech-stack
|
||||
title: Tech Stack
|
||||
@@ -1384,13 +1384,13 @@ sections:
|
||||
columns: [Category, Technology, Version, Purpose, Rationale]
|
||||
instruction: Populate the technology stack table with all relevant Unity technologies
|
||||
examples:
|
||||
- "| **Game Engine** | Unity | 2022.3.21f1 | Core game development platform | Latest LTS version, stable 2D tooling, comprehensive package ecosystem |"
|
||||
- '| **Game Engine** | Unity | 2022.3.21f1 | Core game development platform | Latest LTS version, stable 2D tooling, comprehensive package ecosystem |'
|
||||
- "| **Language** | C# | 10.0 | Primary scripting language | Unity's native language, strong typing, excellent tooling |"
|
||||
- "| **Render Pipeline** | Universal Render Pipeline (URP) | 14.0.10 | 2D/3D rendering | Optimized for mobile, excellent 2D features, future-proof |"
|
||||
- "| **Input System** | Unity Input System | 1.7.0 | Cross-platform input handling | Modern input system, supports multiple devices, rebindable controls |"
|
||||
- "| **Physics** | Unity 2D Physics | Built-in | 2D collision and physics | Integrated Box2D, optimized for 2D games |"
|
||||
- "| **Audio** | Unity Audio | Built-in | Audio playback and mixing | Built-in audio system with mixer support |"
|
||||
- "| **Testing** | Unity Test Framework | 1.1.33 | Unit and integration testing | Built-in testing framework based on NUnit |"
|
||||
- '| **Render Pipeline** | Universal Render Pipeline (URP) | 14.0.10 | 2D/3D rendering | Optimized for mobile, excellent 2D features, future-proof |'
|
||||
- '| **Input System** | Unity Input System | 1.7.0 | Cross-platform input handling | Modern input system, supports multiple devices, rebindable controls |'
|
||||
- '| **Physics** | Unity 2D Physics | Built-in | 2D collision and physics | Integrated Box2D, optimized for 2D games |'
|
||||
- '| **Audio** | Unity Audio | Built-in | Audio playback and mixing | Built-in audio system with mixer support |'
|
||||
- '| **Testing** | Unity Test Framework | 1.1.33 | Unit and integration testing | Built-in testing framework based on NUnit |'
|
||||
|
||||
- id: data-models
|
||||
title: Game Data Models
|
||||
@@ -1408,7 +1408,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: model
|
||||
title: "{{model_name}}"
|
||||
title: '{{model_name}}'
|
||||
template: |
|
||||
**Purpose:** {{model_purpose}}
|
||||
|
||||
@@ -1443,7 +1443,7 @@ sections:
|
||||
sections:
|
||||
- id: system-list
|
||||
repeatable: true
|
||||
title: "{{system_name}} System"
|
||||
title: '{{system_name}} System'
|
||||
template: |
|
||||
**Responsibility:** {{system_description}}
|
||||
|
||||
@@ -1967,7 +1967,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: integration
|
||||
title: "{{service_name}} Integration"
|
||||
title: '{{service_name}} Integration'
|
||||
template: |
|
||||
- **Purpose:** {{service_purpose}}
|
||||
- **Documentation:** {{service_docs_url}}
|
||||
@@ -2079,12 +2079,12 @@ sections:
|
||||
- id: environments
|
||||
title: Build Environments
|
||||
repeatable: true
|
||||
template: "- **{{env_name}}:** {{env_purpose}} - {{platform_settings}}"
|
||||
template: '- **{{env_name}}:** {{env_purpose}} - {{platform_settings}}'
|
||||
- id: platform-specific-builds
|
||||
title: Platform-Specific Build Settings
|
||||
type: code
|
||||
language: text
|
||||
template: "{{platform_build_configurations}}"
|
||||
template: '{{platform_build_configurations}}'
|
||||
|
||||
- id: coding-standards
|
||||
title: Coding Standards
|
||||
@@ -2113,9 +2113,9 @@ sections:
|
||||
columns: [Element, Convention, Example]
|
||||
instruction: Only include if deviating from Unity defaults
|
||||
examples:
|
||||
- "| MonoBehaviour | PascalCase + Component suffix | PlayerController, HealthSystem |"
|
||||
- "| ScriptableObject | PascalCase + Data/Config suffix | PlayerData, GameConfig |"
|
||||
- "| Prefab | PascalCase descriptive | PlayerCharacter, EnvironmentTile |"
|
||||
- '| MonoBehaviour | PascalCase + Component suffix | PlayerController, HealthSystem |'
|
||||
- '| ScriptableObject | PascalCase + Data/Config suffix | PlayerData, GameConfig |'
|
||||
- '| Prefab | PascalCase descriptive | PlayerCharacter, EnvironmentTile |'
|
||||
- id: critical-rules
|
||||
title: Critical Unity Rules
|
||||
instruction: |
|
||||
@@ -2127,7 +2127,7 @@ sections:
|
||||
|
||||
Avoid obvious rules like "follow SOLID principles" or "optimize performance"
|
||||
repeatable: true
|
||||
template: "- **{{rule_name}}:** {{rule_description}}"
|
||||
template: '- **{{rule_name}}:** {{rule_description}}'
|
||||
- id: unity-specifics
|
||||
title: Unity-Specific Guidelines
|
||||
condition: Critical Unity-specific rules needed
|
||||
@@ -2136,7 +2136,7 @@ sections:
|
||||
- id: unity-lifecycle
|
||||
title: Unity Lifecycle Rules
|
||||
repeatable: true
|
||||
template: "- **{{lifecycle_method}}:** {{usage_rule}}"
|
||||
template: '- **{{lifecycle_method}}:** {{usage_rule}}'
|
||||
|
||||
- id: test-strategy
|
||||
title: Test Strategy and Standards
|
||||
|
||||
@@ -1175,7 +1175,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/game-design-document.md
|
||||
title: "{{game_title}} Game Design Document (GDD)"
|
||||
title: '{{game_title}} Game Design Document (GDD)'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1223,8 +1223,8 @@ sections:
|
||||
**Primary:** {{age_range}}, {{player_type}}, {{platform_preference}}
|
||||
**Secondary:** {{secondary_audience}}
|
||||
examples:
|
||||
- "Primary: Ages 8-16, casual mobile gamers, prefer short play sessions"
|
||||
- "Secondary: Adult puzzle enthusiasts, educators looking for teaching tools"
|
||||
- 'Primary: Ages 8-16, casual mobile gamers, prefer short play sessions'
|
||||
- 'Secondary: Adult puzzle enthusiasts, educators looking for teaching tools'
|
||||
- id: platform-technical
|
||||
title: Platform & Technical Requirements
|
||||
instruction: Based on the technical preferences or user input, define the target platforms and Unity-specific requirements
|
||||
@@ -1235,7 +1235,7 @@ sections:
|
||||
**Screen Support:** {{resolution_range}}
|
||||
**Build Targets:** {{build_targets}}
|
||||
examples:
|
||||
- "Primary Platform: Mobile (iOS/Android), Engine: Unity 2022.3 LTS & C#, Performance: 60 FPS on iPhone 8/Galaxy S8"
|
||||
- 'Primary Platform: Mobile (iOS/Android), Engine: Unity 2022.3 LTS & C#, Performance: 60 FPS on iPhone 8/Galaxy S8'
|
||||
- id: unique-selling-points
|
||||
title: Unique Selling Points
|
||||
instruction: List 3-5 key features that differentiate this game from competitors
|
||||
@@ -1286,8 +1286,8 @@ sections:
|
||||
- {{loss_condition_1}} - Trigger: {{unity_trigger}}
|
||||
- {{loss_condition_2}} - Trigger: {{unity_trigger}}
|
||||
examples:
|
||||
- "Victory: Player reaches exit portal - Unity Event: OnTriggerEnter2D with Portal tag"
|
||||
- "Failure: Health reaches zero - Trigger: Health component value <= 0"
|
||||
- 'Victory: Player reaches exit portal - Unity Event: OnTriggerEnter2D with Portal tag'
|
||||
- 'Failure: Health reaches zero - Trigger: Health component value <= 0'
|
||||
|
||||
- id: game-mechanics
|
||||
title: Game Mechanics
|
||||
@@ -1299,7 +1299,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: mechanic
|
||||
title: "{{mechanic_name}}"
|
||||
title: '{{mechanic_name}}'
|
||||
template: |
|
||||
**Description:** {{detailed_description}}
|
||||
|
||||
@@ -1321,8 +1321,8 @@ sections:
|
||||
- {{script_name}}.cs - {{responsibility}}
|
||||
- {{manager_script}}.cs - {{management_role}}
|
||||
examples:
|
||||
- "Components Needed: Rigidbody2D, BoxCollider2D, PlayerMovement script"
|
||||
- "Physics Requirements: 2D Physics material for ground friction, Gravity scale 3"
|
||||
- 'Components Needed: Rigidbody2D, BoxCollider2D, PlayerMovement script'
|
||||
- 'Physics Requirements: 2D Physics material for ground friction, Gravity scale 3'
|
||||
- id: controls
|
||||
title: Controls
|
||||
instruction: Define all input methods for different platforms using Unity's Input System
|
||||
@@ -1377,7 +1377,7 @@ sections:
|
||||
**Late Game:** {{duration}} - {{difficulty_description}}
|
||||
- Unity Config: {{scriptable_object_values}}
|
||||
examples:
|
||||
- "enemy speed: 2.0f, jump height: 4.5f, obstacle density: 0.3f"
|
||||
- 'enemy speed: 2.0f, jump height: 4.5f, obstacle density: 0.3f'
|
||||
- id: economy-resources
|
||||
title: Economy & Resources
|
||||
condition: has_economy
|
||||
@@ -1400,7 +1400,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: level-type
|
||||
title: "{{level_type_name}}"
|
||||
title: '{{level_type_name}}'
|
||||
template: |
|
||||
**Purpose:** {{gameplay_purpose}}
|
||||
**Target Duration:** {{target_time}}
|
||||
@@ -1424,7 +1424,7 @@ sections:
|
||||
|
||||
- {{prefab_name}} - {{prefab_purpose}}
|
||||
examples:
|
||||
- "Environment: TilemapRenderer with Platform tileset, Lighting: 2D Global Light + Point Lights"
|
||||
- 'Environment: TilemapRenderer with Platform tileset, Lighting: 2D Global Light + Point Lights'
|
||||
- id: level-progression
|
||||
title: Level Progression
|
||||
template: |
|
||||
@@ -1439,7 +1439,7 @@ sections:
|
||||
- Addressable Assets: {{addressable_groups}}
|
||||
- Loading Screens: {{loading_implementation}}
|
||||
examples:
|
||||
- "Scene Naming: World{X}_Level{Y}_Name, Addressable Groups: Levels_World1, World_Environments"
|
||||
- 'Scene Naming: World{X}_Level{Y}_Name, Addressable Groups: Levels_World1, World_Environments'
|
||||
|
||||
- id: technical-specifications
|
||||
title: Technical Specifications
|
||||
@@ -1471,7 +1471,7 @@ sections:
|
||||
- Physics Settings: {{physics_config}}
|
||||
examples:
|
||||
- com.unity.addressables 1.20.5 - Asset loading and memory management
|
||||
- "Color Space: Linear, Quality: Mobile/Desktop presets, Gravity: -20"
|
||||
- 'Color Space: Linear, Quality: Mobile/Desktop presets, Gravity: -20'
|
||||
- id: performance-requirements
|
||||
title: Performance Requirements
|
||||
template: |
|
||||
@@ -1487,7 +1487,7 @@ sections:
|
||||
- GC Allocs: <{{gc_limit}}KB per frame
|
||||
- Draw Calls: <{{draw_calls}} per frame
|
||||
examples:
|
||||
- "60 FPS (minimum 30), CPU: <16.67ms, GPU: <16.67ms, GC: <4KB, Draws: <50"
|
||||
- '60 FPS (minimum 30), CPU: <16.67ms, GPU: <16.67ms, GC: <4KB, Draws: <50'
|
||||
- id: platform-specific
|
||||
title: Platform Specific Requirements
|
||||
template: |
|
||||
@@ -1510,7 +1510,7 @@ sections:
|
||||
- Browser Support: {{browser_list}}
|
||||
- Compression: {{compression_format}}
|
||||
examples:
|
||||
- "Resolution: 1280x720 - 4K, Gamepad: Xbox/PlayStation controllers via Input System"
|
||||
- 'Resolution: 1280x720 - 4K, Gamepad: Xbox/PlayStation controllers via Input System'
|
||||
- id: asset-requirements
|
||||
title: Asset Requirements
|
||||
instruction: Define asset specifications for Unity pipeline optimization
|
||||
@@ -1536,7 +1536,7 @@ sections:
|
||||
- Font: {{font_requirements}}
|
||||
- Icon Sizes: {{icon_specifications}}
|
||||
examples:
|
||||
- "Sprites: 32x32 to 256x256 at 16 PPU, Format: RGBA32 for quality/RGBA16 for performance"
|
||||
- 'Sprites: 32x32 to 256x256 at 16 PPU, Format: RGBA32 for quality/RGBA16 for performance'
|
||||
|
||||
- id: technical-architecture-requirements
|
||||
title: Technical Architecture Requirements
|
||||
@@ -1578,8 +1578,8 @@ sections:
|
||||
- Prefabs: {{prefab_naming}}
|
||||
- Scenes: {{scene_naming}}
|
||||
examples:
|
||||
- "Architecture: Component-Based with ScriptableObject data containers"
|
||||
- "Scripts: PascalCase (PlayerController), Prefabs: Player_Prefab, Scenes: Level_01_Forest"
|
||||
- 'Architecture: Component-Based with ScriptableObject data containers'
|
||||
- 'Scripts: PascalCase (PlayerController), Prefabs: Player_Prefab, Scenes: Level_01_Forest'
|
||||
- id: unity-systems-integration
|
||||
title: Unity Systems Integration
|
||||
template: |
|
||||
@@ -1601,8 +1601,8 @@ sections:
|
||||
- **Memory Management:** {{memory_strategy}}
|
||||
- **Build Pipeline:** {{build_automation}}
|
||||
examples:
|
||||
- "Input System: Action Maps for Menu/Gameplay contexts with device switching"
|
||||
- "DOTween: Smooth UI transitions and gameplay animations"
|
||||
- 'Input System: Action Maps for Menu/Gameplay contexts with device switching'
|
||||
- 'DOTween: Smooth UI transitions and gameplay animations'
|
||||
- id: data-management
|
||||
title: Data Management
|
||||
template: |
|
||||
@@ -1625,8 +1625,8 @@ sections:
|
||||
- **Memory Pools:** {{pooling_objects}}
|
||||
- **Asset References:** {{asset_reference_system}}
|
||||
examples:
|
||||
- "Save Data: JSON format with AES encryption, stored in persistent data path"
|
||||
- "ScriptableObjects: Game settings, level configurations, character data"
|
||||
- 'Save Data: JSON format with AES encryption, stored in persistent data path'
|
||||
- 'ScriptableObjects: Game settings, level configurations, character data'
|
||||
|
||||
- id: development-phases
|
||||
title: Development Phases & Epic Planning
|
||||
@@ -1638,15 +1638,15 @@ sections:
|
||||
instruction: Present a high-level list of all phases for user approval. Each phase's design should deliver significant Unity functionality.
|
||||
type: numbered-list
|
||||
examples:
|
||||
- "Phase 1: Unity Foundation & Core Systems: Project setup, input handling, basic scene management"
|
||||
- "Phase 2: Core Game Mechanics: Player controller, physics systems, basic gameplay loop"
|
||||
- "Phase 3: Level Systems & Content Pipeline: Scene loading, prefab systems, level progression"
|
||||
- "Phase 4: Polish & Platform Optimization: Performance tuning, platform-specific features, deployment"
|
||||
- 'Phase 1: Unity Foundation & Core Systems: Project setup, input handling, basic scene management'
|
||||
- 'Phase 2: Core Game Mechanics: Player controller, physics systems, basic gameplay loop'
|
||||
- 'Phase 3: Level Systems & Content Pipeline: Scene loading, prefab systems, level progression'
|
||||
- 'Phase 4: Polish & Platform Optimization: Performance tuning, platform-specific features, deployment'
|
||||
- id: phase-1-foundation
|
||||
title: "Phase 1: Unity Foundation & Core Systems ({{duration}})"
|
||||
title: 'Phase 1: Unity Foundation & Core Systems ({{duration}})'
|
||||
sections:
|
||||
- id: foundation-design
|
||||
title: "Design: Unity Project Foundation"
|
||||
title: 'Design: Unity Project Foundation'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Unity project setup with proper folder structure and naming conventions
|
||||
@@ -1656,9 +1656,9 @@ sections:
|
||||
- Development tools setup (debugging, profiling integration)
|
||||
- Initial build pipeline and platform configuration
|
||||
examples:
|
||||
- "Input System: Configure PlayerInput component with Action Maps for movement and UI"
|
||||
- 'Input System: Configure PlayerInput component with Action Maps for movement and UI'
|
||||
- id: core-systems-design
|
||||
title: "Design: Essential Game Systems"
|
||||
title: 'Design: Essential Game Systems'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Save/Load system implementation with {{save_format}} format
|
||||
@@ -1668,10 +1668,10 @@ sections:
|
||||
- Basic UI framework and canvas configuration
|
||||
- Settings and configuration management with ScriptableObjects
|
||||
- id: phase-2-gameplay
|
||||
title: "Phase 2: Core Gameplay Implementation ({{duration}})"
|
||||
title: 'Phase 2: Core Gameplay Implementation ({{duration}})'
|
||||
sections:
|
||||
- id: gameplay-mechanics-design
|
||||
title: "Design: Primary Game Mechanics"
|
||||
title: 'Design: Primary Game Mechanics'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Player controller with {{movement_type}} movement system
|
||||
@@ -1681,7 +1681,7 @@ sections:
|
||||
- Basic collision detection and response systems
|
||||
- Animation system integration with Animator controllers
|
||||
- id: level-systems-design
|
||||
title: "Design: Level & Content Systems"
|
||||
title: 'Design: Level & Content Systems'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Scene loading and transition system
|
||||
@@ -1691,10 +1691,10 @@ sections:
|
||||
- Collectibles and pickup systems
|
||||
- Victory/defeat condition implementation
|
||||
- id: phase-3-polish
|
||||
title: "Phase 3: Polish & Optimization ({{duration}})"
|
||||
title: 'Phase 3: Polish & Optimization ({{duration}})'
|
||||
sections:
|
||||
- id: performance-design
|
||||
title: "Design: Performance & Platform Optimization"
|
||||
title: 'Design: Performance & Platform Optimization'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Unity Profiler analysis and optimization passes
|
||||
@@ -1704,7 +1704,7 @@ sections:
|
||||
- Build size optimization and asset bundling
|
||||
- Quality settings configuration for different device tiers
|
||||
- id: user-experience-design
|
||||
title: "Design: User Experience & Polish"
|
||||
title: 'Design: User Experience & Polish'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Complete UI/UX implementation with responsive design
|
||||
@@ -1729,10 +1729,10 @@ sections:
|
||||
- Cross Cutting Concerns should flow through epics and stories and not be final stories. For example, adding a logging framework as a last story of an epic, or at the end of a project as a final epic or story would be terrible as we would not have logging from the beginning.
|
||||
elicit: true
|
||||
examples:
|
||||
- "Epic 1: Unity Foundation & Core Systems: Project setup, input handling, basic scene management"
|
||||
- "Epic 2: Core Game Mechanics: Player controller, physics systems, basic gameplay loop"
|
||||
- "Epic 3: Level Systems & Content Pipeline: Scene loading, prefab systems, level progression"
|
||||
- "Epic 4: Polish & Platform Optimization: Performance tuning, platform-specific features, deployment"
|
||||
- 'Epic 1: Unity Foundation & Core Systems: Project setup, input handling, basic scene management'
|
||||
- 'Epic 2: Core Game Mechanics: Player controller, physics systems, basic gameplay loop'
|
||||
- 'Epic 3: Level Systems & Content Pipeline: Scene loading, prefab systems, level progression'
|
||||
- 'Epic 4: Polish & Platform Optimization: Performance tuning, platform-specific features, deployment'
|
||||
|
||||
- id: epic-details
|
||||
title: Epic {{epic_number}} {{epic_title}}
|
||||
@@ -1754,13 +1754,13 @@ sections:
|
||||
- Think "junior developer working for 2-4 hours" - stories must be small, focused, and self-contained
|
||||
- If a story seems complex, break it down further as long as it can deliver a vertical slice
|
||||
elicit: true
|
||||
template: "{{epic_goal}}"
|
||||
template: '{{epic_goal}}'
|
||||
sections:
|
||||
- id: story
|
||||
title: Story {{epic_number}}.{{story_number}} {{story_title}}
|
||||
repeatable: true
|
||||
instruction: Provide a clear, concise description of what this story implements. Focus on the specific game feature or system being built. Reference the GDD section that defines this feature and reference the gamearchitecture section for additional implementation and integration specifics.
|
||||
template: "{{clear_description_of_what_needs_to_be_implemented}}"
|
||||
template: '{{clear_description_of_what_needs_to_be_implemented}}'
|
||||
sections:
|
||||
- id: acceptance-criteria
|
||||
title: Acceptance Criteria
|
||||
@@ -1770,7 +1770,7 @@ sections:
|
||||
title: Functional Requirements
|
||||
type: checklist
|
||||
items:
|
||||
- "{{specific_functional_requirement}}"
|
||||
- '{{specific_functional_requirement}}'
|
||||
- id: technical-requirements
|
||||
title: Technical Requirements
|
||||
type: checklist
|
||||
@@ -1778,14 +1778,14 @@ sections:
|
||||
- Code follows C# best practices
|
||||
- Maintains stable frame rate on target devices
|
||||
- No memory leaks or performance degradation
|
||||
- "{{specific_technical_requirement}}"
|
||||
- '{{specific_technical_requirement}}'
|
||||
- id: game-design-requirements
|
||||
title: Game Design Requirements
|
||||
type: checklist
|
||||
items:
|
||||
- "{{gameplay_requirement_from_gdd}}"
|
||||
- "{{balance_requirement_if_applicable}}"
|
||||
- "{{player_experience_requirement}}"
|
||||
- '{{gameplay_requirement_from_gdd}}'
|
||||
- '{{balance_requirement_if_applicable}}'
|
||||
- '{{player_experience_requirement}}'
|
||||
|
||||
- id: success-metrics
|
||||
title: Success Metrics & Quality Assurance
|
||||
@@ -1803,8 +1803,8 @@ sections:
|
||||
- **Build Size:** Final build <{{size_limit}}MB for mobile, <{{desktop_limit}}MB for desktop
|
||||
- **Battery Life:** Mobile gameplay sessions >{{battery_target}} hours on average device
|
||||
examples:
|
||||
- "Frame Rate: Consistent 60 FPS with <5% drops below 45 FPS on target hardware"
|
||||
- "Crash Rate: <0.5% across iOS/Android, <0.1% on desktop platforms"
|
||||
- 'Frame Rate: Consistent 60 FPS with <5% drops below 45 FPS on target hardware'
|
||||
- 'Crash Rate: <0.5% across iOS/Android, <0.1% on desktop platforms'
|
||||
- id: gameplay-metrics
|
||||
title: Gameplay & User Engagement Metrics
|
||||
type: bullet-list
|
||||
@@ -1816,8 +1816,8 @@ sections:
|
||||
- **Gameplay Completion:** {{completion_rate}}% complete main game content
|
||||
- **Control Responsiveness:** Input lag <{{input_lag}}ms on all platforms
|
||||
examples:
|
||||
- "Tutorial Completion: 85% of players complete movement and basic mechanics tutorial"
|
||||
- "Session Duration: Average 15-20 minutes per session for mobile, 30-45 minutes for desktop"
|
||||
- 'Tutorial Completion: 85% of players complete movement and basic mechanics tutorial'
|
||||
- 'Session Duration: Average 15-20 minutes per session for mobile, 30-45 minutes for desktop'
|
||||
- id: platform-specific-metrics
|
||||
title: Platform-Specific Quality Metrics
|
||||
type: table
|
||||
@@ -1862,17 +1862,17 @@ sections:
|
||||
- Consider cross-platform testing requirements
|
||||
- Account for Unity build and deployment steps
|
||||
examples:
|
||||
- "Foundation stories: Individual Unity systems (Input, Audio, Scene Management) - 1-2 days each"
|
||||
- "Feature stories: Complete gameplay mechanics with UI and feedback - 2-4 days each"
|
||||
- 'Foundation stories: Individual Unity systems (Input, Audio, Scene Management) - 1-2 days each'
|
||||
- 'Feature stories: Complete gameplay mechanics with UI and feedback - 2-4 days each'
|
||||
- id: recommended-agents
|
||||
title: Recommended BMad Agent Sequence
|
||||
type: numbered-list
|
||||
template: |
|
||||
1. **{{agent_name}}**: {{agent_responsibility}}
|
||||
examples:
|
||||
- "Unity Architect: Create detailed technical architecture document with specific Unity implementation patterns"
|
||||
- "Unity Developer: Implement core systems and gameplay mechanics according to architecture"
|
||||
- "QA Tester: Validate performance metrics and cross-platform functionality"
|
||||
- 'Unity Architect: Create detailed technical architecture document with specific Unity implementation patterns'
|
||||
- 'Unity Developer: Implement core systems and gameplay mechanics according to architecture'
|
||||
- 'QA Tester: Validate performance metrics and cross-platform functionality'
|
||||
==================== END: .bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml ====================
|
||||
|
||||
==================== START: .bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml ====================
|
||||
@@ -1883,7 +1883,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/level-design-document.md
|
||||
title: "{{game_title}} Level Design Document"
|
||||
title: '{{game_title}} Level Design Document'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -1944,7 +1944,7 @@ sections:
|
||||
repeatable: true
|
||||
sections:
|
||||
- id: level-category
|
||||
title: "{{category_name}} Levels"
|
||||
title: '{{category_name}} Levels'
|
||||
template: |
|
||||
**Purpose:** {{gameplay_purpose}}
|
||||
|
||||
@@ -2370,7 +2370,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/game-brief.md
|
||||
title: "{{game_title}} Game Brief"
|
||||
title: '{{game_title}} Game Brief'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -2656,21 +2656,21 @@ sections:
|
||||
title: Development Roadmap
|
||||
sections:
|
||||
- id: phase-1-preproduction
|
||||
title: "Phase 1: Pre-Production ({{duration}})"
|
||||
title: 'Phase 1: Pre-Production ({{duration}})'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Detailed Game Design Document creation
|
||||
- Technical architecture planning
|
||||
- Art style exploration and pipeline setup
|
||||
- id: phase-2-prototype
|
||||
title: "Phase 2: Prototype ({{duration}})"
|
||||
title: 'Phase 2: Prototype ({{duration}})'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Core mechanic implementation
|
||||
- Technical proof of concept
|
||||
- Initial playtesting and iteration
|
||||
- id: phase-3-production
|
||||
title: "Phase 3: Production ({{duration}})"
|
||||
title: 'Phase 3: Production ({{duration}})'
|
||||
type: bullet-list
|
||||
template: |
|
||||
- Full feature development
|
||||
|
||||
@@ -514,8 +514,8 @@ template:
|
||||
version: 3.0
|
||||
output:
|
||||
format: markdown
|
||||
filename: "stories/{{epic_name}}/{{story_id}}-{{story_name}}.md"
|
||||
title: "Story: {{story_title}}"
|
||||
filename: 'stories/{{epic_name}}/{{story_id}}-{{story_name}}.md'
|
||||
title: 'Story: {{story_title}}'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -544,7 +544,7 @@ sections:
|
||||
- id: description
|
||||
title: Description
|
||||
instruction: Provide a clear, concise description of what this story implements. Focus on the specific game feature or system being built. Reference the GDD section that defines this feature.
|
||||
template: "{{clear_description_of_what_needs_to_be_implemented}}"
|
||||
template: '{{clear_description_of_what_needs_to_be_implemented}}'
|
||||
|
||||
- id: acceptance-criteria
|
||||
title: Acceptance Criteria
|
||||
@@ -554,7 +554,7 @@ sections:
|
||||
title: Functional Requirements
|
||||
type: checklist
|
||||
items:
|
||||
- "{{specific_functional_requirement}}"
|
||||
- '{{specific_functional_requirement}}'
|
||||
- id: technical-requirements
|
||||
title: Technical Requirements
|
||||
type: checklist
|
||||
@@ -562,14 +562,14 @@ sections:
|
||||
- Code follows C# best practices
|
||||
- Maintains stable frame rate on target devices
|
||||
- No memory leaks or performance degradation
|
||||
- "{{specific_technical_requirement}}"
|
||||
- '{{specific_technical_requirement}}'
|
||||
- id: game-design-requirements
|
||||
title: Game Design Requirements
|
||||
type: checklist
|
||||
items:
|
||||
- "{{gameplay_requirement_from_gdd}}"
|
||||
- "{{balance_requirement_if_applicable}}"
|
||||
- "{{player_experience_requirement}}"
|
||||
- '{{gameplay_requirement_from_gdd}}'
|
||||
- '{{balance_requirement_if_applicable}}'
|
||||
- '{{player_experience_requirement}}'
|
||||
|
||||
- id: technical-specifications
|
||||
title: Technical Specifications
|
||||
@@ -744,7 +744,7 @@ sections:
|
||||
- Performance targets met
|
||||
- No C# compiler errors or warnings
|
||||
- Documentation updated
|
||||
- "{{game_specific_dod_item}}"
|
||||
- '{{game_specific_dod_item}}'
|
||||
|
||||
- id: notes
|
||||
title: Notes
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -530,23 +530,23 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/infrastructure-architecture.md
|
||||
title: "{{project_name}} Infrastructure Architecture"
|
||||
title: '{{project_name}} Infrastructure Architecture'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
elicitation: advanced-elicitation
|
||||
custom_elicitation:
|
||||
title: "Infrastructure Architecture Elicitation Actions"
|
||||
title: 'Infrastructure Architecture Elicitation Actions'
|
||||
sections:
|
||||
- id: infrastructure-overview
|
||||
options:
|
||||
- "Multi-Cloud Strategy Analysis - Evaluate cloud provider options and vendor lock-in considerations"
|
||||
- "Regional Distribution Planning - Analyze latency requirements and data residency needs"
|
||||
- "Environment Isolation Strategy - Design security boundaries and resource segregation"
|
||||
- "Scalability Patterns Review - Assess auto-scaling needs and traffic patterns"
|
||||
- "Compliance Requirements Analysis - Review regulatory and security compliance needs"
|
||||
- "Cost-Benefit Analysis - Compare infrastructure options and TCO"
|
||||
- "Proceed to next section"
|
||||
- 'Multi-Cloud Strategy Analysis - Evaluate cloud provider options and vendor lock-in considerations'
|
||||
- 'Regional Distribution Planning - Analyze latency requirements and data residency needs'
|
||||
- 'Environment Isolation Strategy - Design security boundaries and resource segregation'
|
||||
- 'Scalability Patterns Review - Assess auto-scaling needs and traffic patterns'
|
||||
- 'Compliance Requirements Analysis - Review regulatory and security compliance needs'
|
||||
- 'Cost-Benefit Analysis - Compare infrastructure options and TCO'
|
||||
- 'Proceed to next section'
|
||||
|
||||
sections:
|
||||
- id: initial-setup
|
||||
@@ -606,7 +606,7 @@ sections:
|
||||
sections:
|
||||
- id: environments
|
||||
repeatable: true
|
||||
title: "{{environment_name}} Environment"
|
||||
title: '{{environment_name}} Environment'
|
||||
template: |
|
||||
- **Purpose:** {{environment_purpose}}
|
||||
- **Resources:** {{environment_resources}}
|
||||
@@ -957,24 +957,24 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/platform-infrastructure/platform-implementation.md
|
||||
title: "{{project_name}} Platform Infrastructure Implementation"
|
||||
title: '{{project_name}} Platform Infrastructure Implementation'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
elicitation: advanced-elicitation
|
||||
custom_elicitation:
|
||||
title: "Platform Implementation Elicitation Actions"
|
||||
title: 'Platform Implementation Elicitation Actions'
|
||||
sections:
|
||||
- id: foundation-infrastructure
|
||||
options:
|
||||
- "Platform Layer Security Hardening - Additional security controls and compliance validation"
|
||||
- "Performance Optimization - Network and resource optimization"
|
||||
- "Operational Excellence Enhancement - Automation and monitoring improvements"
|
||||
- "Platform Integration Validation - Verify foundation supports upper layers"
|
||||
- "Developer Experience Analysis - Foundation impact on developer workflows"
|
||||
- "Disaster Recovery Testing - Foundation resilience validation"
|
||||
- "BMAD Workflow Integration - Cross-agent support verification"
|
||||
- "Finalize and Proceed to Container Platform"
|
||||
- 'Platform Layer Security Hardening - Additional security controls and compliance validation'
|
||||
- 'Performance Optimization - Network and resource optimization'
|
||||
- 'Operational Excellence Enhancement - Automation and monitoring improvements'
|
||||
- 'Platform Integration Validation - Verify foundation supports upper layers'
|
||||
- 'Developer Experience Analysis - Foundation impact on developer workflows'
|
||||
- 'Disaster Recovery Testing - Foundation resilience validation'
|
||||
- 'BMAD Workflow Integration - Cross-agent support verification'
|
||||
- 'Finalize and Proceed to Container Platform'
|
||||
|
||||
sections:
|
||||
- id: initial-setup
|
||||
|
||||
858
dist/teams/team-all.txt
vendored
858
dist/teams/team-all.txt
vendored
File diff suppressed because it is too large
Load Diff
842
dist/teams/team-fullstack.txt
vendored
842
dist/teams/team-fullstack.txt
vendored
File diff suppressed because it is too large
Load Diff
18
dist/teams/team-ide-minimal.txt
vendored
18
dist/teams/team-ide-minimal.txt
vendored
@@ -2246,7 +2246,7 @@ template:
|
||||
output:
|
||||
format: markdown
|
||||
filename: docs/stories/{{epic_num}}.{{story_num}}.{{story_title_short}}.md
|
||||
title: "Story {{epic_num}}.{{story_num}}: {{story_title_short}}"
|
||||
title: 'Story {{epic_num}}.{{story_num}}: {{story_title_short}}'
|
||||
|
||||
workflow:
|
||||
mode: interactive
|
||||
@@ -2348,7 +2348,7 @@ sections:
|
||||
sections:
|
||||
- id: agent-model
|
||||
title: Agent Model Used
|
||||
template: "{{agent_model_name_version}}"
|
||||
template: '{{agent_model_name_version}}'
|
||||
instruction: Record the specific AI agent model and version used for development
|
||||
owner: dev-agent
|
||||
editors: [dev-agent]
|
||||
@@ -4999,16 +4999,16 @@ template:
|
||||
output:
|
||||
format: yaml
|
||||
filename: docs/qa/gates/{{epic_num}}.{{story_num}}-{{story_slug}}.yml
|
||||
title: "Quality Gate: {{epic_num}}.{{story_num}}"
|
||||
title: 'Quality Gate: {{epic_num}}.{{story_num}}'
|
||||
|
||||
# Required fields (keep these first)
|
||||
schema: 1
|
||||
story: "{{epic_num}}.{{story_num}}"
|
||||
story_title: "{{story_title}}"
|
||||
gate: "{{gate_status}}" # PASS|CONCERNS|FAIL|WAIVED
|
||||
status_reason: "{{status_reason}}" # 1-2 sentence summary of why this gate decision
|
||||
reviewer: "Quinn (Test Architect)"
|
||||
updated: "{{iso_timestamp}}"
|
||||
story: '{{epic_num}}.{{story_num}}'
|
||||
story_title: '{{story_title}}'
|
||||
gate: '{{gate_status}}' # PASS|CONCERNS|FAIL|WAIVED
|
||||
status_reason: '{{status_reason}}' # 1-2 sentence summary of why this gate decision
|
||||
reviewer: 'Quinn (Test Architect)'
|
||||
updated: '{{iso_timestamp}}'
|
||||
|
||||
# Always present but only active when WAIVED
|
||||
waiver: { active: false }
|
||||
|
||||
640
dist/teams/team-no-ui.txt
vendored
640
dist/teams/team-no-ui.txt
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,147 +1,77 @@
|
||||
# Versioning and Releases
|
||||
# How to Release a New Version
|
||||
|
||||
BMad Method uses a simplified release system with manual control and automatic release notes generation.
|
||||
## Automated Releases (Recommended)
|
||||
|
||||
## 🚀 Release Workflow
|
||||
The easiest way to release new versions is through **automatic semantic releases**. Just commit with the right message format and push and everything else happens automatically.
|
||||
|
||||
### Command Line Release (Recommended)
|
||||
### Commit Message Format
|
||||
|
||||
The fastest way to create a release with beautiful release notes:
|
||||
Use these prefixes to control what type of release happens:
|
||||
|
||||
```bash
|
||||
# Preview what will be in the release
|
||||
npm run preview:release
|
||||
|
||||
# Create a release
|
||||
npm run release:patch # 5.1.0 → 5.1.1 (bug fixes)
|
||||
npm run release:minor # 5.1.0 → 5.2.0 (new features)
|
||||
npm run release:major # 5.1.0 → 6.0.0 (breaking changes)
|
||||
|
||||
# Watch the release process
|
||||
npm run release:watch
|
||||
fix: resolve CLI argument parsing bug # → patch release (4.1.0 → 4.1.1)
|
||||
feat: add new agent orchestration mode # → minor release (4.1.0 → 4.2.0)
|
||||
feat!: redesign CLI interface # → major release (4.1.0 → 5.0.0)
|
||||
```
|
||||
|
||||
### One-Liner Release
|
||||
### What Happens Automatically
|
||||
|
||||
When you push commits with `fix:` or `feat:`, GitHub Actions will:
|
||||
|
||||
1. ✅ Analyze your commit messages
|
||||
2. ✅ Bump version in `package.json`
|
||||
3. ✅ Generate changelog
|
||||
4. ✅ Create git tag
|
||||
5. ✅ **Publish to NPM automatically**
|
||||
6. ✅ Create GitHub release with notes
|
||||
|
||||
### Your Simple Workflow
|
||||
|
||||
```bash
|
||||
npm run preview:release && npm run release:minor && npm run release:watch
|
||||
# Make your changes
|
||||
git add .
|
||||
git commit -m "feat: add team collaboration mode"
|
||||
git push
|
||||
|
||||
# That's it! Release happens automatically 🎉
|
||||
# Users can now run: npx bmad-method (and get the new version)
|
||||
```
|
||||
|
||||
## 📝 What Happens Automatically
|
||||
### Commits That DON'T Trigger Releases
|
||||
|
||||
When you trigger a release, the GitHub Actions workflow automatically:
|
||||
|
||||
1. ✅ **Validates** - Runs tests, linting, and formatting checks
|
||||
2. ✅ **Bumps Version** - Updates `package.json` and installer version
|
||||
3. ✅ **Generates Release Notes** - Categorizes commits since last release:
|
||||
- ✨ **New Features** (`feat:`, `Feature:`)
|
||||
- 🐛 **Bug Fixes** (`fix:`, `Fix:`)
|
||||
- 🔧 **Maintenance** (`chore:`, `Chore:`)
|
||||
- 📦 **Other Changes** (everything else)
|
||||
4. ✅ **Creates Git Tag** - Tags the release version
|
||||
5. ✅ **Publishes to NPM** - With `@latest` tag for user installations
|
||||
6. ✅ **Creates GitHub Release** - With formatted release notes
|
||||
|
||||
## 📋 Sample Release Notes
|
||||
|
||||
The workflow automatically generates professional release notes like this:
|
||||
|
||||
````markdown
|
||||
## 🚀 What's New in v5.2.0
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
- feat: add team collaboration mode
|
||||
- feat: enhance CLI with interactive prompts
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- fix: resolve installation path issues
|
||||
- fix: handle edge cases in agent loading
|
||||
|
||||
### 🔧 Maintenance
|
||||
|
||||
- chore: update dependencies
|
||||
- chore: improve error messages
|
||||
|
||||
## 📦 Installation
|
||||
These commit types won't create releases (use them for maintenance):
|
||||
|
||||
```bash
|
||||
npx bmad-method install
|
||||
chore: update dependencies # No release
|
||||
docs: fix typo in readme # No release
|
||||
style: format code # No release
|
||||
test: add unit tests # No release
|
||||
```
|
||||
````
|
||||
|
||||
**Full Changelog**: https://github.com/bmadcode/BMAD-METHOD/compare/v5.1.0...v5.2.0
|
||||
|
||||
````
|
||||
|
||||
## 🎯 User Installation
|
||||
|
||||
After any release, users can immediately get the new version with:
|
||||
### Test Your Setup
|
||||
|
||||
```bash
|
||||
npx bmad-method install # Always gets latest release
|
||||
npm run release:test # Safe to run locally - tests the config
|
||||
```
|
||||
|
||||
## 📊 Preview Before Release
|
||||
---
|
||||
|
||||
Always preview what will be included in your release:
|
||||
## Manual Release Methods (Exceptions Only)
|
||||
|
||||
⚠️ Only use these methods if you need to bypass the automatic system
|
||||
|
||||
### Quick Manual Version Bump
|
||||
|
||||
```bash
|
||||
npm run preview:release
|
||||
npm run version:patch # 4.1.0 → 4.1.1 (bug fixes)
|
||||
npm run version:minor # 4.1.0 → 4.2.0 (new features)
|
||||
npm run version:major # 4.1.0 → 5.0.0 (breaking changes)
|
||||
|
||||
# Then manually publish:
|
||||
npm publish
|
||||
git push && git push --tags
|
||||
```
|
||||
|
||||
This shows:
|
||||
### Manual GitHub Actions Trigger
|
||||
|
||||
- Commits since last release
|
||||
- Categorized changes
|
||||
- Estimated next version
|
||||
- Release notes preview
|
||||
|
||||
## 🔧 Manual Release (GitHub UI)
|
||||
|
||||
You can also trigger releases through GitHub Actions:
|
||||
|
||||
1. Go to **GitHub Actions** → **Manual Release**
|
||||
2. Click **"Run workflow"**
|
||||
3. Choose version bump type (patch/minor/major)
|
||||
4. Everything else happens automatically
|
||||
|
||||
## 📈 Version Strategy
|
||||
|
||||
- **Patch** (5.1.0 → 5.1.1): Bug fixes, minor improvements
|
||||
- **Minor** (5.1.0 → 5.2.0): New features, enhancements
|
||||
- **Major** (5.1.0 → 6.0.0): Breaking changes, major redesigns
|
||||
|
||||
## 🛠️ Development Workflow
|
||||
|
||||
1. **Develop Freely** - Merge PRs to main without triggering releases
|
||||
2. **Test Unreleased Changes** - Clone repo to test latest main branch
|
||||
3. **Release When Ready** - Use command line or GitHub Actions to cut releases
|
||||
4. **Users Get Updates** - Via simple `npx bmad-method install` command
|
||||
|
||||
This gives you complete control over when releases happen while automating all the tedious parts like version bumping, release notes, and publishing.
|
||||
|
||||
## 🔍 Troubleshooting
|
||||
|
||||
### Check Release Status
|
||||
|
||||
```bash
|
||||
gh run list --workflow="Manual Release"
|
||||
npm view bmad-method dist-tags
|
||||
git tag -l | sort -V | tail -5
|
||||
```
|
||||
|
||||
### View Latest Release
|
||||
|
||||
```bash
|
||||
gh release view --web
|
||||
npm view bmad-method versions --json
|
||||
```
|
||||
|
||||
### If Release Fails
|
||||
|
||||
- Check GitHub Actions logs: `gh run view <run-id> --log-failed`
|
||||
- Verify NPM tokens are configured
|
||||
- Ensure branch protection allows workflow pushes
|
||||
````
|
||||
You can also trigger releases manually through GitHub Actions workflow dispatch if needed.
|
||||
|
||||
15
package.json
15
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "bmad-method",
|
||||
"version": "5.0.1",
|
||||
"version": "5.0.0",
|
||||
"description": "Breakthrough Method of Agile AI-driven Development",
|
||||
"keywords": [
|
||||
"agile",
|
||||
@@ -35,11 +35,8 @@
|
||||
"lint:fix": "eslint . --ext .js,.cjs,.mjs,.yaml --fix",
|
||||
"list:agents": "node tools/cli.js list:agents",
|
||||
"prepare": "husky",
|
||||
"preview:release": "node tools/preview-release-notes.js",
|
||||
"release:major": "gh workflow run \"Manual Release\" -f version_bump=major",
|
||||
"release:minor": "gh workflow run \"Manual Release\" -f version_bump=minor",
|
||||
"release:patch": "gh workflow run \"Manual Release\" -f version_bump=patch",
|
||||
"release:watch": "gh run watch",
|
||||
"release": "semantic-release",
|
||||
"release:test": "semantic-release --dry-run --no-ci || echo 'Config test complete - authentication errors are expected locally'",
|
||||
"validate": "node tools/cli.js validate",
|
||||
"version:all": "node tools/bump-all-versions.js",
|
||||
"version:all:major": "node tools/bump-all-versions.js major",
|
||||
@@ -83,6 +80,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.33.0",
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-n": "^17.21.3",
|
||||
@@ -93,13 +92,11 @@
|
||||
"lint-staged": "^16.1.1",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-packagejson": "^2.5.19",
|
||||
"semantic-release": "^22.0.0",
|
||||
"yaml-eslint-parser": "^1.2.3",
|
||||
"yaml-lint": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.10.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,9 @@ program
|
||||
// Ensure output directory exists
|
||||
await fs.ensureDir(path.dirname(outputPath));
|
||||
|
||||
console.log(`Flattening codebase from: ${inputDir}`);
|
||||
console.log(`Output file: ${outputPath}`);
|
||||
|
||||
try {
|
||||
// Verify input directory exists
|
||||
if (!(await fs.pathExists(inputDir))) {
|
||||
@@ -155,6 +158,10 @@ program
|
||||
if (aggregatedContent.errors.length > 0) {
|
||||
console.log(`Errors: ${aggregatedContent.errors.length}`);
|
||||
}
|
||||
console.log(`Text files: ${aggregatedContent.textFiles.length}`);
|
||||
if (aggregatedContent.binaryFiles.length > 0) {
|
||||
console.log(`Binary files: ${aggregatedContent.binaryFiles.length}`);
|
||||
}
|
||||
|
||||
// Generate XML output using streaming
|
||||
const xmlSpinner = ora('🔧 Generating XML output...').start();
|
||||
@@ -163,7 +170,7 @@ program
|
||||
|
||||
// Calculate and display statistics
|
||||
const outputStats = await fs.stat(outputPath);
|
||||
const stats = await calculateStatistics(aggregatedContent, outputStats.size, inputDir);
|
||||
const stats = calculateStatistics(aggregatedContent, outputStats.size);
|
||||
|
||||
// Display completion summary
|
||||
console.log('\n📊 Completion Summary:');
|
||||
@@ -176,389 +183,8 @@ program
|
||||
console.log(`📝 Total lines of code: ${stats.totalLines.toLocaleString()}`);
|
||||
console.log(`🔢 Estimated tokens: ${stats.estimatedTokens}`);
|
||||
console.log(
|
||||
`📊 File breakdown: ${stats.textFiles} text, ${stats.binaryFiles} binary, ${stats.errorFiles} errors\n`,
|
||||
`📊 File breakdown: ${stats.textFiles} text, ${stats.binaryFiles} binary, ${stats.errorFiles} errors`,
|
||||
);
|
||||
|
||||
// Ask user if they want detailed stats + markdown report
|
||||
const generateDetailed = await promptYesNo(
|
||||
'Generate detailed stats (console + markdown) now?',
|
||||
true,
|
||||
);
|
||||
|
||||
if (generateDetailed) {
|
||||
// Additional detailed stats
|
||||
console.log('\n📈 Size Percentiles:');
|
||||
console.log(
|
||||
` Avg: ${Math.round(stats.avgFileSize).toLocaleString()} B, Median: ${Math.round(
|
||||
stats.medianFileSize,
|
||||
).toLocaleString()} B, p90: ${stats.p90.toLocaleString()} B, p95: ${stats.p95.toLocaleString()} B, p99: ${stats.p99.toLocaleString()} B`,
|
||||
);
|
||||
|
||||
if (Array.isArray(stats.histogram) && stats.histogram.length > 0) {
|
||||
console.log('\n🧮 Size Histogram:');
|
||||
for (const b of stats.histogram.slice(0, 2)) {
|
||||
console.log(` ${b.label}: ${b.count} files, ${b.bytes.toLocaleString()} bytes`);
|
||||
}
|
||||
if (stats.histogram.length > 2) {
|
||||
console.log(` … and ${stats.histogram.length - 2} more buckets`);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(stats.byExtension) && stats.byExtension.length > 0) {
|
||||
const topExt = stats.byExtension.slice(0, 2);
|
||||
console.log('\n📦 Top Extensions:');
|
||||
for (const e of topExt) {
|
||||
const pct = stats.totalBytes ? (e.bytes / stats.totalBytes) * 100 : 0;
|
||||
console.log(
|
||||
` ${e.ext}: ${e.count} files, ${e.bytes.toLocaleString()} bytes (${pct.toFixed(
|
||||
2,
|
||||
)}%)`,
|
||||
);
|
||||
}
|
||||
if (stats.byExtension.length > 2) {
|
||||
console.log(` … and ${stats.byExtension.length - 2} more extensions`);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(stats.byDirectory) && stats.byDirectory.length > 0) {
|
||||
const topDir = stats.byDirectory.slice(0, 2);
|
||||
console.log('\n📂 Top Directories:');
|
||||
for (const d of topDir) {
|
||||
const pct = stats.totalBytes ? (d.bytes / stats.totalBytes) * 100 : 0;
|
||||
console.log(
|
||||
` ${d.dir}: ${d.count} files, ${d.bytes.toLocaleString()} bytes (${pct.toFixed(
|
||||
2,
|
||||
)}%)`,
|
||||
);
|
||||
}
|
||||
if (stats.byDirectory.length > 2) {
|
||||
console.log(` … and ${stats.byDirectory.length - 2} more directories`);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(stats.depthDistribution) && stats.depthDistribution.length > 0) {
|
||||
console.log('\n🌳 Depth Distribution:');
|
||||
const dd = stats.depthDistribution.slice(0, 2);
|
||||
let line = ' ' + dd.map((d) => `${d.depth}:${d.count}`).join(' ');
|
||||
if (stats.depthDistribution.length > 2) {
|
||||
line += ` … +${stats.depthDistribution.length - 2} more`;
|
||||
}
|
||||
console.log(line);
|
||||
}
|
||||
|
||||
if (Array.isArray(stats.longestPaths) && stats.longestPaths.length > 0) {
|
||||
console.log('\n🧵 Longest Paths:');
|
||||
for (const p of stats.longestPaths.slice(0, 2)) {
|
||||
console.log(` ${p.path} (${p.length} chars, ${p.size.toLocaleString()} bytes)`);
|
||||
}
|
||||
if (stats.longestPaths.length > 2) {
|
||||
console.log(` … and ${stats.longestPaths.length - 2} more paths`);
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.temporal) {
|
||||
console.log('\n⏱️ Temporal:');
|
||||
if (stats.temporal.oldest) {
|
||||
console.log(
|
||||
` Oldest: ${stats.temporal.oldest.path} (${stats.temporal.oldest.mtime})`,
|
||||
);
|
||||
}
|
||||
if (stats.temporal.newest) {
|
||||
console.log(
|
||||
` Newest: ${stats.temporal.newest.path} (${stats.temporal.newest.mtime})`,
|
||||
);
|
||||
}
|
||||
if (Array.isArray(stats.temporal.ageBuckets)) {
|
||||
console.log(' Age buckets:');
|
||||
for (const b of stats.temporal.ageBuckets.slice(0, 2)) {
|
||||
console.log(` ${b.label}: ${b.count} files, ${b.bytes.toLocaleString()} bytes`);
|
||||
}
|
||||
if (stats.temporal.ageBuckets.length > 2) {
|
||||
console.log(` … and ${stats.temporal.ageBuckets.length - 2} more buckets`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.quality) {
|
||||
console.log('\n✅ Quality Signals:');
|
||||
console.log(` Zero-byte files: ${stats.quality.zeroByteFiles}`);
|
||||
console.log(` Empty text files: ${stats.quality.emptyTextFiles}`);
|
||||
console.log(` Hidden files: ${stats.quality.hiddenFiles}`);
|
||||
console.log(` Symlinks: ${stats.quality.symlinks}`);
|
||||
console.log(
|
||||
` Large files (>= ${(stats.quality.largeThreshold / (1024 * 1024)).toFixed(
|
||||
0,
|
||||
)} MB): ${stats.quality.largeFilesCount}`,
|
||||
);
|
||||
console.log(
|
||||
` Suspiciously large files (>= 100 MB): ${stats.quality.suspiciousLargeFilesCount}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(stats.duplicateCandidates) && stats.duplicateCandidates.length > 0) {
|
||||
console.log('\n🧬 Duplicate Candidates:');
|
||||
for (const d of stats.duplicateCandidates.slice(0, 2)) {
|
||||
console.log(` ${d.reason}: ${d.count} files @ ${d.size.toLocaleString()} bytes`);
|
||||
}
|
||||
if (stats.duplicateCandidates.length > 2) {
|
||||
console.log(` … and ${stats.duplicateCandidates.length - 2} more groups`);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof stats.compressibilityRatio === 'number') {
|
||||
console.log(
|
||||
`\n🗜️ Compressibility ratio (sampled): ${(stats.compressibilityRatio * 100).toFixed(
|
||||
2,
|
||||
)}%`,
|
||||
);
|
||||
}
|
||||
|
||||
if (stats.git && stats.git.isRepo) {
|
||||
console.log('\n🔧 Git:');
|
||||
console.log(
|
||||
` Tracked: ${stats.git.trackedCount} files, ${stats.git.trackedBytes.toLocaleString()} bytes`,
|
||||
);
|
||||
console.log(
|
||||
` Untracked: ${stats.git.untrackedCount} files, ${stats.git.untrackedBytes.toLocaleString()} bytes`,
|
||||
);
|
||||
if (Array.isArray(stats.git.lfsCandidates) && stats.git.lfsCandidates.length > 0) {
|
||||
console.log(' LFS candidates (top 2):');
|
||||
for (const f of stats.git.lfsCandidates.slice(0, 2)) {
|
||||
console.log(` ${f.path} (${f.size.toLocaleString()} bytes)`);
|
||||
}
|
||||
if (stats.git.lfsCandidates.length > 2) {
|
||||
console.log(` … and ${stats.git.lfsCandidates.length - 2} more`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(stats.largestFiles) && stats.largestFiles.length > 0) {
|
||||
console.log('\n📚 Largest Files (top 2):');
|
||||
for (const f of stats.largestFiles.slice(0, 2)) {
|
||||
// Show LOC for text files when available; omit ext and mtime
|
||||
let locStr = '';
|
||||
if (!f.isBinary && Array.isArray(aggregatedContent?.textFiles)) {
|
||||
const tf = aggregatedContent.textFiles.find((t) => t.path === f.path);
|
||||
if (tf && typeof tf.lines === 'number') {
|
||||
locStr = `, LOC: ${tf.lines.toLocaleString()}`;
|
||||
}
|
||||
}
|
||||
console.log(
|
||||
` ${f.path} – ${f.sizeFormatted} (${f.percentOfTotal.toFixed(2)}%)${locStr}`,
|
||||
);
|
||||
}
|
||||
if (stats.largestFiles.length > 2) {
|
||||
console.log(` … and ${stats.largestFiles.length - 2} more files`);
|
||||
}
|
||||
}
|
||||
|
||||
// Write a comprehensive markdown report next to the XML
|
||||
{
|
||||
const mdPath = outputPath.endsWith('.xml')
|
||||
? outputPath.replace(/\.xml$/i, '.stats.md')
|
||||
: outputPath + '.stats.md';
|
||||
try {
|
||||
const pct = (num, den) => (den ? (num / den) * 100 : 0);
|
||||
const md = [];
|
||||
md.push(
|
||||
`# 🧾 Flatten Stats for ${path.basename(outputPath)}`,
|
||||
'',
|
||||
'## 📊 Summary',
|
||||
`- Total source size: ${stats.totalSize}`,
|
||||
`- Generated XML size: ${stats.xmlSize}`,
|
||||
`- Total lines of code: ${stats.totalLines.toLocaleString()}`,
|
||||
`- Estimated tokens: ${stats.estimatedTokens}`,
|
||||
`- File breakdown: ${stats.textFiles} text, ${stats.binaryFiles} binary, ${stats.errorFiles} errors`,
|
||||
'',
|
||||
'## 📈 Size Percentiles',
|
||||
`Avg: ${Math.round(stats.avgFileSize).toLocaleString()} B, Median: ${Math.round(
|
||||
stats.medianFileSize,
|
||||
).toLocaleString()} B, p90: ${stats.p90.toLocaleString()} B, p95: ${stats.p95.toLocaleString()} B, p99: ${stats.p99.toLocaleString()} B`,
|
||||
'',
|
||||
);
|
||||
|
||||
// Histogram
|
||||
if (Array.isArray(stats.histogram) && stats.histogram.length > 0) {
|
||||
md.push(
|
||||
'## 🧮 Size Histogram',
|
||||
'| Bucket | Files | Bytes |',
|
||||
'| --- | ---: | ---: |',
|
||||
);
|
||||
for (const b of stats.histogram) {
|
||||
md.push(`| ${b.label} | ${b.count} | ${b.bytes.toLocaleString()} |`);
|
||||
}
|
||||
md.push('');
|
||||
}
|
||||
|
||||
// Top Extensions
|
||||
if (Array.isArray(stats.byExtension) && stats.byExtension.length > 0) {
|
||||
md.push(
|
||||
'## 📦 Top Extensions by Bytes (Top 20)',
|
||||
'| Ext | Files | Bytes | % of total |',
|
||||
'| --- | ---: | ---: | ---: |',
|
||||
);
|
||||
for (const e of stats.byExtension.slice(0, 20)) {
|
||||
const p = pct(e.bytes, stats.totalBytes);
|
||||
md.push(
|
||||
`| ${e.ext} | ${e.count} | ${e.bytes.toLocaleString()} | ${p.toFixed(2)}% |`,
|
||||
);
|
||||
}
|
||||
md.push('');
|
||||
}
|
||||
|
||||
// Top Directories
|
||||
if (Array.isArray(stats.byDirectory) && stats.byDirectory.length > 0) {
|
||||
md.push(
|
||||
'## 📂 Top Directories by Bytes (Top 20)',
|
||||
'| Directory | Files | Bytes | % of total |',
|
||||
'| --- | ---: | ---: | ---: |',
|
||||
);
|
||||
for (const d of stats.byDirectory.slice(0, 20)) {
|
||||
const p = pct(d.bytes, stats.totalBytes);
|
||||
md.push(
|
||||
`| ${d.dir} | ${d.count} | ${d.bytes.toLocaleString()} | ${p.toFixed(2)}% |`,
|
||||
);
|
||||
}
|
||||
md.push('');
|
||||
}
|
||||
|
||||
// Depth distribution
|
||||
if (Array.isArray(stats.depthDistribution) && stats.depthDistribution.length > 0) {
|
||||
md.push('## 🌳 Depth Distribution', '| Depth | Count |', '| ---: | ---: |');
|
||||
for (const d of stats.depthDistribution) {
|
||||
md.push(`| ${d.depth} | ${d.count} |`);
|
||||
}
|
||||
md.push('');
|
||||
}
|
||||
|
||||
// Longest paths
|
||||
if (Array.isArray(stats.longestPaths) && stats.longestPaths.length > 0) {
|
||||
md.push(
|
||||
'## 🧵 Longest Paths (Top 25)',
|
||||
'| Path | Length | Bytes |',
|
||||
'| --- | ---: | ---: |',
|
||||
);
|
||||
for (const pth of stats.longestPaths) {
|
||||
md.push(`| ${pth.path} | ${pth.length} | ${pth.size.toLocaleString()} |`);
|
||||
}
|
||||
md.push('');
|
||||
}
|
||||
|
||||
// Temporal
|
||||
if (stats.temporal) {
|
||||
md.push('## ⏱️ Temporal');
|
||||
if (stats.temporal.oldest) {
|
||||
md.push(`- Oldest: ${stats.temporal.oldest.path} (${stats.temporal.oldest.mtime})`);
|
||||
}
|
||||
if (stats.temporal.newest) {
|
||||
md.push(`- Newest: ${stats.temporal.newest.path} (${stats.temporal.newest.mtime})`);
|
||||
}
|
||||
if (Array.isArray(stats.temporal.ageBuckets)) {
|
||||
md.push('', '| Age | Files | Bytes |', '| --- | ---: | ---: |');
|
||||
for (const b of stats.temporal.ageBuckets) {
|
||||
md.push(`| ${b.label} | ${b.count} | ${b.bytes.toLocaleString()} |`);
|
||||
}
|
||||
}
|
||||
md.push('');
|
||||
}
|
||||
|
||||
// Quality signals
|
||||
if (stats.quality) {
|
||||
md.push(
|
||||
'## ✅ Quality Signals',
|
||||
`- Zero-byte files: ${stats.quality.zeroByteFiles}`,
|
||||
`- Empty text files: ${stats.quality.emptyTextFiles}`,
|
||||
`- Hidden files: ${stats.quality.hiddenFiles}`,
|
||||
`- Symlinks: ${stats.quality.symlinks}`,
|
||||
`- Large files (>= ${(stats.quality.largeThreshold / (1024 * 1024)).toFixed(0)} MB): ${stats.quality.largeFilesCount}`,
|
||||
`- Suspiciously large files (>= 100 MB): ${stats.quality.suspiciousLargeFilesCount}`,
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
// Duplicates
|
||||
if (Array.isArray(stats.duplicateCandidates) && stats.duplicateCandidates.length > 0) {
|
||||
md.push(
|
||||
'## 🧬 Duplicate Candidates',
|
||||
'| Reason | Files | Size (bytes) |',
|
||||
'| --- | ---: | ---: |',
|
||||
);
|
||||
for (const d of stats.duplicateCandidates) {
|
||||
md.push(`| ${d.reason} | ${d.count} | ${d.size.toLocaleString()} |`);
|
||||
}
|
||||
md.push('', '### 🧬 Duplicate Groups Details');
|
||||
let dupIndex = 1;
|
||||
for (const d of stats.duplicateCandidates) {
|
||||
md.push(
|
||||
`#### Group ${dupIndex}: ${d.count} files @ ${d.size.toLocaleString()} bytes (${d.reason})`,
|
||||
);
|
||||
if (Array.isArray(d.files) && d.files.length > 0) {
|
||||
for (const fp of d.files) {
|
||||
md.push(`- ${fp}`);
|
||||
}
|
||||
} else {
|
||||
md.push('- (file list unavailable)');
|
||||
}
|
||||
md.push('');
|
||||
dupIndex++;
|
||||
}
|
||||
md.push('');
|
||||
}
|
||||
|
||||
// Compressibility
|
||||
if (typeof stats.compressibilityRatio === 'number') {
|
||||
md.push(
|
||||
'## 🗜️ Compressibility',
|
||||
`Sampled compressibility ratio: ${(stats.compressibilityRatio * 100).toFixed(2)}%`,
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
// Git
|
||||
if (stats.git && stats.git.isRepo) {
|
||||
md.push(
|
||||
'## 🔧 Git',
|
||||
`- Tracked: ${stats.git.trackedCount} files, ${stats.git.trackedBytes.toLocaleString()} bytes`,
|
||||
`- Untracked: ${stats.git.untrackedCount} files, ${stats.git.untrackedBytes.toLocaleString()} bytes`,
|
||||
);
|
||||
if (Array.isArray(stats.git.lfsCandidates) && stats.git.lfsCandidates.length > 0) {
|
||||
md.push('', '### 📦 LFS Candidates (Top 20)', '| Path | Bytes |', '| --- | ---: |');
|
||||
for (const f of stats.git.lfsCandidates.slice(0, 20)) {
|
||||
md.push(`| ${f.path} | ${f.size.toLocaleString()} |`);
|
||||
}
|
||||
}
|
||||
md.push('');
|
||||
}
|
||||
|
||||
// Largest Files
|
||||
if (Array.isArray(stats.largestFiles) && stats.largestFiles.length > 0) {
|
||||
md.push(
|
||||
'## 📚 Largest Files (Top 50)',
|
||||
'| Path | Size | % of total | LOC |',
|
||||
'| --- | ---: | ---: | ---: |',
|
||||
);
|
||||
for (const f of stats.largestFiles) {
|
||||
let loc = '';
|
||||
if (!f.isBinary && Array.isArray(aggregatedContent?.textFiles)) {
|
||||
const tf = aggregatedContent.textFiles.find((t) => t.path === f.path);
|
||||
if (tf && typeof tf.lines === 'number') {
|
||||
loc = tf.lines.toLocaleString();
|
||||
}
|
||||
}
|
||||
md.push(
|
||||
`| ${f.path} | ${f.sizeFormatted} | ${f.percentOfTotal.toFixed(2)}% | ${loc} |`,
|
||||
);
|
||||
}
|
||||
md.push('');
|
||||
}
|
||||
|
||||
await fs.writeFile(mdPath, md.join('\n'));
|
||||
console.log(`\n🧾 Detailed stats report written to: ${mdPath}`);
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ Failed to write stats markdown: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Critical error:', error.message);
|
||||
console.error('An unexpected error occurred.');
|
||||
|
||||
@@ -1,203 +1,40 @@
|
||||
const fs = require('fs-extra');
|
||||
const path = require('node:path');
|
||||
|
||||
// Deno/Node compatibility: explicitly import process
|
||||
const process = require('node:process');
|
||||
const { execFile } = require('node:child_process');
|
||||
const { promisify } = require('node:util');
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
// Simple memoization across calls (keyed by realpath of startDir)
|
||||
const _cache = new Map();
|
||||
|
||||
async function _tryRun(cmd, args, cwd, timeoutMs = 500) {
|
||||
try {
|
||||
const { stdout } = await execFileAsync(cmd, args, {
|
||||
cwd,
|
||||
timeout: timeoutMs,
|
||||
windowsHide: true,
|
||||
maxBuffer: 1024 * 1024,
|
||||
});
|
||||
const out = String(stdout || '').trim();
|
||||
return out || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function _detectVcsTopLevel(startDir) {
|
||||
// Run common VCS root queries in parallel; ignore failures
|
||||
const gitP = _tryRun('git', ['rev-parse', '--show-toplevel'], startDir);
|
||||
const hgP = _tryRun('hg', ['root'], startDir);
|
||||
const svnP = (async () => {
|
||||
const show = await _tryRun('svn', ['info', '--show-item', 'wc-root'], startDir);
|
||||
if (show) return show;
|
||||
const info = await _tryRun('svn', ['info'], startDir);
|
||||
if (info) {
|
||||
const line = info
|
||||
.split(/\r?\n/)
|
||||
.find((l) => l.toLowerCase().startsWith('working copy root path:'));
|
||||
if (line) return line.split(':').slice(1).join(':').trim();
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
const [git, hg, svn] = await Promise.all([gitP, hgP, svnP]);
|
||||
return git || hg || svn || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find the project root by walking up from startDir.
|
||||
* Uses a robust, prioritized set of ecosystem markers (VCS > workspaces/monorepo > lock/build > language config).
|
||||
* Also recognizes package.json with "workspaces" as a workspace root.
|
||||
* You can augment markers via env PROJECT_ROOT_MARKERS as a comma-separated list of file/dir names.
|
||||
* Attempt to find the project root by walking up from startDir
|
||||
* Looks for common project markers like .git, package.json, pyproject.toml, etc.
|
||||
* @param {string} startDir
|
||||
* @returns {Promise<string|null>} project root directory or null if not found
|
||||
*/
|
||||
async function findProjectRoot(startDir) {
|
||||
try {
|
||||
// Resolve symlinks for robustness (e.g., when invoked from a symlinked path)
|
||||
let dir = path.resolve(startDir);
|
||||
try {
|
||||
dir = await fs.realpath(dir);
|
||||
} catch {
|
||||
// ignore if realpath fails; continue with resolved path
|
||||
}
|
||||
const startKey = dir; // preserve starting point for caching
|
||||
if (_cache.has(startKey)) return _cache.get(startKey);
|
||||
const fsRoot = path.parse(dir).root;
|
||||
|
||||
// Helper to safely check for existence
|
||||
const exists = (p) => fs.pathExists(p);
|
||||
|
||||
// Build checks: an array of { makePath: (dir) => string, weight }
|
||||
const checks = [];
|
||||
|
||||
const add = (rel, weight) => {
|
||||
const makePath = (d) => (Array.isArray(rel) ? path.join(d, ...rel) : path.join(d, rel));
|
||||
checks.push({ makePath, weight });
|
||||
};
|
||||
|
||||
// Highest priority: explicit sentinel markers
|
||||
add('.project-root', 110);
|
||||
add('.workspace-root', 110);
|
||||
add('.repo-root', 110);
|
||||
|
||||
// Highest priority: VCS roots
|
||||
add('.git', 100);
|
||||
add('.hg', 95);
|
||||
add('.svn', 95);
|
||||
|
||||
// Monorepo/workspace indicators
|
||||
add('pnpm-workspace.yaml', 90);
|
||||
add('lerna.json', 90);
|
||||
add('turbo.json', 90);
|
||||
add('nx.json', 90);
|
||||
add('rush.json', 90);
|
||||
add('go.work', 90);
|
||||
add('WORKSPACE', 90);
|
||||
add('WORKSPACE.bazel', 90);
|
||||
add('MODULE.bazel', 90);
|
||||
add('pants.toml', 90);
|
||||
|
||||
// Lockfiles and package-manager/top-level locks
|
||||
add('yarn.lock', 85);
|
||||
add('pnpm-lock.yaml', 85);
|
||||
add('package-lock.json', 85);
|
||||
add('bun.lockb', 85);
|
||||
add('Cargo.lock', 85);
|
||||
add('composer.lock', 85);
|
||||
add('poetry.lock', 85);
|
||||
add('Pipfile.lock', 85);
|
||||
add('Gemfile.lock', 85);
|
||||
|
||||
// Build-system root indicators
|
||||
add('settings.gradle', 80);
|
||||
add('settings.gradle.kts', 80);
|
||||
add('gradlew', 80);
|
||||
add('pom.xml', 80);
|
||||
add('build.sbt', 80);
|
||||
add(['project', 'build.properties'], 80);
|
||||
|
||||
// Language/project config markers
|
||||
add('deno.json', 75);
|
||||
add('deno.jsonc', 75);
|
||||
add('pyproject.toml', 75);
|
||||
add('Pipfile', 75);
|
||||
add('requirements.txt', 75);
|
||||
add('go.mod', 75);
|
||||
add('Cargo.toml', 75);
|
||||
add('composer.json', 75);
|
||||
add('mix.exs', 75);
|
||||
add('Gemfile', 75);
|
||||
add('CMakeLists.txt', 75);
|
||||
add('stack.yaml', 75);
|
||||
add('cabal.project', 75);
|
||||
add('rebar.config', 75);
|
||||
add('pubspec.yaml', 75);
|
||||
add('flake.nix', 75);
|
||||
add('shell.nix', 75);
|
||||
add('default.nix', 75);
|
||||
add('.tool-versions', 75);
|
||||
add('package.json', 74); // generic Node project (lower than lockfiles/workspaces)
|
||||
|
||||
// Changesets
|
||||
add(['.changeset', 'config.json'], 70);
|
||||
add('.changeset', 70);
|
||||
|
||||
// Custom markers via env (comma-separated names)
|
||||
if (process.env.PROJECT_ROOT_MARKERS) {
|
||||
for (const name of process.env.PROJECT_ROOT_MARKERS.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)) {
|
||||
add(name, 72);
|
||||
}
|
||||
}
|
||||
|
||||
/** Check for package.json with "workspaces" */
|
||||
const hasWorkspacePackageJson = async (d) => {
|
||||
const pkgPath = path.join(d, 'package.json');
|
||||
if (!(await exists(pkgPath))) return false;
|
||||
try {
|
||||
const raw = await fs.readFile(pkgPath, 'utf8');
|
||||
const pkg = JSON.parse(raw);
|
||||
return Boolean(pkg && pkg.workspaces);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let best = null; // { dir, weight }
|
||||
|
||||
// Try to detect VCS toplevel once up-front; treat as authoritative slightly above .git marker
|
||||
const vcsTop = await _detectVcsTopLevel(dir);
|
||||
if (vcsTop) {
|
||||
best = { dir: vcsTop, weight: 101 };
|
||||
}
|
||||
const root = path.parse(dir).root;
|
||||
const markers = [
|
||||
'.git',
|
||||
'package.json',
|
||||
'pnpm-workspace.yaml',
|
||||
'yarn.lock',
|
||||
'pnpm-lock.yaml',
|
||||
'pyproject.toml',
|
||||
'requirements.txt',
|
||||
'go.mod',
|
||||
'Cargo.toml',
|
||||
'composer.json',
|
||||
'.hg',
|
||||
'.svn',
|
||||
];
|
||||
|
||||
while (true) {
|
||||
// Special check: package.json with "workspaces"
|
||||
if ((await hasWorkspacePackageJson(dir)) && (!best || 90 >= best.weight))
|
||||
best = { dir, weight: 90 };
|
||||
|
||||
// Evaluate all other checks in parallel
|
||||
const results = await Promise.all(
|
||||
checks.map(async (c) => ({ c, ok: await exists(c.makePath(dir)) })),
|
||||
);
|
||||
|
||||
for (const { c, ok } of results) {
|
||||
if (!ok) continue;
|
||||
if (!best || c.weight >= best.weight) {
|
||||
best = { dir, weight: c.weight };
|
||||
}
|
||||
const exists = await Promise.all(markers.map((m) => fs.pathExists(path.join(dir, m))));
|
||||
if (exists.some(Boolean)) {
|
||||
return dir;
|
||||
}
|
||||
|
||||
if (dir === fsRoot) break;
|
||||
if (dir === root) break;
|
||||
dir = path.dirname(dir);
|
||||
}
|
||||
|
||||
const out = best ? best.dir : null;
|
||||
_cache.set(startKey, out);
|
||||
return out;
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,395 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('node:fs/promises');
|
||||
const path = require('node:path');
|
||||
const zlib = require('node:zlib');
|
||||
const { Buffer } = require('node:buffer');
|
||||
const crypto = require('node:crypto');
|
||||
const cp = require('node:child_process');
|
||||
|
||||
const KB = 1024;
|
||||
const MB = 1024 * KB;
|
||||
|
||||
const formatSize = (bytes) => {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
||||
};
|
||||
|
||||
const percentile = (sorted, p) => {
|
||||
if (sorted.length === 0) return 0;
|
||||
const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1));
|
||||
return sorted[idx];
|
||||
};
|
||||
|
||||
async function processWithLimit(items, fn, concurrency = 64) {
|
||||
for (let i = 0; i < items.length; i += concurrency) {
|
||||
await Promise.all(items.slice(i, i + concurrency).map(fn));
|
||||
}
|
||||
}
|
||||
|
||||
async function enrichAllFiles(textFiles, binaryFiles) {
|
||||
/** @type {Array<{ path: string; absolutePath: string; size: number; lines?: number; isBinary: boolean; ext: string; dir: string; depth: number; hidden: boolean; mtimeMs: number; isSymlink: boolean; }>} */
|
||||
const allFiles = [];
|
||||
|
||||
async function enrich(file, isBinary) {
|
||||
const ext = (path.extname(file.path) || '').toLowerCase();
|
||||
const dir = path.dirname(file.path) || '.';
|
||||
const depth = file.path.split(path.sep).filter(Boolean).length;
|
||||
const hidden = file.path.split(path.sep).some((seg) => seg.startsWith('.'));
|
||||
let mtimeMs = 0;
|
||||
let isSymlink = false;
|
||||
try {
|
||||
const lst = await fs.lstat(file.absolutePath);
|
||||
mtimeMs = lst.mtimeMs;
|
||||
isSymlink = lst.isSymbolicLink();
|
||||
} catch {
|
||||
/* ignore lstat errors during enrichment */
|
||||
}
|
||||
allFiles.push({
|
||||
path: file.path,
|
||||
absolutePath: file.absolutePath,
|
||||
size: file.size || 0,
|
||||
lines: file.lines,
|
||||
isBinary,
|
||||
ext,
|
||||
dir,
|
||||
depth,
|
||||
hidden,
|
||||
mtimeMs,
|
||||
isSymlink,
|
||||
});
|
||||
}
|
||||
|
||||
await processWithLimit(textFiles, (f) => enrich(f, false));
|
||||
await processWithLimit(binaryFiles, (f) => enrich(f, true));
|
||||
return allFiles;
|
||||
}
|
||||
|
||||
function buildHistogram(allFiles) {
|
||||
const buckets = [
|
||||
[1 * KB, '0–1KB'],
|
||||
[10 * KB, '1–10KB'],
|
||||
[100 * KB, '10–100KB'],
|
||||
[1 * MB, '100KB–1MB'],
|
||||
[10 * MB, '1–10MB'],
|
||||
[100 * MB, '10–100MB'],
|
||||
[Infinity, '>=100MB'],
|
||||
];
|
||||
const histogram = buckets.map(([_, label]) => ({ label, count: 0, bytes: 0 }));
|
||||
for (const f of allFiles) {
|
||||
for (const [i, bucket] of buckets.entries()) {
|
||||
if (f.size < bucket[0]) {
|
||||
histogram[i].count++;
|
||||
histogram[i].bytes += f.size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return histogram;
|
||||
}
|
||||
|
||||
function aggregateByExtension(allFiles) {
|
||||
const byExtension = new Map();
|
||||
for (const f of allFiles) {
|
||||
const key = f.ext || '<none>';
|
||||
const v = byExtension.get(key) || { ext: key, count: 0, bytes: 0 };
|
||||
v.count++;
|
||||
v.bytes += f.size;
|
||||
byExtension.set(key, v);
|
||||
}
|
||||
return [...byExtension.values()].sort((a, b) => b.bytes - a.bytes);
|
||||
}
|
||||
|
||||
function aggregateByDirectory(allFiles) {
|
||||
const byDirectory = new Map();
|
||||
function addDirBytes(dir, bytes) {
|
||||
const v = byDirectory.get(dir) || { dir, count: 0, bytes: 0 };
|
||||
v.count++;
|
||||
v.bytes += bytes;
|
||||
byDirectory.set(dir, v);
|
||||
}
|
||||
for (const f of allFiles) {
|
||||
const parts = f.dir === '.' ? [] : f.dir.split(path.sep);
|
||||
let acc = '';
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
acc = i === 0 ? parts[0] : acc + path.sep + parts[i];
|
||||
addDirBytes(acc, f.size);
|
||||
}
|
||||
if (parts.length === 0) addDirBytes('.', f.size);
|
||||
}
|
||||
return [...byDirectory.values()].sort((a, b) => b.bytes - a.bytes);
|
||||
}
|
||||
|
||||
function computeDepthAndLongest(allFiles) {
|
||||
const depthDistribution = new Map();
|
||||
for (const f of allFiles) {
|
||||
depthDistribution.set(f.depth, (depthDistribution.get(f.depth) || 0) + 1);
|
||||
}
|
||||
const longestPaths = [...allFiles]
|
||||
.sort((a, b) => b.path.length - a.path.length)
|
||||
.slice(0, 25)
|
||||
.map((f) => ({ path: f.path, length: f.path.length, size: f.size }));
|
||||
const depthDist = [...depthDistribution.entries()]
|
||||
.sort((a, b) => a[0] - b[0])
|
||||
.map(([depth, count]) => ({ depth, count }));
|
||||
return { depthDist, longestPaths };
|
||||
}
|
||||
|
||||
function computeTemporal(allFiles, nowMs) {
|
||||
let oldest = null,
|
||||
newest = null;
|
||||
const ageBuckets = [
|
||||
{ label: '> 1 year', minDays: 365, maxDays: Infinity, count: 0, bytes: 0 },
|
||||
{ label: '6–12 months', minDays: 180, maxDays: 365, count: 0, bytes: 0 },
|
||||
{ label: '1–6 months', minDays: 30, maxDays: 180, count: 0, bytes: 0 },
|
||||
{ label: '7–30 days', minDays: 7, maxDays: 30, count: 0, bytes: 0 },
|
||||
{ label: '1–7 days', minDays: 1, maxDays: 7, count: 0, bytes: 0 },
|
||||
{ label: '< 1 day', minDays: 0, maxDays: 1, count: 0, bytes: 0 },
|
||||
];
|
||||
for (const f of allFiles) {
|
||||
const ageDays = Math.max(0, (nowMs - (f.mtimeMs || nowMs)) / (24 * 60 * 60 * 1000));
|
||||
for (const b of ageBuckets) {
|
||||
if (ageDays >= b.minDays && ageDays < b.maxDays) {
|
||||
b.count++;
|
||||
b.bytes += f.size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!oldest || f.mtimeMs < oldest.mtimeMs) oldest = f;
|
||||
if (!newest || f.mtimeMs > newest.mtimeMs) newest = f;
|
||||
}
|
||||
return {
|
||||
oldest: oldest
|
||||
? { path: oldest.path, mtime: oldest.mtimeMs ? new Date(oldest.mtimeMs).toISOString() : null }
|
||||
: null,
|
||||
newest: newest
|
||||
? { path: newest.path, mtime: newest.mtimeMs ? new Date(newest.mtimeMs).toISOString() : null }
|
||||
: null,
|
||||
ageBuckets,
|
||||
};
|
||||
}
|
||||
|
||||
function computeQuality(allFiles, textFiles) {
|
||||
const zeroByteFiles = allFiles.filter((f) => f.size === 0).length;
|
||||
const emptyTextFiles = textFiles.filter(
|
||||
(f) => (f.size || 0) === 0 || (f.lines || 0) === 0,
|
||||
).length;
|
||||
const hiddenFiles = allFiles.filter((f) => f.hidden).length;
|
||||
const symlinks = allFiles.filter((f) => f.isSymlink).length;
|
||||
const largeThreshold = 50 * MB;
|
||||
const suspiciousThreshold = 100 * MB;
|
||||
const largeFilesCount = allFiles.filter((f) => f.size >= largeThreshold).length;
|
||||
const suspiciousLargeFilesCount = allFiles.filter((f) => f.size >= suspiciousThreshold).length;
|
||||
return {
|
||||
zeroByteFiles,
|
||||
emptyTextFiles,
|
||||
hiddenFiles,
|
||||
symlinks,
|
||||
largeFilesCount,
|
||||
suspiciousLargeFilesCount,
|
||||
largeThreshold,
|
||||
};
|
||||
}
|
||||
|
||||
function computeDuplicates(allFiles, textFiles) {
|
||||
const duplicatesBySize = new Map();
|
||||
for (const f of allFiles) {
|
||||
const key = String(f.size);
|
||||
const arr = duplicatesBySize.get(key) || [];
|
||||
arr.push(f);
|
||||
duplicatesBySize.set(key, arr);
|
||||
}
|
||||
const duplicateCandidates = [];
|
||||
for (const [sizeKey, arr] of duplicatesBySize.entries()) {
|
||||
if (arr.length < 2) continue;
|
||||
const textGroup = arr.filter((f) => !f.isBinary);
|
||||
const otherGroup = arr.filter((f) => f.isBinary);
|
||||
const contentHashGroups = new Map();
|
||||
for (const tf of textGroup) {
|
||||
try {
|
||||
const src = textFiles.find((x) => x.absolutePath === tf.absolutePath);
|
||||
const content = src ? src.content : '';
|
||||
const h = crypto.createHash('sha1').update(content).digest('hex');
|
||||
const g = contentHashGroups.get(h) || [];
|
||||
g.push(tf);
|
||||
contentHashGroups.set(h, g);
|
||||
} catch {
|
||||
/* ignore hashing errors for duplicate detection */
|
||||
}
|
||||
}
|
||||
for (const [_h, g] of contentHashGroups.entries()) {
|
||||
if (g.length > 1)
|
||||
duplicateCandidates.push({
|
||||
reason: 'same-size+text-hash',
|
||||
size: Number(sizeKey),
|
||||
count: g.length,
|
||||
files: g.map((f) => f.path),
|
||||
});
|
||||
}
|
||||
if (otherGroup.length > 1) {
|
||||
duplicateCandidates.push({
|
||||
reason: 'same-size',
|
||||
size: Number(sizeKey),
|
||||
count: otherGroup.length,
|
||||
files: otherGroup.map((f) => f.path),
|
||||
});
|
||||
}
|
||||
}
|
||||
return duplicateCandidates;
|
||||
}
|
||||
|
||||
function estimateCompressibility(textFiles) {
|
||||
let compSampleBytes = 0;
|
||||
let compCompressedBytes = 0;
|
||||
for (const tf of textFiles) {
|
||||
try {
|
||||
const sampleLen = Math.min(256 * 1024, tf.size || 0);
|
||||
if (sampleLen <= 0) continue;
|
||||
const sample = tf.content.slice(0, sampleLen);
|
||||
const gz = zlib.gzipSync(Buffer.from(sample, 'utf8'));
|
||||
compSampleBytes += sampleLen;
|
||||
compCompressedBytes += gz.length;
|
||||
} catch {
|
||||
/* ignore compression errors during sampling */
|
||||
}
|
||||
}
|
||||
return compSampleBytes > 0 ? compCompressedBytes / compSampleBytes : null;
|
||||
}
|
||||
|
||||
function computeGitInfo(allFiles, rootDir, largeThreshold) {
|
||||
const info = {
|
||||
isRepo: false,
|
||||
trackedCount: 0,
|
||||
trackedBytes: 0,
|
||||
untrackedCount: 0,
|
||||
untrackedBytes: 0,
|
||||
lfsCandidates: [],
|
||||
};
|
||||
try {
|
||||
if (!rootDir) return info;
|
||||
const top = cp
|
||||
.execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
||||
cwd: rootDir,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
})
|
||||
.toString()
|
||||
.trim();
|
||||
if (!top) return info;
|
||||
info.isRepo = true;
|
||||
const out = cp.execFileSync('git', ['ls-files', '-z'], {
|
||||
cwd: rootDir,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
});
|
||||
const tracked = new Set(out.toString().split('\0').filter(Boolean));
|
||||
let trackedBytes = 0,
|
||||
trackedCount = 0,
|
||||
untrackedBytes = 0,
|
||||
untrackedCount = 0;
|
||||
const lfsCandidates = [];
|
||||
for (const f of allFiles) {
|
||||
const isTracked = tracked.has(f.path);
|
||||
if (isTracked) {
|
||||
trackedCount++;
|
||||
trackedBytes += f.size;
|
||||
if (f.size >= largeThreshold) lfsCandidates.push({ path: f.path, size: f.size });
|
||||
} else {
|
||||
untrackedCount++;
|
||||
untrackedBytes += f.size;
|
||||
}
|
||||
}
|
||||
info.trackedCount = trackedCount;
|
||||
info.trackedBytes = trackedBytes;
|
||||
info.untrackedCount = untrackedCount;
|
||||
info.untrackedBytes = untrackedBytes;
|
||||
info.lfsCandidates = lfsCandidates.sort((a, b) => b.size - a.size).slice(0, 50);
|
||||
} catch {
|
||||
/* git not available or not a repo, ignore */
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
function computeLargestFiles(allFiles, totalBytes) {
|
||||
const toPct = (num, den) => (den === 0 ? 0 : (num / den) * 100);
|
||||
return [...allFiles]
|
||||
.sort((a, b) => b.size - a.size)
|
||||
.slice(0, 50)
|
||||
.map((f) => ({
|
||||
path: f.path,
|
||||
size: f.size,
|
||||
sizeFormatted: formatSize(f.size),
|
||||
percentOfTotal: toPct(f.size, totalBytes),
|
||||
ext: f.ext || '',
|
||||
isBinary: f.isBinary,
|
||||
mtime: f.mtimeMs ? new Date(f.mtimeMs).toISOString() : null,
|
||||
}));
|
||||
}
|
||||
|
||||
function mdTable(rows, headers) {
|
||||
const header = `| ${headers.join(' | ')} |`;
|
||||
const sep = `| ${headers.map(() => '---').join(' | ')} |`;
|
||||
const body = rows.map((r) => `| ${r.join(' | ')} |`).join('\n');
|
||||
return `${header}\n${sep}\n${body}`;
|
||||
}
|
||||
|
||||
function buildMarkdownReport(largestFiles, byExtensionArr, byDirectoryArr, totalBytes) {
|
||||
const toPct = (num, den) => (den === 0 ? 0 : (num / den) * 100);
|
||||
const md = [];
|
||||
md.push(
|
||||
'\n### Top Largest Files (Top 50)\n',
|
||||
mdTable(
|
||||
largestFiles.map((f) => [
|
||||
f.path,
|
||||
f.sizeFormatted,
|
||||
`${f.percentOfTotal.toFixed(2)}%`,
|
||||
f.ext || '',
|
||||
f.isBinary ? 'binary' : 'text',
|
||||
]),
|
||||
['Path', 'Size', '% of total', 'Ext', 'Type'],
|
||||
),
|
||||
'\n\n### Top Extensions by Bytes (Top 20)\n',
|
||||
);
|
||||
const topExtRows = byExtensionArr
|
||||
.slice(0, 20)
|
||||
.map((e) => [
|
||||
e.ext,
|
||||
String(e.count),
|
||||
formatSize(e.bytes),
|
||||
`${toPct(e.bytes, totalBytes).toFixed(2)}%`,
|
||||
]);
|
||||
md.push(
|
||||
mdTable(topExtRows, ['Ext', 'Count', 'Bytes', '% of total']),
|
||||
'\n\n### Top Directories by Bytes (Top 20)\n',
|
||||
);
|
||||
const topDirRows = byDirectoryArr
|
||||
.slice(0, 20)
|
||||
.map((d) => [
|
||||
d.dir,
|
||||
String(d.count),
|
||||
formatSize(d.bytes),
|
||||
`${toPct(d.bytes, totalBytes).toFixed(2)}%`,
|
||||
]);
|
||||
md.push(mdTable(topDirRows, ['Directory', 'Files', 'Bytes', '% of total']));
|
||||
return md.join('\n');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
KB,
|
||||
MB,
|
||||
formatSize,
|
||||
percentile,
|
||||
processWithLimit,
|
||||
enrichAllFiles,
|
||||
buildHistogram,
|
||||
aggregateByExtension,
|
||||
aggregateByDirectory,
|
||||
computeDepthAndLongest,
|
||||
computeTemporal,
|
||||
computeQuality,
|
||||
computeDuplicates,
|
||||
estimateCompressibility,
|
||||
computeGitInfo,
|
||||
computeLargestFiles,
|
||||
buildMarkdownReport,
|
||||
};
|
||||
@@ -1,79 +1,29 @@
|
||||
const H = require('./stats.helpers.js');
|
||||
|
||||
async function calculateStatistics(aggregatedContent, xmlFileSize, rootDir) {
|
||||
function calculateStatistics(aggregatedContent, xmlFileSize) {
|
||||
const { textFiles, binaryFiles, errors } = aggregatedContent;
|
||||
|
||||
const totalLines = textFiles.reduce((sum, f) => sum + (f.lines || 0), 0);
|
||||
const totalTextSize = textFiles.reduce((sum, file) => sum + file.size, 0);
|
||||
const totalBinarySize = binaryFiles.reduce((sum, file) => sum + file.size, 0);
|
||||
const totalSize = totalTextSize + totalBinarySize;
|
||||
|
||||
const totalLines = textFiles.reduce((sum, file) => sum + file.lines, 0);
|
||||
|
||||
const estimatedTokens = Math.ceil(xmlFileSize / 4);
|
||||
|
||||
// Build enriched file list
|
||||
const allFiles = await H.enrichAllFiles(textFiles, binaryFiles);
|
||||
const totalBytes = allFiles.reduce((s, f) => s + f.size, 0);
|
||||
const sizes = allFiles.map((f) => f.size).sort((a, b) => a - b);
|
||||
const avgSize = sizes.length > 0 ? totalBytes / sizes.length : 0;
|
||||
const medianSize = sizes.length > 0 ? H.percentile(sizes, 50) : 0;
|
||||
const p90 = H.percentile(sizes, 90);
|
||||
const p95 = H.percentile(sizes, 95);
|
||||
const p99 = H.percentile(sizes, 99);
|
||||
|
||||
const histogram = H.buildHistogram(allFiles);
|
||||
const byExtensionArr = H.aggregateByExtension(allFiles);
|
||||
const byDirectoryArr = H.aggregateByDirectory(allFiles);
|
||||
const { depthDist, longestPaths } = H.computeDepthAndLongest(allFiles);
|
||||
const temporal = H.computeTemporal(allFiles, Date.now());
|
||||
const quality = H.computeQuality(allFiles, textFiles);
|
||||
const duplicateCandidates = H.computeDuplicates(allFiles, textFiles);
|
||||
const compressibilityRatio = H.estimateCompressibility(textFiles);
|
||||
const git = H.computeGitInfo(allFiles, rootDir, quality.largeThreshold);
|
||||
const largestFiles = H.computeLargestFiles(allFiles, totalBytes);
|
||||
const markdownReport = H.buildMarkdownReport(
|
||||
largestFiles,
|
||||
byExtensionArr,
|
||||
byDirectoryArr,
|
||||
totalBytes,
|
||||
);
|
||||
const formatSize = (bytes) => {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
};
|
||||
|
||||
return {
|
||||
// Back-compat summary
|
||||
totalFiles: textFiles.length + binaryFiles.length,
|
||||
textFiles: textFiles.length,
|
||||
binaryFiles: binaryFiles.length,
|
||||
errorFiles: errors.length,
|
||||
totalSize: H.formatSize(totalBytes),
|
||||
totalBytes,
|
||||
xmlSize: H.formatSize(xmlFileSize),
|
||||
totalSize: formatSize(totalSize),
|
||||
xmlSize: formatSize(xmlFileSize),
|
||||
totalLines,
|
||||
estimatedTokens: estimatedTokens.toLocaleString(),
|
||||
|
||||
// Distributions and percentiles
|
||||
avgFileSize: avgSize,
|
||||
medianFileSize: medianSize,
|
||||
p90,
|
||||
p95,
|
||||
p99,
|
||||
histogram,
|
||||
|
||||
// Extensions and directories
|
||||
byExtension: byExtensionArr,
|
||||
byDirectory: byDirectoryArr,
|
||||
depthDistribution: depthDist,
|
||||
longestPaths,
|
||||
|
||||
// Temporal
|
||||
temporal,
|
||||
|
||||
// Quality signals
|
||||
quality,
|
||||
|
||||
// Duplicates and compressibility
|
||||
duplicateCandidates,
|
||||
compressibilityRatio,
|
||||
|
||||
// Git-aware
|
||||
git,
|
||||
|
||||
largestFiles,
|
||||
markdownReport,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,413 +0,0 @@
|
||||
/* deno-lint-ignore-file */
|
||||
/*
|
||||
Automatic test matrix for project root detection.
|
||||
Creates temporary fixtures for various ecosystems and validates findProjectRoot().
|
||||
No external options or flags required. Safe to run multiple times.
|
||||
*/
|
||||
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const fs = require('fs-extra');
|
||||
const { promisify } = require('node:util');
|
||||
const { execFile } = require('node:child_process');
|
||||
const process = require('node:process');
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
const { findProjectRoot } = require('./projectRoot.js');
|
||||
|
||||
async function cmdAvailable(cmd) {
|
||||
try {
|
||||
await execFileAsync(cmd, ['--version'], { timeout: 500, windowsHide: true });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
async function testSvnMarker() {
|
||||
const root = await mkTmpDir('svn');
|
||||
const nested = path.join(root, 'proj', 'code');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.ensureDir(path.join(root, '.svn'));
|
||||
const found = await findProjectRoot(nested);
|
||||
assertEqual(found, root, '.svn marker should be detected');
|
||||
return { name: 'svn-marker', ok: true };
|
||||
}
|
||||
|
||||
async function testSymlinkStart() {
|
||||
const root = await mkTmpDir('symlink-start');
|
||||
const nested = path.join(root, 'a', 'b');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, '.project-root'), '\n');
|
||||
const tmp = await mkTmpDir('symlink-tmp');
|
||||
const link = path.join(tmp, 'link-to-b');
|
||||
try {
|
||||
await fs.symlink(nested, link);
|
||||
} catch {
|
||||
// symlink may not be permitted on some systems; skip
|
||||
return { name: 'symlink-start', ok: true, skipped: true };
|
||||
}
|
||||
const found = await findProjectRoot(link);
|
||||
assertEqual(found, root, 'should resolve symlinked start to real root');
|
||||
return { name: 'symlink-start', ok: true };
|
||||
}
|
||||
|
||||
async function testSubmoduleLikeInnerGitFile() {
|
||||
const root = await mkTmpDir('submodule-like');
|
||||
const mid = path.join(root, 'mid');
|
||||
const leaf = path.join(mid, 'leaf');
|
||||
await fs.ensureDir(leaf);
|
||||
// outer repo
|
||||
await fs.ensureDir(path.join(root, '.git'));
|
||||
// inner submodule-like .git file
|
||||
await fs.writeFile(path.join(mid, '.git'), 'gitdir: ../.git/modules/mid\n');
|
||||
const found = await findProjectRoot(leaf);
|
||||
assertEqual(found, root, 'outermost .git should win on tie weight');
|
||||
return { name: 'submodule-like-gitfile', ok: true };
|
||||
}
|
||||
}
|
||||
|
||||
async function mkTmpDir(name) {
|
||||
const base = await fs.realpath(os.tmpdir());
|
||||
const dir = await fs.mkdtemp(path.join(base, `flattener-${name}-`));
|
||||
return dir;
|
||||
}
|
||||
|
||||
function assertEqual(actual, expected, msg) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(`${msg}: expected="${expected}" actual="${actual}"`);
|
||||
}
|
||||
}
|
||||
|
||||
async function testSentinel() {
|
||||
const root = await mkTmpDir('sentinel');
|
||||
const nested = path.join(root, 'a', 'b', 'c');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, '.project-root'), '\n');
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'sentinel .project-root should win');
|
||||
return { name: 'sentinel', ok: true };
|
||||
}
|
||||
|
||||
async function testOtherSentinels() {
|
||||
const root = await mkTmpDir('other-sentinels');
|
||||
const nested = path.join(root, 'x', 'y');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, '.workspace-root'), '\n');
|
||||
const found1 = await findProjectRoot(nested);
|
||||
assertEqual(found1, root, 'sentinel .workspace-root should win');
|
||||
|
||||
await fs.remove(path.join(root, '.workspace-root'));
|
||||
await fs.writeFile(path.join(root, '.repo-root'), '\n');
|
||||
const found2 = await findProjectRoot(nested);
|
||||
assertEqual(found2, root, 'sentinel .repo-root should win');
|
||||
return { name: 'other-sentinels', ok: true };
|
||||
}
|
||||
|
||||
async function testGitCliAndMarker() {
|
||||
const hasGit = await cmdAvailable('git');
|
||||
if (!hasGit) return { name: 'git-cli', ok: true, skipped: true };
|
||||
|
||||
const root = await mkTmpDir('git');
|
||||
const nested = path.join(root, 'pkg', 'src');
|
||||
await fs.ensureDir(nested);
|
||||
await execFileAsync('git', ['init'], { cwd: root, timeout: 2000 });
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'git toplevel should be detected');
|
||||
return { name: 'git-cli', ok: true };
|
||||
}
|
||||
|
||||
async function testHgMarkerOrCli() {
|
||||
// Prefer simple marker test to avoid requiring Mercurial install
|
||||
const root = await mkTmpDir('hg');
|
||||
const nested = path.join(root, 'lib');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.ensureDir(path.join(root, '.hg'));
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, '.hg marker should be detected');
|
||||
return { name: 'hg-marker', ok: true };
|
||||
}
|
||||
|
||||
async function testWorkspacePnpm() {
|
||||
const root = await mkTmpDir('pnpm-workspace');
|
||||
const pkgA = path.join(root, 'packages', 'a');
|
||||
await fs.ensureDir(pkgA);
|
||||
await fs.writeFile(path.join(root, 'pnpm-workspace.yaml'), 'packages:\n - packages/*\n');
|
||||
const found = await findProjectRoot(pkgA);
|
||||
await assertEqual(found, root, 'pnpm-workspace.yaml should be detected');
|
||||
return { name: 'pnpm-workspace', ok: true };
|
||||
}
|
||||
|
||||
async function testPackageJsonWorkspaces() {
|
||||
const root = await mkTmpDir('package-workspaces');
|
||||
const pkgA = path.join(root, 'packages', 'a');
|
||||
await fs.ensureDir(pkgA);
|
||||
await fs.writeJson(
|
||||
path.join(root, 'package.json'),
|
||||
{ private: true, workspaces: ['packages/*'] },
|
||||
{ spaces: 2 },
|
||||
);
|
||||
const found = await findProjectRoot(pkgA);
|
||||
await assertEqual(found, root, 'package.json workspaces should be detected');
|
||||
return { name: 'package.json-workspaces', ok: true };
|
||||
}
|
||||
|
||||
async function testLockfiles() {
|
||||
const root = await mkTmpDir('lockfiles');
|
||||
const nested = path.join(root, 'src');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, 'yarn.lock'), '\n');
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'yarn.lock should be detected');
|
||||
return { name: 'lockfiles', ok: true };
|
||||
}
|
||||
|
||||
async function testLanguageConfigs() {
|
||||
const root = await mkTmpDir('lang-configs');
|
||||
const nested = path.join(root, 'x', 'y');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, 'pyproject.toml'), "[tool.poetry]\nname='tmp'\n");
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'pyproject.toml should be detected');
|
||||
return { name: 'language-configs', ok: true };
|
||||
}
|
||||
|
||||
async function testPreferOuterOnTie() {
|
||||
const root = await mkTmpDir('tie');
|
||||
const mid = path.join(root, 'mid');
|
||||
const leaf = path.join(mid, 'leaf');
|
||||
await fs.ensureDir(leaf);
|
||||
// same weight marker at two levels
|
||||
await fs.writeFile(path.join(root, 'requirements.txt'), '\n');
|
||||
await fs.writeFile(path.join(mid, 'requirements.txt'), '\n');
|
||||
const found = await findProjectRoot(leaf);
|
||||
await assertEqual(found, root, 'outermost directory should win on equal weight');
|
||||
return { name: 'prefer-outermost-tie', ok: true };
|
||||
}
|
||||
|
||||
// Additional coverage: Bazel, Nx/Turbo/Rush, Go workspaces, Deno, Java/Scala, PHP, Rust, Nix, Changesets, env markers,
|
||||
// and priority interaction between package.json and lockfiles.
|
||||
|
||||
async function testBazelWorkspace() {
|
||||
const root = await mkTmpDir('bazel');
|
||||
const nested = path.join(root, 'apps', 'svc');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, 'WORKSPACE'), 'workspace(name="tmp")\n');
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'Bazel WORKSPACE should be detected');
|
||||
return { name: 'bazel-workspace', ok: true };
|
||||
}
|
||||
|
||||
async function testNx() {
|
||||
const root = await mkTmpDir('nx');
|
||||
const nested = path.join(root, 'apps', 'web');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeJson(path.join(root, 'nx.json'), { npmScope: 'tmp' }, { spaces: 2 });
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'nx.json should be detected');
|
||||
return { name: 'nx', ok: true };
|
||||
}
|
||||
|
||||
async function testTurbo() {
|
||||
const root = await mkTmpDir('turbo');
|
||||
const nested = path.join(root, 'packages', 'x');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeJson(path.join(root, 'turbo.json'), { pipeline: {} }, { spaces: 2 });
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'turbo.json should be detected');
|
||||
return { name: 'turbo', ok: true };
|
||||
}
|
||||
|
||||
async function testRush() {
|
||||
const root = await mkTmpDir('rush');
|
||||
const nested = path.join(root, 'apps', 'a');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeJson(path.join(root, 'rush.json'), { projectFolderMinDepth: 1 }, { spaces: 2 });
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'rush.json should be detected');
|
||||
return { name: 'rush', ok: true };
|
||||
}
|
||||
|
||||
async function testGoWorkAndMod() {
|
||||
const root = await mkTmpDir('gowork');
|
||||
const mod = path.join(root, 'modA');
|
||||
const nested = path.join(mod, 'pkg');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, 'go.work'), 'go 1.22\nuse ./modA\n');
|
||||
await fs.writeFile(path.join(mod, 'go.mod'), 'module example.com/a\ngo 1.22\n');
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'go.work should define the workspace root');
|
||||
return { name: 'go-work', ok: true };
|
||||
}
|
||||
|
||||
async function testDenoJson() {
|
||||
const root = await mkTmpDir('deno');
|
||||
const nested = path.join(root, 'src');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeJson(path.join(root, 'deno.json'), { tasks: {} }, { spaces: 2 });
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'deno.json should be detected');
|
||||
return { name: 'deno-json', ok: true };
|
||||
}
|
||||
|
||||
async function testGradleSettings() {
|
||||
const root = await mkTmpDir('gradle');
|
||||
const nested = path.join(root, 'app');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, 'settings.gradle'), "rootProject.name='tmp'\n");
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'settings.gradle should be detected');
|
||||
return { name: 'gradle-settings', ok: true };
|
||||
}
|
||||
|
||||
async function testMavenPom() {
|
||||
const root = await mkTmpDir('maven');
|
||||
const nested = path.join(root, 'module');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, 'pom.xml'), '<project></project>\n');
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'pom.xml should be detected');
|
||||
return { name: 'maven-pom', ok: true };
|
||||
}
|
||||
|
||||
async function testSbtBuild() {
|
||||
const root = await mkTmpDir('sbt');
|
||||
const nested = path.join(root, 'sub');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, 'build.sbt'), 'name := "tmp"\n');
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'build.sbt should be detected');
|
||||
return { name: 'sbt-build', ok: true };
|
||||
}
|
||||
|
||||
async function testComposer() {
|
||||
const root = await mkTmpDir('composer');
|
||||
const nested = path.join(root, 'src');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeJson(path.join(root, 'composer.json'), { name: 'tmp/pkg' }, { spaces: 2 });
|
||||
await fs.writeFile(path.join(root, 'composer.lock'), '{}\n');
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'composer.{json,lock} should be detected');
|
||||
return { name: 'composer', ok: true };
|
||||
}
|
||||
|
||||
async function testCargo() {
|
||||
const root = await mkTmpDir('cargo');
|
||||
const nested = path.join(root, 'src');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, 'Cargo.toml'), "[package]\nname='tmp'\nversion='0.0.0'\n");
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'Cargo.toml should be detected');
|
||||
return { name: 'cargo', ok: true };
|
||||
}
|
||||
|
||||
async function testNixFlake() {
|
||||
const root = await mkTmpDir('nix');
|
||||
const nested = path.join(root, 'work');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, 'flake.nix'), '{ }\n');
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'flake.nix should be detected');
|
||||
return { name: 'nix-flake', ok: true };
|
||||
}
|
||||
|
||||
async function testChangesetConfig() {
|
||||
const root = await mkTmpDir('changeset');
|
||||
const nested = path.join(root, 'pkg');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.ensureDir(path.join(root, '.changeset'));
|
||||
await fs.writeJson(
|
||||
path.join(root, '.changeset', 'config.json'),
|
||||
{ $schema: 'https://unpkg.com/@changesets/config@2.3.1/schema.json' },
|
||||
{ spaces: 2 },
|
||||
);
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, '.changeset/config.json should be detected');
|
||||
return { name: 'changesets', ok: true };
|
||||
}
|
||||
|
||||
async function testEnvCustomMarker() {
|
||||
const root = await mkTmpDir('env-marker');
|
||||
const nested = path.join(root, 'dir');
|
||||
await fs.ensureDir(nested);
|
||||
await fs.writeFile(path.join(root, 'MY_ROOT'), '\n');
|
||||
const prev = process.env.PROJECT_ROOT_MARKERS;
|
||||
process.env.PROJECT_ROOT_MARKERS = 'MY_ROOT';
|
||||
try {
|
||||
const found = await findProjectRoot(nested);
|
||||
await assertEqual(found, root, 'custom env marker should be honored');
|
||||
} finally {
|
||||
if (prev === undefined) delete process.env.PROJECT_ROOT_MARKERS;
|
||||
else process.env.PROJECT_ROOT_MARKERS = prev;
|
||||
}
|
||||
return { name: 'env-custom-marker', ok: true };
|
||||
}
|
||||
|
||||
async function testPackageLowPriorityVsLock() {
|
||||
const root = await mkTmpDir('pkg-vs-lock');
|
||||
const nested = path.join(root, 'nested');
|
||||
await fs.ensureDir(path.join(nested, 'deep'));
|
||||
await fs.writeJson(path.join(nested, 'package.json'), { name: 'nested' }, { spaces: 2 });
|
||||
await fs.writeFile(path.join(root, 'yarn.lock'), '\n');
|
||||
const found = await findProjectRoot(path.join(nested, 'deep'));
|
||||
await assertEqual(found, root, 'lockfile at root should outrank nested package.json');
|
||||
return { name: 'package-vs-lock-priority', ok: true };
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const tests = [
|
||||
testSentinel,
|
||||
testOtherSentinels,
|
||||
testGitCliAndMarker,
|
||||
testHgMarkerOrCli,
|
||||
testWorkspacePnpm,
|
||||
testPackageJsonWorkspaces,
|
||||
testLockfiles,
|
||||
testLanguageConfigs,
|
||||
testPreferOuterOnTie,
|
||||
testBazelWorkspace,
|
||||
testNx,
|
||||
testTurbo,
|
||||
testRush,
|
||||
testGoWorkAndMod,
|
||||
testDenoJson,
|
||||
testGradleSettings,
|
||||
testMavenPom,
|
||||
testSbtBuild,
|
||||
testComposer,
|
||||
testCargo,
|
||||
testNixFlake,
|
||||
testChangesetConfig,
|
||||
testEnvCustomMarker,
|
||||
testPackageLowPriorityVsLock,
|
||||
testSvnMarker,
|
||||
testSymlinkStart,
|
||||
testSubmoduleLikeInnerGitFile,
|
||||
];
|
||||
|
||||
const results = [];
|
||||
for (const t of tests) {
|
||||
try {
|
||||
const r = await t();
|
||||
results.push({ ...r, ok: true });
|
||||
console.log(`✔ ${r.name}${r.skipped ? ' (skipped)' : ''}`);
|
||||
} catch (error) {
|
||||
console.error(`✖ ${t.name}:`, error && error.message ? error.message : error);
|
||||
results.push({ name: t.name, ok: false, error: String(error) });
|
||||
}
|
||||
}
|
||||
|
||||
const failed = results.filter((r) => !r.ok);
|
||||
console.log('\nSummary:');
|
||||
for (const r of results) {
|
||||
console.log(`- ${r.name}: ${r.ok ? 'ok' : 'FAIL'}${r.skipped ? ' (skipped)' : ''}`);
|
||||
}
|
||||
|
||||
if (failed.length > 0) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
run().catch((error) => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -403,7 +403,6 @@ async function promptInstallation() {
|
||||
{ name: 'Cline', value: 'cline' },
|
||||
{ name: 'Gemini CLI', value: 'gemini' },
|
||||
{ name: 'Qwen Code', value: 'qwen-code' },
|
||||
{ name: 'Crush', value: 'crush' },
|
||||
{ name: 'Github Copilot', value: 'github-copilot' },
|
||||
],
|
||||
},
|
||||
|
||||
@@ -28,16 +28,6 @@ ide-configurations:
|
||||
# To use BMad agents in Claude Code:
|
||||
# 1. Type /agent-name (e.g., "/dev", "/pm", "/architect")
|
||||
# 2. Claude will switch to that agent's persona
|
||||
crush:
|
||||
name: Crush
|
||||
rule-dir: .crush/commands/BMad/
|
||||
format: multi-file
|
||||
command-suffix: .md
|
||||
instructions: |
|
||||
# To use BMad agents in Crush:
|
||||
# 1. Press CTRL + P and press TAB
|
||||
# 2. Select agent or task
|
||||
# 3. Crush will switch to that agent's persona / task
|
||||
windsurf:
|
||||
name: Windsurf
|
||||
rule-dir: .windsurf/workflows/
|
||||
|
||||
@@ -47,9 +47,6 @@ class IdeSetup extends BaseIdeSetup {
|
||||
case 'claude-code': {
|
||||
return this.setupClaudeCode(installDir, selectedAgent);
|
||||
}
|
||||
case 'crush': {
|
||||
return this.setupCrush(installDir, selectedAgent);
|
||||
}
|
||||
case 'windsurf': {
|
||||
return this.setupWindsurf(installDir, selectedAgent);
|
||||
}
|
||||
@@ -102,44 +99,6 @@ class IdeSetup extends BaseIdeSetup {
|
||||
return true;
|
||||
}
|
||||
|
||||
async setupCrush(installDir, selectedAgent) {
|
||||
// Setup bmad-core commands
|
||||
const coreSlashPrefix = await this.getCoreSlashPrefix(installDir);
|
||||
const coreAgents = selectedAgent ? [selectedAgent] : await this.getCoreAgentIds(installDir);
|
||||
const coreTasks = await this.getCoreTaskIds(installDir);
|
||||
await this.setupCrushForPackage(
|
||||
installDir,
|
||||
'core',
|
||||
coreSlashPrefix,
|
||||
coreAgents,
|
||||
coreTasks,
|
||||
'.bmad-core',
|
||||
);
|
||||
|
||||
// Setup expansion pack commands
|
||||
const expansionPacks = await this.getInstalledExpansionPacks(installDir);
|
||||
for (const packInfo of expansionPacks) {
|
||||
const packSlashPrefix = await this.getExpansionPackSlashPrefix(packInfo.path);
|
||||
const packAgents = await this.getExpansionPackAgents(packInfo.path);
|
||||
const packTasks = await this.getExpansionPackTasks(packInfo.path);
|
||||
|
||||
if (packAgents.length > 0 || packTasks.length > 0) {
|
||||
// Use the actual directory name where the expansion pack is installed
|
||||
const rootPath = path.relative(installDir, packInfo.path);
|
||||
await this.setupCrushForPackage(
|
||||
installDir,
|
||||
packInfo.name,
|
||||
packSlashPrefix,
|
||||
packAgents,
|
||||
packTasks,
|
||||
rootPath,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async setupClaudeCode(installDir, selectedAgent) {
|
||||
// Setup bmad-core commands
|
||||
const coreSlashPrefix = await this.getCoreSlashPrefix(installDir);
|
||||
@@ -275,94 +234,6 @@ class IdeSetup extends BaseIdeSetup {
|
||||
console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
|
||||
}
|
||||
|
||||
async setupCrushForPackage(installDir, packageName, slashPrefix, agentIds, taskIds, rootPath) {
|
||||
const commandsBaseDir = path.join(installDir, '.crush', 'commands', slashPrefix);
|
||||
const agentsDir = path.join(commandsBaseDir, 'agents');
|
||||
const tasksDir = path.join(commandsBaseDir, 'tasks');
|
||||
|
||||
// Ensure directories exist
|
||||
await fileManager.ensureDirectory(agentsDir);
|
||||
await fileManager.ensureDirectory(tasksDir);
|
||||
|
||||
// Setup agents
|
||||
for (const agentId of agentIds) {
|
||||
// Find the agent file - for expansion packs, prefer the expansion pack version
|
||||
let agentPath;
|
||||
if (packageName === 'core') {
|
||||
// For core, use the normal search
|
||||
agentPath = await this.findAgentPath(agentId, installDir);
|
||||
} else {
|
||||
// For expansion packs, first try to find the agent in the expansion pack directory
|
||||
const expansionPackPath = path.join(installDir, rootPath, 'agents', `${agentId}.md`);
|
||||
if (await fileManager.pathExists(expansionPackPath)) {
|
||||
agentPath = expansionPackPath;
|
||||
} else {
|
||||
// Fall back to core if not found in expansion pack
|
||||
agentPath = await this.findAgentPath(agentId, installDir);
|
||||
}
|
||||
}
|
||||
|
||||
const commandPath = path.join(agentsDir, `${agentId}.md`);
|
||||
|
||||
if (agentPath) {
|
||||
// Create command file with agent content
|
||||
let agentContent = await fileManager.readFile(agentPath);
|
||||
|
||||
// Replace {root} placeholder with the appropriate root path for this context
|
||||
agentContent = agentContent.replaceAll('{root}', rootPath);
|
||||
|
||||
// Add command header
|
||||
let commandContent = `# /${agentId} Command\n\n`;
|
||||
commandContent += `When this command is used, adopt the following agent persona:\n\n`;
|
||||
commandContent += agentContent;
|
||||
|
||||
await fileManager.writeFile(commandPath, commandContent);
|
||||
console.log(chalk.green(`✓ Created agent command: /${agentId}`));
|
||||
}
|
||||
}
|
||||
|
||||
// Setup tasks
|
||||
for (const taskId of taskIds) {
|
||||
// Find the task file - for expansion packs, prefer the expansion pack version
|
||||
let taskPath;
|
||||
if (packageName === 'core') {
|
||||
// For core, use the normal search
|
||||
taskPath = await this.findTaskPath(taskId, installDir);
|
||||
} else {
|
||||
// For expansion packs, first try to find the task in the expansion pack directory
|
||||
const expansionPackPath = path.join(installDir, rootPath, 'tasks', `${taskId}.md`);
|
||||
if (await fileManager.pathExists(expansionPackPath)) {
|
||||
taskPath = expansionPackPath;
|
||||
} else {
|
||||
// Fall back to core if not found in expansion pack
|
||||
taskPath = await this.findTaskPath(taskId, installDir);
|
||||
}
|
||||
}
|
||||
|
||||
const commandPath = path.join(tasksDir, `${taskId}.md`);
|
||||
|
||||
if (taskPath) {
|
||||
// Create command file with task content
|
||||
let taskContent = await fileManager.readFile(taskPath);
|
||||
|
||||
// Replace {root} placeholder with the appropriate root path for this context
|
||||
taskContent = taskContent.replaceAll('{root}', rootPath);
|
||||
|
||||
// Add command header
|
||||
let commandContent = `# /${taskId} Task\n\n`;
|
||||
commandContent += `When this command is used, execute the following task:\n\n`;
|
||||
commandContent += taskContent;
|
||||
|
||||
await fileManager.writeFile(commandPath, commandContent);
|
||||
console.log(chalk.green(`✓ Created task command: /${taskId}`));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(chalk.green(`\n✓ Created Crush commands for ${packageName} in ${commandsBaseDir}`));
|
||||
console.log(chalk.dim(` - Agents in: ${agentsDir}`));
|
||||
console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
|
||||
}
|
||||
|
||||
async setupWindsurf(installDir, selectedAgent) {
|
||||
const windsurfWorkflowDir = path.join(installDir, '.windsurf', 'workflows');
|
||||
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bmad-method",
|
||||
"version": "5.0.1",
|
||||
"version": "5.0.0",
|
||||
"description": "BMad Method installer - AI-powered Agile development framework",
|
||||
"keywords": [
|
||||
"bmad",
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
const { execSync } = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
|
||||
// Get the latest stable tag (exclude beta tags)
|
||||
const allTags = execSync('git tag -l | sort -V', { encoding: 'utf8' }).split('\n').filter(Boolean);
|
||||
const stableTags = allTags.filter((tag) => !tag.includes('beta'));
|
||||
const latestTag = stableTags.at(-1) || 'v5.0.0';
|
||||
|
||||
// Get commits since last tag
|
||||
const commits = execSync(`git log ${latestTag}..HEAD --pretty=format:"- %s" --reverse`, {
|
||||
encoding: 'utf8',
|
||||
})
|
||||
.split('\n')
|
||||
.filter(Boolean);
|
||||
|
||||
// Categorize commits
|
||||
const features = commits.filter((commit) => /^- (feat|Feature)/.test(commit));
|
||||
const fixes = commits.filter((commit) => /^- (fix|Fix)/.test(commit));
|
||||
const chores = commits.filter((commit) => /^- (chore|Chore)/.test(commit));
|
||||
const others = commits.filter(
|
||||
(commit) => !/^- (feat|Feature|fix|Fix|chore|Chore|release:|Release:)/.test(commit),
|
||||
);
|
||||
|
||||
// Get next version (you can modify this logic)
|
||||
const currentVersion = require('../package.json').version;
|
||||
const versionParts = currentVersion.split('.').map(Number);
|
||||
const nextVersion = `${versionParts[0]}.${versionParts[1] + 1}.0`; // Default to minor bump
|
||||
|
||||
console.log(`## 🚀 What's New in v${nextVersion}\n`);
|
||||
|
||||
if (features.length > 0) {
|
||||
console.log('### ✨ New Features');
|
||||
for (const feature of features) console.log(feature);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
if (fixes.length > 0) {
|
||||
console.log('### 🐛 Bug Fixes');
|
||||
for (const fix of fixes) console.log(fix);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
if (others.length > 0) {
|
||||
console.log('### 📦 Other Changes');
|
||||
for (const other of others) console.log(other);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
if (chores.length > 0) {
|
||||
console.log('### 🔧 Maintenance');
|
||||
for (const chore of chores) console.log(chore);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('\n## 📦 Installation\n');
|
||||
console.log('```bash');
|
||||
console.log('npx bmad-method install');
|
||||
console.log('```');
|
||||
|
||||
console.log(
|
||||
`\n**Full Changelog**: https://github.com/bmadcode/BMAD-METHOD/compare/${latestTag}...v${nextVersion}`,
|
||||
);
|
||||
|
||||
console.log(`\n---\n📊 **Summary**: ${commits.length} commits since ${latestTag}`);
|
||||
console.log(`🏷️ **Previous tag**: ${latestTag}`);
|
||||
console.log(`🚀 **Next version**: v${nextVersion} (estimated)`);
|
||||
30
tools/semantic-release-sync-installer.js
Normal file
30
tools/semantic-release-sync-installer.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Semantic-release plugin to sync installer package.json version
|
||||
*/
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
// This function runs during the "prepare" step of semantic-release
|
||||
function prepare(_, { nextRelease, logger }) {
|
||||
// Define the path to the installer package.json file
|
||||
const file = path.join(process.cwd(), 'tools/installer/package.json');
|
||||
|
||||
// If the file does not exist, skip syncing and log a message
|
||||
if (!fs.existsSync(file)) return logger.log('Installer package.json not found, skipping');
|
||||
|
||||
// Read and parse the package.json file
|
||||
const package_ = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
|
||||
// Update the version field with the next release version
|
||||
package_.version = nextRelease.version;
|
||||
|
||||
// Write the updated JSON back to the file
|
||||
fs.writeFileSync(file, JSON.stringify(package_, null, 2) + '\n');
|
||||
|
||||
// Log success message
|
||||
logger.log(`Synced installer package.json to version ${nextRelease.version}`);
|
||||
}
|
||||
|
||||
// Export the prepare function so semantic-release can use it
|
||||
module.exports = { prepare };
|
||||
@@ -31,35 +31,18 @@ async function bumpVersion(type = 'patch') {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const currentVersion = getCurrentVersion();
|
||||
const versionParts = currentVersion.split('.').map(Number);
|
||||
let newVersion;
|
||||
console.log(chalk.yellow('⚠️ Manual version bumping is disabled.'));
|
||||
console.log(chalk.blue('🤖 This project uses semantic-release for automated versioning.'));
|
||||
console.log('');
|
||||
console.log(chalk.bold('To create a new release, use conventional commits:'));
|
||||
console.log(chalk.cyan(' feat: new feature (minor version bump)'));
|
||||
console.log(chalk.cyan(' fix: bug fix (patch version bump)'));
|
||||
console.log(chalk.cyan(' feat!: breaking change (major version bump)'));
|
||||
console.log('');
|
||||
console.log(chalk.dim('Example: git commit -m "feat: add new installer features"'));
|
||||
console.log(chalk.dim('Then push to main branch to trigger automatic release.'));
|
||||
|
||||
switch (type) {
|
||||
case 'major': {
|
||||
newVersion = `${versionParts[0] + 1}.0.0`;
|
||||
break;
|
||||
}
|
||||
case 'minor': {
|
||||
newVersion = `${versionParts[0]}.${versionParts[1] + 1}.0`;
|
||||
break;
|
||||
}
|
||||
case 'patch': {
|
||||
newVersion = `${versionParts[0]}.${versionParts[1]}.${versionParts[2] + 1}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(chalk.blue(`Bumping version: ${currentVersion} → ${newVersion}`));
|
||||
|
||||
// Update package.json
|
||||
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
||||
packageJson.version = newVersion;
|
||||
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2) + '\n');
|
||||
|
||||
console.log(chalk.green(`✓ Updated package.json to ${newVersion}`));
|
||||
|
||||
return newVersion;
|
||||
return null;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
Reference in New Issue
Block a user