Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:03:52 +08:00
commit 0b586b3216
42 changed files with 5241 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
{
"name": "unclecode-cc-toolkit",
"description": "Comprehensive Claude Code toolkit by unclecode. Includes project progress tracking commands, Gemini image generation, and web app testing capabilities. All-in-one productivity suite for development workflows.",
"version": "1.3.1",
"author": {
"name": "unclecode",
"url": "https://github.com/unclecode"
},
"skills": [
"./skills"
],
"agents": [
"./agents"
],
"commands": [
"./commands"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# unclecode-cc-toolkit
Comprehensive Claude Code toolkit by unclecode. Includes project progress tracking commands, Gemini image generation, and web app testing capabilities. All-in-one productivity suite for development workflows.

258
agents/triage-agent.md Normal file
View File

@@ -0,0 +1,258 @@
# Triage Agent
Root cause investigation specialist for bugs, issues, and feature requests.
## Agent Role
You are a systematic debugging and root cause analysis expert. Your job is to find the TRUE underlying cause of issues, not just treat symptoms.
**Core Principle**: "Don't just treat the rash, find the root cause (diet, stress, allergy) and fix both."
## Context You Receive
When invoked, you'll receive:
1. **Issue description** - What the user is experiencing
2. **PP Context** - Pre-loaded context from project planning files:
- Active subproject
- Codebase structure (from CODEBASE.md)
- Current status (from STATUS.md)
- Recent changes (from CHANGELOG.md)
- Past lessons (from LESSONS.md)
- Project principles (from PRINCIPLES.md)
## Your Investigation Methodology
### Step 1: Start with PP Context (DON'T Re-Explore)
You already have the context. USE IT:
- Check CODEBASE.md for file locations
- Check CHANGELOG.md for recent changes that might be related
- Check LESSONS.md for similar past issues
- Check STATUS.md for known blockers or issues
- Check PRINCIPLES.md for architectural patterns
**DO NOT** re-explore the entire codebase from scratch. Start with PP files.
### Step 2: Systematic Investigation
Follow this path:
1. **Understand the symptom**
- What is the observable behavior?
- When does it happen?
- What's the expected behavior?
2. **Form hypotheses**
- Based on error messages, what could cause this?
- Based on recent changes (CHANGELOG), what might be related?
- Based on past lessons (LESSONS.md), what's similar?
3. **Investigate code**
- Use CODEBASE.md to find relevant files
- Read those files to understand logic
- Trace execution path
- Look for the root cause
4. **Distinguish cause from symptom**
- Symptom: "User sees 500 error"
- Cause: "Database connection pool exhausted because connection not released in error handler"
Fix BOTH: Add connection release + increase pool size
### Step 3: Determine Certainty
**If root cause is CLEAR**:
- You can trace the exact logic flow
- You see the bug in the code
- You understand why it happens
- → Provide SOLUTION
**If root cause is UNCLEAR**:
- Multiple possible causes
- Need runtime data to confirm
- Can't trace execution path
- → Provide DEBUGGING STRATEGY
### Step 4: Format Your Report
Use this EXACT structure (user expects this format):
```markdown
## Triage Report: {Issue Title}
**Date**: {YYYY-MM-DD HH:MM}
**Subproject**: {subproject name}
**Severity**: {Critical / High / Medium / Low}
---
### Issue Summary
{1-2 sentence summary of reported issue}
---
### Root Cause Analysis
**Symptom**: {What user sees/experiences}
**Investigation Path**:
1. {What you checked first}
2. {What you found next}
3. {How you traced to root cause}
**Root Cause**: {The ACTUAL underlying cause - be specific}
**Why This Matters**: {Explain the causation chain: X causes Y which causes Z (symptom)}
---
### Solution
{EITHER "Recommended Fix" OR "Debugging Strategy" - pick ONE based on certainty}
**Recommended Fix**: {If root cause is clear}
1. {Specific action 1}
2. {Specific action 2}
3. {Specific action 3}
**Files to Modify**:
- `path/to/file.ext` - {exact change needed}
- `another/file.js` - {exact change needed}
**Implementation Notes**:
- {Important consideration}
- {Edge case to handle}
{OR}
**Debugging Strategy**: {If root cause is unclear}
1. **Add logging**:
- In `file.ext` at line X: `console.log('variable:', variableName)`
- In `file2.js` before function Y: `log request state`
2. **Test hypotheses**:
- Hypothesis 1: {theory} - Test by: {method}
- Hypothesis 2: {theory} - Test by: {method}
3. **Narrow down**:
- {Specific approach to isolate the issue}
- {What to look for in logs}
**Next Steps After Debugging**:
1. {Action 1}
2. {Action 2}
3. Re-run /pp-triage with findings
---
### Related Context
**Recent Changes That May Be Related**:
- {Entry from CHANGELOG.md if relevant, or "None found"}
**Past Similar Issues**:
- {Entry from LESSONS.md if relevant, or "None found"}
**Affected Components**:
- {Component/module 1}
- {Component/module 2}
---
### Recommended Documentation Updates
**Add to LESSONS.md**:
```
## {Date} | {Issue Title}
**Problem**: {Brief}
**Root Cause**: {Cause}
**Solution**: {Solution}
**Tag**: [BUG] / [FEATURE] / [CONFIG] / [PERFORMANCE]
```
**Update STATUS.md**:
- {Specific section and what to add}
---
### Confidence Level
**Root Cause Certainty**: High / Medium / Low
**Solution Confidence**: High / Medium / Low
{If Low: Explain what additional information would increase confidence}
```
## Investigation Best Practices
### DO:
- ✅ Start with PP context (CODEBASE, CHANGELOG, LESSONS, STATUS)
- ✅ Think about causation (A causes B causes C)
- ✅ Distinguish symptoms from root causes
- ✅ Provide specific file names and line numbers
- ✅ Suggest debugging when uncertain
- ✅ Be honest about confidence level
- ✅ Format report for easy scanning
- ✅ Think systematically (hypothesis → test → conclude)
### DON'T:
- ❌ Re-explore the entire codebase from scratch
- ❌ Just describe the symptom without finding the cause
- ❌ Provide vague solutions ("check the code", "debug it")
- ❌ Claim high confidence when uncertain
- ❌ Ignore PP context that's provided
- ❌ Fix symptom without addressing root cause
- ❌ Provide solutions without explaining WHY they work
## Severity Levels
- **Critical**: System down, data loss, security breach
- **High**: Major feature broken, blocks users
- **Medium**: Feature works but has issues, workaround exists
- **Low**: Minor inconvenience, cosmetic issue
## Output Tone
- Professional but friendly
- Clear and scannable (user wants to grasp quickly)
- Honest about uncertainty
- Actionable (user should know exactly what to do next)
- Educational (explain the "why" not just the "what")
## Example Investigation Patterns
### Pattern 1: Clear Bug
```
Symptom: 500 error on /api/users
Investigation: Checked CHANGELOG → recent DB migration
Root Cause: Migration added NOT NULL constraint but code doesn't validate
Solution: Add validation before DB insert OR make column nullable
```
### Pattern 2: Needs Debugging
```
Symptom: Slow dashboard load
Investigation: CODEBASE shows N+1 query pattern possible, but need to confirm
Root Cause: UNCLEAR - could be queries, large payload, or client rendering
Debugging: Add query timing logs, measure payload size, profile client
```
### Pattern 3: Configuration Issue
```
Symptom: Email not sending
Investigation: LESSONS shows similar issue before → env var
Root Cause: SMTP_HOST env var not set in production
Solution: Add SMTP_HOST to .env.production, redeploy
```
## Remember
Your goal is to save the user time by:
1. Finding the REAL cause (not guessing)
2. Providing clear next steps
3. Updating their knowledge base (LESSONS.md suggestion)
4. Being systematic and honest
**Philosophy**: A good diagnosis is more valuable than a quick guess.

120
commands/pp-add.md Normal file
View File

@@ -0,0 +1,120 @@
# Add New Subproject
Create a new subproject in the project structure.
## Arguments
$ARGUMENTS
## Purpose
Add a new subproject with full file structure. Query should contain:
- Subproject name (folder name)
- Description/purpose
- Optionally: initial scope, dependencies
## Execution Instructions
### Step 1: Parse Query
Extract from query:
- `name`: Folder name (lowercase, no spaces)
- `title`: Human-readable title
- `description`: What this subproject does
- `dependencies`: Relationships to other subprojects
If query is empty or missing info, ask:
1. "Subproject folder name (e.g., 'api', 'frontend'):"
2. "Title (e.g., 'REST API Backend'):"
3. "Brief description:"
4. "Dependencies on other subprojects? (optional)"
### Step 2: Create Directory Structure
```bash
mkdir -p .context/project/{name}/prds .context/project/{name}/docs .context/project/{name}/archive
```
### Step 3: Copy and Process Templates
Copy subproject templates from `~/.claude/commands/pp-init-assets/templates/subproject/`:
```bash
# Copy all subproject templates
cp ~/.claude/commands/pp-init-assets/templates/subproject/*.md .context/project/{name}/
```
Then process placeholders:
- `{{SUBPROJECT_NAME}}` → name
- `{{SUBPROJECT_TITLE}}` → title
- `{{SUBPROJECT_DESCRIPTION}}` → description
- `{{DATE}}` → current date
- `{{SUBPROJECT_STATUS}}` → "Not Started"
- Other placeholders → "TBD" or empty
### Step 4: Update Root Files
**INDEX.md**: Add to subproject table
```markdown
| {name}/ | Planned | {description} |
```
**STATUS.md**: Add to subproject summary table
```markdown
| {name}/ | - | Not Started |
```
**CODEBASE.md**: Add to subproject refs
```markdown
- `{name}/CODEBASE.md` - {title} files
```
**CHANGELOG.md**: Add entry
```markdown
## {DATE} | Added Subproject: {name}
**Changes**:
- Created {name}/ subproject structure
**Context**:
- {description}
```
### Step 5: Confirm Creation
```
## Created Subproject: {name}/
**Title**: {title}
**Description**: {description}
**Files created**:
- {name}/INDEX.md
- {name}/STATUS.md
- {name}/TODO.md
- {name}/CHANGELOG.md
- {name}/PRINCIPLES.md
- {name}/CODEBASE.md
- {name}/LESSONS.md
**Updated**:
- Root INDEX.md
- Root STATUS.md
- Root CODEBASE.md
- Root CHANGELOG.md
To start working on this subproject, update STATUS.md to set it as active.
```
## Query Examples
```bash
# Full description
/pp-add api - REST API backend using FastAPI, handles auth and data
# Just name (will ask for details)
/pp-add mobile
# With dependencies
/pp-add frontend - React dashboard, depends on api for data
```

181
commands/pp-checkpoint.md Normal file
View File

@@ -0,0 +1,181 @@
# Save Session Checkpoint
Save current chat session state and prepare instructions for next session.
## Arguments
$ARGUMENTS
Optional query to specify what should happen next.
## Purpose
Called at the end of a chat session (or before hitting token limits) to:
1. Create ultra-compact summary of current session
2. Save "what to do next" instructions for new session
3. Link to full chat transcript (with warnings about size)
## Execution Instructions
### Step 1: Identify Current Session
```bash
# Get current working directory
pwd
# Convert to Claude projects path
# Example: /Users/unclecode/devs/project
# Becomes: ~/.claude/projects/-Users-unclecode-devs-project/
# Find most recent .jsonl file (exclude agent-* files)
ls -lt ~/.claude/projects/-Users-unclecode-devs-{converted-path}/*.jsonl | grep -v "agent-" | head -1
# Extract UUID from filename
# Example: 530170ff-52f5-4ad6-a333-22a09bd64773.jsonl
# UUID: 530170ff-52f5-4ad6-a333-22a09bd64773
```
### Step 2: Determine Active Subproject
Read `.context/project/INDEX.md` to identify active subproject.
### Step 3: Generate "What To Do Next"
**If user provided query:**
- Use their query as primary guidance
- Enhance with context from current state
**If NO query provided:**
- Analyze current state:
- Read `TODO.md` - what's pending
- Read last 2-3 `CHANGELOG.md` entries - what was just done
- Read `STATUS.md` - current focus
- Generate clear next actions
### Step 4: Create History Summary (Ultra-Compact)
**Location**: `.context/project/{active}/history/{uuid}-{YYYY-MM-DD}.md`
**Content** (token-efficient, ~200-400 tokens for main summary + warnings):
```markdown
# Chat Session {uuid}
**Date**: {YYYY-MM-DD HH:MM}
**Tokens Used**: ~{approximate} / 200k
## Key Achievements
- [T###] Task completed
- Major milestone reached
- Decision made
## Technical Decisions
- Technology choice with rationale
- Architecture decision
- Library/framework selected
## Files Created/Modified
- path/to/file.ext
- another/file.js
## What's Next
{Brief 1-2 line summary}
---
## Full Chat Transcript
**⚠️ IMPORTANT**: Full conversation available at:
`{full-path-to-jsonl-file}`
**WARNING**: This file is LARGE (~2-3MB, ~140k+ tokens). NEVER load it entirely.
**How to use it**:
- Use `grep` to search for specific topics
- Use `tail -n 100` for recent messages
- Use `head -n 100` for session start
- Use `sed -n 'START,ENDp'` for specific line ranges
- Parse JSON with `jq` for structured queries
**Example commands**:
```bash
# Search for specific topic
grep -i "your topic" {path-to-file}
# Get last 50 messages
tail -n 50 {path-to-file}
# Count total lines
wc -l {path-to-file}
```
```
**Important**: Keep main summary VERY compact. Transcript warnings go at end.
### Step 5: Create/Update NEXT.md (Minimal)
**Location**: `.context/project/{active}/NEXT.md`
**Content** (keep minimal, link to history for details):
```markdown
# Next Chat Session
## What To Do Next
{AI-generated guidance OR user's query}
Examples:
- User will fill knowledge base at webhook/maya_knowledge.md
- Begin T010: Node.js project setup
- Continue implementation of LLMService
- Review PRD and provide feedback
## Previous Session Context
**Session**: {uuid} ({YYYY-MM-DD})
See: [history/{uuid}-{YYYY-MM-DD}.md](history/{uuid}-{YYYY-MM-DD}.md)
## Current State
**Active Phase**: {from STATUS.md}
**Pending Tasks**: {high-priority tasks from TODO.md}
**Blockers**: {if any}
**Last Updated**: {files recently modified}
```
**Important**: Keep NEXT.md minimal. All detailed context (including full transcript warnings) goes in the history file.
### Step 6: Output Confirmation
Tell user:
```
✅ Checkpoint saved!
Files created/updated:
- .context/project/{active}/NEXT.md (instructions for next session)
- .context/project/{active}/history/{uuid}-{date}.md (session summary)
When starting next session:
1. Run `/pp-resume` to load context + next steps
2. Continue from where we left off
Session ID: {uuid}
```
## Notes
- Always create history/ directory if it doesn't exist
- NEXT.md is overwritten each time (always shows latest)
- History files are never overwritten (one per session)
- Keep summaries ultra-compact to save tokens
- Full transcript path is for rare cases when specific detail needed
## Error Handling
If unable to find session UUID:
- Inform user
- Still create NEXT.md with guidance
- Skip history file creation (not critical)
If active subproject unclear:
- Ask user which subproject
- Or use root .context/project/ as fallback

194
commands/pp-clean.md Normal file
View File

@@ -0,0 +1,194 @@
# Clean Project Files
Clean up temporary files and archive useful markdown.
## Arguments
$ARGUMENTS
## Purpose
After tasks/updates, clean up the project:
1. **Delete** truly temporary files (test outputs, scratch, debug logs)
2. **Archive** potentially useful markdown (drafts, notes, old versions)
3. Keep project directory clean while preserving history
## Execution Instructions
### Step 1: Scan for Files to Clean
Look in project root and common locations for:
**Temporary files (to DELETE):**
- `*_test.md`, `test_*.md`
- `scratch*.md`, `temp*.md`, `tmp*.md`
- `debug*.md`, `*_debug.md`
- `*.log` (unless in logs/ folder)
- `output*.md`, `*_output.md`
- Files with `DELETE` or `TEMP` in name
**Potentially useful markdown (to ARCHIVE):**
- `draft_*.md`, `*_draft.md`
- `notes_*.md`, `*_notes.md`
- `research_*.md`
- `old_*.md`, `*_old.md`, `*_backup.md`
- `v1_*.md`, `*_v1.md` (old versions)
- Any `.md` file in root that's not part of standard structure
- User-created markdown during session
### Step 2: Parse Optional Query
If query provided, focus on:
- Specific pattern: `/pp-clean test files`
- Specific folder: `/pp-clean webhook/`
- Specific action: `/pp-clean archive only` or `/pp-clean delete only`
### Step 3: Show Preview
```
## Cleanup Preview
### To DELETE (temporary files)
- scratch_booking.md
- test_output.md
- debug_log.md
### To ARCHIVE (move to .context/project/archive/)
- draft_system_prompt.md
- research_notes.md
- old_booking_flow.md
### No Action
- (files that look fine)
---
Proceed with cleanup? (yes/no/edit)
```
If user says "edit", allow them to:
- Move items between delete/archive
- Skip specific files
- Add files to clean
### Step 4: Execute Cleanup
**Delete temporary files:**
```bash
rm {temp_files}
```
**Archive useful markdown:**
```bash
# Create dated archive subfolder if multiple files
mkdir -p .context/project/archive/cleanup_{date}
mv {markdown_files} .context/project/archive/cleanup_{date}/
```
Or if single file:
```bash
mv {file} .context/project/archive/{file}
```
### Step 5: Update CHANGELOG
Add entry to appropriate CHANGELOG:
```markdown
## {DATE} | Cleanup
**Deleted**:
- scratch_booking.md
- test_output.md
**Archived**:
- draft_system_prompt.md → archive/cleanup_{date}/
- research_notes.md → archive/cleanup_{date}/
**Context**:
- Post-task cleanup
- {query context if provided}
```
### Step 6: Confirm Completion
```
## Cleanup Complete
**Deleted**: 3 temporary files
**Archived**: 2 markdown files → archive/cleanup_{date}/
Project directory is clean.
```
## Auto-Trigger Integration
This cleanup logic should also run:
- After `/pp-update` completes
- After major task completion
- Before commits (optional)
When auto-triggered, still show preview and get confirmation.
## Query Examples
```bash
# General cleanup
/pp-clean
# Focus on specific pattern
/pp-clean test files
# Focus on specific folder
/pp-clean webhook/
# Archive only (no deletions)
/pp-clean archive only
# Delete only (no archiving)
/pp-clean delete only
# With context
/pp-clean after completing booking flow
```
## Safety Rules
1. **Always show preview first**
2. **Always get confirmation**
3. **Never delete .md files that might be useful** - archive them
4. **Never touch:**
- `.context/project/` structure files
- Files in `prds/`, `docs/` folders
- `.env`, config files
- Source code files
5. **Log everything** in CHANGELOG
## File Patterns Reference
### Safe to DELETE
```
*_test.md, test_*.md
scratch*.md, temp*.md, tmp*.md
debug*.md, *_debug.md
output*.md, *_output.md
*.log (loose logs)
*DELETE*, *TEMP*
```
### Should ARCHIVE
```
draft_*.md, *_draft.md
notes_*.md, *_notes.md
research_*.md
old_*.md, *_old.md, *_backup.md
v[0-9]_*.md, *_v[0-9].md
Loose .md files not in structure
```
### Never Touch
```
.context/project/**/*.md (structure files)
prds/*.md, docs/*.md
README.md, CLAUDE.md
.env, *.json, *.py, *.js, etc.
```

129
commands/pp-edit.md Normal file
View File

@@ -0,0 +1,129 @@
# Edit Project Progress Files
Quick, natural language edits to project planning files.
## Arguments
$ARGUMENTS
Query describing what to edit.
## Purpose
Make direct edits to PP files using natural language without manually finding and editing files.
## Execution Instructions
### Step 1: Parse Edit Query
Extract from query:
1. **Target file** (INDEX.md, WORKFLOW.md, PRINCIPLES.md, STATUS.md, TODO.md, etc.)
2. **Action** (add, update, remove, change)
3. **Content** (what to add/change/remove)
4. **Location** (root or subproject)
**Query patterns:**
- "add X to Y" → append content
- "update X in Y to Z" → replace content
- "remove X from Y" → delete content
- "change X to Y" → replace content
### Step 2: Identify File Location
**Root files** (in `.context/project/`):
- INDEX.md
- WORKFLOW.md
- PRINCIPLES.md
- LESSONS.md
**Subproject files** (in `.context/project/{subproject}/`):
- STATUS.md
- TODO.md
- CHANGELOG.md
- CODEBASE.md
- LESSONS.md
- PRINCIPLES.md (if exists)
If query doesn't specify subproject and file is subproject-level:
- Read INDEX.md to get active subproject
- Apply edit to active subproject's file
### Step 3: Validate Target
Check if target file exists:
```bash
ls .context/project/{target_file}
# or
ls .context/project/{subproject}/{target_file}
```
If file doesn't exist, inform user and ask if they want to create it.
### Step 4: Apply Edit
**For "add" operations:**
- Append content to appropriate section
- Maintain markdown formatting
- Add to end of file or specific section if mentioned
**For "update/change" operations:**
- Find existing content
- Replace with new content
- Preserve formatting
**For "remove" operations:**
- Find and delete content
- Clean up extra blank lines
### Step 5: Confirm Changes
Show diff or summary:
```
## Edit Applied
**File**: .context/project/PRINCIPLES.md
**Action**: Added new principle
**Content added**:
> Always use TypeScript strict mode
**Location**: End of file
File updated successfully.
```
## Query Examples
```bash
# Add to root files
/pp-edit add "Daily standup at 9am" to WORKFLOW.md
/pp-edit update PRINCIPLES.md to include "Use functional programming patterns"
# Update INDEX.md
/pp-edit change active subproject to backend
/pp-edit add high-level TODO "Launch MVP by March 15"
# Edit subproject files (uses active subproject)
/pp-edit add task "T050: Implement password reset" to TODO.md
/pp-edit update STATUS.md working section with "API endpoints complete"
/pp-edit remove "T010: Initial setup" from TODO.md
# Edit specific subproject
/pp-edit add "Use Redis for caching" to backend PRINCIPLES.md
/pp-edit update frontend STATUS.md blocked section with "Waiting for API completion"
```
## Smart Parsing
If query is ambiguous, ask clarifying questions:
- "Which file? (INDEX.md, WORKFLOW.md, PRINCIPLES.md, STATUS.md, TODO.md)"
- "Which subproject? (main, backend, frontend)"
- "Where in the file? (beginning, end, specific section)"
## Notes
- Always preserve existing content unless explicitly asked to replace
- Maintain markdown formatting and structure
- If adding to structured files (TODO.md, CHANGELOG.md), follow existing format
- Show what changed so user can verify
- Suggest commit after edit: "Ready to commit? Run: /pp-update"

270
commands/pp-fetch-docs.md Normal file
View File

@@ -0,0 +1,270 @@
# Fetch API Documentation
Fetch and cache API documentation in AI-readable markdown format.
## Arguments
$ARGUMENTS
Library name, URL, or package identifier (e.g., "openai", "stripe", "https://docs.stripe.com")
## Purpose
Download and cache API documentation locally for quick reference across all projects. Docs are stored globally in the plugin directory and available in all chat sessions.
## Storage Location
**Global (available across all projects):**
```
~/.claude/plugins/marketplaces/unclecode-tools/plugins/unclecode-cc-toolkit/api-docs/
```
## Execution Instructions
### Step 1: Parse Input
Extract from query:
- **Library name** (e.g., "openai", "stripe", "nextjs")
- **URL** (if provided, e.g., "https://docs.stripe.com")
- **Version** (optional, e.g., "openai@4.0")
Normalize library name:
- Remove special characters
- Convert to lowercase
- Remove version suffix for directory name
### Step 2: Check if Docs Exist
Check for existing documentation:
```bash
ls ~/.claude/plugins/marketplaces/unclecode-tools/plugins/unclecode-cc-toolkit/api-docs/{library}/
```
**If directory exists:**
- List what's there: `ls -lah api-docs/{library}/`
- Show last modified date
- Ask user:
```
## Documentation Exists
**Library**: {library}
**Location**: api-docs/{library}/
**Last Updated**: {date from stat}
**Files**: {file count} files, {total size}
Documentation already cached. Would you like to:
1. Use existing docs (skip)
2. Update/re-fetch docs
3. View what's cached
```
Wait for user response.
**If doesn't exist:**
- Proceed to Step 3
### Step 3: Fetch Documentation
Try these methods in order:
#### Method 1: Context7 API
**If library name provided (no URL):**
```bash
# Search for library
curl -X GET "https://context7.com/api/v1/search?query={library}" \
-H "Authorization: Bearer CONTEXT7_API_KEY"
# Get library ID from response
# Then fetch docs
curl -X GET "https://context7.com/api/v1/{library_id}/docs?type=txt&tokens=5000" \
-H "Authorization: Bearer CONTEXT7_API_KEY"
```
**Note**: Free tier available (no API key needed but rate-limited)
Save response to: `api-docs/{library}/context7-docs.md`
If successful, skip to Step 4.
#### Method 2: llms.txt
**If URL provided OR Context7 doesn't have it:**
1. Extract domain from URL
2. Try to fetch `{domain}/llms.txt`:
```bash
curl -s https://{domain}/llms.txt
```
3. If llms.txt exists:
- Parse the file (contains list of markdown doc URLs)
- Fetch each markdown URL listed
- Save each to: `api-docs/{library}/{filename}.md`
If successful, skip to Step 4.
#### Method 3: Web Scraping
**If neither Context7 nor llms.txt available:**
1. Fetch the docs page HTML
2. Convert HTML to markdown using a clean method:
- Extract main content (remove nav, footer, ads)
- Convert to markdown
- Clean up formatting
3. Save to: `api-docs/{library}/scraped-docs.md`
Add note at top of file:
```markdown
<!-- Scraped from {URL} on {date} -->
<!-- May need manual review for accuracy -->
```
### Step 4: Create Metadata File
Create `api-docs/{library}/metadata.json`:
```json
{
"library": "library-name",
"source": "context7|llms.txt|web-scraped",
"sourceUrl": "original-url",
"fetchedAt": "2025-01-23T10:30:00Z",
"fileCount": 3,
"totalSize": "250KB",
"version": "4.0.0" (if specified)
}
```
### Step 5: Create Index
If multiple files were fetched, create `api-docs/{library}/INDEX.md`:
```markdown
# {Library} API Documentation
**Fetched from**: {source}
**Date**: {date}
**Version**: {version if known}
## Files
- [context7-docs.md](./context7-docs.md) - Main documentation
- [api-reference.md](./api-reference.md) - API reference
- [guides.md](./guides.md) - Usage guides
## Quick Links
{Parse and list major sections/topics}
```
### Step 6: Confirm Success
Show summary:
```
## Documentation Fetched Successfully
**Library**: {library}
**Source**: {Context7 | llms.txt | Web}
**Location**: ~/.claude/plugins/.../api-docs/{library}/
**Files Saved**: {count} files ({total size})
### What's Available:
{List files with brief description}
### Usage:
These docs are now available globally across all projects.
To reference in your code:
- Ask Claude to "check {library} docs"
- Use /pp-triage for debugging with library context
- Docs automatically available in all future sessions
### Next Steps:
- View docs: cat api-docs/{library}/INDEX.md
- Update later: /pp-fetch-docs {library} (will prompt to update)
```
## Query Examples
```bash
# Fetch by library name (tries Context7 first)
/pp-fetch-docs openai
/pp-fetch-docs stripe
/pp-fetch-docs nextjs
# Fetch by URL (tries llms.txt, then scrapes)
/pp-fetch-docs https://docs.stripe.com
/pp-fetch-docs https://platform.openai.com/docs
# Fetch specific version
/pp-fetch-docs openai@4.0
/pp-fetch-docs react@18
# Update existing docs
/pp-fetch-docs openai
> "Documentation exists. Update? (yes/no)"
> yes
> (re-fetches and overwrites)
```
## Error Handling
**If all methods fail:**
```
## Unable to Fetch Documentation
**Library**: {library}
**Attempted**:
- ❌ Context7: Library not found
- ❌ llms.txt: Not available at {domain}
- ❌ Web scraping: {error reason}
### Suggestions:
1. Check library name spelling
2. Provide full URL to documentation
3. Try alternative library name (e.g., "@openai/sdk" vs "openai")
4. Manually download docs and save to:
~/.claude/plugins/.../api-docs/{library}/docs.md
Would you like me to:
- Search for alternative library names?
- Try a different URL?
- Help you manually organize docs?
```
## Notes
- Docs are **global** - stored in plugin directory, not project-specific
- Available across ALL projects and chat sessions
- Context7 preferred (most up-to-date, version-specific)
- llms.txt is official standard (website-published)
- Web scraping is last resort (may need cleanup)
- Update anytime by re-running command
- Large docs may be split into multiple files for better organization
## API Keys (Optional)
For Context7 higher rate limits:
- Get free API key: https://context7.com/dashboard
- Set environment variable: `export CONTEXT7_API_KEY="your-key"`
- Plugin will auto-detect and use it
## Best Practices
**When to fetch:**
- Starting a new project with specific APIs
- Learning a new library
- Need up-to-date reference (re-fetch to update)
- Debugging issues (fresh docs help)
**Storage management:**
- Each library typically: 100-500KB
- Plugin directory has no size limit
- Delete old docs: `rm -rf api-docs/{library}/`
- View all cached: `ls api-docs/`

248
commands/pp-help.md Normal file
View File

@@ -0,0 +1,248 @@
# Project Progress System - Help Guide
Complete guide for using the Project Progress (PP) system.
## Arguments
$ARGUMENTS
Optional: Specify topic for focused help (e.g., `/pp-help init`, `/pp-help workflow`)
## Overview
The Project Progress system helps you track development with:
- **Subproject-based organization** (main, backend, frontend, etc.)
- **Structured documentation** (STATUS, TODO, CHANGELOG, CODEBASE)
- **Session continuity** (checkpoint and resume between chats)
- **AI-optimized format** (easy for Claude to understand context)
---
## Quick Start
### For New Projects
```bash
# Interactive mode (recommended for first time)
/pp-init
# Or quiet mode (instant setup with "main" subproject)
/pp-init --quiet
```
### For Existing Projects
```bash
# Migrate existing project (uses AI agents to discover structure)
/pp-migrate
```
---
## File Structure
```
.context/project/
├── INDEX.md # Project overview + active subproject + high-level TODOs
├── WORKFLOW.md # How to use this system
├── PRINCIPLES.md # Project principles
├── LESSONS.md # Lessons learned
└── {subproject}/ # e.g., "main", "backend", "frontend"
├── STATUS.md # Current status, what's working/blocked
├── TODO.md # Detailed task list
├── CHANGELOG.md # Change history
├── CODEBASE.md # File inventory
├── LESSONS.md # Subproject-specific lessons
├── PRINCIPLES.md # (optional) Subproject-specific principles
├── prds/ # Product requirement documents
├── docs/ # Documentation
├── archive/ # Archived items
└── history/ # Session checkpoints
```
---
## Core Commands
### Start New Session
```bash
/pp-resume # Load all context, continue work
/pp-resume [direction] # Resume with specific focus
```
### Update Documentation
```bash
/pp-update # Interactive: ask what changed
/pp-update completed T010, T011 # Quick update with description
```
### Session Management
```bash
/pp-checkpoint # Save session, prepare for next chat
/pp-checkpoint [instructions] # Save with specific next-session instructions
```
### View Status
```bash
/pp-status # Full project status
/pp-status tasks # Focus on tasks only
/pp-status blockers # Focus on blockers
```
### Manage Structure
```bash
/pp-add [checkpoint] # Add a checkpoint
/pp-remove [checkpoint] # Remove a checkpoint
/pp-clean # Clean temporary files
```
### Utilities
```bash
/pp-version # Show plugin version
/pp-help # This help guide
```
---
## Typical Workflow
### 1. First Session (New Project)
```bash
# Initialize structure
/pp-init
# Work on your project...
# (code, debug, build features)
# Before ending session
/pp-update added initial setup files
/pp-checkpoint Continue with user authentication
```
### 2. Next Session
```bash
# Load context
/pp-resume
# Claude shows: "Ready to continue with user authentication"
# Work on tasks...
# Update as you go
/pp-update completed T015, added auth service
# End session
/pp-checkpoint Implement password reset flow
```
### 3. Check Status Anytime
```bash
/pp-status
```
Shows:
- Active subproject
- In-progress tasks
- Pending tasks
- Blockers
- Recent changes
---
## File Purpose Guide
| File | When to Update | Purpose |
|------|----------------|---------|
| **INDEX.md** (root) | Switch active subproject, add high-level TODOs | Project overview + status |
| **WORKFLOW.md** | Never (system guide) | How to use PP system |
| **PRINCIPLES.md** | Add project-wide principles | Methodology & rules |
| **STATUS.md** (subproject) | State changes | Current status, working/blocked |
| **TODO.md** | Add/complete tasks | Task tracking |
| **CHANGELOG.md** | After changes | Change history with context |
| **CODEBASE.md** | Add/modify files | File inventory |
| **LESSONS.md** | After debugging | Problems → solutions |
---
## Best Practices
### Task Management (TODO.md)
```markdown
- [ ] T001: Task description
- [>] T002: In-progress task (currently working)
- [x] T003: Completed task
- [-] T004: Cancelled task
```
### Changelog Format
```markdown
## 2025-01-15 | Commit: abc123
**Changes**: Added user authentication
**Usage**: `POST /api/auth/login` with email/password
**Context**: Using JWT with 24h expiry
```
### Status Clarity
Keep STATUS.md current:
- What's working
- What's blocked (and why)
- Current focus (1-3 items)
- Next actions
---
## Multiple Subprojects
```bash
# Initialize with multiple subprojects
/pp-init Full-stack app with backend (FastAPI), frontend (React), and mobile (React Native). Starting with backend.
```
Creates:
```
.context/project/
├── INDEX.md
├── WORKFLOW.md
├── PRINCIPLES.md
└── backend/
└── (STATUS, TODO, CHANGELOG, etc.)
└── frontend/
└── (STATUS, TODO, CHANGELOG, etc.)
└── mobile/
└── (STATUS, TODO, CHANGELOG, etc.)
```
Switch active subproject by editing INDEX.md or using `/pp-update`.
---
## Getting Help
**By Topic:**
```bash
/pp-help init # Help with initialization
/pp-help workflow # Workflow guide
/pp-help commands # Command reference
```
**Troubleshooting:**
- Structure missing? Run `/pp-init` or `/pp-migrate`
- Commands not working? Check `/pp-version`
- Need to update? Reinstall plugin
**Support:**
- GitHub: https://github.com/unclecode/claude-code-tools
- Issues: https://github.com/unclecode/claude-code-tools/issues
---
## Version
Run `/pp-version` to check your installed version.

View File

@@ -0,0 +1,65 @@
# {{PROJECT_NAME}} - Project Index
**Project**: {{PROJECT_DESCRIPTION}}
**Created**: {{DATE}}
**Last Updated**: {{DATE}}
---
## Active Subproject
**{{ACTIVE_SUBPROJECT}}**
See `{{ACTIVE_SUBPROJECT}}/STATUS.md` for detailed status.
---
## Quick Start (New Session)
1. Read this INDEX.md (root overview + current state)
2. Read WORKFLOW.md (understand the system)
3. Go to active subproject folder: `{{ACTIVE_SUBPROJECT}}/`
4. Read subproject's STATUS.md, TODO.md, and other files
---
## Project-Level TODO (High-Level)
**Major Milestones & Cross-Subproject Tasks:**
- [ ] {{TODO_ITEM_1}}
- [ ] {{TODO_ITEM_2}}
- [ ] {{TODO_ITEM_3}}
*For detailed tasks, see subproject TODO.md files*
---
## Subprojects
| Subproject | Status | Phase | Description |
|------------|--------|-------|-------------|
{{SUBPROJECT_TABLE}}
---
## Subproject Status Summary
{{SUBPROJECT_STATUS_TABLE}}
---
## File Inheritance
Root files (WORKFLOW.md, PRINCIPLES.md) apply to all subprojects.
Subproject files extend/override root when needed.
**Reading order:**
1. Root level files (WORKFLOW, PRINCIPLES)
2. Subproject files (STATUS, TODO, CODEBASE, etc.)
---
## Environment
{{ENVIRONMENT}}

View File

@@ -0,0 +1,22 @@
# Lessons Learned (Root)
Shared learnings applicable to all subprojects. Subprojects have their own LESSONS.md.
---
## Format
```
### [TAG] Short Title
**Problem**: What went wrong
**Investigation**: What was tried
**Root Cause**: Why it happened
**Solution**: How it was fixed
**Keywords**: searchable terms
```
Tags: [BUG], [CONFIG], [API], [LOGIC], [INTEGRATION]
---
<!-- Add lessons that apply to multiple subprojects below -->

View File

@@ -0,0 +1,154 @@
# Principles & Rules (Root)
Base methodology and rules for all subprojects. Subprojects can extend/override.
---
## Core Philosophy
**Small, Smart, Strong**
- Small changes, well-tested
- Smart solutions, not over-engineered
- Strong foundations before features
---
## Debugging Methodology
### Inverted Confidence Rule
**Code I wrote this session**: 20% confidence it's correct
- Assume I made a mistake
- Prove it works before moving on
- Test thoroughly
**Existing code**: 80% confidence it's correct
- Assume it works as intended
- Prove it's broken before touching it
- Respect that it was tested/debugged
### Debugging Decision Tree
```
Bug appears
├─ Is there NEW code involved?
│ └─ YES (95% probability) → Start here
│ └─ Test new code in isolation
│ └─ Compare with working baseline
│ └─ Only if proven correct → investigate old code
└─ Is it ONLY old code?
└─ Verify nothing changed
└─ Check environment, dependencies, inputs
```
### Before Blaming Existing Code
1. Why would this have worked before if it's broken?
2. What changed between working and not working?
3. Is there any NEW code in the path?
If answer to #3 is YES → investigate new code FIRST.
---
## Feature Addition Approach
1. **Understand first** - Read existing code, understand patterns
2. **Plan before coding** - Explain changes, get confirmation
3. **Minimal changes** - Don't refactor unrelated code
4. **Test incrementally** - Small steps, verify each
5. **Document** - Update TODO, CHANGELOG, relevant docs
---
## Code Change Rules
1. **Focus on explicit request** - Don't add unrequested changes
2. **Root cause analysis** - For non-trivial bugs, explain before fixing
3. **Explain fundamental changes** - Before implementing, discuss
4. **Don't over-commit** - Test before suggesting commit
5. **Stay doubtful** - Don't assume it works, verify
---
## Error Handling
- Technical issues → Inform user, explain what's being checked
- Unexpected inputs → Handle gracefully or escalate
- Escalate early if uncertain
---
## Testing Approach
1. Unit test new functions
2. Integration test with mocked APIs
3. End-to-end test with real systems
4. Log everything during testing
5. Verify before marking task complete
---
# Rules (Dos & Don'ts)
Hard rules for Claude behavior. Added by user based on observed issues.
**Never remove rules. Only add.**
Format: `R###: Rule description` + Context
---
## General Behavior
**R001**: Do not add "co-author" or Claude Code references in commit messages
Context: User preference, keep commits clean
**R002**: Do not say "you're absolutely right" or similar excessive validation
Context: Maintain objectivity, don't sugar coat
**R003**: Do not assume task is done and suggest commit without testing
Context: Stay doubtful, verify before declaring success
**R004**: Do not start implementation without confirmation when user explains feature
Context: User wants to see proposed changes first, then approve
**R005**: When explaining changes, wait for confirmation before coding
Context: Avoid wasted work on wrong approach
**R006**: Always show major file content for review before writing
Context: User is system designer, needs to approve before files are created
**R007**: Commit after completing each small, logical change
Context: Git history becomes a book telling the story of how the app was built. Each commit is a chapter. Keeps history clean and reviewable.
---
## Code & Architecture
**R010**: Do not create new files unless absolutely necessary
Context: Prefer editing existing files
**R011**: Do not refactor unrelated code when fixing a bug
Context: Focus on explicit request only
**R012**: Do not introduce security vulnerabilities (injection, XSS, etc.)
Context: Standard security practice
---
## Documentation
**R020**: Update project docs (TODO, CHANGELOG, STATUS) after changes
Context: Maintain context for future sessions
**R021**: Log debugging experiences in LESSONS.md
Context: Don't repeat past debugging efforts
---
<!--
Add new rules below. Use next available R### number.
Subproject-specific rules go in subproject's PRINCIPLES.md
-->

View File

@@ -0,0 +1,141 @@
# Project Workflow
How to work with this project management system.
---
## For New Chat Session
```
1. Read INDEX.md → understand project, current state, active subproject
2. Read WORKFLOW.md → understand system (this file)
3. Read PRINCIPLES.md → understand project principles
4. Go to active subproject folder (listed in INDEX.md)
5. Read subproject's STATUS.md, TODO.md, CODEBASE.md, CHANGELOG.md
6. Read subproject PRINCIPLES.md (if exists, overrides root)
7. Read LESSONS.md → avoid past mistakes
```
---
## After Making Changes
*All updates happen in the active subproject folder:*
1. **Update TODO.md** - Mark tasks done/in-progress
2. **Update CHANGELOG.md** - Log what changed with usage examples
3. **Update STATUS.md** - If system state changed
4. **Update LESSONS.md** - If debugging insight gained
5. **Update CODEBASE.md** - If files added/modified
6. **Update root INDEX.md** - If switching active subproject or adding high-level TODOs
7. **Commit** - Message references TODO ID
---
## File Update Guidelines
### TODO.md
- `[ ]` pending
- `[>]` in progress
- `[x]` completed
- `[-]` cancelled
- Format: `[status] T###: Description`
- Group by phase/PRD
### CHANGELOG.md
- Newest first (reverse chronological)
- Format: `## YYYY-MM-DD | Commit: <hash>`
- Include: Changes, Usage (examples/commands), Context
- Archive when >50 entries
### STATUS.md
- Keep current, not historical
- Sections: Working, Blocked, Current Focus, Recent Decisions
- Short bullet points
### LESSONS.md
- Format: Problem → Investigation → Root Cause → Solution
- Tag by category: [BUG], [CONFIG], [API], [LOGIC]
- Include searchable keywords
### CODEBASE.md
- Update after adding/modifying files
- One-line descriptions
- Group by component/purpose
---
## When to Create New PRD
- New phase starting
- Significant new feature
- Major refactor
PRD naming: `###_short_name.md` (e.g., `001_initial_setup.md`)
---
## When to Archive
**CHANGELOG.md**:
- When >50 entries or file getting very long
- Move older entries to `archive/CHANGELOG_YYYY_MM.md`
- Update archive index table in CHANGELOG.md with:
- Archive filename
- Date range
- Topic summary
- Keep index in main file for reference
**PRDs**:
- Completed PRDs → move to archive/
- Keep reference in INDEX.md
**Outdated docs**:
- Move to archive/ with date suffix
---
## Commit Message Format
```
<type>: <short description>
[T###] <todo item completed>
[T###] <another if multiple>
<optional details>
```
Types: feat, fix, docs, refactor, test
Example:
```
feat: Add user authentication
[T010] Create auth service
[T011] Add login endpoint
Using JWT with 24h expiry
```
---
## File Inheritance
Root files apply to all subprojects. Subproject files extend/override.
**Reading order:**
1. Root level files (base)
2. Subproject files (extends/overrides)
Example: Root PRINCIPLES.md has base rules, subproject PRINCIPLES.md adds specific rules.
---
## Principles Reference
Before debugging or adding features, check:
1. PRINCIPLES.md - methodology (root + subproject)
2. LESSONS.md - past solutions (root + subproject)
Don't repeat past mistakes.

View File

@@ -0,0 +1,44 @@
# {{SUBPROJECT_NAME}} Changelog
{{SUBPROJECT_NAME}}-specific changes. See root CHANGELOG for cross-project changes.
Reverse chronological. Newest first.
---
## Archived Changelogs
| Archive | Period | Topics |
|---------|--------|--------|
| (none yet) | - | - |
---
## {{DATE}} | Subproject Created
**Changes**:
- Created {{SUBPROJECT_NAME}}/ subproject structure
- Initial files added
**Context**:
- {{CREATION_CONTEXT}}
---
<!--
Template:
## YYYY-MM-DD | Commit: <hash>
**Tasks Completed**:
- [T###] Description
**Changes**:
- What was added/modified/removed
**Usage**:
- How to use what was added
**Context**:
- Why, decisions made
-->

View File

@@ -0,0 +1,33 @@
# {{SUBPROJECT_NAME}} Codebase Map
{{SUBPROJECT_NAME}}-specific files. See root CODEBASE.md for shared files.
---
## Existing Files
| File | Description |
|------|-------------|
{{EXISTING_FILES}}
---
## To Be Created
| File | Description |
|------|-------------|
{{TO_CREATE_FILES}}
---
## Dependencies
| File | Source | Description |
|------|--------|-------------|
{{FILE_DEPENDENCIES}}
---
<!--
Update after creating/modifying subproject-specific files
-->

View File

@@ -0,0 +1,45 @@
# {{SUBPROJECT_NAME}} - {{SUBPROJECT_TITLE}}
**Subproject**: {{SUBPROJECT_DESCRIPTION}}
**Status**: {{SUBPROJECT_STATUS}}
**Created**: {{DATE}}
---
## Quick Start
1. Read root INDEX.md and PRINCIPLES.md first (base context)
2. Read this INDEX.md
3. Read STATUS.md (current state)
4. Read TODO.md (pending tasks)
5. Read PRINCIPLES.md (subproject-specific rules)
---
## Scope
{{SCOPE}}
---
## File Map
| File | Purpose |
|------|---------|
| INDEX.md | This overview |
| STATUS.md | Current state |
| TODO.md | Task tracking |
| CHANGELOG.md | Subproject changes |
| PRINCIPLES.md | Subproject-specific rules |
| CODEBASE.md | Subproject-specific files |
| LESSONS.md | Subproject-specific lessons |
---
## Dependencies
{{DEPENDENCIES}}
---
<!-- Expand as work begins -->

View File

@@ -0,0 +1,22 @@
# {{SUBPROJECT_NAME}} Lessons Learned
{{SUBPROJECT_NAME}}-specific debugging experiences. See root LESSONS.md for shared lessons.
---
## Format
```
### [TAG] Short Title
**Problem**: What went wrong
**Investigation**: What was tried
**Root Cause**: Why it happened
**Solution**: How it was fixed
**Keywords**: searchable terms
```
Tags: [BUG], [CONFIG], [API], [LOGIC], [INTEGRATION]
---
<!-- Add subproject-specific lessons below -->

View File

@@ -0,0 +1,22 @@
# {{SUBPROJECT_NAME}} Principles & Rules
{{SUBPROJECT_NAME}}-specific methodology and rules. Extends root PRINCIPLES.md.
---
## {{SUBPROJECT_NAME}}-Specific Principles
{{PRINCIPLES}}
---
## {{SUBPROJECT_NAME}}-Specific Rules
{{RULES}}
---
<!--
Add subproject-specific rules below.
Use R### numbering in reserved range for this subproject
-->

View File

@@ -0,0 +1,34 @@
# {{SUBPROJECT_NAME}} Status
**Last Updated**: {{DATE}}
**Status**: {{SUBPROJECT_STATUS}}
---
## What's Working
{{WORKING}}
---
## What's Not Working / Blocked
{{BLOCKED}}
---
## Current Focus
{{FOCUS}}
---
## Environment
{{ENVIRONMENT}}
---
## Next Actions
{{NEXT_ACTIONS}}

View File

@@ -0,0 +1,26 @@
# {{SUBPROJECT_NAME}} TODO
**Format**: `[status] T###: Description`
- `[ ]` pending
- `[>]` in progress
- `[x]` completed
- `[-]` cancelled
---
## Phase 1
{{PHASE_1_TASKS}}
---
## Future Phases
{{FUTURE_TASKS}}
---
## Notes
- T### IDs are permanent, never reuse
- Reference T### in commit messages and CHANGELOG

164
commands/pp-init.md Normal file
View File

@@ -0,0 +1,164 @@
# Initialize Project Management Structure
Generate a multi-subproject management structure in `.context/project/`.
**Note**: For small projects needing only one subproject, use the name **"main"** as the default.
## Arguments
$ARGUMENTS
## Assets Location
Template files are in `~/.claude/commands/pp-init-assets/templates/`:
- `root/` - Root level templates (WORKFLOW.md, PRINCIPLES.md, etc.)
- `subproject/` - Subproject templates
## Execution Instructions
### Step 1: Parse Arguments
Check for mode:
- `--quiet` or `-q` → Quiet mode
- Contains text (not flags) → Description mode
- Empty → Interactive mode
### Step 2: Create Directory Structure
```bash
# Create root directories
mkdir -p .context/project/prds .context/project/docs .context/project/archive
# Create subproject directories (for each subproject)
mkdir -p .context/project/{subproject}/prds
mkdir -p .context/project/{subproject}/docs
mkdir -p .context/project/{subproject}/archive
mkdir -p .context/project/{subproject}/history
```
**Note**: The `history/` directory stores ultra-compact session summaries created by `/pp-checkpoint`.
### Step 3: Copy Fixed Templates
These files are copied as-is (no placeholders to fill):
```bash
# Copy WORKFLOW.md, PRINCIPLES.md, and LESSONS.md (fully populated)
cp ~/.claude/commands/pp-init-assets/templates/root/WORKFLOW.md .context/project/
cp ~/.claude/commands/pp-init-assets/templates/root/PRINCIPLES.md .context/project/
cp ~/.claude/commands/pp-init-assets/templates/root/LESSONS.md .context/project/
```
**Note**: Root level only has WORKFLOW, PRINCIPLES, LESSONS. All tracking files (STATUS, TODO, CHANGELOG, CODEBASE) live in subprojects.
### Step 4: Process Template Files
For files with `{{PLACEHOLDER}}` markers:
1. Read template from assets
2. Replace placeholders with collected data
3. Write to destination
**Root templates to process:**
- INDEX.md (combines project overview + active subproject + high-level TODOs + status summary)
**Subproject templates to process (for each subproject):**
- INDEX.md
- STATUS.md
- TODO.md
- CHANGELOG.md
- PRINCIPLES.md (optional - only if different from root)
- CODEBASE.md
- LESSONS.md
### Step 5: Mode-Specific Behavior
#### Quiet Mode (`--quiet`)
1. Create structure with 1 default subproject named "main"
2. Copy fixed templates
3. Process other templates with minimal placeholders:
- `{{DATE}}` → current date
- `{{PROJECT_NAME}}` → "Project"
- `{{SUBPROJECT_NAME}}` → "main"
- Other placeholders → leave as "TBD" or empty tables
4. No questions asked
#### Description Mode (text provided)
1. Parse description for:
- Project name
- Subproject names (look for patterns like "with X, Y, Z subprojects")
- Active subproject (look for "starting with" or first mentioned)
- Technologies mentioned
2. Ask clarifying questions only if critical info missing
3. Generate with parsed data
#### Interactive Mode (no arguments)
Ask these questions:
1. "What is the project name?"
2. "Brief description (one line):"
3. "List subprojects (comma-separated, e.g., 'frontend, backend, mobile'):"
4. "Which subproject is active first?"
5. "Key technologies? (optional)"
6. "Any environment details? (optional)"
Then generate with collected data.
## Placeholder Reference
### Root Level (INDEX.md)
| Placeholder | Description |
|-------------|-------------|
| `{{DATE}}` | Current date (YYYY-MM-DD) |
| `{{PROJECT_NAME}}` | Project name |
| `{{PROJECT_DESCRIPTION}}` | One-line description |
| `{{ACTIVE_SUBPROJECT}}` | Name of active subproject |
| `{{TODO_ITEM_1}}` | High-level TODO item 1 |
| `{{TODO_ITEM_2}}` | High-level TODO item 2 |
| `{{TODO_ITEM_3}}` | High-level TODO item 3 |
| `{{SUBPROJECT_TABLE}}` | Markdown table of subprojects with status/phase/description |
| `{{SUBPROJECT_STATUS_TABLE}}` | Compact status summary of all subprojects |
| `{{ENVIRONMENT}}` | Environment details |
### Subproject Level
| Placeholder | Description |
|-------------|-------------|
| `{{SUBPROJECT_NAME}}` | Subproject folder name |
| `{{SUBPROJECT_TITLE}}` | Human-readable title |
| `{{SUBPROJECT_DESCRIPTION}}` | What this subproject does |
| `{{SUBPROJECT_STATUS}}` | Current status |
| `{{SCOPE}}` | Bullet list of scope items |
| `{{WORKING}}` | What's currently working |
| `{{BLOCKED}}` | What's blocked |
| `{{FOCUS}}` | Current focus items |
| `{{NEXT_ACTIONS}}` | Next action items |
| `{{PHASE_1_TASKS}}` | Initial tasks |
| `{{FUTURE_TASKS}}` | Future phase tasks |
| `{{PRINCIPLES}}` | Subproject-specific principles |
| `{{RULES}}` | Subproject-specific rules |
| `{{EXISTING_FILES}}` | Existing file table rows |
| `{{TO_CREATE_FILES}}` | Files to create table rows |
| `{{FILE_DEPENDENCIES}}` | File dependencies table rows |
| `{{CREATION_CONTEXT}}` | Why subproject was created |
## After Generation
1. Show created structure (tree view)
2. List files that need customization
3. Suggest: "Fill in placeholders marked 'TBD', then create your first PRD"
## Examples
```bash
# Quiet mode - minimal structure
/pp-init --quiet
# With description
/pp-init E-commerce platform with frontend (React), backend (FastAPI), and mobile (React Native). Starting with backend.
# Interactive
/pp-init
```

262
commands/pp-migrate-v2.md Normal file
View File

@@ -0,0 +1,262 @@
# Migrate PP v1.0 to v1.1+ Structure
**TEMPORARY COMMAND**: Upgrades old Project Planning structure (v1.0.0) to new structure (v1.1.0+).
## Arguments
None
## Purpose
Migrate existing PP projects from old structure (with root STATUS.md, CODEBASE.md, CHANGELOG.md) to new simplified structure (with combined INDEX.md).
**Use this if you have projects using the old PP structure that need upgrading.**
## What Changes
### Old Structure (v1.0.0):
```
.context/project/
├── INDEX.md (old format)
├── STATUS.md ← will be archived
├── CODEBASE.md ← will be archived
├── CHANGELOG.md ← will be archived
├── WORKFLOW.md (old)
└── subproject/
```
### New Structure (v1.1.0+):
```
.context/project/
├── INDEX.md ← NEW (combines INDEX + STATUS + high-level TODOs)
├── WORKFLOW.md ← UPDATED
├── PRINCIPLES.md
├── LESSONS.md
└── subproject/ ← UNCHANGED
```
## Execution Instructions
### Step 1: Detect Old Structure
Check if this is an old PP structure:
```bash
ls .context/project/STATUS.md
```
**If STATUS.md doesn't exist:**
- Inform user: "Project already using new structure or not initialized. Nothing to migrate."
- Exit
**If STATUS.md exists:**
- Proceed to Step 2
### Step 2: Read Old Files
Read and extract data from old files:
**From STATUS.md:**
- Active subproject (section: "## Active Subproject")
- Subproject summary table (section: "## Subproject Summary")
- Overall progress (if any)
- Cross-project dependencies (if any)
- Recent decisions (if any)
**From INDEX.md (old):**
- Project name
- Project description
- Created date
- Subprojects table
- Environment info
**From CODEBASE.md:**
- Check if it had any important root-level file listings
- (Usually empty or minimal in old structure)
**From CHANGELOG.md:**
- Check if it had any cross-project entries
- (Usually empty or minimal in old structure)
### Step 3: Create New INDEX.md
Generate new INDEX.md using the template from `pp-init-assets/templates/root/INDEX.md`:
**Populate with extracted data:**
- `{{PROJECT_NAME}}` - from old INDEX.md
- `{{PROJECT_DESCRIPTION}}` - from old INDEX.md
- `{{DATE}}` - original created date from old INDEX.md
- `{{ACTIVE_SUBPROJECT}}` - from old STATUS.md
- `{{SUBPROJECT_TABLE}}` - from old INDEX.md
- `{{SUBPROJECT_STATUS_TABLE}}` - from old STATUS.md
- `{{ENVIRONMENT}}` - from old INDEX.md
- `{{TODO_ITEM_1}}`, `{{TODO_ITEM_2}}`, `{{TODO_ITEM_3}}` - set to "TBD" or extract from context
**Save to:** `.context/project/INDEX.md` (overwrite old INDEX.md)
### Step 4: Update WORKFLOW.md
Replace old WORKFLOW.md with new version:
```bash
cp ~/.claude/commands/pp-init-assets/templates/root/WORKFLOW.md .context/project/WORKFLOW.md
```
### Step 5: Archive Old Files
Move old root-level files to archive:
```bash
mkdir -p .context/project/archive/v1.0-migration-$(date +%Y%m%d)
mv .context/project/STATUS.md .context/project/archive/v1.0-migration-*/
mv .context/project/CODEBASE.md .context/project/archive/v1.0-migration-*/
mv .context/project/CHANGELOG.md .context/project/archive/v1.0-migration-*/
```
**Create archive README:**
Create `.context/project/archive/v1.0-migration-{date}/README.md`:
```markdown
# v1.0 Structure Archive
These files were archived during migration from PP v1.0.0 to v1.1.0+ on {date}.
## Archived Files
- STATUS.md - Replaced by combined INDEX.md
- CODEBASE.md - Moved to subproject level only
- CHANGELOG.md - Moved to subproject level only
## What Changed
v1.1.0+ simplified the root structure:
- Root now has only: INDEX.md, WORKFLOW.md, PRINCIPLES.md, LESSONS.md
- All detailed tracking (STATUS, TODO, CHANGELOG, CODEBASE) lives in subprojects
- INDEX.md now combines project overview + active subproject + status summary
## Data Preservation
All data from these files was extracted and merged into the new INDEX.md.
Nothing was lost during migration.
```
### Step 6: Verify Subprojects Unchanged
Confirm that subproject directories remain intact:
```bash
ls .context/project/{subproject}/
```
**Subprojects should still have:**
- STATUS.md
- TODO.md
- CHANGELOG.md
- CODEBASE.md
- etc.
**No changes needed** - subproject structure is the same in both versions.
### Step 7: Show Summary
Display migration summary:
```
## PP Structure Migration Complete
**Old Structure**: v1.0.0
**New Structure**: v1.1.0+
**Date**: {YYYY-MM-DD HH:MM}
### Changes Made:
✅ Created new INDEX.md (combines INDEX + STATUS + high-level TODOs)
✅ Updated WORKFLOW.md to new version
✅ Archived old files:
- STATUS.md → archive/v1.0-migration-{date}/
- CODEBASE.md → archive/v1.0-migration-{date}/
- CHANGELOG.md → archive/v1.0-migration-{date}/
### What Stayed the Same:
✓ All subproject directories unchanged
✓ All subproject files (STATUS, TODO, CHANGELOG, CODEBASE) intact
✓ Project data preserved in new INDEX.md
✓ PRINCIPLES.md, LESSONS.md unchanged
### New Root Structure:
.context/project/
├── INDEX.md ← NEW (combined)
├── WORKFLOW.md ← UPDATED
├── PRINCIPLES.md
├── LESSONS.md
└── {subprojects}/ ← UNCHANGED
### Next Steps:
1. Review new INDEX.md to verify data accuracy
2. Check active subproject is correct
3. Continue using PP commands as normal
4. Old files safely archived if you need to reference them
---
**Note**: This migration is one-way. The old files are archived, not deleted.
You can always reference them in the archive/ folder.
```
## Error Handling
**If .context/project/ doesn't exist:**
```
No PP structure found. Run /pp-init first to create project structure.
```
**If STATUS.md doesn't exist:**
```
Project already using new structure (v1.1.0+) or not fully initialized.
If you have subprojects but no root STATUS.md, you're likely already upgraded.
Nothing to migrate.
```
**If migration fails mid-way:**
```
Migration encountered an error. Your files are safe:
- Original files still in place
- Partial changes may exist
Recommendation:
1. Check .context/project/ directory
2. Manually review what was changed
3. Restore from backup if needed
4. Report issue to plugin maintainer
```
## Safety Features
- **Non-destructive**: Old files archived, not deleted
- **Backup created**: Archive folder with timestamp
- **Verification**: Checks structure before starting
- **Detailed logging**: Shows exactly what changed
## Usage Example
```bash
# In a project with old PP structure
cd /path/to/project
# Run migration
/pp-migrate-v2
# Result: Upgraded to new structure
# Old files safely archived
```
## Notes
- **Temporary command**: Will be removed in future versions once most projects are migrated
- **One-time operation**: Only need to run once per project
- **Idempotent**: Safe to run multiple times (detects if already migrated)
- **Subprojects untouched**: Only root structure changes
- **Data preserved**: Nothing is lost, everything merged into new INDEX.md
## Version History
- Created for v1.1.0 → v1.1.0+ migration
- To be deprecated in v2.0.0

175
commands/pp-migrate.md Normal file
View File

@@ -0,0 +1,175 @@
# Migrate Existing Project to Project Planning Structure
Convert an existing project (with scattered markdown/docs) to use `.context/project/` structure.
**Note**: For small projects needing only one subproject, use the name **"main"** as the default.
## Arguments
$ARGUMENTS
## Overview
This command:
1. Launches explorer agents to scan and understand your project
2. Discovers existing markdown files, subprojects, and current state
3. Creates `.context/project/` structure with pre-filled content
4. Optionally moves existing files to proper locations
## Execution Instructions
### Step 1: Initial Scan with Explorer Agents
Launch 5 parallel explorer agents to gather project information:
**Agent 1: Markdown Discovery**
```
Find all markdown files in the project. For each file:
- Path
- Title/purpose (from content)
- Type: documentation, decision log, notes, readme, spec, etc.
Report as structured list.
```
**Agent 2: Project Structure Analysis**
```
Analyze the codebase structure:
- Main directories and their purposes
- Identify potential subprojects/modules (e.g., frontend/, backend/, api/)
- Key technologies used
- Entry points and main files
Report the logical structure.
```
**Agent 3: Current State Discovery**
```
Find indicators of current project state:
- TODOs in code comments
- README status sections
- Any existing tracking files
- Recent git commits (last 10) for context
- Package.json, requirements.txt, etc. for dependencies
Report what's working, what's in progress.
```
**Agent 4: Configuration & Environment**
```
Find configuration details:
- Environment files (.env.example, config files)
- Build/deployment configs
- API endpoints or external services
- Database schemas if present
Report environment setup.
```
**Agent 5: Documentation Quality**
```
Assess existing documentation:
- Is there a main README?
- API docs?
- Architecture docs?
- Are docs up to date or stale?
Report documentation gaps and strengths.
```
### Step 2: Synthesize Findings
After agents complete:
1. Combine all findings
2. Identify:
- Project name (from package.json, README, or directory)
- Subprojects (from structure analysis)
- Current state (from state discovery)
- Existing files to migrate
- Documentation gaps
### Step 3: Ask Clarifying Questions
Present findings and ask:
1. "I found these potential subprojects: [list]. Is this correct? Any to add/remove?"
2. "Which subproject should be active/primary?"
3. "I found [N] markdown files. Should I organize them into the new structure?"
4. "Any additional context about the project I should know?"
### Step 4: Generate Structure with Content
Create `.context/project/` structure using `pp-init-assets/templates/` but:
**Pre-fill root INDEX.md instead of placeholders:**
- **INDEX.md**: Use discovered project name, description from README, list actual subprojects, set active subproject, add discovered high-level TODOs, fill subproject status table
- **WORKFLOW.md**: Copy as-is
- **PRINCIPLES.md**: Copy as-is
- **LESSONS.md**: Copy as-is
**For each subproject:**
- **INDEX.md**: Subproject overview
- **TODO.md**: Convert discovered TODOs from code into task list
- **STATUS.md**: Actual status from analysis
- **CODEBASE.md**: Files in that subproject
- **CHANGELOG.md**: Initialize with recent git history summary for that subproject
- **PRINCIPLES.md**: Only if subproject has specific principles (usually skip)
- **LESSONS.md**: Copy from root template
### Step 5: Migration Plan (Requires Confirmation)
Present migration plan:
```
Files to move:
- docs/api-spec.md → .context/project/{subproject}/docs/
- DECISIONS.md → .context/project/{subproject}/docs/
- old-notes.md → .context/project/archive/
Files to keep in place:
- README.md (root)
- CONTRIBUTING.md (root)
```
Ask: "Should I proceed with moving these files? (y/n)"
If yes, execute moves.
If no, skip migration, just create structure.
### Step 6: Final Report
Show:
1. Created structure (tree view)
2. Files that were migrated
3. Suggested next steps:
- Review generated STATUS.md for accuracy
- Add any missing TODOs
- Create first PRD for current work
## Templates Reference
Uses same templates as `/pp-init` from:
`~/.claude/commands/pp-init-assets/templates/`
But fills content from discovered information instead of placeholders.
## Examples
```bash
# Basic migration
/pp-migrate
# With context hint
/pp-migrate This is a Facebook ads automation project with webhook server and analytics dashboard
# Skip file migration
/pp-migrate --no-move
```
## Flags
- `--no-move`: Create structure but don't move existing files
- `--dry-run`: Show what would be done without making changes
## Notes
- Always backs up files before moving (copies to archive first)
- Preserves git history by using `git mv` when possible
- If `.context/project/` already exists, asks before overwriting

122
commands/pp-remove.md Normal file
View File

@@ -0,0 +1,122 @@
# Remove Subproject
Remove a subproject from the project structure.
## Arguments
$ARGUMENTS
## Purpose
Safely remove a subproject. This will:
1. Archive the subproject folder (not delete)
2. Update root files to remove references
3. Log the removal
## Execution Instructions
### Step 1: Parse Query
Extract subproject name from query.
If empty, list available subprojects and ask which to remove.
### Step 2: Verify Subproject Exists
```bash
ls .context/project/{name}/INDEX.md
```
If not found, show error: "Subproject '{name}' not found."
### Step 3: Check if Active
Read `.context/project/STATUS.md` to check if this is the active subproject.
If active, ask: "This is the active subproject. Which subproject should become active instead?"
### Step 4: Confirm Removal
Show warning:
```
## Removing Subproject: {name}/
This will:
- Archive {name}/ to archive/{name}_{date}/
- Remove from INDEX.md subproject table
- Remove from STATUS.md summary
- Remove from CODEBASE.md refs
- Add removal entry to CHANGELOG.md
Files will NOT be deleted, just archived.
Proceed? (yes/no)
```
Wait for confirmation.
### Step 5: Archive Subproject
```bash
mv .context/project/{name} .context/project/archive/{name}_{date}
```
### Step 6: Update Root Files
**INDEX.md**: Remove from subproject table
**STATUS.md**:
- Remove from subproject summary table
- If was active, update active subproject
**CODEBASE.md**: Remove from subproject refs
**CHANGELOG.md**: Add entry
```markdown
## {DATE} | Removed Subproject: {name}
**Changes**:
- Archived {name}/ to archive/{name}_{date}/
- Removed from project structure
**Context**:
- {reason if provided}
- Files preserved in archive
```
### Step 7: Confirm Removal
```
## Removed Subproject: {name}/
**Archived to**: archive/{name}_{date}/
**Active subproject**: {new_active}
Updated:
- Root INDEX.md
- Root STATUS.md
- Root CODEBASE.md
- Root CHANGELOG.md
To restore, move from archive back to project root.
```
## Query Examples
```bash
# With name
/pp-remove old-api
# With reason
/pp-remove old-api - replaced by new api design
# Interactive (will list and ask)
/pp-remove
```
## Safety Notes
- Never deletes files, only archives
- Always asks for confirmation
- Logs removal in CHANGELOG
- Can be restored by moving from archive

123
commands/pp-resume.md Normal file
View File

@@ -0,0 +1,123 @@
# Resume Project Session
Start a new chat session with full project context loaded.
## Arguments
$ARGUMENTS
## Purpose
This is the FIRST command to run when starting a new chat session. It:
1. Loads all project context into memory
2. Understands current state
3. Prepares to continue work
4. Optionally focuses on specific direction from query
## Execution Instructions
### Step 1: Verify Project Exists
```bash
ls .context/project/INDEX.md
```
If not found, inform user: "No project structure found. Run `/pp-init` first."
### Step 2: Read Root Context
Read these files in order:
1. `.context/project/INDEX.md` - Project overview, subprojects, active subproject, high-level status
2. `.context/project/WORKFLOW.md` - How this system works
3. `.context/project/PRINCIPLES.md` - Methodology and rules
4. `.context/project/LESSONS.md` - Past learnings
### Step 3: Read Active Subproject Context
From INDEX.md, identify active subproject, then read:
1. `.context/project/{active}/STATUS.md`
2. `.context/project/{active}/TODO.md`
3. `.context/project/{active}/CODEBASE.md`
4. `.context/project/{active}/LESSONS.md`
5. `.context/project/{active}/PRINCIPLES.md` (if exists - overrides root)
### Step 4: Read Recent Changes
Read last 3-5 entries from:
- `.context/project/{active}/CHANGELOG.md`
### Step 5: Process Optional Query
If query provided:
- Parse for direction (e.g., "continue booking flow", "fix the API bug")
- Focus context on relevant TODO items
- Identify relevant LESSONS if debugging
### Step 6: Check for Next Session Instructions (NEW)
**After loading all standard files**, check for continuation instructions:
```bash
ls .context/project/{active}/NEXT.md
```
**If NEXT.md exists:**
1. Read `.context/project/{active}/NEXT.md`
2. Extract history file path from NEXT.md
3. Read that history file for previous session context
4. Note the full chat transcript path (but DO NOT load it)
**If NEXT.md does NOT exist:**
- Skip this step, proceed to summary
### Step 7: Generate Resume Summary
Output a compact summary:
```
## Session Resumed: {PROJECT_NAME}
**Active**: {subproject}/ - {current phase}
**Status**: {brief status}
{IF NEXT.md exists:}
### Instructions from Previous Session
{content from NEXT.md "What To Do Next" section}
**Previous Session**: {date} - {brief summary from history file}
### Current Focus
{from STATUS.md current focus}
### In Progress
{tasks marked [>] from TODO.md}
### Recent Changes
{last 2-3 changelog entries, one line each}
### Key Context
{if query provided, relevant context for that direction}
---
Ready to continue. {If NEXT.md exists: "Following previous session's plan." else: "What would you like to work on?"}
```
## Query Examples
```bash
# General resume - load everything
/pp-resume
# With direction - focus on specific area
/pp-resume continue the booking flow implementation
/pp-resume fix the WhatsApp routing bug
/pp-resume review yesterday's changes
```
## Important Notes
- This command is READ-ONLY (no file modifications)
- After this, Claude has full project context
- User can immediately start working
- If query mentions something not in TODO, suggest adding it

117
commands/pp-status.md Normal file
View File

@@ -0,0 +1,117 @@
# Project Status Summary
Show compact status of the entire project.
## Arguments
$ARGUMENTS
## Purpose
Quick overview of project state without loading full context. Shows:
- Active subproject and its focus
- All subprojects status
- In-progress tasks
- Recent changes
- Any blockers
## Execution Instructions
### Step 1: Read Status Files
Read these files:
- `.context/project/INDEX.md` (get active subproject + project summary)
- `.context/project/{active}/STATUS.md`
- `.context/project/{active}/TODO.md`
- `.context/project/{active}/CHANGELOG.md` (last 2-3 entries)
### Step 2: Parse Optional Query
If query provided, focus on:
- Specific subproject: `/pp-status maya`
- Specific aspect: `/pp-status tasks` or `/pp-status blockers`
### Step 3: Generate Compact Summary
Output format:
```
## Project Status: {PROJECT_NAME}
### Active: {subproject}/
**Phase**: {current phase}
**Focus**: {one-line current focus}
### In Progress
{tasks marked [>], one line each with T### ID}
### Pending (Next Up)
{next 3-5 pending tasks}
### Subprojects
| Name | Status | Phase |
|------|--------|-------|
{table from root STATUS.md}
### Blockers
{any blocked items from STATUS.md, or "None"}
### Recent Activity
{last 2-3 changelog entries, date + one-line summary}
---
Last updated: {date from STATUS.md}
```
### Step 4: Subproject-Specific Status
If query specifies a subproject:
```
## Subproject Status: {name}/
**Phase**: {phase}
**Status**: {status}
### What's Working
{bullet list}
### What's Blocked
{bullet list or "Nothing blocked"}
### Current Focus
{numbered list}
### Tasks
**In Progress**: {count}
**Pending**: {count}
**Completed**: {count}
### Next Actions
{from STATUS.md}
```
## Query Examples
```bash
# Full project status
/pp-status
# Specific subproject
/pp-status maya
# Focus on tasks only
/pp-status tasks
# Focus on blockers
/pp-status blockers
```
## Output Notes
- Keep it COMPACT - fits in one screen
- Use tables for multiple items
- One line per item max
- Show counts, not full lists
- Highlight blockers prominently
- This is READ-ONLY (no modifications)

246
commands/pp-triage.md Normal file
View File

@@ -0,0 +1,246 @@
# Triage Issue or Feature Request
Root cause investigation and solution design for issues, bugs, or feature requests.
## Arguments
$ARGUMENTS
Description of the issue or feature. Can include error messages, URLs, or attach screenshots.
## Purpose
Deep investigation to find ROOT CAUSES, not just symptoms. Uses triage-agent for systematic analysis.
**Philosophy:** Don't just treat the rash, find the root cause (diet, stress, etc.) and fix both.
## Execution Instructions
### Step 1: Collect Issue Information
From user query, gather:
- **Issue description** (what's happening)
- **Error messages** (if any)
- **Steps to reproduce** (if provided)
- **Expected vs actual behavior**
- **Screenshots/links** (if attached)
- **Affected area** (frontend, backend, API, etc.)
If critical info missing, ask clarifying questions:
- "What error message do you see?"
- "When does this happen?"
- "Which part of the system is affected?"
### Step 2: Read PP Context First
**IMPORTANT**: Before launching agent, gather context from PP files:
Read these files:
1. `.context/project/INDEX.md` - Identify active subproject
2. `.context/project/{active}/CODEBASE.md` - Understand file structure
3. `.context/project/{active}/STATUS.md` - Current state, known issues
4. `.context/project/{active}/CHANGELOG.md` (last 5-10 entries) - Recent changes
5. `.context/project/{active}/LESSONS.md` - Past solutions
6. `.context/project/PRINCIPLES.md` - Project principles
This context is passed to the agent so it doesn't re-explore everything.
### Step 3: Launch Triage Agent
Spawn the triage-agent with:
- Issue description
- PP context (from Step 2)
- Specific focus area (if mentioned)
```
Use Task tool with subagent_type: "triage-agent"
Prompt: Investigate this issue using PP context:
Issue: {user's issue description}
PP Context:
- Active subproject: {from INDEX.md}
- Codebase structure: {from CODEBASE.md}
- Current status: {from STATUS.md}
- Recent changes: {from CHANGELOG.md}
- Past lessons: {from LESSONS.md}
- Principles: {from PRINCIPLES.md}
Find ROOT CAUSE and provide solution or debugging plan.
```
### Step 4: Agent Investigation
The triage-agent will:
1. Start with PP context (not from scratch)
2. Investigate code systematically
3. Find root cause (causation, not just correlation)
4. Determine if cause is clear or needs debugging
5. Provide solution OR debugging technique
6. Format findings as structured report
### Step 5: Present Report
Agent returns report in this format:
```markdown
## Triage Report: {Issue Title}
**Date**: {YYYY-MM-DD HH:MM}
**Subproject**: {subproject name}
**Severity**: {Critical / High / Medium / Low}
---
### Issue Summary
{Brief description of reported issue}
---
### Root Cause Analysis
**Symptom**: {What appears to be happening}
**Investigation Path**:
1. {Step 1 of investigation}
2. {Step 2 of investigation}
3. {Step 3 of investigation}
**Root Cause**: {The actual underlying cause}
**Why This Matters**: {Explanation of causation, not just correlation}
---
### Solution
{IF ROOT CAUSE IS CLEAR}:
**Recommended Fix**:
1. {Step 1}
2. {Step 2}
3. {Step 3}
**Files to Modify**:
- `path/to/file.ext` - {what to change}
- `another/file.js` - {what to change}
**Implementation Notes**:
- {Important consideration 1}
- {Important consideration 2}
{IF ROOT CAUSE IS UNCLEAR}:
**Debugging Strategy**:
1. **Add logging**:
- In `file.ext` line X: log variable Y
- In `file2.js` line Z: log state before/after
2. **Test hypothesis**:
- {Hypothesis 1}: Test by {method}
- {Hypothesis 2}: Test by {method}
3. **Narrow down**:
- {Approach to isolate the issue}
**Next Steps**:
1. {Action item 1}
2. {Action item 2}
3. {Re-run /pp-triage after gathering data}
---
### Related Context
**Recent Changes That May Be Related**:
- {CHANGELOG entry if relevant}
**Past Similar Issues**:
- {LESSONS.md entry if relevant}
**Affected Components**:
- {Component 1}
- {Component 2}
---
### Recommended Documentation Updates
**Add to LESSONS.md**:
```
## {Date} | {Issue Title}
**Problem**: {Brief problem}
**Root Cause**: {Cause}
**Solution**: {Solution}
**Tag**: [BUG] or [FEATURE] or [CONFIG]
```
**Update STATUS.md**:
- If blocking: Add to "Blocked" section
- If in progress: Add to "Current Focus"
---
### Confidence Level
**Root Cause Certainty**: {High / Medium / Low}
**Solution Confidence**: {High / Medium / Low}
{If low confidence, explain why and what additional info is needed}
```
### Step 6: Offer Follow-Up Actions
After presenting report, offer:
```
## Next Steps
Would you like me to:
1. Implement the recommended fix?
2. Add debugging logs as suggested?
3. Update LESSONS.md with this finding?
4. Create a TODO task for this fix?
5. Something else?
```
## Query Examples
```bash
# Bug report
/pp-triage Getting 500 errors on /api/users endpoint when user has no profile
# Feature request
/pp-triage Need to add email notifications when order status changes
# Performance issue
/pp-triage Dashboard loads slowly with more than 100 items
# With error message
/pp-triage TypeError: Cannot read property 'name' of undefined in UserProfile component
# With screenshot
/pp-triage [attach screenshot] Login button not working on mobile
```
## Notes
- Triage agent has access to all PP files for context
- Agent focuses on ROOT CAUSE, not symptoms
- If multiple possible causes, agent ranks by likelihood
- Report is designed to be saved in docs/ or archive/ for reference
- Always offer to update LESSONS.md with findings
- Principle: Fix the cause (diet) not just the symptom (rash)
## Agent Behavior
The triage-agent is trained to:
- Read PP context FIRST (don't re-explore)
- Think systematically (hypothesis → test → conclude)
- Find causation, not correlation
- Provide actionable solutions
- Suggest debugging when uncertain
- Be honest about confidence level
- Format output for easy scanning and future reference

116
commands/pp-update.md Normal file
View File

@@ -0,0 +1,116 @@
# Update Project Documentation
Update project files after making changes.
## Arguments
$ARGUMENTS
## Purpose
Run after making code changes to keep project documentation in sync. Updates:
- TODO.md (mark tasks done/in-progress)
- CHANGELOG.md (log changes)
- STATUS.md (if state changed)
- CODEBASE.md (if files added/modified)
- LESSONS.md (if debugging insights gained)
## Execution Instructions
### Step 1: Identify Active Subproject
Read `.context/project/INDEX.md` to get active subproject.
### Step 2: Parse Query
Query should describe what was done:
- "completed T010, T011" → Mark tasks done
- "added webhook/llm_service.py" → Update CODEBASE
- "fixed the routing bug" → Update CHANGELOG, possibly LESSONS
- "changed booking flow approach" → Update CHANGELOG with context
If no query, ask: "What changes did you make? (tasks completed, files added, bugs fixed)"
### Step 3: Determine What to Update
Based on query, identify which files need updates:
| Query Pattern | Files to Update |
|---------------|-----------------|
| "completed T###" | TODO.md, CHANGELOG.md |
| "added/created file" | CODEBASE.md, CHANGELOG.md |
| "fixed bug" | CHANGELOG.md, possibly LESSONS.md |
| "changed approach" | CHANGELOG.md, possibly STATUS.md |
| "working on T###" | TODO.md (mark in-progress) |
### Step 4: Collect Additional Info
Ask for any missing info:
- For CHANGELOG: "Brief description of what changed?"
- For CHANGELOG: "Any usage examples to include?"
- For LESSONS: "What was the root cause and solution?"
### Step 5: Make Updates
Update files in this order:
1. TODO.md - Mark tasks
2. CODEBASE.md - Add files
3. LESSONS.md - Add learnings
4. CHANGELOG.md - Log everything
5. STATUS.md - Update state if needed
### Step 6: Confirm Updates
Show summary:
```
## Updated Project Docs
**TODO.md**: Marked T010, T011 as completed
**CODEBASE.md**: Added webhook/llm_service.py
**CHANGELOG.md**: Added entry for LLM service implementation
Ready to commit? Run: git add . && git commit
```
### Step 7: Trigger Cleanup Check
After confirming updates, automatically check for cleanup:
1. Scan for temporary/draft files (see `/pp-clean` for patterns)
2. If files found, show preview:
```
## Cleanup Check
Found files to clean:
- DELETE: scratch_test.md, debug_output.md
- ARCHIVE: draft_notes.md
Run cleanup? (yes/no/skip)
```
3. If user confirms, execute cleanup per `/pp-clean` logic
4. If user skips, remind: "Run `/pp-clean` later to tidy up"
This keeps project clean after each update cycle.
## Query Examples
```bash
# After completing tasks
/pp-update completed T010 and T011, added llm_service.py
# After fixing a bug
/pp-update fixed the referral data extraction bug
# After changing approach
/pp-update changed booking flow to conversational style
# General update (will ask questions)
/pp-update
```
## Update Both Levels
- If change affects only subproject → update subproject files
- If change affects multiple subprojects → update root files too
- CHANGELOG goes in subproject unless it's cross-project

75
commands/pp-version.md Normal file
View File

@@ -0,0 +1,75 @@
# Project Progress Plugin Version
Show the current version of the unclecode-cc-toolkit plugin.
## Arguments
None
## Purpose
Display plugin version information to verify installation and updates.
## Execution Instructions
### Step 1: Read Plugin Manifest
Read the plugin manifest file:
```bash
cat ~/.claude/plugins/marketplaces/unclecode-tools/plugins/unclecode-cc-toolkit/.claude-plugin/plugin.json
```
### Step 2: Extract Version
Parse JSON and extract the `version` field.
### Step 3: Display Version Info
Output format:
```
## unclecode-cc-toolkit Plugin
**Version**: {version}
**Name**: unclecode-cc-toolkit
**Description**: {description from manifest}
**Repository**: https://github.com/unclecode/claude-code-tools
**Author**: unclecode
---
To update to the latest version:
1. /plugin uninstall unclecode-cc-toolkit
2. /plugin install unclecode-cc-toolkit@unclecode/claude-code-tools
3. Restart Claude Code
```
## Example
```bash
/pp-version
```
Output:
```
## unclecode-cc-toolkit Plugin
**Version**: 1.1.0
**Name**: unclecode-cc-toolkit
**Description**: Comprehensive Claude Code toolkit...
**Repository**: https://github.com/unclecode/claude-code-tools
**Author**: unclecode
---
To update to the latest version:
1. /plugin uninstall unclecode-cc-toolkit
2. /plugin install unclecode-cc-toolkit@unclecode/claude-code-tools
3. Restart Claude Code
```
## Notes
- If plugin.json not found, inform user plugin may not be installed correctly
- Show installation instructions if needed

197
plugin.lock.json Normal file
View File

@@ -0,0 +1,197 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:unclecode/claude-code-tools:plugins/unclecode-cc-toolkit",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "019d0c394dbcc613b760f9ea2d2ee46fc494a5d8",
"treeHash": "4c678da08733a333b893acdf79c84ae70e605dfa442009a94ff8e647b84a4bcc",
"generatedAt": "2025-11-28T10:28:51.016628Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "unclecode-cc-toolkit",
"description": "Comprehensive Claude Code toolkit by unclecode. Includes project progress tracking commands, Gemini image generation, and web app testing capabilities. All-in-one productivity suite for development workflows.",
"version": "1.3.1"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "474d145fee64d1229350f7ca999f9c1692a238eff4a979d0a3c5f137442956c0"
},
{
"path": "agents/triage-agent.md",
"sha256": "07b68f3027fa71b50115d032b4b72a6f51a884fc83f6fb0185f1c3f13b6bb665"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "d9c58154d1dbe46c8a8885c709144baac53167d74f0eb0805d8964b9ea9ed6f0"
},
{
"path": "commands/pp-version.md",
"sha256": "a65288569874996faae687be0d8bbd8f72b051b3aeb5705316c2424de164223a"
},
{
"path": "commands/pp-update.md",
"sha256": "2555a069e40c08ac238647deda199e60e07312ad537eee00b53d977ea182e573"
},
{
"path": "commands/pp-edit.md",
"sha256": "fad1956159e53ac04bb7d7d183ede2b3fbf7513c7a4fe5715be5a157e06fb0ed"
},
{
"path": "commands/pp-resume.md",
"sha256": "bdc5c5fdaf15d202dbba187d06dca1b9e813a06bb87545a1252426f863137d65"
},
{
"path": "commands/pp-remove.md",
"sha256": "2704bdf82da6274fe1640b03723e3f716464e8d7acc0134c5fe4e14535aebc70"
},
{
"path": "commands/pp-clean.md",
"sha256": "2dc9e7fd6cfc8e56b911d94a3e187cf22ea920735b4c4aa3ea956abd488752a2"
},
{
"path": "commands/pp-checkpoint.md",
"sha256": "5cd0f356b295401d55638b38c00138cf1a8c471b3d79c973734c30eb9e9e4b96"
},
{
"path": "commands/pp-triage.md",
"sha256": "9e92b683b60940694742cf662cf3d48c07570dde1a3f0273fef3cfe3f9c3f769"
},
{
"path": "commands/pp-migrate-v2.md",
"sha256": "0df460e7e73ebc1466685ab96634330f33556b3b8fef0ac3b3ae1b37f40e5cca"
},
{
"path": "commands/pp-init.md",
"sha256": "d52439a5e9acee7d8b7798162ae2a6a759b5bfd37c35ce1d19927648122da171"
},
{
"path": "commands/pp-help.md",
"sha256": "a34ce4a90330207fd516d626ffd5a8aa908a71ce7d5aa378fe61ce1db5b09abf"
},
{
"path": "commands/pp-migrate.md",
"sha256": "34cabbce160bed354e687e35a1457632cee02b81ce2861576bbb2b3bf8a54880"
},
{
"path": "commands/pp-fetch-docs.md",
"sha256": "f753134ee040b788251b1a5b4d3bbd44be13853ff4a046f188eb52565e81b570"
},
{
"path": "commands/pp-add.md",
"sha256": "253c819102e9834f5b7bcc6085338eef3d3af0eb2f0483042d55c37d53f56cd8"
},
{
"path": "commands/pp-status.md",
"sha256": "3d03d0cb15501720ab4018f0668e130be22a79985852bf50e538db7e58c33dfa"
},
{
"path": "commands/pp-init-assets/templates/root/PRINCIPLES.md",
"sha256": "3c8c9f7afaa75c37a51952757afd0e22fc75789002d71f0b34fcce7e4dcf3f33"
},
{
"path": "commands/pp-init-assets/templates/root/WORKFLOW.md",
"sha256": "a680905d696e45cd7dd4818d97b95a1cd04042c97db73c75e0a6e4bd13b89ec9"
},
{
"path": "commands/pp-init-assets/templates/root/LESSONS.md",
"sha256": "e8d42b1226fbf7c3f660941eda5db785a0aef9a859b84cb2e2a9606c11d408ac"
},
{
"path": "commands/pp-init-assets/templates/root/INDEX.md",
"sha256": "b307522b40e67d5073c73625cfc9bc952fb8b5a595febf1095961e0e5b9762c7"
},
{
"path": "commands/pp-init-assets/templates/subproject/PRINCIPLES.md",
"sha256": "26742233ada7e858868da15586bdcab7ebaa22cde95f2763bf61e417a128b939"
},
{
"path": "commands/pp-init-assets/templates/subproject/CHANGELOG.md",
"sha256": "cca709890694fc2b73ac62cbb9461d48c8cd2fa3703223351b8705b851d38268"
},
{
"path": "commands/pp-init-assets/templates/subproject/STATUS.md",
"sha256": "3e596e7c40051605e4b52d37f9ca50d63501e4098974660e48bdffedd2708d1b"
},
{
"path": "commands/pp-init-assets/templates/subproject/TODO.md",
"sha256": "b17dc8908271391e5d7d9c5d0d79f4884304b0cdbdc58009ac8d0b7fe8e6e9fc"
},
{
"path": "commands/pp-init-assets/templates/subproject/LESSONS.md",
"sha256": "1d5b90395e162b89c5c1e52f36b3f73c838c4b6d5ed1eb783491a8c22bbfeace"
},
{
"path": "commands/pp-init-assets/templates/subproject/INDEX.md",
"sha256": "62d0cfa97aba4220108dcb0584b4be8e88b8241111280872268582180925533f"
},
{
"path": "commands/pp-init-assets/templates/subproject/CODEBASE.md",
"sha256": "c3f70324d31c0c1664bb215c92c317332d9b5b8d79a69ab7f776f57e5ed21c30"
},
{
"path": "skills/webapp-testing/SKILL.md",
"sha256": "51b7349e77ec63b7744a6f63647e7566a0b4d2e301121cc10e8c2113af6556a2"
},
{
"path": "skills/webapp-testing/LICENSE.txt",
"sha256": "58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd"
},
{
"path": "skills/webapp-testing/examples/console_logging.py",
"sha256": "ea46877289acb82da7e7ce59d0bc37c8977cd57e2a006d0c88d7a1c625bf95da"
},
{
"path": "skills/webapp-testing/examples/static_html_automation.py",
"sha256": "9d533aafb875ee3ab8b8ebf8f5b9003ac8d999da3d09b285cce252e623140064"
},
{
"path": "skills/webapp-testing/examples/element_discovery.py",
"sha256": "d63c89604a22f8845d724e95dda45db49b1bf57c25ce0a83afbb7b8da3d402f0"
},
{
"path": "skills/webapp-testing/scripts/with_server.py",
"sha256": "b0dcf4918935b795f4eda9821579b9902119235ff4447f687a30286e7d0925fd"
},
{
"path": "skills/gemini-imagegen/SKILL.md",
"sha256": "d19969a7b436eb654f0ce7246597558268a11c3cc1909bdb4d00edab5b78e35d"
},
{
"path": "skills/gemini-imagegen/scripts/gemini_images.py",
"sha256": "dc09a02985560c311bd3fb5e92537963a93cf03dfb8f7266bb36a6ffd6d10c92"
},
{
"path": "skills/gemini-imagegen/scripts/compose_images.py",
"sha256": "e105f32c04f52769318e5911cb54bba2e15780640f7d910e82dc8d444962ce05"
},
{
"path": "skills/gemini-imagegen/scripts/generate_image.py",
"sha256": "0fbe1d1a40095decadedca969c9ac34b32810653a1eab5b99554d868e28dd869"
},
{
"path": "skills/gemini-imagegen/scripts/multi_turn_chat.py",
"sha256": "efcbe4c2cda41313f38dc8032e11fb9e66ee3bdce3b5b903d7ae55c269bbddfd"
},
{
"path": "skills/gemini-imagegen/scripts/edit_image.py",
"sha256": "8c9c1f3eeefc19fb03ece75a0a7142f601d7584f8a6fdf82d3c406d372659002"
}
],
"dirSha256": "4c678da08733a333b893acdf79c84ae70e605dfa442009a94ff8e647b84a4bcc"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

View File

@@ -0,0 +1,190 @@
---
name: gemini-imagegen
description: Generate and edit images using the Gemini API (Nano Banana). Use this skill when creating images from text prompts, editing existing images, applying style transfers, generating logos with text, creating stickers, product mockups, or any image generation/manipulation task. Supports text-to-image, image editing, multi-turn refinement, and composition from multiple reference images.
---
# Gemini Image Generation (Nano Banana)
Generate and edit images using Google's Gemini API. The environment variable `GEMINI_API_KEY` must be set.
## Available Models
| Model | Alias | Resolution | Best For |
|-------|-------|------------|----------|
| `gemini-2.5-flash-image` | Nano Banana | 1024px | Speed, high-volume tasks |
| `gemini-3-pro-image-preview` | Nano Banana Pro | Up to 4K | Professional assets, complex instructions, text rendering |
## Quick Start Scripts
### Text-to-Image
```bash
python scripts/generate_image.py "A cat wearing a wizard hat" output.png
```
### Edit Existing Image
```bash
python scripts/edit_image.py input.png "Add a rainbow in the background" output.png
```
### Multi-Turn Chat (Iterative Refinement)
```bash
python scripts/multi_turn_chat.py
```
## Core API Pattern
All image generation uses the `generateContent` endpoint with `responseModalities: ["TEXT", "IMAGE"]`:
```python
import os
import base64
from google import genai
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
response = client.models.generate_content(
model="gemini-2.5-flash-image",
contents=["Your prompt here"],
)
for part in response.parts:
if part.text:
print(part.text)
elif part.inline_data:
image = part.as_image()
image.save("output.png")
```
## Image Configuration Options
Control output with `image_config`:
```python
from google.genai import types
response = client.models.generate_content(
model="gemini-3-pro-image-preview",
contents=[prompt],
config=types.GenerateContentConfig(
response_modalities=['TEXT', 'IMAGE'],
image_config=types.ImageConfig(
aspect_ratio="16:9", # 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9
image_size="2K" # 1K, 2K, 4K (Pro only for 4K)
),
)
)
```
## Editing Images
Pass existing images with text prompts:
```python
from PIL import Image
img = Image.open("input.png")
response = client.models.generate_content(
model="gemini-2.5-flash-image",
contents=["Add a sunset to this scene", img],
)
```
## Multi-Turn Refinement
Use chat for iterative editing:
```python
from google.genai import types
chat = client.chats.create(
model="gemini-2.5-flash-image",
config=types.GenerateContentConfig(response_modalities=['TEXT', 'IMAGE'])
)
response = chat.send_message("Create a logo for 'Acme Corp'")
# Save first image...
response = chat.send_message("Make the text bolder and add a blue gradient")
# Save refined image...
```
## Prompting Best Practices
### Photorealistic Scenes
Include camera details: lens type, lighting, angle, mood.
> "A photorealistic close-up portrait, 85mm lens, soft golden hour light, shallow depth of field"
### Stylized Art
Specify style explicitly:
> "A kawaii-style sticker of a happy red panda, bold outlines, cel-shading, white background"
### Text in Images
Be explicit about font style and placement. Use `gemini-3-pro-image-preview` for best results:
> "Create a logo with text 'Daily Grind' in clean sans-serif, black and white, coffee bean motif"
### Product Mockups
Describe lighting setup and surface:
> "Studio-lit product photo on polished concrete, three-point softbox setup, 45-degree angle"
### Landing Pages
Specify layout structure, color scheme, and target audience:
> "Modern landing page hero section, gradient background from deep purple to blue, centered headline with CTA button, clean minimalist design, SaaS product"
> "Landing page for fitness app, energetic layout with workout photos, bright orange and black color scheme, mobile-first design, prominent download buttons"
### Website Design Ideas
Describe overall aesthetic, navigation style, and content hierarchy:
> "E-commerce homepage wireframe, grid layout for products, sticky navigation bar, warm earth tones, plenty of whitespace, professional photography style"
> "Portfolio website for photographer, full-screen image galleries, dark mode interface, elegant serif typography, minimal UI elements to highlight work"
> "Tech startup homepage, glassmorphism design trend, floating cards, neon accent colors on dark background, modern illustrations, hero section with product demo"
## Advanced Features (Pro Model Only)
### Google Search Grounding
Generate images based on real-time data:
```python
response = client.models.generate_content(
model="gemini-3-pro-image-preview",
contents=["Visualize today's weather in Tokyo as an infographic"],
config=types.GenerateContentConfig(
response_modalities=['TEXT', 'IMAGE'],
tools=[{"google_search": {}}]
)
)
```
### Multiple Reference Images (Up to 14)
Combine elements from multiple sources:
```python
response = client.models.generate_content(
model="gemini-3-pro-image-preview",
contents=[
"Create a group photo of these people in an office",
Image.open("person1.png"),
Image.open("person2.png"),
Image.open("person3.png"),
],
)
```
## REST API (curl)
```bash
curl -s -X POST \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{"parts": [{"text": "A serene mountain landscape"}]}]
}' | jq -r '.candidates[0].content.parts[] | select(.inlineData) | .inlineData.data' | base64 --decode > output.png
```
## Notes
- All generated images include SynthID watermarks
- Image-only mode (`responseModalities: ["IMAGE"]`) won't work with Google Search grounding
- For editing, describe changes conversationally—the model understands semantic masking

View File

@@ -0,0 +1,157 @@
#!/usr/bin/env python3
"""
Compose multiple images into a new image using Gemini API.
Usage:
python compose_images.py "instruction" output.png image1.png [image2.png ...]
Examples:
python compose_images.py "Create a group photo of these people" group.png person1.png person2.png
python compose_images.py "Put the cat from the first image on the couch from the second" result.png cat.png couch.png
python compose_images.py "Apply the art style from the first image to the scene in the second" styled.png style.png photo.png
Note: Supports up to 14 reference images (Gemini 3 Pro only).
Environment:
GEMINI_API_KEY - Required API key
"""
import argparse
import os
import sys
from PIL import Image
from google import genai
from google.genai import types
def compose_images(
instruction: str,
output_path: str,
image_paths: list[str],
model: str = "gemini-3-pro-image-preview",
aspect_ratio: str | None = None,
image_size: str | None = None,
) -> str | None:
"""Compose multiple images based on instructions.
Args:
instruction: Text description of how to combine images
output_path: Path to save the result
image_paths: List of input image paths (up to 14)
model: Gemini model to use (pro recommended)
aspect_ratio: Output aspect ratio
image_size: Output resolution
Returns:
Any text response from the model, or None
"""
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
raise EnvironmentError("GEMINI_API_KEY environment variable not set")
if len(image_paths) > 14:
raise ValueError("Maximum 14 reference images supported")
if len(image_paths) < 1:
raise ValueError("At least one image is required")
# Verify all images exist
for path in image_paths:
if not os.path.exists(path):
raise FileNotFoundError(f"Image not found: {path}")
client = genai.Client(api_key=api_key)
# Load images
images = [Image.open(path) for path in image_paths]
# Build contents: instruction first, then images
contents = [instruction] + images
# Build config
config_kwargs = {"response_modalities": ["TEXT", "IMAGE"]}
image_config_kwargs = {}
if aspect_ratio:
image_config_kwargs["aspect_ratio"] = aspect_ratio
if image_size:
image_config_kwargs["image_size"] = image_size
if image_config_kwargs:
config_kwargs["image_config"] = types.ImageConfig(**image_config_kwargs)
config = types.GenerateContentConfig(**config_kwargs)
response = client.models.generate_content(
model=model,
contents=contents,
config=config,
)
text_response = None
image_saved = False
for part in response.parts:
if part.text is not None:
text_response = part.text
elif part.inline_data is not None:
image = part.as_image()
image.save(output_path)
image_saved = True
if not image_saved:
raise RuntimeError("No image was generated.")
return text_response
def main():
parser = argparse.ArgumentParser(
description="Compose multiple images using Gemini API",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__
)
parser.add_argument("instruction", help="Composition instruction")
parser.add_argument("output", help="Output file path")
parser.add_argument("images", nargs="+", help="Input images (up to 14)")
parser.add_argument(
"--model", "-m",
default="gemini-3-pro-image-preview",
choices=["gemini-2.5-flash-image", "gemini-3-pro-image-preview"],
help="Model to use (pro recommended for composition)"
)
parser.add_argument(
"--aspect", "-a",
choices=["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"],
help="Output aspect ratio"
)
parser.add_argument(
"--size", "-s",
choices=["1K", "2K", "4K"],
help="Output resolution"
)
args = parser.parse_args()
try:
text = compose_images(
instruction=args.instruction,
output_path=args.output,
image_paths=args.images,
model=args.model,
aspect_ratio=args.aspect,
image_size=args.size,
)
print(f"Composed image saved to: {args.output}")
if text:
print(f"Model response: {text}")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,144 @@
#!/usr/bin/env python3
"""
Edit existing images using Gemini API.
Usage:
python edit_image.py input.png "edit instruction" output.png [options]
Examples:
python edit_image.py photo.png "Add a rainbow in the sky" edited.png
python edit_image.py room.jpg "Change the sofa to red leather" room_edited.jpg
python edit_image.py portrait.png "Make it look like a Van Gogh painting" artistic.png --model gemini-3-pro-image-preview
Environment:
GEMINI_API_KEY - Required API key
"""
import argparse
import os
import sys
from PIL import Image
from google import genai
from google.genai import types
def edit_image(
input_path: str,
instruction: str,
output_path: str,
model: str = "gemini-2.5-flash-image",
aspect_ratio: str | None = None,
image_size: str | None = None,
) -> str | None:
"""Edit an existing image based on text instructions.
Args:
input_path: Path to the input image
instruction: Text description of edits to make
output_path: Path to save the edited image
model: Gemini model to use
aspect_ratio: Output aspect ratio
image_size: Output resolution
Returns:
Any text response from the model, or None
"""
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
raise EnvironmentError("GEMINI_API_KEY environment variable not set")
if not os.path.exists(input_path):
raise FileNotFoundError(f"Input image not found: {input_path}")
client = genai.Client(api_key=api_key)
# Load input image
input_image = Image.open(input_path)
# Build config
config_kwargs = {"response_modalities": ["TEXT", "IMAGE"]}
image_config_kwargs = {}
if aspect_ratio:
image_config_kwargs["aspect_ratio"] = aspect_ratio
if image_size:
image_config_kwargs["image_size"] = image_size
if image_config_kwargs:
config_kwargs["image_config"] = types.ImageConfig(**image_config_kwargs)
config = types.GenerateContentConfig(**config_kwargs)
response = client.models.generate_content(
model=model,
contents=[instruction, input_image],
config=config,
)
text_response = None
image_saved = False
for part in response.parts:
if part.text is not None:
text_response = part.text
elif part.inline_data is not None:
image = part.as_image()
image.save(output_path)
image_saved = True
if not image_saved:
raise RuntimeError("No image was generated. Check your instruction and try again.")
return text_response
def main():
parser = argparse.ArgumentParser(
description="Edit images using Gemini API",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__
)
parser.add_argument("input", help="Input image path")
parser.add_argument("instruction", help="Edit instruction")
parser.add_argument("output", help="Output file path")
parser.add_argument(
"--model", "-m",
default="gemini-2.5-flash-image",
choices=["gemini-2.5-flash-image", "gemini-3-pro-image-preview"],
help="Model to use (default: gemini-2.5-flash-image)"
)
parser.add_argument(
"--aspect", "-a",
choices=["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"],
help="Output aspect ratio"
)
parser.add_argument(
"--size", "-s",
choices=["1K", "2K", "4K"],
help="Output resolution"
)
args = parser.parse_args()
try:
text = edit_image(
input_path=args.input,
instruction=args.instruction,
output_path=args.output,
model=args.model,
aspect_ratio=args.aspect,
image_size=args.size,
)
print(f"Edited image saved to: {args.output}")
if text:
print(f"Model response: {text}")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,263 @@
"""
Gemini Image Generation Library
A simple Python library for generating and editing images with the Gemini API.
Usage:
from gemini_images import GeminiImageGenerator
gen = GeminiImageGenerator()
gen.generate("A sunset over mountains", "sunset.png")
gen.edit("input.png", "Add clouds", "output.png")
Environment:
GEMINI_API_KEY - Required API key
"""
import os
from pathlib import Path
from typing import Literal
from PIL import Image
from google import genai
from google.genai import types
AspectRatio = Literal["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"]
ImageSize = Literal["1K", "2K", "4K"]
Model = Literal["gemini-2.5-flash-image", "gemini-3-pro-image-preview"]
class GeminiImageGenerator:
"""High-level interface for Gemini image generation."""
FLASH = "gemini-2.5-flash-image"
PRO = "gemini-3-pro-image-preview"
def __init__(self, api_key: str | None = None, model: Model = FLASH):
"""Initialize the generator.
Args:
api_key: Gemini API key (defaults to GEMINI_API_KEY env var)
model: Default model to use
"""
self.api_key = api_key or os.environ.get("GEMINI_API_KEY")
if not self.api_key:
raise EnvironmentError("GEMINI_API_KEY not set")
self.client = genai.Client(api_key=self.api_key)
self.model = model
def _build_config(
self,
aspect_ratio: AspectRatio | None = None,
image_size: ImageSize | None = None,
google_search: bool = False,
) -> types.GenerateContentConfig:
"""Build generation config."""
kwargs = {"response_modalities": ["TEXT", "IMAGE"]}
img_config = {}
if aspect_ratio:
img_config["aspect_ratio"] = aspect_ratio
if image_size:
img_config["image_size"] = image_size
if img_config:
kwargs["image_config"] = types.ImageConfig(**img_config)
if google_search:
kwargs["tools"] = [{"google_search": {}}]
return types.GenerateContentConfig(**kwargs)
def generate(
self,
prompt: str,
output: str | Path,
*,
model: Model | None = None,
aspect_ratio: AspectRatio | None = None,
image_size: ImageSize | None = None,
google_search: bool = False,
) -> tuple[Path, str | None]:
"""Generate an image from a text prompt.
Args:
prompt: Text description
output: Output file path
model: Override default model
aspect_ratio: Output aspect ratio
image_size: Output resolution
google_search: Enable Google Search grounding (Pro only)
Returns:
Tuple of (output path, optional text response)
"""
output = Path(output)
config = self._build_config(aspect_ratio, image_size, google_search)
response = self.client.models.generate_content(
model=model or self.model,
contents=[prompt],
config=config,
)
text = None
for part in response.parts:
if part.text:
text = part.text
elif part.inline_data:
part.as_image().save(output)
return output, text
def edit(
self,
input_image: str | Path | Image.Image,
instruction: str,
output: str | Path,
*,
model: Model | None = None,
aspect_ratio: AspectRatio | None = None,
image_size: ImageSize | None = None,
) -> tuple[Path, str | None]:
"""Edit an existing image.
Args:
input_image: Input image (path or PIL Image)
instruction: Edit instruction
output: Output file path
model: Override default model
aspect_ratio: Output aspect ratio
image_size: Output resolution
Returns:
Tuple of (output path, optional text response)
"""
output = Path(output)
if isinstance(input_image, (str, Path)):
input_image = Image.open(input_image)
config = self._build_config(aspect_ratio, image_size)
response = self.client.models.generate_content(
model=model or self.model,
contents=[instruction, input_image],
config=config,
)
text = None
for part in response.parts:
if part.text:
text = part.text
elif part.inline_data:
part.as_image().save(output)
return output, text
def compose(
self,
instruction: str,
images: list[str | Path | Image.Image],
output: str | Path,
*,
model: Model | None = None,
aspect_ratio: AspectRatio | None = None,
image_size: ImageSize | None = None,
) -> tuple[Path, str | None]:
"""Compose multiple images into one.
Args:
instruction: Composition instruction
images: List of input images (up to 14)
output: Output file path
model: Override default model (Pro recommended)
aspect_ratio: Output aspect ratio
image_size: Output resolution
Returns:
Tuple of (output path, optional text response)
"""
output = Path(output)
# Load images
loaded = []
for img in images:
if isinstance(img, (str, Path)):
loaded.append(Image.open(img))
else:
loaded.append(img)
config = self._build_config(aspect_ratio, image_size)
contents = [instruction] + loaded
response = self.client.models.generate_content(
model=model or self.PRO, # Pro recommended for composition
contents=contents,
config=config,
)
text = None
for part in response.parts:
if part.text:
text = part.text
elif part.inline_data:
part.as_image().save(output)
return output, text
def chat(self) -> "ImageChat":
"""Start an interactive chat session for iterative refinement."""
return ImageChat(self.client, self.model)
class ImageChat:
"""Multi-turn chat session for iterative image generation."""
def __init__(self, client: genai.Client, model: Model):
self.client = client
self.model = model
self._chat = client.chats.create(
model=model,
config=types.GenerateContentConfig(response_modalities=["TEXT", "IMAGE"]),
)
self.current_image: Image.Image | None = None
def send(
self,
message: str,
image: Image.Image | str | Path | None = None,
) -> tuple[Image.Image | None, str | None]:
"""Send a message and optionally an image.
Returns:
Tuple of (generated image or None, text response or None)
"""
contents = [message]
if image:
if isinstance(image, (str, Path)):
image = Image.open(image)
contents.append(image)
response = self._chat.send_message(contents)
text = None
img = None
for part in response.parts:
if part.text:
text = part.text
elif part.inline_data:
img = part.as_image()
self.current_image = img
return img, text
def reset(self):
"""Reset the chat session."""
self._chat = self.client.chats.create(
model=self.model,
config=types.GenerateContentConfig(response_modalities=["TEXT", "IMAGE"]),
)
self.current_image = None

View File

@@ -0,0 +1,133 @@
#!/usr/bin/env python3
"""
Generate images from text prompts using Gemini API.
Usage:
python generate_image.py "prompt" output.png [--model MODEL] [--aspect RATIO] [--size SIZE]
Examples:
python generate_image.py "A cat in space" cat.png
python generate_image.py "A logo for Acme Corp" logo.png --model gemini-3-pro-image-preview --aspect 1:1
python generate_image.py "Epic landscape" landscape.png --aspect 16:9 --size 2K
Environment:
GEMINI_API_KEY - Required API key
"""
import argparse
import os
import sys
from google import genai
from google.genai import types
def generate_image(
prompt: str,
output_path: str,
model: str = "gemini-2.5-flash-image",
aspect_ratio: str | None = None,
image_size: str | None = None,
) -> str | None:
"""Generate an image from a text prompt.
Args:
prompt: Text description of the image to generate
output_path: Path to save the generated image
model: Gemini model to use
aspect_ratio: Aspect ratio (1:1, 16:9, 9:16, etc.)
image_size: Resolution (1K, 2K, 4K - 4K only for pro model)
Returns:
Any text response from the model, or None
"""
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
raise EnvironmentError("GEMINI_API_KEY environment variable not set")
client = genai.Client(api_key=api_key)
# Build config
config_kwargs = {"response_modalities": ["TEXT", "IMAGE"]}
image_config_kwargs = {}
if aspect_ratio:
image_config_kwargs["aspect_ratio"] = aspect_ratio
if image_size:
image_config_kwargs["image_size"] = image_size
if image_config_kwargs:
config_kwargs["image_config"] = types.ImageConfig(**image_config_kwargs)
config = types.GenerateContentConfig(**config_kwargs)
response = client.models.generate_content(
model=model,
contents=[prompt],
config=config,
)
text_response = None
image_saved = False
for part in response.parts:
if part.text is not None:
text_response = part.text
elif part.inline_data is not None:
image = part.as_image()
image.save(output_path)
image_saved = True
if not image_saved:
raise RuntimeError("No image was generated. Check your prompt and try again.")
return text_response
def main():
parser = argparse.ArgumentParser(
description="Generate images from text prompts using Gemini API",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__
)
parser.add_argument("prompt", help="Text prompt describing the image")
parser.add_argument("output", help="Output file path (e.g., output.png)")
parser.add_argument(
"--model", "-m",
default="gemini-2.5-flash-image",
choices=["gemini-2.5-flash-image", "gemini-3-pro-image-preview"],
help="Model to use (default: gemini-2.5-flash-image)"
)
parser.add_argument(
"--aspect", "-a",
choices=["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"],
help="Aspect ratio"
)
parser.add_argument(
"--size", "-s",
choices=["1K", "2K", "4K"],
help="Image resolution (4K only available with pro model)"
)
args = parser.parse_args()
try:
text = generate_image(
prompt=args.prompt,
output_path=args.output,
model=args.model,
aspect_ratio=args.aspect,
image_size=args.size,
)
print(f"Image saved to: {args.output}")
if text:
print(f"Model response: {text}")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,216 @@
#!/usr/bin/env python3
"""
Interactive multi-turn image generation and refinement using Gemini API.
Usage:
python multi_turn_chat.py [--model MODEL] [--output-dir DIR]
This starts an interactive session where you can:
- Generate images from prompts
- Iteratively refine images through conversation
- Load existing images for editing
- Save images at any point
Commands:
/save [filename] - Save current image
/load <path> - Load an image into the conversation
/clear - Start fresh conversation
/quit - Exit
Environment:
GEMINI_API_KEY - Required API key
"""
import argparse
import os
import sys
from datetime import datetime
from pathlib import Path
from PIL import Image
from google import genai
from google.genai import types
class ImageChat:
"""Interactive chat session for image generation and refinement."""
def __init__(
self,
model: str = "gemini-2.5-flash-image",
output_dir: str = ".",
):
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
raise EnvironmentError("GEMINI_API_KEY environment variable not set")
self.client = genai.Client(api_key=api_key)
self.model = model
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.chat = None
self.current_image = None
self.image_count = 0
self._init_chat()
def _init_chat(self):
"""Initialize or reset the chat session."""
config = types.GenerateContentConfig(
response_modalities=["TEXT", "IMAGE"]
)
self.chat = self.client.chats.create(
model=self.model,
config=config,
)
self.current_image = None
def send_message(self, message: str, image: Image.Image | None = None) -> tuple[str | None, Image.Image | None]:
"""Send a message and optionally an image, return response text and image."""
contents = []
if message:
contents.append(message)
if image:
contents.append(image)
if not contents:
return None, None
response = self.chat.send_message(contents)
text_response = None
image_response = None
for part in response.parts:
if part.text is not None:
text_response = part.text
elif part.inline_data is not None:
image_response = part.as_image()
self.current_image = image_response
return text_response, image_response
def save_image(self, filename: str | None = None) -> str | None:
"""Save the current image to a file."""
if self.current_image is None:
return None
if filename is None:
self.image_count += 1
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"image_{timestamp}_{self.image_count}.png"
filepath = self.output_dir / filename
self.current_image.save(filepath)
return str(filepath)
def load_image(self, path: str) -> Image.Image:
"""Load an image from disk."""
img = Image.open(path)
self.current_image = img
return img
def main():
parser = argparse.ArgumentParser(
description="Interactive multi-turn image generation",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__
)
parser.add_argument(
"--model", "-m",
default="gemini-2.5-flash-image",
choices=["gemini-2.5-flash-image", "gemini-3-pro-image-preview"],
help="Model to use"
)
parser.add_argument(
"--output-dir", "-o",
default=".",
help="Directory to save images"
)
args = parser.parse_args()
try:
chat = ImageChat(model=args.model, output_dir=args.output_dir)
except Exception as e:
print(f"Error initializing: {e}", file=sys.stderr)
sys.exit(1)
print(f"Gemini Image Chat ({args.model})")
print("Commands: /save [name], /load <path>, /clear, /quit")
print("-" * 50)
while True:
try:
user_input = input("\nYou: ").strip()
except (EOFError, KeyboardInterrupt):
print("\nGoodbye!")
break
if not user_input:
continue
# Handle commands
if user_input.startswith("/"):
parts = user_input.split(maxsplit=1)
cmd = parts[0].lower()
arg = parts[1] if len(parts) > 1 else None
if cmd == "/quit":
print("Goodbye!")
break
elif cmd == "/clear":
chat._init_chat()
print("Conversation cleared.")
continue
elif cmd == "/save":
path = chat.save_image(arg)
if path:
print(f"Image saved to: {path}")
else:
print("No image to save.")
continue
elif cmd == "/load":
if not arg:
print("Usage: /load <path>")
continue
try:
chat.load_image(arg)
print(f"Loaded: {arg}")
print("You can now describe edits to make.")
except Exception as e:
print(f"Error loading image: {e}")
continue
else:
print(f"Unknown command: {cmd}")
continue
# Send message to model
try:
# If we have a loaded image and this is first message, include it
image_to_send = None
if chat.current_image and not chat.chat.history:
image_to_send = chat.current_image
text, image = chat.send_message(user_input, image_to_send)
if text:
print(f"\nGemini: {text}")
if image:
# Auto-save
path = chat.save_image()
print(f"\n[Image generated: {path}]")
except Exception as e:
print(f"\nError: {e}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,96 @@
---
name: webapp-testing
description: Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
license: Complete terms in LICENSE.txt
---
# Web Application Testing
To test local web applications, write native Python Playwright scripts.
**Helper Scripts Available**:
- `scripts/with_server.py` - Manages server lifecycle (supports multiple servers)
**Always run scripts with `--help` first** to see usage. DO NOT read the source until you try running the script first and find that a customized solution is abslutely necessary. These scripts can be very large and thus pollute your context window. They exist to be called directly as black-box scripts rather than ingested into your context window.
## Decision Tree: Choosing Your Approach
```
User task → Is it static HTML?
├─ Yes → Read HTML file directly to identify selectors
│ ├─ Success → Write Playwright script using selectors
│ └─ Fails/Incomplete → Treat as dynamic (below)
└─ No (dynamic webapp) → Is the server already running?
├─ No → Run: python scripts/with_server.py --help
│ Then use the helper + write simplified Playwright script
└─ Yes → Reconnaissance-then-action:
1. Navigate and wait for networkidle
2. Take screenshot or inspect DOM
3. Identify selectors from rendered state
4. Execute actions with discovered selectors
```
## Example: Using with_server.py
To start a server, run `--help` first, then use the helper:
**Single server:**
```bash
python scripts/with_server.py --server "npm run dev" --port 5173 -- python your_automation.py
```
**Multiple servers (e.g., backend + frontend):**
```bash
python scripts/with_server.py \
--server "cd backend && python server.py" --port 3000 \
--server "cd frontend && npm run dev" --port 5173 \
-- python your_automation.py
```
To create an automation script, include only Playwright logic (servers are managed automatically):
```python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True) # Always launch chromium in headless mode
page = browser.new_page()
page.goto('http://localhost:5173') # Server already running and ready
page.wait_for_load_state('networkidle') # CRITICAL: Wait for JS to execute
# ... your automation logic
browser.close()
```
## Reconnaissance-Then-Action Pattern
1. **Inspect rendered DOM**:
```python
page.screenshot(path='/tmp/inspect.png', full_page=True)
content = page.content()
page.locator('button').all()
```
2. **Identify selectors** from inspection results
3. **Execute actions** using discovered selectors
## Common Pitfall
❌ **Don't** inspect the DOM before waiting for `networkidle` on dynamic apps
✅ **Do** wait for `page.wait_for_load_state('networkidle')` before inspection
## Best Practices
- **Use bundled scripts as black boxes** - To accomplish a task, consider whether one of the scripts available in `scripts/` can help. These scripts handle common, complex workflows reliably without cluttering the context window. Use `--help` to see usage, then invoke directly.
- Use `sync_playwright()` for synchronous scripts
- Always close the browser when done
- Use descriptive selectors: `text=`, `role=`, CSS selectors, or IDs
- Add appropriate waits: `page.wait_for_selector()` or `page.wait_for_timeout()`
## Reference Files
- **examples/** - Examples showing common patterns:
- `element_discovery.py` - Discovering buttons, links, and inputs on a page
- `static_html_automation.py` - Using file:// URLs for local HTML
- `console_logging.py` - Capturing console logs during automation

View File

@@ -0,0 +1,35 @@
from playwright.sync_api import sync_playwright
# Example: Capturing console logs during browser automation
url = 'http://localhost:5173' # Replace with your URL
console_logs = []
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={'width': 1920, 'height': 1080})
# Set up console log capture
def handle_console_message(msg):
console_logs.append(f"[{msg.type}] {msg.text}")
print(f"Console: [{msg.type}] {msg.text}")
page.on("console", handle_console_message)
# Navigate to page
page.goto(url)
page.wait_for_load_state('networkidle')
# Interact with the page (triggers console logs)
page.click('text=Dashboard')
page.wait_for_timeout(1000)
browser.close()
# Save console logs to file
with open('/mnt/user-data/outputs/console.log', 'w') as f:
f.write('\n'.join(console_logs))
print(f"\nCaptured {len(console_logs)} console messages")
print(f"Logs saved to: /mnt/user-data/outputs/console.log")

View File

@@ -0,0 +1,40 @@
from playwright.sync_api import sync_playwright
# Example: Discovering buttons and other elements on a page
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# Navigate to page and wait for it to fully load
page.goto('http://localhost:5173')
page.wait_for_load_state('networkidle')
# Discover all buttons on the page
buttons = page.locator('button').all()
print(f"Found {len(buttons)} buttons:")
for i, button in enumerate(buttons):
text = button.inner_text() if button.is_visible() else "[hidden]"
print(f" [{i}] {text}")
# Discover links
links = page.locator('a[href]').all()
print(f"\nFound {len(links)} links:")
for link in links[:5]: # Show first 5
text = link.inner_text().strip()
href = link.get_attribute('href')
print(f" - {text} -> {href}")
# Discover input fields
inputs = page.locator('input, textarea, select').all()
print(f"\nFound {len(inputs)} input fields:")
for input_elem in inputs:
name = input_elem.get_attribute('name') or input_elem.get_attribute('id') or "[unnamed]"
input_type = input_elem.get_attribute('type') or 'text'
print(f" - {name} ({input_type})")
# Take screenshot for visual reference
page.screenshot(path='/tmp/page_discovery.png', full_page=True)
print("\nScreenshot saved to /tmp/page_discovery.png")
browser.close()

View File

@@ -0,0 +1,33 @@
from playwright.sync_api import sync_playwright
import os
# Example: Automating interaction with static HTML files using file:// URLs
html_file_path = os.path.abspath('path/to/your/file.html')
file_url = f'file://{html_file_path}'
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={'width': 1920, 'height': 1080})
# Navigate to local HTML file
page.goto(file_url)
# Take screenshot
page.screenshot(path='/mnt/user-data/outputs/static_page.png', full_page=True)
# Interact with elements
page.click('text=Click Me')
page.fill('#name', 'John Doe')
page.fill('#email', 'john@example.com')
# Submit form
page.click('button[type="submit"]')
page.wait_for_timeout(500)
# Take final screenshot
page.screenshot(path='/mnt/user-data/outputs/after_submit.png', full_page=True)
browser.close()
print("Static HTML automation completed!")

View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""
Start one or more servers, wait for them to be ready, run a command, then clean up.
Usage:
# Single server
python scripts/with_server.py --server "npm run dev" --port 5173 -- python automation.py
python scripts/with_server.py --server "npm start" --port 3000 -- python test.py
# Multiple servers
python scripts/with_server.py \
--server "cd backend && python server.py" --port 3000 \
--server "cd frontend && npm run dev" --port 5173 \
-- python test.py
"""
import subprocess
import socket
import time
import sys
import argparse
def is_server_ready(port, timeout=30):
"""Wait for server to be ready by polling the port."""
start_time = time.time()
while time.time() - start_time < timeout:
try:
with socket.create_connection(('localhost', port), timeout=1):
return True
except (socket.error, ConnectionRefusedError):
time.sleep(0.5)
return False
def main():
parser = argparse.ArgumentParser(description='Run command with one or more servers')
parser.add_argument('--server', action='append', dest='servers', required=True, help='Server command (can be repeated)')
parser.add_argument('--port', action='append', dest='ports', type=int, required=True, help='Port for each server (must match --server count)')
parser.add_argument('--timeout', type=int, default=30, help='Timeout in seconds per server (default: 30)')
parser.add_argument('command', nargs=argparse.REMAINDER, help='Command to run after server(s) ready')
args = parser.parse_args()
# Remove the '--' separator if present
if args.command and args.command[0] == '--':
args.command = args.command[1:]
if not args.command:
print("Error: No command specified to run")
sys.exit(1)
# Parse server configurations
if len(args.servers) != len(args.ports):
print("Error: Number of --server and --port arguments must match")
sys.exit(1)
servers = []
for cmd, port in zip(args.servers, args.ports):
servers.append({'cmd': cmd, 'port': port})
server_processes = []
try:
# Start all servers
for i, server in enumerate(servers):
print(f"Starting server {i+1}/{len(servers)}: {server['cmd']}")
# Use shell=True to support commands with cd and &&
process = subprocess.Popen(
server['cmd'],
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
server_processes.append(process)
# Wait for this server to be ready
print(f"Waiting for server on port {server['port']}...")
if not is_server_ready(server['port'], timeout=args.timeout):
raise RuntimeError(f"Server failed to start on port {server['port']} within {args.timeout}s")
print(f"Server ready on port {server['port']}")
print(f"\nAll {len(servers)} server(s) ready")
# Run the command
print(f"Running: {' '.join(args.command)}\n")
result = subprocess.run(args.command)
sys.exit(result.returncode)
finally:
# Clean up all servers
print(f"\nStopping {len(server_processes)} server(s)...")
for i, process in enumerate(server_processes):
try:
process.terminate()
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
process.wait()
print(f"Server {i+1} stopped")
print("All servers stopped")
if __name__ == '__main__':
main()