
Keep
- 1 installs
- 12 repo stars
- Updated October 28, 2025
- lackeyjb/claude-keep
Helps with ai & agent building tasks.
About
keep is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- keep
- AI & Agent Building
- AI-coding skill
Keep by the numbers
- 1 all-time installs (skills.sh)
- Ranked #14,102 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/lackeyjb/claude-keep --skill keepAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 12 |
| Last updated | October 28, 2025 |
| Repository | lackeyjb/claude-keep ↗ |
What it does
Helps with ai & agent building tasks.
Files
Keep - Session Resume Detection
Keep is a skill that helps detect when you have interrupted work from a previous session and suggests resuming it. Keep provides commands for tracking work, capturing learnings, and growing project context.
Your Role
At the start of a new conversation (first 1-2 exchanges), check if the user has active work in progress and suggest resuming it. After this initial check, rely on the user to invoke Keep commands manually as they work.
Session Resume Detection
Resuming Interrupted Work
User signals:
- New session starting with no clear context established
- User says "what was I working on?" or "where was I?" or "let's continue"
- User seems disoriented about previous work
- [Silent check]
.claude/state.mdexists with Active Work section and recent timestamp
Your response: Proactively check for resumable work
Detection logic: 1. Early in conversation (first 1-2 exchanges), check if .claude/state.md exists 2. If exists, parse "Active Work" section for issue number and timestamps 3. Calculate time since "Last Updated" timestamp 4. Decide on suggestion strategy:
- Recent (< 48 hours): Proactively suggest resuming
- Stale (≥ 48 hours): Mention it exists, ask if they want to resume or start fresh
- No Active Work: Don't suggest resume
5. Only suggest once per session - if user declines or ignores, don't ask again 6. Balance helpfulness with not interrupting their flow - if user has clear new intent, prioritize that
Example (Recent Work - Proactive):
User: [starts new session, asks general question about project]
You: [After responding to their question] "By the way, I see you were working on issue #1234 (Add rate limiting) - last updated 6 hours ago. You had made good progress: middleware 80% complete, 3 decisions captured. Want to pick up where you left off with `/keep:start 1234`?"
User: "yes"
You: *executes /keep:start 1234*Example (Stale Work - Ask First):
User: [starts new session]
You: "I notice you have work in progress on issue #1234 (Add rate limiting) from 5 days ago. Would you like to resume that work, or start something new?"
User: "let's start fresh"
You: "Sounds good! Use `/keep:start` to get recommendations for what to work on next."Example (User Explicitly Asks):
User: "What was I working on?"
You: *reads .claude/state.md* "You were working on issue #1234 - Add rate limiting. Started 2 days ago, last saved 8 hours ago. Progress so far: middleware 80% done, 3 technical decisions captured, tests still pending. Use `/keep:start 1234` to resume with full context loaded."Integration principles:
- Check state.md early but not intrusively (don't interrupt user's clear intent)
- Only suggest once per session - respect user's choice
- Be conversational, not mechanical: "last updated 6 hours ago" vs "Last Updated: 2024-10-23T14:30:00Z"
- If user declines resume, transition smoothly to helping with their actual request
- Balance being helpful with not being pushy - when in doubt, wait for user cues
Available Commands
Keep provides four commands that users invoke manually as they work:
/keep:start [issue-number]
Start work on a GitHub issue with context loading. Omit issue number for recommendations based on continuity and priority.
/keep:save [--sync]
Save progress and capture learnings. Suggests CLAUDE.md updates ONLY when high-value patterns emerge (3+ decisions passing 6-month test). All CLAUDE.md updates enforce strict size limits. Use --sync to post update to GitHub.
/keep:done [--close]
Complete work, generate summary, and recommend next issue. Detects PR state and handles closing intelligently. Use --close to auto-close issue.
/keep:grow [directory] [--update|--condense]
Create or update CLAUDE.md files for project context. Analyzes directory and generates concise documentation (200 line max for root, 150 line max for modules). Use --update to update existing, --condense to prune bloated files.
Command Delegation
Keep uses a sub-agent architecture. Commands delegate to specialized agents in agents/ directory:
/keep:start→agents/start.md/keep:save→agents/save.md/keep:done→agents/done.md/keep:grow→agents/grow.md
Each agent operates in its own context window and provides workflow hints to help users learn the natural cadence.
---
Example: Resume Detection
User: [starts new session]
You: [checks .claude/state.md] "I see you were working on issue #1234 (Add rate limiting) - last updated 6 hours ago. You had made good progress: middleware 80% complete, 3 decisions captured. Want to pick up where you left off with `/keep:start 1234`?"
User: "yes"
You: *executes /keep:start 1234*---
Remember: Your primary role is detecting resumable work at session start. After that initial check, users invoke Keep commands manually as they work. Agents provide workflow hints to teach the natural cadence.
Keep File Format Specifications
This document contains complete specifications for all Keep file formats. Load this file when creating new files or when needing format details.
CRITICAL: Conciseness Guidelines
CLAUDE.md files MUST be concise:
- Root CLAUDE.md: 200 line MAXIMUM (target 120-150)
- Module CLAUDE.md: 150 line MAXIMUM (target 80-100)
- Size enforced on every update
- Pruning required when >80% capacity
The "6-Month Test": Before adding anything, ask: "Will this matter to me 6 months from now?"
What TO include:
- ✅ Non-obvious gotchas and surprises
- ✅ Security/performance implications
- ✅ Architectural decisions with "why"
- ✅ Common mistakes to avoid
- ✅ Framework/library quirks worth remembering
What NOT to include:
- ❌ File paths/directory structure (visible via ls/grep)
- ❌ Standard framework usage patterns
- ❌ Implementation details visible in code
- ❌ API documentation (belongs in code comments)
- ❌ Setup steps in package.json/README
- ❌ Obvious naming conventions
- ❌ Generic best practices from docs
- ❌ Tool installation instructions
- ❌ Comprehensive feature lists
Writing style:
- Use bullets, NOT paragraphs
- 1 line per point when possible
- Examples ONLY when they clarify gotchas
- No fluff, preamble, or generic explanations
---
Root CLAUDE.md
Purpose: Project-wide context that Claude Code automatically loads
Location: {project-root}/CLAUDE.md
Structure:
# Project: {Project Name}
## Tech Stack
- Runtime/Language versions
- Major frameworks and libraries
- Database and infrastructure
## Architecture
- High-level architecture pattern (MVC, microservices, etc.)
- Key architectural decisions and rationale
## Project Structure
- Directory organization
- Module responsibilities
- Important file locations
## Development
- Setup instructions
- Common commands
- Environment variables
## Conventions
- Naming conventions
- Code style guidelines
- Testing approach
## Recent Changes (Last 3-6 months)
- Significant architectural changes
- New patterns adopted
- DeprecationsManagement:
- Created manually or by Keep skill
- Updated by Keep skill suggestions (with user approval)
- MAXIMUM 200 lines (enforced - target 120-150)
- Size validated on every update
- Pruning required when >80% capacity
Example (CONCISE - 45 lines):
# Project: TaskMaster API
## Tech Stack
- Node 18, TypeScript 5, Express 4
- PostgreSQL 14 + TypeORM, Redis
- Jest for testing
## Architecture
RESTful API with repository pattern (controllers → services → repositories)
Key decisions:
- TypeORM for type safety and migrations
- Redis for caching + rate limiting
- JWT access (15min) + refresh tokens (7d rotation)
## Developmentnpm run dev # Port 3000 npm test # Run tests
## Conventions
- camelCase functions, PascalCase classes
- Tests co-located: *.test.ts
- Conventional commits (feat:, fix:)
## Gotchas
- Health checks (/health, /metrics) bypass rate limiting - don't apply middleware globally
- Refresh tokens use one-time rotation - invalidate immediately after use
- bcrypt at 12 rounds - don't increase (perf impact)
## Recent Changes
- 2024-10: Added rate limiting (express-rate-limit + Redis)
- 2024-09: Migrated Sequelize → TypeORMAnti-pattern Example (TOO VERBOSE - 110 lines):
# Project: TaskMaster API
## Overview
TaskMaster API is a comprehensive task management system built with modern
TypeScript and Node.js. It provides a RESTful interface for managing tasks,
users, and teams with enterprise-grade authentication and authorization.
## Tech Stack
### Runtime & Language
- Node.js 18.x LTS - chosen for stability and long-term support
- TypeScript 5.x - provides type safety and better developer experience
- ES2022 target - modern JavaScript features
### Web Framework
- Express 4.x - battle-tested, extensive ecosystem
- cors - handle cross-origin requests
- helmet - security headers
- compression - gzip responses
[... 80 more lines of obvious details ...]Why the concise version is better:
- ✅ 45 lines vs 110 lines (59% smaller)
- ✅ Focuses on non-obvious gotchas
- ✅ Skips details visible in package.json
- ✅ Emphasizes "why" decisions were made
- ✅ No fluff or generic explanations
---
Nested CLAUDE.md Files
Purpose: Domain-specific context for modules/directories
Location: {project-root}/{directory}/CLAUDE.md
Structure:
# {Module Name}
## Purpose
What this module does and why it exists
## Key Patterns
- Specific patterns used in this module
- Important abstractions
- Design decisions
## API / Public Interface
- Key functions/classes
- How other modules interact with this
## Recent Learnings
- Gotchas discovered while working here
- Performance considerations
- Security considerations
- Common mistakes to avoid
## Dependencies
- External dependencies specific to this module
- Internal dependencies (other modules)
## Testing
- Testing approach for this module
- Key test filesManagement:
- Created by Keep skill when working in new area (with user approval)
- Updated as patterns emerge and learnings accumulate
- MAXIMUM 150 lines (enforced - target 80-100)
- Keep skill suggests updates, user reviews and approves
- Size validated on every update
- Pruning required when >80% capacity
When to Create:
- Working in a directory for 2nd+ time
- Non-obvious patterns worth documenting
- Complex gotchas that benefit from explanation
- Don't create prematurely - let need emerge
- Must pass "6-month test": will this matter later?
When NOT to Create:
- File structure is obvious
- Standard framework patterns only
- No surprising gotchas
- Code is self-explanatory
Example (CONCISE - 35 lines):
# Authentication Module
## Purpose
User auth with JWT + refresh token rotation, rate limiting for brute force protection.
## Key Patterns
- JWT access (15min) + refresh (7d, one-time rotation in Redis)
- bcrypt 12 rounds - don't increase (perf impact)
- Per-IP rate limiting: 5/15min (login), 3/15min (reset)
## Gotchas
- **Health checks bypass rate limiting** - exclude /health, /metrics or monitoring breaks
- **Refresh tokens are one-time use** - invalidate immediately after rotation
- **Rate limit headers auto-added** - don't manually set X-RateLimit-*
- **Redis failures degrade gracefully** - falls back to in-memory (per-instance)
- **Never log tokens** - use token.substring(0,10) for debugging
## Common Mistakes
- ❌ Forgetting to hash passwords in test fixtures
- ❌ Applying rate limiting globally (breaks health checks)
- ❌ Not invalidating old refresh token on rotation
## Testing
- Mock Redis in unit tests (redis-mock)
- Real Redis for integration testsAnti-pattern Example (TOO VERBOSE - 95 lines):
# Authentication Module
## Overview
This module provides comprehensive authentication and authorization services
for the TaskMaster API. It implements industry-standard security practices
including JWT tokens, refresh token rotation, password hashing, and rate
limiting to protect against brute force attacks.
## Purpose
The authentication module is responsible for:
- User registration and login
- Password hashing and validation
- JWT token generation and validation
- Refresh token management and rotation
- Rate limiting on authentication endpoints
- Session management
[... 75 more lines of API docs, obvious patterns, implementation details ...]Why the concise version is better:
- ✅ 35 lines vs 95 lines (63% smaller)
- ✅ Focuses on gotchas and surprises
- ✅ No API documentation (use code comments)
- ✅ Emphasizes common mistakes
- ✅ Skips obvious implementation details
---
.claude/state.md
Purpose: Current session state - what you're working on right now
Location: .claude/state.md
Structure:
# Session State
**Last Updated:** {ISO 8601 timestamp}
## Active Work
**Current Issue:** #{number} - {title}
**Branch:** {branch-name}
**Started:** {ISO 8601 timestamp}
### Progress
- ✅ {completed item}
- ✅ {completed item}
- 🔄 {in progress item} ({percentage}% done)
- ⏸️ {pending item}
- ⏸️ {pending item}
### Next Steps
1. {next action}
2. {next action}
3. {next action}
### Open Questions
- {question}
→ Decision: {decision if made, or "TBD"}
- {question}
→ Decision: {decision if made, or "TBD"}
## Recent Work
**Previous Issue:** #{number} - {title} (Completed {YYYY-MM-DD})
**Previous Issue:** #{number} - {title} (Completed {YYYY-MM-DD})
**Previous Issue:** #{number} - {title} (Completed {YYYY-MM-DD})
## Blockers
{description of blocker, or "None currently"}
## Context
- Working primarily in {directories}
- Related to {epic or theme}
- Builds on work from #{related-issue}Management:
- Updated by Keep skill on
/keep:start,/keep:save,/keep:done - Read by Keep skill at session start for intelligent resume detection
- Auto-saved every 15 minutes during active work (if auto-save enabled)
- Human-readable markdown (Keep skill parses it)
Resume Detection:
- Checked proactively at conversation start (session boundary detection)
- Active Work section with recent timestamp (< 48h) = resumable work
- Issue number from "Current Issue" field used for
/keep:start {issue}suggestion - Time since "Last Updated" determines suggestion strategy:
- < 48 hours: Proactively suggest resume
- ≥ 48 hours: Mention stale work, ask if user wants to resume
- Missing "Active Work" section = no resume suggestion
Example:
# Session State
**Last Updated:** 2024-10-23T14:30:00Z
## Active Work
**Current Issue:** #1234 - Add rate limiting to authentication
**Branch:** feature/rate-limiting
**Started:** 2024-10-23T10:00:00Z
### Progress
- ✅ Researched rate limiting approaches
- ✅ Installed express-rate-limit
- 🔄 Implementing middleware (80% done)
- ⏸️ Need to add tests
- ⏸️ Need to update API docs
### Next Steps
1. Complete middleware implementation
2. Write unit tests for rate limiter
3. Add integration tests
4. Update API documentation
### Open Questions
- Should rate limit be per IP or per user?
→ Decision: Per IP for unauthenticated, per user for authenticated
- What are the limits?
→ TBD: Need to discuss with team
## Recent Work
**Previous Issue:** #1200 - User authentication (Completed 2024-10-22)
**Previous Issue:** #1150 - Database migrations (Completed 2024-10-20)
## Blockers
None currently
## Context
- Working primarily in src/auth/
- Related to security improvements epic
- Builds on work from #1100 (JWT implementation)---
.claude/work/{issue-number}.md
Purpose: Detailed tracking for a specific issue
Location: .claude/work/{issue-number}.md
Structure:
# Issue #{number}: {title}
**GitHub:** {full URL to issue}
**Pull Request:** {full URL to PR, if exists}
**Status:** {in_progress|completed}
**Started:** {ISO 8601 timestamp}
**Last Updated:** {ISO 8601 timestamp}
## Issue Description
{description from GitHub}
## Approach
{planned approach - can be filled in during work start}
## Progress Log
### {ISO 8601 timestamp}
- {what was done}
- {what was done}
### {ISO 8601 timestamp}
- {what was done}
## Decisions Made
1. **{decision}:** {rationale}
- Alternative considered: {alternative} (rejected because {reason})
- Impact: {what this affects}
2. **{decision}:** {rationale}
## Files Modified
- {file path} ({created|modified})
- {brief description of changes}
- {file path} ({created|modified})
- {brief description of changes}
## Learnings
- {learning or gotcha}
- {learning or gotcha}
## Tests
- [ ] {test to write}
- [ ] {test to write}
- [x] {completed test}
## Next Actions
1. {next action}
2. {next action}
## Related Issues
- #{number} - {title} ({prerequisite|follow-up|related})
- #{number} - {title} ({relationship})Management:
- Created by
/keep-startcommand - Updated by Keep skill during work (progress, decisions, learnings)
- Moved to
.claude/archive/when issue completed - Source for GitHub issue updates
Example:
# Issue #1234: Add rate limiting to authentication
**GitHub:** https://github.com/myuser/taskmaster-api/issues/1234
**Pull Request:** https://github.com/myuser/taskmaster-api/pull/456
**Status:** completed
**Started:** 2024-10-23T10:00:00Z
**Completed:** 2024-10-23T16:00:00Z
## Issue Description
Add rate limiting to authentication endpoints to prevent brute force attacks.
## Approach
1. Use express-rate-limit middleware (widely used, good TypeScript support)
2. Redis store for distributed rate limiting (already in stack)
3. Apply to /login, /register, /reset-password
4. Conservative limits initially (5 per 15min)
5. Exclude health check endpoints
## Progress Log
### 2024-10-23T16:00:00Z
- ✅ All tests passing (unit + integration)
- ✅ Updated API documentation
- ✅ Deployed to staging
- Ready for review
### 2024-10-23T14:30:00Z
- Implemented rate limiter middleware in src/auth/middleware/rateLimiter.ts
- Configured limits: 5 requests per 15 minutes for /login
- Configured limits: 3 requests per 15 minutes for /reset-password
- Applied middleware to auth routes
### 2024-10-23T12:00:00Z
- Researched options: express-rate-limit vs rate-limiter-flexible
- Decided on express-rate-limit (simpler, sufficient for needs)
- Installed dependencies: express-rate-limit, rate-limit-redis
### 2024-10-23T10:00:00Z
- Started work on issue
- Read issue description and requirements
- Reviewed existing auth implementation
## Decisions Made
1. **Rate limit strategy:** Per-IP for unauthenticated routes
- Rationale: Simplest approach, prevents IP-based brute force
- Alternative considered: Per-user for authenticated (complexity not needed yet)
- Impact: Future enhancement could add per-user for authenticated routes
2. **Storage backend:** Redis (not in-memory)
- Rationale: Already using Redis, need distributed limiting
- Alternative considered: In-memory (rejected - won't work with multiple instances)
- Impact: Requires Redis connection, but we already have it
3. **Limit values:** 5 per 15min (login), 3 per 15min (reset)
- Rationale: Conservative start, can adjust based on monitoring
- Alternative considered: 10 per 15min (rejected - too permissive)
- Impact: May need tuning after observing real usage
4. **Health check exclusion:** Exclude /health and /metrics
- Rationale: Monitoring shouldn't be rate limited
- Alternative considered: Apply to all routes (rejected - breaks monitoring)
- Impact: Need separate middleware chain for health endpoints
## Files Modified
- src/auth/middleware/rateLimiter.ts (created)
- Main rate limiter middleware
- Redis store configuration
- Error handling for Redis failures
- src/auth/routes.ts (modified)
- Applied rate limiting to auth routes
- Excluded health checks from rate limiting
- src/auth/middleware/index.ts (modified)
- Export rate limiter middleware
- package.json (modified)
- Added: express-rate-limit ^7.0.0
- Added: rate-limit-redis ^4.0.0
- tests/unit/auth/rateLimiter.test.ts (created)
- Tests for rate limiter middleware
- Mock Redis for unit tests
- tests/integration/auth/rateLimiting.test.ts (created)
- End-to-end rate limiting tests
- Verify limits enforced correctly
- docs/api/authentication.md (modified)
- Documented rate limits
- Added X-RateLimit-* headers documentation
## Learnings
1. **express-rate-limit TypeScript support**
- Excellent type definitions out of the box
- Easy to configure with our Redis setup
2. **Health check exclusion pattern**
- Need to apply rate limiting selectively
- Use route-specific middleware, not global
3. **Rate limit headers**
- X-RateLimit-Limit, X-RateLimit-Remaining automatically added
- Helps clients implement retry logic
4. **Testing with Redis**
- Use redis-mock for unit tests (fast, isolated)
- Use real Redis for integration tests (accurate)
5. **Error handling**
- If Redis fails, rate limiting degrades gracefully
- Falls back to in-memory (per instance, but better than nothing)
## Tests
- [x] Unit tests for rateLimiter middleware (8/8 passing)
- [x] Integration tests for login rate limiting (3/3 passing)
- [x] Test rate limit header presence (2/2 passing)
- [x] Manual testing on staging environment
## Next Actions
None - work complete
## Related Issues
- #1100 - JWT implementation (prerequisite)
- #1250 - Add monitoring for rate limit hits (follow-up, created)---
Archive Files
Purpose: Completed work files preserved for reference
Location: .claude/archive/{issue-number}.md
Format: Same as .claude/work/{issue-number}.md
Management:
- Moved from
.claude/work/when issue completed - Preserved indefinitely for reference
- Can be searched for related patterns/decisions
- Not actively updated after archival
Usage:
- Load when starting similar work
- Reference for "how did we solve X before?"
- Source of patterns for CLAUDE.md suggestions
---
Format Conventions
Timestamps
- Always use ISO 8601 format:
YYYY-MM-DDTHH:MM:SSZ - Use UTC timezone (Z suffix)
- Examples:
2024-10-23T14:30:00Z
Issue References
- Always prefix with
#:#1234 - Include title after dash:
#1234 - Add rate limiting - Link full URL in work files:
https://github.com/user/repo/issues/1234
Status Indicators
- ✅ Completed
- 🔄 In progress
- ⏸️ Pending/not started
- ❌ Blocked or failed
File Paths
- Always use relative paths from project root
- Use forward slashes:
src/auth/middleware/rateLimiter.ts - Note create vs modify:
(created)or(modified)
Markdown Structure
- Use
##for major sections - Use
###for timestamps in progress log - Use
**bold**for field names:**Status:** in_progress - Use lists for progress items, decisions, learnings
---
Validation
When creating or updating files, ensure:
1. All required sections present 2. Timestamps in ISO 8601 format 3. Issue numbers prefixed with # 4. Status indicators consistent 5. File paths valid and relative 6. Markdown formatting correct 7. Links functional (for GitHub URLs)
If file format invalid or corrupted:
- Warn user
- Attempt to repair if possible
- Preserve user data above all else
- Never silently delete or overwrite
GitHub Completion Summary Template
Use this template when posting completion summaries to GitHub issues (from /keep-done).
Template Format
## ✅ Work Complete - {date} {time}
{If PR exists: Completed via PR #{number}}
### Summary
{1-2 paragraph summary of what was accomplished and why}
### Changes Made
- {file}: {what changed}
- {file}: {what changed}
### Key Decisions
1. **{decision}**: {rationale}
2. **{decision}**: {rationale}
### Testing
- ✅ {test type} passing
- ⏸️ {test type} needed
### Learnings
{key insights captured}
### Follow-up
- {follow-up item if any}Best Practices
Summary:
- Focus on what was accomplished and why
- Explain the value delivered
- Keep to 1-2 paragraphs
- Be conversational but professional
Changes Made:
- List key files with brief descriptions
- Focus on major changes, not every file
- Group related changes if many files
Key Decisions:
- Include the decision AND rationale
- Note alternatives considered
- Explain impact on codebase
- Limit to top 3-5 decisions
Testing:
- Note what tests passed
- Note what testing is still needed
- Include manual testing if relevant
Learnings:
- Share insights useful to team
- Document gotchas discovered
- Note patterns that worked well
- Keep concise - 2-4 key learnings
Follow-up:
- Link to created follow-up issues
- Note known limitations
- Mention future improvements
- Omit if no follow-up needed
Example
## ✅ Work Complete - 2024-10-23 16:00
Completed via PR #456
### Summary
Implemented rate limiting for authentication endpoints using express-rate-limit with Redis store. The solution prevents brute force attacks by limiting login attempts to 5 per 15 minutes per IP address, while excluding health check endpoints from rate limiting to preserve monitoring capabilities.
### Changes Made
- `src/auth/middleware/rateLimiter.ts` - Created rate limiter middleware with Redis store and error handling
- `src/auth/routes.ts` - Applied rate limiting to auth routes, excluded health checks
- `tests/unit/auth/rateLimiter.test.ts` - Added comprehensive unit tests
- `tests/integration/auth/rateLimiting.test.ts` - Added end-to-end tests
- `docs/api/authentication.md` - Documented rate limits and response headers
### Key Decisions
1. **Per-IP rate limiting**: Simplest approach for unauthenticated routes, prevents IP-based brute force
2. **Redis store**: Enables distributed rate limiting across multiple instances
3. **Conservative limits**: 5/15min for login, 3/15min for password reset - can adjust based on monitoring
4. **Health check exclusion**: Monitoring endpoints excluded to preserve availability monitoring
### Testing
- ✅ Unit tests passing (8/8)
- ✅ Integration tests passing (5/5)
- ✅ Manual testing completed on staging
### Learnings
- express-rate-limit has excellent TypeScript support and auto-adds X-RateLimit-* headers
- Redis store required for distributed limiting; gracefully degrades to in-memory if Redis fails
- Health endpoints need separate middleware chain to exclude from rate limiting
### Follow-up
Created #1250 to add monitoring dashboard for rate limit hitsPosting to GitHub
Post via gh CLI:
gh issue comment {number} --body "$(cat <<'EOF'
## ✅ Work Complete - {timestamp}
### Summary
{summary here}
### Changes Made
- {file}: {description}
### Key Decisions
1. **{decision}**: {rationale}
### Testing
- ✅ {tests passing}
### Learnings
{learnings}
EOF
)"GitHub Progress Update Template
Use this template when posting progress updates to GitHub issues (from /keep-save --sync).
Template Format
## Progress Update - {date} {time}
✅ Completed:
- {completed item}
- {completed item}
🔄 In Progress:
- {current item} ({percentage}% done)
💡 Key Decisions:
- {decision}: {rationale}
Next: {next steps}Best Practices
Focus on outcomes:
- ✅ "Implemented rate limiting to prevent brute force attacks"
- ❌ "Added code to rate limit authentication"
Explain rationale:
- ✅ "Used Redis store to enable distributed rate limiting across instances"
- ❌ "Used Redis store"
Keep it concise:
- Highlight key progress only
- Summarize decisions, don't list all details
- Keep next steps brief (1-2 items)
Use clear status indicators:
- ✅ for completed
- 🔄 for in progress (with percentage if known)
- Note blocking issues if any
Example
## Progress Update - 2024-10-23 14:30
✅ Completed:
- Installed express-rate-limit and rate-limit-redis
- Configured rate limiter middleware with Redis store
- Applied rate limiting to authentication routes
🔄 In Progress:
- Writing unit tests for rate limiter (60% done)
💡 Key Decisions:
- Using per-IP rate limiting for unauthenticated routes (simpler than per-user)
- Conservative limits: 5 per 15min for login, 3 per 15min for password reset
- Excluded health check endpoints to preserve monitoring
Next: Complete unit tests, then add integration testsPosting to GitHub
Post via gh CLI:
gh issue comment {number} --body "$(cat <<'EOF'
## Progress Update - {timestamp}
✅ Completed:
- {item}
Next: {next steps}
EOF
)"Keep Troubleshooting Guide
Error handling, recovery procedures, and graceful degradation strategies for Keep.
---
GitHub Issues
GitHub CLI Not Installed
Symptom:
Error: gh: command not foundDetection:
which gh # Returns emptyResponse:
⚠️ GitHub CLI not installed
Keep can work in local-only mode:
• Track work locally in .claude/work/
• Manually sync to GitHub later
• Install gh CLI anytime to enable GitHub features
Continue in local-only mode? [yes / abort]Graceful degradation:
- Continue with all local operations
- Skip GitHub fetching/posting
- Note in work files that sync needed
- Remind user periodically about GitHub features
---
Network Errors
Symptoms:
Error: failed to fetch issue: network unreachable
Error: timeout connecting to api.github.comResponse:
⚠️ GitHub unavailable (network error)
Work saved locally:
• .claude/work/{issue}.md updated
• .claude/state.md updated
Will sync to GitHub when connection restored.
Continue working? [yes / abort]Graceful degradation:
- Save all progress locally
- Mark work files with "sync pending"
- Offer retry on next operation
- Continue full workflow without GitHub
---
Rate Limiting
Symptom:
Error: API rate limit exceeded for userDetection: Parse error response from gh commands
Response:
⚠️ GitHub API rate limit exceeded
Rate limit resets at: {reset_time}
You can:
1. Wait {minutes} minutes
2. Continue in local-only mode
3. Use cached/local data
Continue with local-only mode? [yes / wait / abort]Graceful degradation:
- Use cached issue data if available
- Continue with local operations
- Retry GitHub ops after reset time
---
Authentication Errors
Symptoms:
Error: authentication required
Error: HTTP 401: UnauthorizedResponse:
⚠️ GitHub authentication failed
Please authenticate:
gh auth login
Or continue in local-only mode.
[retry / local-only mode / abort]Graceful degradation:
- Switch to local-only mode
- Note that sync will require auth
- Remind user to authenticate later
---
File System Issues
Corrupted State File
Symptom:
.claude/state.mdhas invalid format- Missing required sections
- Conflicting data
Detection:
- Markdown parse errors
- Missing required fields
- Active issue mismatch with work files
Recovery:
⚠️ State file corrupted - reconstructing from work files
Found active work:
• #1234 - Add rate limiting (started 2024-10-23)
Recent work:
• #1200 - Authentication (completed 2024-10-22)
Reconstructed state:
[Show reconstructed state.md content]
Does this look correct? [yes / no / edit]Recovery procedure: 1. Check .claude/work/ for active issues (not archived) 2. Check .claude/archive/ for recent completed work (last 3) 3. Rebuild state.md structure 4. Populate with discovered data 5. Show user for confirmation 6. Save reconstructed state
Preserve user data:
- Backup corrupted file to
.claude/state.md.backup - Never silently delete
- Ask user to confirm reconstruction
---
Missing Work File
Symptom:
state.mdreferences issue but no work file exists- User tries to save progress but no active work file
Response:
⚠️ Work file missing for issue #1234
I can recreate it from:
1. GitHub issue (if available)
2. Git history
3. Fresh start
Choose recovery method: [github / fresh / abort]Recovery options:
Option 1: From GitHub 1. Fetch issue details via gh issue view 2. Create new work file with issue data 3. Note in work file: "Recreated {timestamp}" 4. Continue normally
Option 2: Fresh start 1. Create minimal work file 2. Prompt user for context 3. Continue from current point
---
Conflicting State
Symptom:
- Active work in state.md but work file is archived
- Multiple work files not archived
- Branch doesn't match active issue
Detection: Compare state.md with file system state
Response:
⚠️ State conflict detected
State says: #1234 active
Found: .claude/archive/1234.md (archived)
Possible causes:
• /keep-done ran but state update failed
• Manual file operations
Fix by:
1. Clear active work from state (mark #1234 complete)
2. Restore #1234 to active (un-archive)
Choose resolution: [1 / 2 / manual review]Resolution:
- Present user with options
- Never auto-resolve ambiguity
- Explain what each choice does
- Preserve all data
---
Permission Errors
Symptom:
Error: EACCES: permission denied, open '.claude/work/1234.md'Response:
⚠️ Permission denied writing to .claude/work/
Check file permissions:
ls -la .claude/work/
Required permissions:
User needs write access to .claude/ directory
[retry after fixing / abort]Graceful degradation:
- Not applicable (can't continue without write access)
- Guide user to fix permissions
- Offer to retry after fix
---
Data Integrity
Preserve User Data - Golden Rule
Never silently:
- Delete files
- Overwrite without backup
- Discard user content
Always:
- Backup before destructive operations
- Warn user of data loss
- Ask confirmation for ambiguous operations
- Preserve original data somewhere
Backup pattern:
# Before overwriting corrupted file
cp .claude/state.md .claude/state.md.backup.{timestamp}
# Then recreate---
Validation Before Destructive Operations
Before archiving work file: 1. Confirm work is complete 2. Verify state.md updated 3. Confirm GitHub synced (if online) 4. Only then move to archive
Before overwriting state.md: 1. Validate new state structure 2. Backup existing file 3. Write new file 4. Verify readable
---
Git Issues
Detached HEAD State
Symptom:
warning: You are in 'detached HEAD' stateResponse:
⚠️ Git is in detached HEAD state
Keep can still work, but commits won't be on a branch.
Suggested fix:
git checkout -b feature/issue-1234
Continue anyway? [yes / fix first]---
Merge Conflicts in .claude/
Symptom:
<<<<<<< HEAD
**Current Issue:** #1234
=======
**Current Issue:** #1235
>>>>>>> feature-branchResponse:
⚠️ Merge conflict in .claude/state.md
Keep cannot resolve this automatically.
Please resolve the conflict manually:
1. Edit .claude/state.md
2. Remove conflict markers
3. Keep correct active issue
4. Run /keep-save to validate
[done - retry / abort]Prevention:
- Recommend: Don't work on multiple issues in parallel
- Use feature branches per issue
- Complete work before switching
---
Recovery Commands
Validate State
If user reports issues, validate state:
# Check state file exists and is readable
cat .claude/state.md
# Check for active work files
ls -la .claude/work/
# Check recent archives
ls -la .claude/archive/ | head -5
# Verify GitHub connectivity
gh auth status
gh issue list --limit 1Rebuild Index
If state is completely lost:
# Find all work files
find .claude/work/ -name "*.md"
# Find recent archives (last 30 days)
find .claude/archive/ -name "*.md" -mtime -30
# Use to rebuild state.mdClear Stuck State
If state is irrecoverably broken:
⚠️ State cannot be recovered automatically
Recommended: Start fresh
1. Backup: cp .claude/state.md .claude/state.md.broken
2. Delete: rm .claude/state.md
3. Restart: /keep-start {issue-number}
Proceed? [yes / manual recovery]---
Error Messages
User-Friendly Format
Good error messages:
⚠️ {What went wrong}
{Why this happened (if known)}
{What user can do about it}
[Action choices]Example:
⚠️ Cannot save progress - no active work
This happens when:
• No issue is currently being worked on
• Work was completed but state not updated
To fix:
• Start new work: /keep-start {issue-number}
• Resume existing: /keep-start {issue-number}
What would you like to do?Avoid Technical Jargon
Bad:
Error: ENOENT: no such file or directory, open '.claude/work/undefined.md'Good:
⚠️ Work file not found
I couldn't find a work file for the current issue.
This usually means work hasn't been started yet.
Start work with: /keep-start {issue-number}---
Testing Error Scenarios
When implementing error handling, test these scenarios:
GitHub:
- [ ] gh CLI not installed
- [ ] Network offline
- [ ] Rate limit exceeded
- [ ] Authentication failed
- [ ] Repository not found
- [ ] Issue doesn't exist
File System:
- [ ] state.md corrupted
- [ ] state.md missing
- [ ] work file missing
- [ ] Permission denied
- [ ] Disk full
- [ ] Conflicting work files
Git:
- [ ] Detached HEAD
- [ ] Merge conflicts in .claude/
- [ ] Dirty working directory
- [ ] No git repo
- [ ] Corrupted git state
Data:
- [ ] Invalid timestamps
- [ ] Malformed markdown
- [ ] Missing required sections
- [ ] Conflicting state data
---
Philosophy
Fail gracefully:
- Degrade features, don't break workflows
- Continue with reduced functionality
- Never lose user data
Be transparent:
- Explain what went wrong
- Explain why it matters
- Explain options to fix
Preserve state:
- Backup before destructive operations
- Never silently delete or overwrite
- Ask user to resolve ambiguity
Guide recovery:
- Offer specific fix suggestions
- Provide commands when helpful
- Link to documentation if complex
Learn and prevent:
- Log common errors
- Add validation to prevent recurrence
- Improve error messages based on user feedback
Keep Workflow Examples
This document contains detailed workflow examples showing how Keep operates in practice. Load this file when need detailed workflow guidance or when users ask "how does X work?"
---
Workflow 1: Start New Work
User Invocation
User: /keep-start 1234System Flow
┌─────────────────────────────────────────────────────┐
│ 1. Fetch Issue from GitHub │
│ gh issue view 1234 --json title,body,labels │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 2. Load Context │
│ ├─ CLAUDE.md (auto-loaded by Claude Code) │
│ ├─ Relevant module CLAUDE.md (auto-loaded) │
│ ├─ Read .claude/state.md │
│ └─ Search .claude/archive/ for related work │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 3. Parse Issue │
│ ├─ Extract requirements │
│ ├─ Identify dependencies ("depends on #...") │
│ ├─ Note labels and priorities │
│ └─ Analyze acceptance criteria │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 4. Suggest Approach │
│ Based on: │
│ ├─ Project patterns (from CLAUDE.md) │
│ ├─ Similar past work (from archive) │
│ └─ Technical constraints (from issue) │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 5. Create Work File │
│ .claude/work/1234.md with: │
│ ├─ Issue metadata (URL, status, timestamps) │
│ ├─ Description │
│ ├─ Suggested approach │
│ └─ Empty sections (progress, decisions, etc.) │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 6. Update State │
│ .claude/state.md: │
│ ├─ Set active issue: #1234 │
│ ├─ Record start time │
│ ├─ Note branch if specified │
│ └─ Move previous issue to "Recent Work" │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 7. Present to User │
│ Conversational presentation with: │
│ ├─ Issue summary │
│ ├─ Context loaded │
│ ├─ Suggested approach │
│ ├─ Related work │
│ └─ Questions if clarification needed │
└─────────────────────────────────────────────────────┘Example Output
┌────────────────────────────────────────┐
│ ✅ Ready to work on issue #1234 │
│ │
│ 📋 Issue: Add rate limiting to auth │
│ 🏷️ Labels: security, enhancement │
│ │
│ 📚 Context loaded: │
│ ├─ CLAUDE.md (project overview) │
│ ├─ src/CLAUDE.md (code patterns) │
│ └─ src/auth/CLAUDE.md (auth patterns) │
│ │
│ 🔗 Related work: │
│ └─ #1100 - JWT implementation │
│ │
│ 💡 Suggested approach: │
│ Based on existing auth patterns, I │
│ suggest using express-rate-limit with │
│ Redis store. This fits our current │
│ architecture and JWT pattern from │
│ #1100. │
│ │
│ The issue mentions preventing brute │
│ force attacks. Should we: │
│ • Rate limit per-IP (simpler) │
│ • Rate limit per-user (more complex) │
│ • Both (comprehensive) │
│ │
│ Where would you like to start? │
└────────────────────────────────────────┘---
Workflow 2: Save Progress
User Invocation
User: /keep-saveOr automatic trigger (if auto-save enabled):
[15 minutes elapsed since last save]
[Auto-save triggered]System Flow
┌─────────────────────────────────────────────────────┐
│ 1. Review Recent Conversation │
│ Analyze last 30 minutes (or since last save): │
│ ├─ Concrete steps completed │
│ ├─ Decisions made with rationale │
│ ├─ Learnings and gotchas discovered │
│ └─ Questions raised or resolved │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 2. Update Work File │
│ .claude/work/{issue}.md: │
│ ├─ Add timestamped progress log entry │
│ ├─ Document decisions in Decisions section │
│ ├─ Capture learnings in Learnings section │
│ └─ Update files modified list │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 3. Update State │
│ .claude/state.md: │
│ ├─ Update progress indicators │
│ ├─ Update next steps │
│ └─ Note any new questions │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 4. Check Learning Threshold │
│ Count decisions by directory: │
│ ├─ src/auth/: 3 decisions │
│ └─ Threshold met! (3+ decisions) │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 5. Generate CLAUDE.md Proposal (if threshold met) │
│ ├─ Read current src/auth/CLAUDE.md │
│ ├─ Draft new section or updates │
│ ├─ Generate diff showing changes │
│ └─ Prepare rationale │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 6. Present to User │
│ ├─ Confirm what was captured │
│ ├─ Show proposed CLAUDE.md updates (if any) │
│ └─ Ask for approval │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 7. Optional GitHub Sync │
│ If --sync flag or user confirms: │
│ ├─ Generate progress summary │
│ ├─ Post as issue comment │
│ └─ Record sync timestamp │
└─────────────────────────────────────────────────────┘Example Output (without context update)
┌────────────────────────────────────────┐
│ 💾 Progress saved (14:30) │
│ │
│ 📝 Captured: │
│ - 1 progress entry │
│ - 2 decisions │
│ - 1 learning │
└────────────────────────────────────────┘Example Output (with context update suggestion)
┌────────────────────────────────────────┐
│ 💾 Progress saved │
│ │
│ 📝 Captured: │
│ - 2 progress entries │
│ - 3 decisions │
│ - 1 learning │
│ │
│ 💡 Suggestion: Update src/auth/ │
│ CLAUDE.md with rate-limiting │
│ pattern? │
│ │
│ Proposed addition: │
│ ───────────────────────────────── │
│ ## Rate Limiting │
│ │
│ - Use express-rate-limit middleware │
│ - Redis store for distributed limiting │
│ - Limits: 5/15min for /login │
│ - Exclude health checks │
│ ───────────────────────────────── │
│ │
│ This will help future work in auth/ │
│ have this rate limiting context ready. │
│ │
│ Add this to src/auth/CLAUDE.md? │
│ [yes / edit / later / no] │
└────────────────────────────────────────┘---
Workflow 3: Complete Work
User Invocation
User: /keep-doneSystem Flow
┌─────────────────────────────────────────────────────┐
│ 1. Read Complete Work File │
│ Load .claude/work/{issue}.md: │
│ ├─ All progress entries │
│ ├─ All decisions made │
│ ├─ All learnings captured │
│ └─ Files modified list │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 2. Generate Comprehensive Summary │
│ ├─ What was accomplished (outcomes) │
│ ├─ Why decisions were made (rationale) │
│ ├─ What was learned (insights) │
│ ├─ Testing status │
│ └─ Follow-up needed (if any) │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 3. Check for Context Updates │
│ Review all learnings: │
│ ├─ Which CLAUDE.md files should be updated? │
│ ├─ Generate proposed updates │
│ └─ Prepare diffs for review │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 4. Present Summary and Get Approvals │
│ ├─ Show work summary │
│ ├─ Show proposed CLAUDE.md updates │
│ └─ Get user approval for updates │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 5. Sync to GitHub │
│ ├─ Post completion comment with summary │
│ ├─ Ask about closing issue │
│ └─ If confirmed: gh issue close {number} │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 6. Archive Work File │
│ mv .claude/work/{issue}.md │
│ .claude/archive/{issue}.md │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 7. Update State │
│ .claude/state.md: │
│ ├─ Clear active issue │
│ ├─ Add to recent work │
│ └─ Update context (hot areas) │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 8. Recommend Next Work │
│ ├─ Fetch open issues from GitHub │
│ ├─ Score using algorithm │
│ ├─ Present top 3-5 recommendations │
│ └─ Offer to start immediately │
└─────────────────────────────────────────────────────┘Example Output
┌────────────────────────────────────────┐
│ 🎉 Issue #1234 complete! │
│ │
│ 📊 Summary: │
│ - 4 files modified │
│ - 5 decisions documented │
│ - 3 learnings captured │
│ - All tests passing ✅ │
│ │
│ 💡 Context updates: │
│ ✅ src/auth/CLAUDE.md (already done) │
│ │
│ 🔗 Posting summary to GitHub... │
│ │
│ Summary posted: │
│ https://github.com/user/repo/issues/ │
│ 1234#issuecomment-123456 │
│ │
│ Close issue #1234? │
│ [yes / no / later] │
└────────────────────────────────────────┘
User: yes
┌────────────────────────────────────────┐
│ ✅ Issue #1234 closed │
│ │
│ ⏭️ Recommended next work: │
│ │
│ 🔥 Hot: #1250 - Add rate limit │
│ monitoring │
│ ├─ Builds on #1234 │
│ ├─ Same area: src/auth/ │
│ ├─ Labels: enhancement │
│ └─ Estimated: 2-3 hours │
│ │
│ 📋 Other options: │
│ 2. #1245 - OAuth integration │
│ └─ Same area, larger scope │
│ │
│ 3. #1180 - Fix session bug [urgent] │
│ └─ Different area, high priority │
│ │
│ Start #1250? │
│ [yes / show more / choose different] │
└────────────────────────────────────────┘GitHub Comment Format
What gets posted to the issue:
## ✅ Work Complete - 2024-10-23 16:00
### Summary
Implemented rate limiting for authentication endpoints using express-rate-limit with Redis store. The solution prevents brute force attacks by limiting login attempts to 5 per 15 minutes per IP address, while excluding health check endpoints from rate limiting to preserve monitoring capabilities.
### Changes Made
- `src/auth/middleware/rateLimiter.ts` - Created rate limiter middleware with Redis store and error handling
- `src/auth/routes.ts` - Applied rate limiting to auth routes, excluded health checks
- `tests/unit/auth/rateLimiter.test.ts` - Added comprehensive unit tests
- `tests/integration/auth/rateLimiting.test.ts` - Added end-to-end tests
- `docs/api/authentication.md` - Documented rate limits and response headers
### Key Decisions
1. **Per-IP rate limiting**: Simplest approach for unauthenticated routes, prevents IP-based brute force
2. **Redis store**: Enables distributed rate limiting across multiple instances
3. **Conservative limits**: 5/15min for login, 3/15min for password reset - can adjust based on monitoring
4. **Health check exclusion**: Monitoring endpoints excluded to preserve availability monitoring
### Testing
- ✅ Unit tests passing (8/8)
- ✅ Integration tests passing (5/5)
- ✅ Manual testing completed on staging
### Learnings
- express-rate-limit has excellent TypeScript support and auto-adds X-RateLimit-* headers
- Redis store required for distributed limiting; gracefully degrades to in-memory if Redis fails
- Health endpoints need separate middleware chain to exclude from rate limiting
### Follow-up
Created #1250 to add monitoring dashboard for rate limit hits---
Workflow 4: Recommend Next Work
User Invocation
User: /keep-nextOr automatically after /keep-done
System Flow
┌─────────────────────────────────────────────────────┐
│ 1. Fetch Open Issues │
│ gh issue list --state open │
│ --json number,title,labels,body,updatedAt │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 2. Load Context │
│ ├─ Read .claude/state.md (recent work) │
│ ├─ Read .claude/archive/ (last 3 completed) │
│ └─ Note hot directories and patterns │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 3. Score Each Issue │
│ For each open issue: │
│ ├─ Continuity score (0-100) │
│ ├─ Priority score (0-100) │
│ ├─ Freshness score (0-100) │
│ ├─ Dependency score (0-100) │
│ └─ Weighted total │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 4. Sort and Select Top Recommendations │
│ ├─ Sort by total score (descending) │
│ ├─ Group: hot recommendation vs other options │
│ └─ Select top 3-5 for presentation │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 5. Prepare Rationale │
│ For each recommendation: │
│ ├─ Why it scores high │
│ ├─ Relationship to recent work │
│ ├─ Effort estimate (if possible) │
│ └─ Key labels/priorities │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 6. Present Recommendations │
│ ├─ Top recommendation with detail │
│ ├─ Other good options (brief) │
│ └─ Offer to start, show more, or choose other │
└─────────────────────────────────────────────────────┘Scoring Details
# Continuity Score (0-100)
continuity = 0
if same_directory(issue, recent_work):
continuity += 50
if overlapping_labels(issue, recent_work):
continuity += 30
if similar_tech(issue, recent_work):
continuity += 20
# Priority Score (0-100)
if "urgent" in labels:
priority = 100
elif "high-priority" in labels:
priority = 75
elif "medium" in labels or no priority label:
priority = 50
else: # "low-priority"
priority = 25
# Freshness Score (0-100)
days_since_update = (now - issue.updated_at).days
if days_since_update <= 7:
freshness = 100
elif days_since_update <= 14:
freshness = 75
elif days_since_update <= 30:
freshness = 50
else:
freshness = 25
# Dependency Score (0-100)
dependency = 100 # Assume unblocked
blockers = find_blockers(issue.body) # Parse "depends on #123"
for blocker_id in blockers:
blocker_issue = get_issue(blocker_id)
if blocker_issue.state == "open":
dependency -= 25 # Reduce for each open blocker
# Weighted Total
score = (
continuity * 0.30 +
priority * 0.30 +
freshness * 0.20 +
dependency * 0.20
)Example Output
┌────────────────────────────────────────┐
│ 🎯 Recommended Next Work │
│ │
│ 🔥 Hot Recommendation: │
│ #1250 - Add monitoring for rate limits │
│ ├─ Score: 92/100 │
│ ├─ Builds directly on #1234 (just done)│
│ ├─ Same area: src/auth/ │
│ ├─ Context is fresh in memory │
│ └─ Estimated: 2-3 hours │
│ │
│ 📋 Other Good Options: │
│ │
│ 2. #1245 - OAuth integration [high] │
│ └─ Score: 78 | Same area, larger │
│ scope (1-2 days) │
│ │
│ 3. #1180 - Fix session bug [urgent] │
│ └─ Score: 75 | Different area but │
│ marked urgent │
│ │
│ 4. #1300 - Add API documentation │
│ └─ Score: 55 | No blockers, medium │
│ priority │
│ │
│ Start #1250? │
│ [yes / show more / choose different] │
└────────────────────────────────────────┘---
Error Handling Workflows
GitHub Unavailable
┌─────────────────────────────────────────────────────┐
│ Attempt GitHub Operation │
│ gh issue view 1234 │
└──────────────┬──────────────────────────────────────┘
│
▼ [Error]
┌─────────────────────────────────────────────────────┐
│ Check Error Type │
│ ├─ gh not installed │
│ ├─ Network error │
│ └─ Rate limit │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Graceful Degradation │
│ ├─ Warn user about limitation │
│ ├─ Offer local-only mode │
│ ├─ Continue workflow without GitHub │
│ └─ Note sync needed for later │
└─────────────────────────────────────────────────────┘Example:
⚠️ GitHub unavailable (gh not found)
I'll continue in local-only mode. You can:
• Manually provide issue details
• Work without issue tracking
• Install gh CLI later and sync
Continue? [yes / abort]Corrupted State File
┌─────────────────────────────────────────────────────┐
│ Attempt to Read .claude/state.md │
└──────────────┬──────────────────────────────────────┘
│
▼ [Parse Error]
┌─────────────────────────────────────────────────────┐
│ Detect Corruption │
│ ├─ Invalid format │
│ ├─ Missing sections │
│ └─ Conflicting data │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Reconstruct from Work Files │
│ ├─ Check .claude/work/ for active issues │
│ ├─ Check .claude/archive/ for recent work │
│ └─ Rebuild state.md from available data │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Notify User │
│ ├─ Explain what was repaired │
│ ├─ Show reconstructed state │
│ └─ Ask for confirmation │
└─────────────────────────────────────────────────────┘Example:
⚠️ State file corrupted, reconstructed from work files
Found active work:
• #1234 - Add rate limiting (started 2024-10-23)
Recent work:
• #1200 - Authentication (completed 2024-10-22)
Does this look correct? [yes / no]---
Advanced Workflows
Creating New CLAUDE.md
Triggered by:
- User runs
/keep-grow src/payments/ - Keep detects threshold (2+ sessions in directory)
- Keep notices missing context in active area
┌─────────────────────────────────────────────────────┐
│ 1. Analyze Directory │
│ ├─ Scan file names and types │
│ ├─ Read key files (exports, interfaces) │
│ ├─ Identify patterns and abstractions │
│ └─ Detect frameworks/libraries in use │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 2. Assess Value │
│ ├─ Is this a cohesive module? │
│ ├─ Are patterns clear enough? │
│ ├─ Would future work benefit? │
│ └─ Is it too early? (avoid premature docs) │
└──────────────┬──────────────────────────────────────┘
│
▼ [If valuable]
┌─────────────────────────────────────────────────────┐
│ 3. Generate Proposal │
│ Draft CLAUDE.md with: │
│ ├─ Purpose │
│ ├─ Key Patterns │
│ ├─ API/Interface │
│ ├─ Recent Learnings (from current work) │
│ └─ Dependencies and Testing │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 4. Present for Review │
│ ├─ Show complete proposed content │
│ ├─ Explain benefit │
│ └─ Offer: create / edit / skip │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 5. Create File (if approved) │
│ ├─ Write src/payments/CLAUDE.md │
│ ├─ Confirm creation │
│ └─ Note auto-loading for future work │
└─────────────────────────────────────────────────────┘Example Output:
┌────────────────────────────────────────┐
│ 📝 Proposed CLAUDE.md for src/payments/│
│ │
│ I've analyzed src/payments/ and found │
│ clear patterns worth documenting: │
│ │
│ • Stripe API integration │
│ • Webhook validation pattern │
│ • Idempotency key usage │
│ • Payment record repository │
│ │
│ [Full proposed content shown...] │
│ │
│ Create src/payments/CLAUDE.md? │
│ [yes / edit / later / no] │
│ │
│ This will help future work in payments/│
│ have immediate context about patterns. │
└────────────────────────────────────────┘---
Workflow 5: Zero-Issues Project Initialization
User Invocation
User: /keep-startWith no issue number and no open GitHub issues.
System Flow
┌─────────────────────────────────────────────────────┐
│ 1. Check CLAUDE.md Context │
│ ├─ Look for root CLAUDE.md │
│ ├─ If missing/stale: Offer /keep-grow first │
│ └─ Ensure project context loaded │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 2. Discovery (using native tools) │
│ Planning docs (Glob): │
│ ├─ {ROADMAP,TODO,PLAN,VISION}*.md │
│ └─ Read and parse list items, checkboxes │
│ │
│ Code signals (Grep): │
│ ├─ Pattern: TODO:|FIXME:|HACK:|BUG: │
│ └─ Extract with line numbers │
│ │
│ Test coverage (Glob): │
│ ├─ Find tests: **/*.{test,spec}.* │
│ └─ Find source: src/**/* │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 3. Synthesize Suggestions │
│ ├─ Priority: roadmap > FIXME > TODO > tests │
│ ├─ Generate 3-5 actionable suggestions │
│ ├─ Include source attribution │
│ └─ Suggest labels and priorities │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 4. Present & Create Issues │
│ ├─ Show findings conversationally │
│ ├─ User selects which to create │
│ ├─ Generate natural issue bodies │
│ ├─ Create via gh issue create │
│ └─ Display created issue URLs │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 5. Start Work │
│ ├─ Ask: "Which issue to start?" │
│ ├─ Transition to normal /keep-start workflow │
│ └─ Load context, create work file │
└─────────────────────────────────────────────────────┘Example Output
┌────────────────────────────────────────┐
│ 👋 Welcome to Keep! │
│ │
│ I notice you don't have any GitHub │
│ issues yet. Let me help you get │
│ started! │
│ │
│ 📚 Checking project context... │
│ ✅ Found CLAUDE.md (current) │
│ │
│ 📋 Searching for planning documents... │
│ ✅ Found ROADMAP.md with 5 features │
│ ✅ Found TODO.md with 3 tasks │
│ │
│ 🔍 Analyzing codebase... │
│ • 12 TODO comments found │
│ • 8 files missing test coverage │
│ • 3 modules need documentation │
│ │
│ 🎯 Synthesized 5 starter issues: │
│ │
│ 1. **Implement user authentication** │
│ [feature, high] │
│ Source: ROADMAP.md line 15 │
│ Files: src/auth/ │
│ │
│ 2. **Add error handling to API │
│ endpoints** [bug, high] │
│ Source: TODO in src/api/routes.ts:34│
│ Files: src/api/routes.ts │
│ │
│ 3. **Write tests for payment module** │
│ [test, medium] │
│ Source: Missing test coverage │
│ Files: tests/payments/ │
│ │
│ 4. **Document database schema** │
│ [docs, medium] │
│ Source: Undocumented src/db/ │
│ Files: docs/, src/db/ │
│ │
│ 5. **Refactor user service** │
│ [refactor, low] │
│ Source: TODO in src/services/user.ts│
│ Files: src/services/user.ts │
│ │
│ Which issues should I create? │
│ [all / 1,2,3 / custom] │
└────────────────────────────────────────┘
User: 1,2,3
┌────────────────────────────────────────┐
│ Creating issues... │
│ │
│ ✅ Created #1: Implement user │
│ authentication │
│ https://github.com/user/repo/ │
│ issues/1 │
│ │
│ ✅ Created #2: Add error handling to │
│ API endpoints │
│ https://github.com/user/repo/ │
│ issues/2 │
│ │
│ ✅ Created #3: Write tests for payment │
│ module │
│ https://github.com/user/repo/ │
│ issues/3 │
│ │
│ 🎉 Created 3 issues! │
│ │
│ Which issue would you like to start │
│ working on? │
│ [1 / 2 / 3] │
└────────────────────────────────────────┘
User: 1
┌────────────────────────────────────────┐
│ ✅ Ready to work on issue #1 │
│ │
│ 📋 Issue: Implement user authentication│
│ 🏷️ Labels: feature, enhancement │
│ │
│ 📚 Context loaded: │
│ ├─ CLAUDE.md (project overview) │
│ └─ ROADMAP.md (source document) │
│ │
│ 💡 From roadmap: │
│ Implement JWT-based authentication │
│ with refresh tokens. Should support │
│ login, logout, and password reset. │
│ │
│ Suggested approach: │
│ Based on project structure, implement │
│ in src/auth/ using existing patterns. │
│ │
│ Where would you like to start? │
└────────────────────────────────────────┘Handling Different Scenarios
Scenario A: No Planning Docs, Many TODOs
📋 Searching for planning documents...
ℹ️ No planning documents found
🔍 Analyzing codebase...
• 25 TODO comments found
• 15 files missing tests
🎯 Suggestions based on codebase analysis:
1. **Fix authentication rate limiting**
Source: FIXME in src/auth/middleware.ts:45
...Scenario B: Planning Docs, No TODO Comments
📋 Searching for planning documents...
✅ Found ROADMAP.md with 8 features
✅ Found VISION.md
🔍 Analyzing codebase...
• No TODO comments found
• 3 files missing tests
🎯 Suggestions from roadmap:
1. **Add real-time notifications**
Source: ROADMAP.md - Q1 Features
...Scenario C: Nothing Found
📋 Searching for planning documents...
ℹ️ No planning documents found
🔍 Analyzing codebase...
• No TODO comments found
• No obvious gaps detected
💭 No automated suggestions available.
Would you like to:
1. Create a ROADMAP.md to plan features
2. Create an issue manually
3. Use /keep-grow to document code first
4. Work in local-only mode
What would you prefer?---
Summary
These workflows show Keep in action:
- Start: Fetch issue, load context, create tracking, present starting point
- Save: Capture progress/learnings, suggest context updates, optional sync
- Done: Summarize, sync to GitHub, archive, recommend next
- Next: Score open issues, recommend based on context and continuity
- Grow: Create CLAUDE.md when patterns emerge
- Zero-Issues Init: Discover work using native tools (Glob/Grep), synthesize suggestions, create issues naturally
All workflows degrade gracefully when GitHub unavailable, preserve user data, and respect user control through approval gates.
Zero-Issues Project Initialization
Detailed patterns and implementation guide for discovering starter work when a project has no open GitHub issues.
---
When to Trigger
Zero-issues initialization starts when:
/keep-startcalled without issue numbergh issue listreturns empty array- User asks "what should I work on?" with no open issues
---
Three-Phase Workflow
Phase 1: Discovery
Use Claude Code's native tools to find actionable work:
1.1 Check CLAUDE.md Context
# Check if root CLAUDE.md exists and is recentDecision logic:
- If root CLAUDE.md missing: Offer
/keep-grow .first - If root CLAUDE.md stale (>3 months): Suggest updating
- If present and current: Continue to discovery
Rationale: Project context is essential before suggesting work
1.2 Find Planning Documents
Use Glob to find planning docs:
Pattern: {ROADMAP,TODO,PLAN,BACKLOG,VISION,CONTRIBUTING}*.md
Path: project rootParse patterns:
- List items:
- [ ] Task description(checkboxes) - List items:
- Task description(bullets) - Numbered items:
1. Task description - Headers as categories:
## Feature Namefollowed by description
Extract:
- Task description
- Source file and line number
- Any priority indicators (!, HIGH, P0, etc.)
- Any size estimates (small, 2h, etc.)
Example parse:
ROADMAP.md:
15: - [ ] Implement user authentication
16: - JWT tokens with refresh
17: - Estimated: 2 days→ Extract: "Implement user authentication", source: "ROADMAP.md:15", estimate: "2 days"
1.3 Scan Codebase Signals
Use Grep to find code comments:
Pattern: TODO:|FIXME:|HACK:|BUG:
Options: -n (line numbers), -B 1 -A 1 (context), -i (case insensitive)Categorize by type:
FIXME:/BUG:→ High priority (bugs/issues)TODO:→ Medium priority (planned improvements)HACK:→ Medium priority (refactoring needed)
Extract context:
// FIXME: Rate limiting not implemented for login endpoint
// This allows brute force attacks
async function login(req, res) { ... }→ Extract: "Add rate limiting to login endpoint", source: "src/auth/login.ts:45", priority: high
1.4 Assess Test Coverage
Find test files (Glob):
Pattern: **/*.{test,spec}.{js,ts,py,go,rs}Find source files (Glob):
Pattern: {src,lib}/**/*.{js,ts,py,go,rs}Identify gaps:
- Directories with source but no tests
- Modules with partial test coverage
- Critical paths without tests (auth, payments, etc.)
Example:
src/payments/
├── stripe.ts
├── processor.ts
└── refunds.ts
tests/
└── (no payments/ directory)→ Extract: "Add tests for payment module", source: "Missing coverage in src/payments/"
---
Phase 2: Synthesis
2.1 Prioritization Logic
Priority ranking: 1. Planning docs (ROADMAP, TODO, etc.) - Explicitly planned work 2. FIXME/BUG comments - Important fixes 3. TODO comments - Planned improvements 4. Missing tests - Quality improvements 5. Documentation gaps - Lower priority
Within each category, prioritize by:
- Explicit priority markers (!, HIGH, urgent)
- Frequency (multiple TODOs in same area)
- Recency (recent comments)
- Critical areas (auth, payments, security)
2.2 Generate Issue Suggestions
For each potential issue:
Title generation:
- Imperative mood: "Add", "Fix", "Implement", "Refactor"
- Specific and actionable
- Clear scope
Description generation:
{What needs to be done - 1-2 sentences}
**Context:**
{Why this matters or what problem it solves}
**Source:**
{file:line or document name}
**Affected files:**
{list of files if known}
**Acceptance criteria:**
- {criterion 1}
- {criterion 2}Label suggestions:
enhancement- New featuresbug- Bug fixestesting- Test additionsdocumentation- Docsrefactor- Code cleanupsecurity- Security issues
Priority labels:
- FIXME/BUG →
high - Roadmap items →
mediumorhigh - TODO →
medium - Tests/docs →
lowormedium
2.3 Limit to 3-5 Suggestions
Selection criteria:
- Mix of types (features, bugs, tests)
- Mix of sizes (quick wins + larger work)
- Highest priority items
- Diverse areas (avoid all from one module)
Presentation order: 1. Highest value/priority first 2. Group by type if helpful 3. Include source attribution for each
---
Phase 3: Interactive Creation
3.1 Present Findings
Conversational presentation:
I notice you don't have any open issues. Let me help find starter work!
📚 Project context: ✅ CLAUDE.md found
📋 Planning documents:
• ROADMAP.md: 5 planned features
• TODO.md: 3 pending tasks
🔍 Codebase analysis:
• 12 TODO/FIXME comments
• 8 files missing tests
🎯 5 Starter Issue Suggestions:
1. **Implement user authentication** [enhancement, high]
Source: ROADMAP.md line 15
2. **Fix rate limiting in API** [bug, medium]
Source: FIXME in src/api/routes.ts:34
3. **Add tests for payment module** [testing, medium]
Source: Missing coverage in src/payments/
4. **Document database schema** [documentation, low]
Source: Undocumented src/db/
5. **Refactor user service** [refactor, medium]
Source: TODO in src/services/user.ts:12
Which issues should I create? [all / 1,2,3 / none - work locally]3.2 Create Issues
Before creating: 1. Collect all unique labels from selected issues 2. Check existing labels: gh label list --json name --jq '.[].name' 3. Create missing labels: gh label create "label-name" (uses default color)
For each selected issue:
1. Generate natural issue body:
{Description of what needs to be done}
**Context:**
{Why this matters or what problem it solves}
**Source:**
{Where this came from - file:line or document}
**Affected files:**
{List of relevant files if known}
**Acceptance criteria:**
- {What defines done}2. Create via gh CLI:
gh issue create \
--title "Title here" \
--body "Body here" \
--label "label1,label2"3. Display created issue URL
3.3 Transition to Start
After creating issues:
🎉 Created 3 issues!
Which issue would you like to start working on?
[1 / 2 / 3 / none]If user selects issue:
- Transition to normal
/keep-start {number}workflow - Load context
- Create work file
- Begin work
---
Handling Edge Cases
No Planning Docs Found
📋 Searching for planning documents...
ℹ️ No planning documents found
🔍 Analyzing codebase...
• 12 TODO comments found
• 8 files missing tests
🎯 Suggestions based on codebase signals:
[Continue with TODO/test-based suggestions]No TODOs, No Test Gaps
📋 Searching for planning documents...
ℹ️ No planning documents found
🔍 Analyzing codebase...
• No TODO comments found
• No obvious gaps detected
💭 No automated suggestions available.
Would you like to:
1. Create a ROADMAP.md to plan features
2. Create an issue manually
3. Use /keep-grow to document code first
4. Work in local-only mode
What would you prefer?GitHub Offline
⚠️ GitHub unavailable (gh not found)
I can still analyze your codebase for potential work:
[Show findings]
However, I can't create issues without GitHub. You can:
1. Install gh CLI and retry
2. Create issues manually later
3. Work in local-only mode
Continue analyzing? [yes / no]Context Missing
⚠️ No CLAUDE.md found at project root
Before suggesting work, I recommend creating project context:
Run /keep-grow . to create CLAUDE.md
This will help me:
- Understand your tech stack
- Suggest relevant work
- Provide better context
Create CLAUDE.md first? [yes / skip and continue]---
Search Patterns Reference
Planning Document Patterns
File patterns (Glob):
{ROADMAP,TODO,PLAN,BACKLOG,VISION,CONTRIBUTING}*.md
{roadmap,todo,plan,backlog,vision,contributing}*.md
docs/{ROADMAP,TODO,PLAN}*.md
.github/{ROADMAP,TODO}*.mdContent patterns (parse with Read):
- Checkboxes:
- [ ] {task} - Bullets:
- {task} - Numbered:
{number}. {task} - Headers:
## {feature}+ description
Code Comment Patterns
Grep patterns:
Pattern: TODO:|FIXME:|HACK:|BUG:|XXX:|NOTE:
Options: -i -n -B 1 -A 1
Type: All code files (or use --type for specific languages)Priority mapping:
- FIXME, BUG → high
- TODO, XXX → medium
- HACK, NOTE → low/medium
- With "urgent", "critical" → high
- With "nice-to-have", "maybe" → low
Test Coverage Patterns
Test file patterns (Glob):
**/*.{test,spec}.{js,ts,jsx,tsx}
**/*.{test,spec}.py
**/*_test.{go,rs}
tests/**/*
__tests__/**/*Source file patterns (Glob):
src/**/*.{js,ts,jsx,tsx}
lib/**/*.{js,ts}
src/**/*.py
*.go (in relevant dirs)
src/**/*.rs---
Example Outputs
Full Discovery with Planning Docs
📚 Project context: ✅ CLAUDE.md found (updated 2024-10-15)
📋 Planning documents:
• ROADMAP.md: 8 planned features
• CONTRIBUTING.md: 2 good-first-issues
🔍 Codebase analysis:
• 3 FIXME comments (high priority)
• 9 TODO comments
• 5 directories missing tests
• 2 modules need documentation
🎯 5 Starter Issue Suggestions:
1. **Fix rate limiting bypass in login** [bug, high]
Source: FIXME in src/auth/login.ts:45
Files: src/auth/login.ts
2. **Implement user profile page** [enhancement, high]
Source: ROADMAP.md line 23
Files: src/pages/, src/components/
3. **Add tests for payment processing** [testing, high]
Source: Missing coverage in src/payments/
Files: tests/payments/
4. **Refactor database connection pooling** [refactor, medium]
Source: TODO in src/db/pool.ts:12
Files: src/db/pool.ts
5. **Document API authentication flow** [documentation, low]
Source: Undocumented in docs/
Files: docs/api/
Which issues should I create? [all / 1,2,3 / custom selection]Code-Only Discovery (No Planning Docs)
📚 Project context: ✅ CLAUDE.md found
📋 Searching for planning documents...
ℹ️ No planning documents found
🔍 Analyzing codebase...
• 15 TODO/FIXME comments found
• 8 files missing test coverage
• 3 security-related comments
🎯 4 Starter Issue Suggestions:
1. **Fix SQL injection vulnerability** [bug, security, high]
Source: FIXME in src/db/queries.ts:67
Files: src/db/queries.ts
2. **Add input validation to API endpoints** [enhancement, security, high]
Source: TODO in src/api/validation.ts:23
Files: src/api/
3. **Write tests for authentication module** [testing, medium]
Source: Missing coverage in src/auth/
Files: tests/auth/
4. **Optimize database queries** [performance, medium]
Source: TODO in src/db/users.ts:45
Files: src/db/users.ts
Which issues should I create? [all / 1,2,3 / custom]---
Philosophy
Source transparency:
- Always show where suggestions came from
- Include file:line references
- Link to planning docs
User control:
- Let user select which issues to create
- Offer "none - work locally" option
- Allow custom selection (e.g., "1,3,5")
Natural generation:
- Generate natural, helpful issue bodies
- No rigid templates
- Context-aware descriptions
- Clear acceptance criteria
Graceful degradation:
- Work offline (can't create issues, but show suggestions)
- No planning docs → Focus on code signals
- Nothing found → Offer alternatives
- Missing context → Suggest creating it first
#!/usr/bin/env python3
"""
GitHub sync helper for Keep
Provides functions for GitHub API operations with:
- Authentication handling
- Rate limit management
- Retry logic
- Error handling
Use when `gh` CLI insufficient or for programmatic access.
"""
import json
import os
import subprocess
import sys
import time
from typing import Dict, List, Optional, Any
class GitHubError(Exception):
"""Base exception for GitHub operations"""
pass
class RateLimitError(GitHubError):
"""Raised when GitHub rate limit exceeded"""
pass
class NotFoundError(GitHubError):
"""Raised when resource not found"""
pass
def gh_command(args: List[str], retries: int = 3) -> Dict[str, Any]:
"""
Execute gh CLI command with retry logic
Args:
args: Command arguments (e.g., ['issue', 'view', '123'])
retries: Number of retry attempts
Returns:
Parsed JSON response
Raises:
GitHubError: If command fails after retries
RateLimitError: If rate limit exceeded
NotFoundError: If resource not found
"""
cmd = ['gh'] + args
for attempt in range(retries):
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True
)
# Parse JSON if output present
if result.stdout.strip():
return json.loads(result.stdout)
return {}
except subprocess.CalledProcessError as e:
error_msg = e.stderr.strip()
# Check for rate limit
if 'rate limit' in error_msg.lower():
if attempt < retries - 1:
wait_time = 2 ** attempt # Exponential backoff
print(f"Rate limit hit, waiting {wait_time}s...", file=sys.stderr)
time.sleep(wait_time)
continue
raise RateLimitError(f"GitHub rate limit exceeded: {error_msg}")
# Check for not found
if '404' in error_msg or 'not found' in error_msg.lower():
raise NotFoundError(f"Resource not found: {error_msg}")
# Check for authentication
if 'authentication' in error_msg.lower():
raise GitHubError(f"Authentication failed: {error_msg}")
# Other errors - retry
if attempt < retries - 1:
wait_time = 2 ** attempt
print(f"Command failed, retrying in {wait_time}s...", file=sys.stderr)
time.sleep(wait_time)
continue
raise GitHubError(f"GitHub command failed: {error_msg}")
except json.JSONDecodeError as e:
raise GitHubError(f"Invalid JSON response: {e}")
raise GitHubError(f"Command failed after {retries} retries")
def fetch_issue(issue_number: str) -> Dict[str, Any]:
"""
Fetch issue details from GitHub
Args:
issue_number: Issue number (without #)
Returns:
Issue data with keys: title, body, labels, state, etc.
"""
return gh_command([
'issue', 'view', str(issue_number),
'--json', 'number,title,body,labels,state,createdAt,updatedAt,url'
])
def list_issues(state: str = 'open', limit: Optional[int] = None) -> List[Dict[str, Any]]:
"""
List issues from repository
Args:
state: Issue state ('open', 'closed', 'all')
limit: Maximum number of issues to return
Returns:
List of issue data
"""
args = [
'issue', 'list',
'--state', state,
'--json', 'number,title,body,labels,state,createdAt,updatedAt'
]
if limit:
args.extend(['--limit', str(limit)])
result = gh_command(args)
return result if isinstance(result, list) else []
def post_comment(issue_number: str, body: str) -> Dict[str, Any]:
"""
Post comment to issue
Args:
issue_number: Issue number (without #)
body: Comment body (markdown)
Returns:
Comment data
"""
return gh_command([
'issue', 'comment', str(issue_number),
'--body', body
])
def close_issue(issue_number: str, reason: Optional[str] = None) -> Dict[str, Any]:
"""
Close an issue
Args:
issue_number: Issue number (without #)
reason: Optional closing reason
Returns:
Updated issue data
"""
args = ['issue', 'close', str(issue_number)]
if reason:
args.extend(['--comment', reason])
return gh_command(args)
def create_issue(
title: str,
body: str,
labels: Optional[List[str]] = None,
milestone: Optional[str] = None,
assignees: Optional[List[str]] = None
) -> Dict[str, Any]:
"""
Create a new GitHub issue
Args:
title: Issue title
body: Issue body (markdown)
labels: List of label names
milestone: Milestone name or number
assignees: List of GitHub usernames
Returns:
Created issue data
Raises:
GitHubError: If creation fails
"""
args = ['issue', 'create', '--title', title, '--body', body]
if labels:
for label in labels:
args.extend(['--label', label])
if milestone:
args.extend(['--milestone', milestone])
if assignees:
for assignee in assignees:
args.extend(['--assignee', assignee])
return gh_command(args)
def list_labels() -> List[Dict[str, Any]]:
"""
List repository labels
Returns:
List of label data with name, description, color
"""
return gh_command([
'label', 'list',
'--json', 'name,description,color'
])
def list_milestones(state: str = 'open') -> List[Dict[str, Any]]:
"""
List repository milestones
Args:
state: Milestone state ('open', 'closed', 'all')
Returns:
List of milestone data
"""
return gh_command([
'api', 'repos/{owner}/{repo}/milestones',
'-f', f'state={state}'
])
def parse_dependencies(issue_body: str) -> List[str]:
"""
Parse dependency references from issue body
Looks for patterns like:
- "depends on #123"
- "blocked by #456"
- "requires #789"
Args:
issue_body: Issue body text
Returns:
List of issue numbers (as strings)
"""
import re
patterns = [
r'depends?\s+on\s+#(\d+)',
r'blocked?\s+by\s+#(\d+)',
r'requires?\s+#(\d+)',
r'needs?\s+#(\d+)',
]
dependencies = set()
for pattern in patterns:
matches = re.finditer(pattern, issue_body, re.IGNORECASE)
dependencies.update(match.group(1) for match in matches)
return sorted(dependencies)
def check_gh_available() -> bool:
"""
Check if gh CLI is available
Returns:
True if gh CLI available, False otherwise
"""
try:
subprocess.run(
['gh', '--version'],
capture_output=True,
check=True
)
return True
except (subprocess.CalledProcessError, FileNotFoundError):
return False
def get_repo_info() -> Dict[str, Any]:
"""
Get current repository information
Returns:
Repository data with keys: owner, name, url, etc.
"""
return gh_command([
'repo', 'view',
'--json', 'owner,name,url,description'
])
def main():
"""CLI interface for testing"""
import argparse
parser = argparse.ArgumentParser(description='GitHub sync helper')
subparsers = parser.add_subparsers(dest='command', help='Commands')
# fetch-issue command
fetch_parser = subparsers.add_parser('fetch-issue', help='Fetch issue details')
fetch_parser.add_argument('number', help='Issue number')
# list-issues command
list_parser = subparsers.add_parser('list-issues', help='List issues')
list_parser.add_argument('--state', default='open', choices=['open', 'closed', 'all'])
list_parser.add_argument('--limit', type=int, help='Max issues to return')
# post-comment command
comment_parser = subparsers.add_parser('post-comment', help='Post comment')
comment_parser.add_argument('number', help='Issue number')
comment_parser.add_argument('body', help='Comment body')
# close-issue command
close_parser = subparsers.add_parser('close-issue', help='Close issue')
close_parser.add_argument('number', help='Issue number')
close_parser.add_argument('--reason', help='Closing reason')
# create-issue command
create_parser = subparsers.add_parser('create-issue', help='Create issue')
create_parser.add_argument('title', help='Issue title')
create_parser.add_argument('body', help='Issue body')
create_parser.add_argument('--label', action='append', help='Label to add (can be repeated)')
create_parser.add_argument('--milestone', help='Milestone')
create_parser.add_argument('--assignee', action='append', help='Assignee (can be repeated)')
# list-labels command
subparsers.add_parser('list-labels', help='List repository labels')
# list-milestones command
milestones_parser = subparsers.add_parser('list-milestones', help='List milestones')
milestones_parser.add_argument('--state', default='open', choices=['open', 'closed', 'all'])
# check command
subparsers.add_parser('check', help='Check gh CLI availability')
args = parser.parse_args()
try:
if args.command == 'fetch-issue':
result = fetch_issue(args.number)
print(json.dumps(result, indent=2))
elif args.command == 'list-issues':
result = list_issues(args.state, args.limit)
print(json.dumps(result, indent=2))
elif args.command == 'post-comment':
result = post_comment(args.number, args.body)
print(json.dumps(result, indent=2))
elif args.command == 'close-issue':
result = close_issue(args.number, args.reason)
print(json.dumps(result, indent=2))
elif args.command == 'create-issue':
result = create_issue(
title=args.title,
body=args.body,
labels=args.label,
milestone=args.milestone,
assignees=args.assignee
)
print(json.dumps(result, indent=2))
elif args.command == 'list-labels':
result = list_labels()
print(json.dumps(result, indent=2))
elif args.command == 'list-milestones':
result = list_milestones(args.state)
print(json.dumps(result, indent=2))
elif args.command == 'check':
available = check_gh_available()
print(json.dumps({'available': available}))
sys.exit(0 if available else 1)
else:
parser.print_help()
sys.exit(1)
except GitHubError as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()
#!/usr/bin/env python3
"""
Issue scoring algorithm for Keep
Scores open issues based on:
- Continuity (30%): Same area as recent work
- Priority (30%): Labels indicating urgency
- Freshness (20%): Recent activity
- Dependencies (20%): Blockers cleared
Usage:
python score_issues.py --recent-work .claude/state.md [--issues issues.json]
"""
import json
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Any
# Weight distribution for scoring
WEIGHT_CONTINUITY = 0.30
WEIGHT_PRIORITY = 0.30
WEIGHT_FRESHNESS = 0.20
WEIGHT_DEPENDENCY = 0.20
def parse_state_file(state_path: str) -> Dict[str, Any]:
"""
Parse .claude/state.md to extract recent work context
Returns dict with:
- recent_directories: List of directories worked in
- recent_labels: List of labels from recent issues
- recent_issues: List of recent issue numbers
"""
try:
content = Path(state_path).read_text()
except FileNotFoundError:
return {
'recent_directories': [],
'recent_labels': [],
'recent_issues': []
}
# Simple parsing - look for key patterns
directories = set()
labels = set()
issues = set()
for line in content.split('\n'):
# Extract directories from "Working primarily in src/auth/"
if 'working primarily in' in line.lower():
parts = line.split('in')[-1].strip()
directories.update(d.strip().rstrip('/,') for d in parts.split() if d.strip())
# Extract issue numbers
if '#' in line:
import re
issue_nums = re.findall(r'#(\d+)', line)
issues.update(issue_nums)
return {
'recent_directories': list(directories),
'recent_labels': list(labels),
'recent_issues': list(issues)
}
def calculate_continuity_score(issue: Dict[str, Any], context: Dict[str, Any]) -> Tuple[float, str]:
"""
Calculate continuity score (0-100)
Higher score for issues in same area as recent work
"""
score = 0
reasons = []
# Check if issue mentions directories from recent work
issue_text = f"{issue.get('title', '')} {issue.get('body', '')}".lower()
for directory in context['recent_directories']:
if directory.lower() in issue_text:
score += 50
reasons.append(f"mentions {directory}")
break
# Check for overlapping labels
issue_labels = {label['name'].lower() for label in issue.get('labels', [])}
recent_labels = {label.lower() for label in context['recent_labels']}
overlap = issue_labels & recent_labels
if overlap:
score += 30
reasons.append(f"related: {', '.join(overlap)}")
# Check if references recent issues
issue_body = issue.get('body', '')
for recent_issue in context['recent_issues']:
if f'#{recent_issue}' in issue_body:
score += 20
reasons.append(f"references #{recent_issue}")
break
rationale = '; '.join(reasons) if reasons else 'no continuity'
return min(score, 100), rationale
def calculate_priority_score(issue: Dict[str, Any]) -> Tuple[float, str]:
"""
Calculate priority score (0-100) from labels
"""
labels = {label['name'].lower() for label in issue.get('labels', [])}
if 'urgent' in labels:
return 100, 'urgent'
elif 'high-priority' in labels or 'high' in labels:
return 75, 'high-priority'
elif 'low-priority' in labels or 'low' in labels:
return 25, 'low-priority'
else:
return 50, 'medium (default)'
def calculate_freshness_score(issue: Dict[str, Any]) -> Tuple[float, str]:
"""
Calculate freshness score (0-100) based on last update
"""
updated_at = issue.get('updatedAt')
if not updated_at:
return 50, 'unknown update time'
try:
# Parse ISO 8601 timestamp
updated = datetime.fromisoformat(updated_at.replace('Z', '+00:00'))
now = datetime.now(timezone.utc)
days_ago = (now - updated).days
if days_ago <= 7:
return 100, f'updated {days_ago}d ago'
elif days_ago <= 14:
return 75, f'updated {days_ago}d ago'
elif days_ago <= 30:
return 50, f'updated {days_ago}d ago'
else:
return 25, f'updated {days_ago}d ago'
except (ValueError, AttributeError):
return 50, 'invalid update time'
def parse_blockers(issue_body: str) -> List[str]:
"""
Parse blocker references from issue body
Looks for:
- "depends on #123"
- "blocked by #456"
- "requires #789"
"""
import re
if not issue_body:
return []
patterns = [
r'depends?\s+on\s+#(\d+)',
r'blocked?\s+by\s+#(\d+)',
r'requires?\s+#(\d+)',
r'needs?\s+#(\d+)',
]
blockers = set()
for pattern in patterns:
matches = re.finditer(pattern, issue_body, re.IGNORECASE)
blockers.update(match.group(1) for match in matches)
return list(blockers)
def calculate_dependency_score(
issue: Dict[str, Any],
all_issues: List[Dict[str, Any]]
) -> Tuple[float, str]:
"""
Calculate dependency score (0-100)
Lower score if has open blockers
"""
blockers = parse_blockers(issue.get('body', ''))
if not blockers:
return 100, 'no dependencies'
# Check status of blockers
issue_map = {str(i['number']): i for i in all_issues}
open_blockers = []
closed_blockers = []
for blocker_num in blockers:
blocker = issue_map.get(blocker_num)
if blocker:
if blocker.get('state') == 'OPEN':
open_blockers.append(blocker_num)
else:
closed_blockers.append(blocker_num)
else:
# Unknown blocker - assume open (conservative)
open_blockers.append(blocker_num)
if not open_blockers:
return 90, f"dependencies resolved: #{', #'.join(closed_blockers)}"
# Penalty for each open blocker
penalty = len(open_blockers) * 25
score = max(0, 100 - penalty)
reason = f"blocked by #{', #'.join(open_blockers)}"
if closed_blockers:
reason += f" (#{', #'.join(closed_blockers)} done)"
return score, reason
def score_issue(
issue: Dict[str, Any],
context: Dict[str, Any],
all_issues: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""
Score a single issue
Returns dict with:
- total_score: Weighted total (0-100)
- continuity_score, continuity_reason
- priority_score, priority_reason
- freshness_score, freshness_reason
- dependency_score, dependency_reason
"""
continuity_score, continuity_reason = calculate_continuity_score(issue, context)
priority_score, priority_reason = calculate_priority_score(issue)
freshness_score, freshness_reason = calculate_freshness_score(issue)
dependency_score, dependency_reason = calculate_dependency_score(issue, all_issues)
total_score = (
continuity_score * WEIGHT_CONTINUITY +
priority_score * WEIGHT_PRIORITY +
freshness_score * WEIGHT_FRESHNESS +
dependency_score * WEIGHT_DEPENDENCY
)
return {
'number': issue['number'],
'title': issue['title'],
'total_score': round(total_score, 1),
'continuity_score': round(continuity_score, 1),
'continuity_reason': continuity_reason,
'priority_score': round(priority_score, 1),
'priority_reason': priority_reason,
'freshness_score': round(freshness_score, 1),
'freshness_reason': freshness_reason,
'dependency_score': round(dependency_score, 1),
'dependency_reason': dependency_reason,
}
def score_all_issues(
issues: List[Dict[str, Any]],
context: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""
Score all issues and return sorted by score descending
"""
scored = [score_issue(issue, context, issues) for issue in issues]
return sorted(scored, key=lambda x: x['total_score'], reverse=True)
def handle_zero_issues(context: Dict[str, Any]) -> Dict[str, Any]:
"""
Handle case when no issues exist
Args:
context: Context dict from parse_state_file
Returns:
Dict with zero-issues status and next steps
"""
return {
'status': 'no_issues',
'message': 'No issues found in repository',
'suggested_action': 'initialize_project',
'next_steps': [
'Check CLAUDE.md files are current',
'Search for planning documents (ROADMAP.md, TODO.md, etc.)',
'Analyze codebase for improvement opportunities',
'Create 3-5 starter issues based on findings',
'Start work on chosen issue'
],
'recommendations': []
}
def format_recommendations(scored_issues: List[Dict[str, Any]], top_n: int = 5) -> str:
"""
Format scored issues as recommendations
"""
if not scored_issues:
zero_result = handle_zero_issues({})
return zero_result['message'] + "\n\nSuggested next steps:\n" + \
'\n'.join(f" {i+1}. {step}" for i, step in enumerate(zero_result['next_steps']))
lines = ["🎯 Recommended Next Work\n"]
# Top recommendation
top = scored_issues[0]
lines.append(f"🔥 Hot Recommendation:")
lines.append(f"#{top['number']} - {top['title']}")
lines.append(f"├─ Score: {top['total_score']}/100")
lines.append(f"├─ {top['continuity_reason']}")
lines.append(f"└─ Priority: {top['priority_reason']}")
lines.append("")
# Other options
if len(scored_issues) > 1:
lines.append("📋 Other Good Options:\n")
for i, issue in enumerate(scored_issues[1:top_n], start=2):
lines.append(f"{i}. #{issue['number']} - {issue['title']}")
lines.append(f" └─ Score: {issue['total_score']} | {issue['priority_reason']}")
if issue['dependency_score'] < 100:
lines.append(f" {issue['dependency_reason']}")
lines.append("")
return '\n'.join(lines)
def main():
"""CLI interface"""
import argparse
parser = argparse.ArgumentParser(description='Score GitHub issues')
parser.add_argument(
'--recent-work',
default='.claude/state.md',
help='Path to state.md file'
)
parser.add_argument(
'--issues',
help='Path to JSON file with issues (or use stdin)'
)
parser.add_argument(
'--top',
type=int,
default=5,
help='Number of recommendations to show'
)
parser.add_argument(
'--json',
action='store_true',
help='Output as JSON instead of formatted text'
)
args = parser.parse_args()
# Load context
context = parse_state_file(args.recent_work)
# Load issues
if args.issues:
with open(args.issues) as f:
issues = json.load(f)
else:
issues = json.load(sys.stdin)
# Ensure issues is a list
if isinstance(issues, dict):
issues = [issues]
# Score issues
scored = score_all_issues(issues, context)
# Output
if args.json:
print(json.dumps(scored, indent=2))
else:
print(format_recommendations(scored, args.top))
if __name__ == '__main__':
main()