54 Commits

Author SHA1 Message Date
0d120bd041 Merge pull request 'feat: add plugin name prefixes to hooks and improve git-flow sync' (#105) from feat/hook-improvements-and-sync-prune into development 2026-01-22 21:14:32 +00:00
508832dae1 feat: add plugin name prefixes to hooks and improve git-flow sync
- Add [plugin-name] prefix to all hook messages for better identification
- Make doc-guardian hook notification-only (non-blocking)
- Add stale branch detection to /commit-sync with git fetch --prune
- Enhance /branch-cleanup to handle stale branches separately

Closes improvements for hook UX and git workflow

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 16:13:17 -05:00
0b23a02886 Merge pull request 'fix: add curl fallback to /debug-report when MCP tools unavailable' (#103) from fix/issue-100-debug-report-fallback into development
Reviewed-on: #103
2026-01-22 20:01:32 +00:00
71987ee537 fix: switch to development branch before adding issue comments
The MCP server's branch-aware security blocks write operations on
protected branches (main, fix/*, etc). After pushing a feature branch
and creating a PR, we must switch back to development before adding
comments to issues via MCP tools.
2026-01-22 14:28:01 -05:00
b7829dca05 fix: add curl fallback to /debug-report when MCP tools unavailable
When MCP tools are not available in a session (the very scenario
/debug-report is designed to diagnose), the command now falls back to:

1. Check for Gitea credentials at ~/.config/claude/gitea.env
2. Use curl + jq to create the issue via Gitea REST API
3. If no credentials, save report to local file for manual submission

Security measures:
- Uses mktemp -m 600 for restrictive file permissions
- Uses jq --rawfile for safe JSON construction (no command substitution)
- Proper cleanup of temporary files

Fixes #100

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 14:19:05 -05:00
9b0e9a69b1 Merge pull request 'development' (#102) from development into main
Reviewed-on: #102
2026-01-22 18:36:36 +00:00
ad0e14d07f Merge pull request 'feat: add debugging infrastructure and CLAUDE.md optimization' (#101) from feat/debugging-infrastructure into development
Reviewed-on: #101
2026-01-22 18:36:15 +00:00
7fd5fffedf feat: add debugging infrastructure and CLAUDE.md optimization
- Add docs/DEBUGGING-CHECKLIST.md with systematic troubleshooting guide
- Enhance SessionStart hooks to detect missing MCP venvs and warn users
- Add Installation Paths and Debugging sections to CLAUDE.md
- Add Plugin Commands by Category table to Quick Start
- Condense Versioning section for better readability
- Add scripts/check-venv.sh for programmatic venv checking
- Update docs/CANONICAL-PATHS.md with new files

Addresses issues #97, #98, #99

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 13:33:32 -05:00
620173eef6 Merge pull request 'Release: suggest_labels fix' (#96) from development into main 2026-01-22 15:54:28 +00:00
0fe4f62a30 Merge pull request 'fix: expose repo parameter in suggest_labels MCP tool' (#95) from fix/suggest-labels-repo-parameter into development 2026-01-22 15:54:26 +00:00
533810f018 fix: expose repo parameter in suggest_labels MCP tool
The suggest_labels tool accepted a repo parameter in the implementation
but didn't expose it in the MCP tool schema, causing it to always rely
on auto-detection which failed in some contexts.

Fixes #94

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 10:54:07 -05:00
2ee23a39d8 Merge pull request 'Release: Critical MCP setup documentation' (#93) from development into main 2026-01-22 15:35:36 +00:00
894c85bd54 Merge pull request 'docs: add critical setup instructions for installed marketplace' (#92) from docs/mcp-setup-critical-fix into development 2026-01-22 15:35:23 +00:00
01809a7367 docs: add critical setup instructions for installed marketplace
MCP servers fail when venvs don't exist in ~/.claude/plugins/marketplaces/.
Claude Code doesn't run setup.sh when installing marketplaces, so users
must run it manually.

Added:
- Critical warning section at top of UPDATING.md
- Step to run setup in installed location after updates
- Troubleshooting for "X MCP servers failed" error

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 10:35:07 -05:00
a20f1bfdf8 Merge pull request 'Release: pr-review MCP config fix' (#91) from development into main 2026-01-22 15:22:30 +00:00
7879e07815 Merge pull request 'fix: add missing PYTHONPATH env to pr-review MCP config' (#90) from fix/pr-review-mcp-pythonpath into development 2026-01-22 15:21:11 +00:00
eced0fbd07 fix: add missing PYTHONPATH env to pr-review MCP config
Aligns pr-review .mcp.json with projman by adding PYTHONPATH environment
variable. This inconsistency may have caused MCP server failures when
both plugins are loaded.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 10:20:27 -05:00
aa6d7f5866 Merge pull request 'Release: SessionStart hook fix' (#89) from development into main 2026-01-22 15:08:29 +00:00
3e5197779d Merge pull request 'fix: correct SessionStart hook structure in projman and pr-review' (#88) from fix/session-start-hook-structure into development 2026-01-22 15:08:11 +00:00
9206931a3c fix: correct SessionStart hook structure in projman and pr-review
Remove incorrect nested matcher/hooks structure from SessionStart hooks.
SessionStart events don't use matchers - that format is only for tool-based
hooks like PreToolUse/PostToolUse.

Fixes recurring "SessionStart:startup hook error" on Claude Code startup.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 10:07:12 -05:00
ff3be54f1c Merge pull request 'Release: Add debug commands documentation' (#87) from development into main 2026-01-22 13:11:35 +00:00
1b0f5f4973 Merge pull request 'docs: add debug commands to projman documentation' (#86) from docs/add-debug-commands-documentation into development 2026-01-22 13:11:08 +00:00
8ed0d8f207 docs: add debug commands to projman documentation
- Add /debug-report and /debug-review to projman README
- Add debug commands to COMMANDS-CHEATSHEET.md
- Remove obsolete doc-guardian Stop hook references from cheatsheet
- Update Architecture section with debug command files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 08:10:33 -05:00
007b55916c Merge pull request 'Release: doc-guardian README fix' (#85) from development into main 2026-01-22 12:58:20 +00:00
eeef35aa61 Merge pull request 'fix: correct doc-guardian README to match actual implementation' (#84) from fix/doc-guardian-readme-accuracy into development 2026-01-22 12:44:50 +00:00
be2d989899 fix: correct doc-guardian README to match actual implementation
- Remove false claim about Stop hook (was removed in d2ad90d)
- Fix Solution section to accurately describe prompt-based behavior
- Remove misleading "queue" language since there's no persistent queue

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 07:42:51 -05:00
306143882a Merge pull request 'Release v3.1.0: Debug workflow commands + hook fix' (#83) from development into main
Reviewed-on: #83
2026-01-21 23:00:12 +00:00
0c07820b5a Merge pull request 'fix: remove broken Stop hook from doc-guardian' (#82) from fix/remove-broken-stop-hook into development 2026-01-21 22:56:50 +00:00
d2ad90d5bb fix: remove broken Stop hook from doc-guardian
The Stop hook referenced a non-existent "internal queue" for tracking
documentation drift. Each hook runs in isolation with no way to pass
data between invocations, so the queue concept couldn't work.

The hook was causing errors on every session end:
"Stop hook error: Prompt hook condition was not met..."

Changes:
- Removed the Stop hook entirely
- Updated PostToolUse hook to report drift immediately when found
  (instead of referencing non-existent queue)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:55:53 -05:00
642dca7062 Merge pull request 'Release v3.1.0: Debug workflow commands' (#81) from development into main
Reviewed-on: #81
2026-01-21 22:47:22 +00:00
faafced061 Merge pull request 'feat: add debug workflow commands and bump to v3.1.0' (#80) from feat/debug-workflow-v3.1.0 into development 2026-01-21 22:46:13 +00:00
c3df0f95e6 feat: add debug workflow commands and bump to v3.1.0
New commands for structured debugging workflow:

/debug-report (for test projects):
- Runs 5 diagnostic MCP tool tests
- Captures full project context (git remote, cwd, branch)
- Generates structured issue with hypothesis
- Creates issue in marketplace repo automatically

/debug-review (for marketplace repo):
- Lists open diagnostic issues for triage
- Maps errors to relevant code files
- MANDATORY: Reads files before proposing fixes
- Three human approval gates
- Creates feature branch, commits, PR with linking

Also includes:
- Dynamic label format detection in suggest_labels
- Rewritten labels-sync.md with explicit execution steps
- Version bump to 3.1.0

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:44:54 -05:00
f714957d83 Merge pull request 'Merge development into main: labels-sync repo detection fix' (#79) from development into main
Reviewed-on: #79
2026-01-21 22:33:11 +00:00
40af243229 Merge pull request 'fix: rewrite labels-sync.md with explicit repo detection steps' (#78) from fix/labels-sync-repo-detection into development 2026-01-21 22:26:12 +00:00
69b71fc7cf fix: rewrite labels-sync.md with explicit repo detection steps
ROOT CAUSE: The MCP server runs with cwd set to the plugin directory,
not the user's project. It cannot auto-detect the repository.

SOLUTION: The command documentation now explicitly instructs Claude to:
1. Run `git remote get-url origin` via Bash first
2. Parse the URL to extract owner/repo
3. Pass repo parameter to ALL MCP tool calls

Also removed the "Label Reference" section that was causing Claude
to ask about creating a local reference file.

Key changes:
- Added "CRITICAL: Execution Steps" section with numbered steps
- Added "DO NOT" section to prevent common mistakes
- Removed confusing reference file documentation
- Made all MCP tool calls show required repo parameter

Fixes #77

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:16:48 -05:00
5ad207520a Merge pull request 'Merge development into main: dynamic label format detection' (#76) from development into main
Reviewed-on: #76
2026-01-21 22:06:52 +00:00
78d77c1e0a Merge pull request 'fix: make suggest_labels detect actual label format from repository' (#75) from fix/dynamic-label-format into development 2026-01-21 22:04:55 +00:00
5cf43d5de2 fix: make suggest_labels detect actual label format from repository
The suggest_labels function now dynamically detects the label naming convention
used in the repository (slash format like Type/Bug or colon-space format like
Type: Bug) instead of hardcoding slash format.

Changes:
- Added _build_label_lookup() to parse and normalize label formats
- Added _find_label() to find actual labels from the lookup
- Updated suggest_labels() to accept optional repo parameter
- Labels are fetched first, then suggestions match actual names
- Supports Efforts/Effort normalization (handles singular/plural)

Fixes issue #73 sub-issue 3: label format mismatch

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:58:44 -05:00
5da29c8e35 Merge pull request 'Release: fix project directory detection for MCP server' (#72) from development into main
Reviewed-on: #72
2026-01-21 21:41:55 +00:00
b2c51251f3 Merge pull request 'Release: labels-sync documentation improvements' (#69) from development into main
Reviewed-on: #69
2026-01-21 21:16:28 +00:00
4557d2ce40 Merge pull request 'Release: repo auto-detection and org validation fixes' (#66) from development into main
Reviewed-on: #66
2026-01-21 20:15:02 +00:00
df2f5ebb47 Merge pull request 'Release: fix user-owned repo labels' (#63) from development into main
Reviewed-on: #63
2026-01-21 19:48:24 +00:00
195ca5c10c Merge pull request 'Release v3.0.1' (#60) from development into main
Reviewed-on: #60
2026-01-21 17:07:45 +00:00
6c142a9710 Merge pull request 'development' (#57) from development into main
Reviewed-on: #57
2026-01-21 16:56:12 +00:00
39105688a5 Merge pull request 'development' (#54) from development into main
Reviewed-on: #54
2026-01-21 16:24:26 +00:00
b5144de0cf Merge pull request 'development' (#52) from development into main
Reviewed-on: #52
2026-01-21 15:15:35 +00:00
a70df64cae Merge pull request 'development' (#50) from development into main
Reviewed-on: #50
2026-01-21 02:50:35 +00:00
394c91f8cf Merge pull request 'development' (#48) from development into main
Reviewed-on: #48
2026-01-21 02:13:48 +00:00
374912b463 Merge pull request 'development' (#46) from development into main
Reviewed-on: #46
2026-01-21 01:58:17 +00:00
50ebe83c0a Merge pull request 'development' (#44) from development into main
Reviewed-on: #44
2026-01-21 01:21:53 +00:00
4ede59e89a Merge pull request 'development' (#42) from development into main
Reviewed-on: #42
2026-01-20 22:31:17 +00:00
aa7bb8f1a4 Merge pull request 'development' (#40) from development into main
Reviewed-on: personal-projects/support-claude-mktplace#40
2026-01-20 22:14:18 +00:00
09d82b310e Merge pull request 'development' (#38) from development into main
Reviewed-on: personal-projects/support-claude-mktplace#38
2026-01-20 22:04:00 +00:00
1c694b6469 Merge pull request 'development' (#36) from development into main
Reviewed-on: personal-projects/support-claude-mktplace#36
2026-01-20 17:49:26 +00:00
25 changed files with 1712 additions and 359 deletions

View File

@@ -6,12 +6,12 @@
}, },
"metadata": { "metadata": {
"description": "Project management plugins with Gitea and NetBox integrations", "description": "Project management plugins with Gitea and NetBox integrations",
"version": "3.0.1" "version": "3.1.0"
}, },
"plugins": [ "plugins": [
{ {
"name": "projman", "name": "projman",
"version": "3.0.0", "version": "3.1.0",
"description": "Sprint planning and project management with Gitea integration", "description": "Sprint planning and project management with Gitea integration",
"source": "./plugins/projman", "source": "./plugins/projman",
"author": { "author": {

View File

@@ -4,6 +4,64 @@ All notable changes to the Leo Claude Marketplace will be documented in this fil
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [3.1.1] - 2026-01-22
### Added
- **git-flow:** `/commit-sync` now prunes stale remote-tracking branches with `git fetch --prune`
- **git-flow:** `/commit-sync` detects and reports local branches with deleted upstreams
- **git-flow:** `/branch-cleanup` now handles stale branches (upstream gone) separately from merged branches
- **git-flow:** New `GIT_CLEANUP_STALE` environment variable for stale branch cleanup control
### Changed
- **All hooks:** Added `[plugin-name]` prefix to all hook messages for better identification
- `[projman]`, `[pr-review]`, `[code-sentinel]`, `[doc-guardian]` prefixes
- **doc-guardian:** Hook now notification-only (no file reads or blocking operations)
- Suggests running `/doc-sync` instead of performing inline checks
- Significantly reduces workflow interruption
### Fixed
- doc-guardian hook no longer stalls workflow with deep file analysis
---
## [3.1.0] - 2026-01-21
### Added
#### Debug Workflow Commands (projman)
- **`/debug-report`** - Run diagnostics in test projects, create structured issues in marketplace
- Runs 5 diagnostic MCP tool tests with explicit repo parameter
- Captures full project context (git remote, cwd, branch)
- Generates structured issue with hypothesis and investigation steps
- Creates issue in configured marketplace repository automatically
- **`/debug-review`** - Investigate diagnostic issues with human approval gates
- Lists open diagnostic issues for triage
- Maps errors to relevant code files using error-to-file mapping
- MANDATORY: Reads relevant files before proposing any fix
- Three approval gates: investigation summary, fix approach, PR creation
- Creates feature branch, commits, and PR with proper linking
#### MCP Server Improvements
- Dynamic label format detection in `suggest_labels`
- Supports slash format (`Type/Bug`) and colon-space format (`Type: Bug`)
- Fetches actual labels from repo and matches suggestions to real format
- Handles Effort/Efforts singular/plural normalization
### Changed
- **`/labels-sync`** completely rewritten with explicit execution steps
- Step 1 now explicitly requires running `git remote get-url origin` via Bash
- All MCP tool calls show required `repo` parameter
- Added "DO NOT" section preventing common mistakes
- Removed confusing "Label Reference" section that caused file creation prompts
### Fixed
- MCP tools no longer fail with "Use 'owner/repo' format" error
- Root cause: MCP server is sandboxed and cannot auto-detect project directory
- Solution: Command documentation now instructs Claude to detect repo via Bash first
---
## [3.0.1] - 2026-01-21 ## [3.0.1] - 2026-01-21
### Added ### Added

View File

@@ -28,19 +28,23 @@ A plugin marketplace for Claude Code containing:
# Validate marketplace compliance # Validate marketplace compliance
./scripts/validate-marketplace.sh ./scripts/validate-marketplace.sh
# Setup commands (in a target project with plugin installed) # After updates
/initial-setup # First time: full setup wizard ./scripts/post-update.sh # Rebuild venvs, verify symlinks
/project-init # New project: quick config
/project-sync # After repo move: sync config
# Run projman commands
/sprint-plan # Start sprint planning
/sprint-status # Check progress
/review # Pre-close code quality review
/test-check # Verify tests before close
/sprint-close # Complete sprint
``` ```
### Plugin Commands by Category
| Category | Commands |
|----------|----------|
| **Setup** | `/initial-setup`, `/project-init`, `/project-sync` |
| **Sprint** | `/sprint-plan`, `/sprint-start`, `/sprint-status`, `/sprint-close` |
| **Quality** | `/review`, `/test-check`, `/test-gen` |
| **PR Review** | `/pr-review:initial-setup`, `/pr-review:project-init` |
| **Docs** | `/doc-audit`, `/doc-sync` |
| **Security** | `/security-scan`, `/refactor`, `/refactor-dry` |
| **Config** | `/config-analyze`, `/config-optimize` |
| **Debug** | `/debug-report`, `/debug-review` |
## Repository Structure ## Repository Structure
``` ```
@@ -208,42 +212,47 @@ Stored in Gitea Wiki under `lessons-learned/sprints/`.
| Document | Purpose | | Document | Purpose |
|----------|---------| |----------|---------|
| `docs/CANONICAL-PATHS.md` | **Single source of truth** for paths | | `docs/CANONICAL-PATHS.md` | **Single source of truth** for paths |
| `docs/COMMANDS-CHEATSHEET.md` | All commands quick reference with workflow examples | | `docs/COMMANDS-CHEATSHEET.md` | All commands quick reference |
| `docs/CONFIGURATION.md` | Centralized setup guide | | `docs/CONFIGURATION.md` | Centralized setup guide |
| `docs/DEBUGGING-CHECKLIST.md` | Systematic troubleshooting guide |
| `docs/UPDATING.md` | Update guide for the marketplace | | `docs/UPDATING.md` | Update guide for the marketplace |
| `plugins/projman/CONFIGURATION.md` | Quick reference (links to central) | | `plugins/projman/CONFIGURATION.md` | Projman quick reference (links to central) |
| `plugins/projman/README.md` | Projman full documentation | | `plugins/projman/README.md` | Projman full documentation |
## Versioning and Changelog Rules ## Installation Paths
### Version Display Understanding where files live is critical for debugging:
**The marketplace version is displayed ONLY in the main `README.md` title.**
- Format: `# Leo Claude Marketplace - vX.Y.Z` | Context | Path | Purpose |
- Do NOT add version numbers to individual plugin documentation titles |---------|------|---------|
- Do NOT add version numbers to configuration guides | **Source** | `~/claude-plugins-work/` | Development - edit here |
- Do NOT add version numbers to CLAUDE.md or other docs | **Installed** | `~/.claude/plugins/marketplaces/leo-claude-mktplace/` | Runtime - Claude uses this |
| **Cache** | `~/.claude/` | Plugin metadata and settings |
### Changelog Maintenance (MANDATORY) **Key insight:** Edits to source require reinstall/update to take effect at runtime.
**`CHANGELOG.md` is the authoritative source for version history.**
When releasing a new version: ## Debugging & Troubleshooting
1. Update main `README.md` title with new version
2. Update `CHANGELOG.md` with:
- Version number and date: `## [X.Y.Z] - YYYY-MM-DD`
- **Added**: New features, commands, files
- **Changed**: Modifications to existing functionality
- **Fixed**: Bug fixes
- **Removed**: Deleted features, files, deprecated items
3. Update `marketplace.json` metadata version
4. Update plugin `plugin.json` versions if plugin-specific changes
### Version Format See `docs/DEBUGGING-CHECKLIST.md` for systematic troubleshooting.
- Follow [Semantic Versioning](https://semver.org/): MAJOR.MINOR.PATCH
- MAJOR: Breaking changes **Common Issues:**
- MINOR: New features, backward compatible | Symptom | Likely Cause | Fix |
- PATCH: Bug fixes, minor improvements |---------|--------------|-----|
| "X MCP servers failed" | Missing venv in installed path | `cd ~/.claude/plugins/marketplaces/leo-claude-mktplace && ./scripts/setup.sh` |
| MCP tools not available | Symlink broken or venv missing | Run `/debug-report` to diagnose |
| Changes not taking effect | Editing source, not installed | Reinstall plugin or edit installed path |
**Debug Commands:**
- `/debug-report` - Run full diagnostics, create issue if needed
- `/debug-review` - Investigate and propose fixes
## Versioning Rules
- Version displayed ONLY in main `README.md` title: `# Leo Claude Marketplace - vX.Y.Z`
- `CHANGELOG.md` is authoritative for version history
- Follow [SemVer](https://semver.org/): MAJOR.MINOR.PATCH
- On release: Update README title → CHANGELOG → marketplace.json → plugin.json files
--- ---
**Last Updated:** 2026-01-20 **Last Updated:** 2026-01-22

View File

@@ -1,4 +1,4 @@
# Leo Claude Marketplace - v3.0.1 # Leo Claude Marketplace - v3.1.1
A collection of Claude Code plugins for project management, infrastructure automation, and development workflows. A collection of Claude Code plugins for project management, infrastructure automation, and development workflows.

View File

@@ -18,6 +18,7 @@ leo-claude-mktplace/
│ ├── architecture/ # Draw.io diagrams and specs │ ├── architecture/ # Draw.io diagrams and specs
│ ├── CANONICAL-PATHS.md # This file - single source of truth │ ├── CANONICAL-PATHS.md # This file - single source of truth
│ ├── CONFIGURATION.md # Centralized configuration guide │ ├── CONFIGURATION.md # Centralized configuration guide
│ ├── DEBUGGING-CHECKLIST.md # Systematic troubleshooting guide
│ ├── UPDATING.md # Update guide │ ├── UPDATING.md # Update guide
│ └── workflows/ # Workflow documentation │ └── workflows/ # Workflow documentation
├── hooks/ # Shared hooks (if any) ├── hooks/ # Shared hooks (if any)
@@ -103,6 +104,10 @@ leo-claude-mktplace/
│ ├── skills/ │ ├── skills/
│ └── claude-md-integration.md │ └── claude-md-integration.md
├── scripts/ # Setup and maintenance scripts ├── scripts/ # Setup and maintenance scripts
│ ├── setup.sh # Initial setup (create venvs, config templates)
│ ├── post-update.sh # Post-update (rebuild venvs, verify symlinks)
│ ├── check-venv.sh # Check if venvs exist (for hooks)
│ └── validate-marketplace.sh # Marketplace compliance validation
├── CLAUDE.md ├── CLAUDE.md
├── README.md ├── README.md
├── LICENSE ├── LICENSE
@@ -156,6 +161,7 @@ The symlink target is relative: `../../../mcp-servers/{server}`
| Update guide | `docs/UPDATING.md` | | Update guide | `docs/UPDATING.md` |
| Configuration guide | `docs/CONFIGURATION.md` | | Configuration guide | `docs/CONFIGURATION.md` |
| Commands cheat sheet | `docs/COMMANDS-CHEATSHEET.md` | | Commands cheat sheet | `docs/COMMANDS-CHEATSHEET.md` |
| Debugging checklist | `docs/DEBUGGING-CHECKLIST.md` |
--- ---

View File

@@ -20,6 +20,8 @@ Quick reference for all commands in the Leo Claude Marketplace.
| **projman** | `/project-sync` | | X | Sync config with git remote after repo move/rename | | **projman** | `/project-sync` | | X | Sync config with git remote after repo move/rename |
| **projman** | *SessionStart hook* | X | | Detects git remote vs .env mismatch, warns to run /project-sync | | **projman** | *SessionStart hook* | X | | Detects git remote vs .env mismatch, warns to run /project-sync |
| **projman** | `/test-gen` | | X | Generate comprehensive tests for specified code | | **projman** | `/test-gen` | | X | Generate comprehensive tests for specified code |
| **projman** | `/debug-report` | | X | Run diagnostics and create structured issue in marketplace |
| **projman** | `/debug-review` | | X | Investigate diagnostic issues and propose fixes with approval gates |
| **git-flow** | `/commit` | | X | Create commit with auto-generated conventional message | | **git-flow** | `/commit` | | X | Create commit with auto-generated conventional message |
| **git-flow** | `/commit-push` | | X | Commit and push to remote in one operation | | **git-flow** | `/commit-push` | | X | Commit and push to remote in one operation |
| **git-flow** | `/commit-merge` | | X | Commit current changes, then merge into target branch | | **git-flow** | `/commit-merge` | | X | Commit current changes, then merge into target branch |
@@ -40,7 +42,6 @@ Quick reference for all commands in the Leo Claude Marketplace.
| **doc-guardian** | `/doc-audit` | | X | Full documentation audit - scans for doc drift | | **doc-guardian** | `/doc-audit` | | X | Full documentation audit - scans for doc drift |
| **doc-guardian** | `/doc-sync` | | X | Synchronize pending documentation updates | | **doc-guardian** | `/doc-sync` | | X | Synchronize pending documentation updates |
| **doc-guardian** | *PostToolUse hook* | X | | Silently detects doc drift on Write/Edit | | **doc-guardian** | *PostToolUse hook* | X | | Silently detects doc drift on Write/Edit |
| **doc-guardian** | *Stop hook* | X | | Offers to sync docs at session end |
| **code-sentinel** | `/security-scan` | | X | Full security audit (SQL injection, XSS, secrets, etc.) | | **code-sentinel** | `/security-scan` | | X | Full security audit (SQL injection, XSS, secrets, etc.) |
| **code-sentinel** | `/refactor` | | X | Apply refactoring patterns to improve code | | **code-sentinel** | `/refactor` | | X | Apply refactoring patterns to improve code |
| **code-sentinel** | `/refactor-dry` | | X | Preview refactoring without applying changes | | **code-sentinel** | `/refactor-dry` | | X | Preview refactoring without applying changes |
@@ -78,7 +79,6 @@ Quick reference for all commands in the Leo Claude Marketplace.
| **projman** | SessionStart | Checks git remote vs .env; warns if mismatch detected | | **projman** | SessionStart | Checks git remote vs .env; warns if mismatch detected |
| **pr-review** | SessionStart | Checks git remote vs .env; warns if mismatch detected | | **pr-review** | SessionStart | Checks git remote vs .env; warns if mismatch detected |
| **doc-guardian** | PostToolUse (Write/Edit) | Silently tracks documentation drift | | **doc-guardian** | PostToolUse (Write/Edit) | Silently tracks documentation drift |
| **doc-guardian** | Stop | Prompts to sync if drift detected |
| **code-sentinel** | PreToolUse (Write/Edit) | Scans for security issues; blocks critical vulnerabilities | | **code-sentinel** | PreToolUse (Write/Edit) | Scans for security issues; blocks critical vulnerabilities |
| **project-hygiene** | PostToolUse (Write/Edit) | Cleans temp files, warns about misplaced files | | **project-hygiene** | PostToolUse (Write/Edit) | Cleans temp files, warns about misplaced files |
@@ -214,4 +214,4 @@ Ensure credentials are configured in `~/.config/claude/gitea.env` or `~/.config/
--- ---
*Last Updated: 2026-01-21* *Last Updated: 2026-01-22*

213
docs/DEBUGGING-CHECKLIST.md Normal file
View File

@@ -0,0 +1,213 @@
# Debugging Checklist for Marketplace Troubleshooting
**Purpose:** Systematic approach to diagnose and fix plugin loading issues.
Last Updated: 2026-01-22
---
## Step 1: Identify the Loading Path
Claude Code loads plugins from different locations depending on context:
| Location | Path | When Used |
|----------|------|-----------|
| **Source** | `~/claude-plugins-work/` | When developing in this directory |
| **Installed** | `~/.claude/plugins/marketplaces/leo-claude-mktplace/` | After marketplace install |
| **Cache** | `~/.claude/` | Plugin metadata, settings |
**Determine which path Claude is using:**
```bash
# Check if installed marketplace exists
ls -la ~/.claude/plugins/marketplaces/leo-claude-mktplace/
# Check Claude's current plugin loading
cat ~/.claude/settings.local.json | grep -A5 "mcpServers"
```
**Key insight:** If you're editing source but Claude uses installed, your changes won't take effect.
---
## Step 2: Verify Files Exist at Runtime Location
Check the files Claude will actually load:
```bash
# For installed marketplace
RUNTIME=~/.claude/plugins/marketplaces/leo-claude-mktplace
# Check MCP server exists
ls -la $RUNTIME/mcp-servers/gitea/
ls -la $RUNTIME/mcp-servers/netbox/
# Check plugin manifests
ls -la $RUNTIME/plugins/projman/.claude-plugin/plugin.json
ls -la $RUNTIME/plugins/pr-review/.claude-plugin/plugin.json
# Check .mcp.json files
cat $RUNTIME/plugins/projman/.mcp.json
```
---
## Step 3: Verify Virtual Environments Exist
**This is the most common failure point after installation.**
MCP servers require Python venvs to exist at the INSTALLED location:
```bash
RUNTIME=~/.claude/plugins/marketplaces/leo-claude-mktplace
# Check venvs exist
ls -la $RUNTIME/mcp-servers/gitea/.venv/bin/python
ls -la $RUNTIME/mcp-servers/netbox/.venv/bin/python
# If missing, create them:
cd $RUNTIME && ./scripts/setup.sh
```
**Common error:** "X MCP servers failed to start" = venvs don't exist in installed path.
---
## Step 4: Verify Symlink Resolution
Plugins use symlinks to shared MCP servers. Verify they resolve correctly:
```bash
RUNTIME=~/.claude/plugins/marketplaces/leo-claude-mktplace
# Check symlinks exist and resolve
readlink -f $RUNTIME/plugins/projman/mcp-servers/gitea
readlink -f $RUNTIME/plugins/pr-review/mcp-servers/gitea
readlink -f $RUNTIME/plugins/cmdb-assistant/mcp-servers/netbox
# Should resolve to:
# $RUNTIME/mcp-servers/gitea
# $RUNTIME/mcp-servers/netbox
```
**If broken:** Symlinks are relative. If directory structure differs, they'll break.
---
## Step 5: Test MCP Server Startup
Manually test if the MCP server can start:
```bash
RUNTIME=~/.claude/plugins/marketplaces/leo-claude-mktplace
# Test Gitea MCP
cd $RUNTIME/mcp-servers/gitea
PYTHONPATH=. .venv/bin/python -c "from mcp_server.server import main; print('OK')"
# Test NetBox MCP
cd $RUNTIME/mcp-servers/netbox
PYTHONPATH=. .venv/bin/python -c "from mcp_server.server import main; print('OK')"
```
**If import fails:** Check requirements.txt installed, check Python version compatibility.
---
## Step 6: Verify Configuration Files
Check environment variables are set:
```bash
# System-level credentials (should exist)
cat ~/.config/claude/gitea.env
# Should contain: GITEA_API_URL, GITEA_API_TOKEN
cat ~/.config/claude/netbox.env
# Should contain: NETBOX_API_URL, NETBOX_API_TOKEN
# Project-level config (in target project)
cat /path/to/project/.env
# Should contain: GITEA_ORG, GITEA_REPO
```
---
## Step 7: Verify Hooks Configuration
Check hooks are valid:
```bash
RUNTIME=~/.claude/plugins/marketplaces/leo-claude-mktplace
# List all hooks.json files
find $RUNTIME/plugins -name "hooks.json" -exec echo "=== {} ===" \; -exec cat {} \;
# Verify hook events are valid
# Valid: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd,
# Notification, Stop, SubagentStop, PreCompact
# INVALID: task-completed, file-changed, git-commit-msg-needed
```
---
## Quick Diagnostic Commands
Run these to quickly identify issues:
```bash
RUNTIME=~/.claude/plugins/marketplaces/leo-claude-mktplace
echo "=== Installation Status ==="
[ -d "$RUNTIME" ] && echo "Installed: YES" || echo "Installed: NO"
echo -e "\n=== Virtual Environments ==="
[ -f "$RUNTIME/mcp-servers/gitea/.venv/bin/python" ] && echo "Gitea venv: OK" || echo "Gitea venv: MISSING"
[ -f "$RUNTIME/mcp-servers/netbox/.venv/bin/python" ] && echo "NetBox venv: OK" || echo "NetBox venv: MISSING"
echo -e "\n=== Symlinks ==="
[ -L "$RUNTIME/plugins/projman/mcp-servers/gitea" ] && echo "projman->gitea: OK" || echo "projman->gitea: MISSING"
[ -L "$RUNTIME/plugins/pr-review/mcp-servers/gitea" ] && echo "pr-review->gitea: OK" || echo "pr-review->gitea: MISSING"
[ -L "$RUNTIME/plugins/cmdb-assistant/mcp-servers/netbox" ] && echo "cmdb-assistant->netbox: OK" || echo "cmdb-assistant->netbox: MISSING"
echo -e "\n=== Config Files ==="
[ -f ~/.config/claude/gitea.env ] && echo "gitea.env: OK" || echo "gitea.env: MISSING"
[ -f ~/.config/claude/netbox.env ] && echo "netbox.env: OK" || echo "netbox.env: MISSING"
```
---
## Common Issues and Fixes
| Issue | Symptom | Fix |
|-------|---------|-----|
| Missing venvs | "X MCP servers failed" | `cd ~/.claude/plugins/marketplaces/leo-claude-mktplace && ./scripts/setup.sh` |
| Broken symlinks | MCP tools not available | Reinstall marketplace or manually recreate symlinks |
| Wrong path edits | Changes don't take effect | Edit installed path or reinstall after source changes |
| Missing credentials | MCP connection errors | Create `~/.config/claude/gitea.env` with API credentials |
| Invalid hook events | Hooks don't fire | Use only valid event names (see Step 7) |
---
## After Fixing Issues
1. **Restart Claude Code** - Plugins are loaded at startup
2. **Verify fix works** - Run a simple command that uses the MCP
3. **Document the issue** - If it's a new failure mode, add to this checklist
---
## Automated Diagnostics
Use these commands for automated checking:
- `/debug-report` - Run full diagnostics, create issue if problems found
- `/debug-review` - Investigate existing diagnostic issues and propose fixes
---
## Related Documentation
- `CLAUDE.md` - Installation Paths and Troubleshooting sections
- `docs/CONFIGURATION.md` - Setup and configuration guide
- `docs/UPDATING.md` - Update procedures

View File

@@ -4,15 +4,32 @@ This guide covers how to update your local installation when new versions are re
--- ---
## Quick Update ## ⚠️ CRITICAL: Run Setup in Installed Location
When Claude Code installs a marketplace, it copies files to `~/.claude/plugins/marketplaces/` but **does NOT create Python virtual environments**. You must run setup manually after installation or update.
**After installing or updating the marketplace:**
```bash ```bash
# 1. Pull latest changes cd ~/.claude/plugins/marketplaces/leo-claude-mktplace && ./scripts/setup.sh
```
This creates the required `.venv` directories for MCP servers. Without this step, **all MCP servers will fail to start**.
---
## Quick Update (Source Repository)
```bash
# 1. Pull latest changes to source
cd /path/to/leo-claude-mktplace cd /path/to/leo-claude-mktplace
git pull origin main git pull origin main
# 2. Run post-update script # 2. Run post-update script (updates source repo venvs)
./scripts/post-update.sh ./scripts/post-update.sh
# 3. CRITICAL: Run setup in installed marketplace location
cd ~/.claude/plugins/marketplaces/leo-claude-mktplace && ./scripts/setup.sh
``` ```
**Then restart your Claude Code session** to load any changes. **Then restart your Claude Code session** to load any changes.
@@ -132,10 +149,34 @@ deactivate
### MCP server won't start after update ### MCP server won't start after update
**Most common cause:** Virtual environments don't exist in the installed marketplace.
```bash
# Fix: Run setup in installed location
cd ~/.claude/plugins/marketplaces/leo-claude-mktplace && ./scripts/setup.sh
```
If that doesn't work:
1. Check Python version: `python3 --version` (requires 3.10+) 1. Check Python version: `python3 --version` (requires 3.10+)
2. Verify venv exists: `ls mcp-servers/gitea/.venv` 2. Verify venv exists in INSTALLED location:
3. Restart Claude Code session ```bash
4. Check logs for specific errors ls ~/.claude/plugins/marketplaces/leo-claude-mktplace/mcp-servers/gitea/.venv
ls ~/.claude/plugins/marketplaces/leo-claude-mktplace/mcp-servers/netbox/.venv
```
3. If missing, the symlinks won't resolve. Run setup.sh as shown above.
4. Restart Claude Code session
5. Check logs for specific errors
### "X MCP servers failed" on startup
This almost always means the venvs don't exist in the installed marketplace:
```bash
cd ~/.claude/plugins/marketplaces/leo-claude-mktplace && ./scripts/setup.sh
```
Then restart Claude Code.
### New commands not available ### New commands not available

View File

@@ -220,6 +220,10 @@ class GiteaMCPServer:
"context": { "context": {
"type": "string", "type": "string",
"description": "Issue title + description or sprint context" "description": "Issue title + description or sprint context"
},
"repo": {
"type": "string",
"description": "Repository name (owner/repo format)"
} }
}, },
"required": ["context"] "required": ["context"]

View File

@@ -8,6 +8,7 @@ Provides async wrappers for label operations with:
""" """
import asyncio import asyncio
import logging import logging
import re
from typing import List, Dict, Optional from typing import List, Dict, Optional
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
@@ -59,107 +60,202 @@ class LabelTools:
'total_count': len(org_labels) + len(repo_labels) 'total_count': len(org_labels) + len(repo_labels)
} }
async def suggest_labels(self, context: str) -> List[str]: async def suggest_labels(self, context: str, repo: Optional[str] = None) -> List[str]:
""" """
Analyze context and suggest appropriate labels. Analyze context and suggest appropriate labels from repository's actual labels.
This method fetches actual labels from the repository and matches them
dynamically, supporting any label naming convention (slash, colon-space, etc.).
Args: Args:
context: Issue title + description or sprint context context: Issue title + description or sprint context
repo: Repository in 'owner/repo' format (optional, uses default if not provided)
Returns: Returns:
List of suggested label names List of suggested label names that exist in the repository
""" """
# Fetch actual labels from repository
target_repo = repo or self.gitea.repo
if not target_repo:
logger.warning("No repository specified, returning empty suggestions")
return []
try:
labels_data = await self.get_labels(target_repo)
all_labels = labels_data.get('organization', []) + labels_data.get('repository', [])
label_names = [label['name'] for label in all_labels]
except Exception as e:
logger.warning(f"Failed to fetch labels: {e}. Using fallback suggestions.")
label_names = []
# Build label lookup for dynamic matching
label_lookup = self._build_label_lookup(label_names)
suggested = [] suggested = []
context_lower = context.lower() context_lower = context.lower()
# Type detection (exclusive - only one) # Type detection (exclusive - only one)
type_label = None
if any(word in context_lower for word in ['bug', 'error', 'fix', 'broken', 'crash', 'fail']): if any(word in context_lower for word in ['bug', 'error', 'fix', 'broken', 'crash', 'fail']):
suggested.append('Type/Bug') type_label = self._find_label(label_lookup, 'type', 'bug')
elif any(word in context_lower for word in ['refactor', 'extract', 'restructure', 'architecture', 'service extraction']): elif any(word in context_lower for word in ['refactor', 'extract', 'restructure', 'architecture', 'service extraction']):
suggested.append('Type/Refactor') type_label = self._find_label(label_lookup, 'type', 'refactor')
elif any(word in context_lower for word in ['feature', 'add', 'implement', 'new', 'create']): elif any(word in context_lower for word in ['feature', 'add', 'implement', 'new', 'create']):
suggested.append('Type/Feature') type_label = self._find_label(label_lookup, 'type', 'feature')
elif any(word in context_lower for word in ['docs', 'documentation', 'readme', 'guide']): elif any(word in context_lower for word in ['docs', 'documentation', 'readme', 'guide']):
suggested.append('Type/Documentation') type_label = self._find_label(label_lookup, 'type', 'documentation')
elif any(word in context_lower for word in ['test', 'testing', 'spec', 'coverage']): elif any(word in context_lower for word in ['test', 'testing', 'spec', 'coverage']):
suggested.append('Type/Test') type_label = self._find_label(label_lookup, 'type', 'test')
elif any(word in context_lower for word in ['chore', 'maintenance', 'update', 'upgrade']): elif any(word in context_lower for word in ['chore', 'maintenance', 'update', 'upgrade']):
suggested.append('Type/Chore') type_label = self._find_label(label_lookup, 'type', 'chore')
if type_label:
suggested.append(type_label)
# Priority detection # Priority detection
priority_label = None
if any(word in context_lower for word in ['critical', 'urgent', 'blocker', 'blocking', 'emergency']): if any(word in context_lower for word in ['critical', 'urgent', 'blocker', 'blocking', 'emergency']):
suggested.append('Priority/Critical') priority_label = self._find_label(label_lookup, 'priority', 'critical')
elif any(word in context_lower for word in ['high', 'important', 'asap', 'soon']): elif any(word in context_lower for word in ['high', 'important', 'asap', 'soon']):
suggested.append('Priority/High') priority_label = self._find_label(label_lookup, 'priority', 'high')
elif any(word in context_lower for word in ['low', 'nice-to-have', 'optional', 'later']): elif any(word in context_lower for word in ['low', 'nice-to-have', 'optional', 'later']):
suggested.append('Priority/Low') priority_label = self._find_label(label_lookup, 'priority', 'low')
else: else:
suggested.append('Priority/Medium') priority_label = self._find_label(label_lookup, 'priority', 'medium')
if priority_label:
suggested.append(priority_label)
# Complexity detection # Complexity detection
complexity_label = None
if any(word in context_lower for word in ['simple', 'trivial', 'easy', 'quick']): if any(word in context_lower for word in ['simple', 'trivial', 'easy', 'quick']):
suggested.append('Complexity/Simple') complexity_label = self._find_label(label_lookup, 'complexity', 'simple')
elif any(word in context_lower for word in ['complex', 'difficult', 'challenging', 'intricate']): elif any(word in context_lower for word in ['complex', 'difficult', 'challenging', 'intricate']):
suggested.append('Complexity/Complex') complexity_label = self._find_label(label_lookup, 'complexity', 'complex')
else: else:
suggested.append('Complexity/Medium') complexity_label = self._find_label(label_lookup, 'complexity', 'medium')
if complexity_label:
suggested.append(complexity_label)
# Efforts detection # Effort detection (supports both "Effort" and "Efforts" naming)
effort_label = None
if any(word in context_lower for word in ['xs', 'tiny', '1 hour', '2 hours']): if any(word in context_lower for word in ['xs', 'tiny', '1 hour', '2 hours']):
suggested.append('Efforts/XS') effort_label = self._find_label(label_lookup, 'effort', 'xs')
elif any(word in context_lower for word in ['small', 's ', '1 day', 'half day']): elif any(word in context_lower for word in ['small', 's ', '1 day', 'half day']):
suggested.append('Efforts/S') effort_label = self._find_label(label_lookup, 'effort', 's')
elif any(word in context_lower for word in ['medium', 'm ', '2 days', '3 days']): elif any(word in context_lower for word in ['medium', 'm ', '2 days', '3 days']):
suggested.append('Efforts/M') effort_label = self._find_label(label_lookup, 'effort', 'm')
elif any(word in context_lower for word in ['large', 'l ', '1 week', '5 days']): elif any(word in context_lower for word in ['large', 'l ', '1 week', '5 days']):
suggested.append('Efforts/L') effort_label = self._find_label(label_lookup, 'effort', 'l')
elif any(word in context_lower for word in ['xl', 'extra large', '2 weeks', 'sprint']): elif any(word in context_lower for word in ['xl', 'extra large', '2 weeks', 'sprint']):
suggested.append('Efforts/XL') effort_label = self._find_label(label_lookup, 'effort', 'xl')
if effort_label:
suggested.append(effort_label)
# Component detection (based on keywords) # Component detection (based on keywords)
component_keywords = { component_mappings = {
'Component/Backend': ['backend', 'server', 'api', 'database', 'service'], 'backend': ['backend', 'server', 'api', 'database', 'service'],
'Component/Frontend': ['frontend', 'ui', 'interface', 'react', 'vue', 'component'], 'frontend': ['frontend', 'ui', 'interface', 'react', 'vue', 'component'],
'Component/API': ['api', 'endpoint', 'rest', 'graphql', 'route'], 'api': ['api', 'endpoint', 'rest', 'graphql', 'route'],
'Component/Database': ['database', 'db', 'sql', 'migration', 'schema', 'postgres'], 'database': ['database', 'db', 'sql', 'migration', 'schema', 'postgres'],
'Component/Auth': ['auth', 'authentication', 'login', 'oauth', 'token', 'session'], 'auth': ['auth', 'authentication', 'login', 'oauth', 'token', 'session'],
'Component/Deploy': ['deploy', 'deployment', 'docker', 'kubernetes', 'ci/cd'], 'deploy': ['deploy', 'deployment', 'docker', 'kubernetes', 'ci/cd'],
'Component/Testing': ['test', 'testing', 'spec', 'jest', 'pytest', 'coverage'], 'testing': ['test', 'testing', 'spec', 'jest', 'pytest', 'coverage'],
'Component/Docs': ['docs', 'documentation', 'readme', 'guide', 'wiki'] 'docs': ['docs', 'documentation', 'readme', 'guide', 'wiki']
} }
for label, keywords in component_keywords.items(): for component, keywords in component_mappings.items():
if any(keyword in context_lower for keyword in keywords): if any(keyword in context_lower for keyword in keywords):
label = self._find_label(label_lookup, 'component', component)
if label and label not in suggested:
suggested.append(label) suggested.append(label)
# Tech stack detection # Tech stack detection
tech_keywords = { tech_mappings = {
'Tech/Python': ['python', 'fastapi', 'django', 'flask', 'pytest'], 'python': ['python', 'fastapi', 'django', 'flask', 'pytest'],
'Tech/JavaScript': ['javascript', 'js', 'node', 'npm', 'yarn'], 'javascript': ['javascript', 'js', 'node', 'npm', 'yarn'],
'Tech/Docker': ['docker', 'dockerfile', 'container', 'compose'], 'docker': ['docker', 'dockerfile', 'container', 'compose'],
'Tech/PostgreSQL': ['postgres', 'postgresql', 'psql', 'sql'], 'postgresql': ['postgres', 'postgresql', 'psql', 'sql'],
'Tech/Redis': ['redis', 'cache', 'session store'], 'redis': ['redis', 'cache', 'session store'],
'Tech/Vue': ['vue', 'vuejs', 'nuxt'], 'vue': ['vue', 'vuejs', 'nuxt'],
'Tech/FastAPI': ['fastapi', 'pydantic', 'starlette'] 'fastapi': ['fastapi', 'pydantic', 'starlette']
} }
for label, keywords in tech_keywords.items(): for tech, keywords in tech_mappings.items():
if any(keyword in context_lower for keyword in keywords): if any(keyword in context_lower for keyword in keywords):
label = self._find_label(label_lookup, 'tech', tech)
if label and label not in suggested:
suggested.append(label) suggested.append(label)
# Source detection (based on git branch or context) # Source detection (based on git branch or context)
source_label = None
if 'development' in context_lower or 'dev/' in context_lower: if 'development' in context_lower or 'dev/' in context_lower:
suggested.append('Source/Development') source_label = self._find_label(label_lookup, 'source', 'development')
elif 'staging' in context_lower or 'stage/' in context_lower: elif 'staging' in context_lower or 'stage/' in context_lower:
suggested.append('Source/Staging') source_label = self._find_label(label_lookup, 'source', 'staging')
elif 'production' in context_lower or 'prod' in context_lower: elif 'production' in context_lower or 'prod' in context_lower:
suggested.append('Source/Production') source_label = self._find_label(label_lookup, 'source', 'production')
if source_label:
suggested.append(source_label)
# Risk detection # Risk detection
risk_label = None
if any(word in context_lower for word in ['breaking', 'breaking change', 'major', 'risky']): if any(word in context_lower for word in ['breaking', 'breaking change', 'major', 'risky']):
suggested.append('Risk/High') risk_label = self._find_label(label_lookup, 'risk', 'high')
elif any(word in context_lower for word in ['safe', 'low risk', 'minor']): elif any(word in context_lower for word in ['safe', 'low risk', 'minor']):
suggested.append('Risk/Low') risk_label = self._find_label(label_lookup, 'risk', 'low')
if risk_label:
suggested.append(risk_label)
logger.info(f"Suggested {len(suggested)} labels based on context") logger.info(f"Suggested {len(suggested)} labels based on context and {len(label_names)} available labels")
return suggested return suggested
def _build_label_lookup(self, label_names: List[str]) -> Dict[str, Dict[str, str]]:
"""
Build a lookup dictionary for label matching.
Supports various label formats:
- Slash format: Type/Bug, Priority/High
- Colon-space format: Type: Bug, Priority: High
- Colon format: Type:Bug
Args:
label_names: List of actual label names from repository
Returns:
Nested dict: {category: {value: actual_label_name}}
"""
lookup: Dict[str, Dict[str, str]] = {}
for label in label_names:
# Try different separator patterns
# Pattern: Category<separator>Value
# Separators: /, : , :
match = re.match(r'^([^/:]+)(?:/|:\s*|:)(.+)$', label)
if match:
category = match.group(1).lower().rstrip('s') # Normalize: "Efforts" -> "effort"
value = match.group(2).lower()
if category not in lookup:
lookup[category] = {}
lookup[category][value] = label
return lookup
def _find_label(self, lookup: Dict[str, Dict[str, str]], category: str, value: str) -> Optional[str]:
"""
Find actual label name from lookup.
Args:
lookup: Label lookup dictionary
category: Category to search (e.g., 'type', 'priority')
value: Value to find (e.g., 'bug', 'high')
Returns:
Actual label name if found, None otherwise
"""
category_lower = category.lower().rstrip('s') # Normalize
value_lower = value.lower()
if category_lower in lookup and value_lower in lookup[category_lower]:
return lookup[category_lower][value_lower]
return None

View File

@@ -40,10 +40,141 @@ async def test_get_labels(label_tools):
assert result['total_count'] == 4 assert result['total_count'] == 4
# ========================================
# LABEL LOOKUP TESTS (NEW)
# ========================================
def test_build_label_lookup_slash_format():
"""Test building label lookup with slash format labels"""
mock_client = Mock()
mock_client.repo = 'test/repo'
tools = LabelTools(mock_client)
labels = ['Type/Bug', 'Type/Feature', 'Priority/High', 'Priority/Low']
lookup = tools._build_label_lookup(labels)
assert 'type' in lookup
assert 'bug' in lookup['type']
assert lookup['type']['bug'] == 'Type/Bug'
assert lookup['type']['feature'] == 'Type/Feature'
assert 'priority' in lookup
assert lookup['priority']['high'] == 'Priority/High'
def test_build_label_lookup_colon_space_format():
"""Test building label lookup with colon-space format labels"""
mock_client = Mock()
mock_client.repo = 'test/repo'
tools = LabelTools(mock_client)
labels = ['Type: Bug', 'Type: Feature', 'Priority: High', 'Effort: M']
lookup = tools._build_label_lookup(labels)
assert 'type' in lookup
assert 'bug' in lookup['type']
assert lookup['type']['bug'] == 'Type: Bug'
assert lookup['type']['feature'] == 'Type: Feature'
assert 'priority' in lookup
assert lookup['priority']['high'] == 'Priority: High'
# Test singular "Effort" (not "Efforts")
assert 'effort' in lookup
assert lookup['effort']['m'] == 'Effort: M'
def test_build_label_lookup_efforts_normalization():
"""Test that 'Efforts' is normalized to 'effort' for matching"""
mock_client = Mock()
mock_client.repo = 'test/repo'
tools = LabelTools(mock_client)
labels = ['Efforts/XS', 'Efforts/S', 'Efforts/M']
lookup = tools._build_label_lookup(labels)
# 'Efforts' should be normalized to 'effort'
assert 'effort' in lookup
assert lookup['effort']['xs'] == 'Efforts/XS'
def test_find_label():
"""Test finding labels from lookup"""
mock_client = Mock()
mock_client.repo = 'test/repo'
tools = LabelTools(mock_client)
lookup = {
'type': {'bug': 'Type: Bug', 'feature': 'Type: Feature'},
'priority': {'high': 'Priority: High', 'low': 'Priority: Low'}
}
assert tools._find_label(lookup, 'type', 'bug') == 'Type: Bug'
assert tools._find_label(lookup, 'priority', 'high') == 'Priority: High'
assert tools._find_label(lookup, 'type', 'nonexistent') is None
assert tools._find_label(lookup, 'nonexistent', 'bug') is None
# ========================================
# SUGGEST LABELS WITH DYNAMIC FORMAT TESTS
# ========================================
def _create_tools_with_labels(labels):
"""Helper to create LabelTools with mocked labels"""
import asyncio
mock_client = Mock()
mock_client.repo = 'test/repo'
mock_client.is_org_repo = Mock(return_value=False)
mock_client.get_labels = Mock(return_value=[{'name': l} for l in labels])
return LabelTools(mock_client)
@pytest.mark.asyncio
async def test_suggest_labels_with_slash_format():
"""Test label suggestion with slash format labels"""
labels = [
'Type/Bug', 'Type/Feature', 'Type/Refactor',
'Priority/Critical', 'Priority/High', 'Priority/Medium', 'Priority/Low',
'Complexity/Simple', 'Complexity/Medium', 'Complexity/Complex',
'Component/Auth'
]
tools = _create_tools_with_labels(labels)
context = "Fix critical bug in login authentication"
suggestions = await tools.suggest_labels(context)
assert 'Type/Bug' in suggestions
assert 'Priority/Critical' in suggestions
assert 'Component/Auth' in suggestions
@pytest.mark.asyncio
async def test_suggest_labels_with_colon_space_format():
"""Test label suggestion with colon-space format labels"""
labels = [
'Type: Bug', 'Type: Feature', 'Type: Refactor',
'Priority: Critical', 'Priority: High', 'Priority: Medium', 'Priority: Low',
'Complexity: Simple', 'Complexity: Medium', 'Complexity: Complex',
'Effort: XS', 'Effort: S', 'Effort: M', 'Effort: L', 'Effort: XL'
]
tools = _create_tools_with_labels(labels)
context = "Fix critical bug for tiny 1 hour fix"
suggestions = await tools.suggest_labels(context)
# Should return colon-space format labels
assert 'Type: Bug' in suggestions
assert 'Priority: Critical' in suggestions
assert 'Effort: XS' in suggestions
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_bug(): async def test_suggest_labels_bug():
"""Test label suggestion for bug context""" """Test label suggestion for bug context"""
tools = LabelTools(Mock()) labels = [
'Type/Bug', 'Type/Feature',
'Priority/Critical', 'Priority/High', 'Priority/Medium', 'Priority/Low',
'Complexity/Simple', 'Complexity/Medium', 'Complexity/Complex',
'Component/Auth'
]
tools = _create_tools_with_labels(labels)
context = "Fix critical bug in login authentication" context = "Fix critical bug in login authentication"
suggestions = await tools.suggest_labels(context) suggestions = await tools.suggest_labels(context)
@@ -56,7 +187,8 @@ async def test_suggest_labels_bug():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_feature(): async def test_suggest_labels_feature():
"""Test label suggestion for feature context""" """Test label suggestion for feature context"""
tools = LabelTools(Mock()) labels = ['Type/Feature', 'Priority/Medium', 'Complexity/Medium']
tools = _create_tools_with_labels(labels)
context = "Add new feature to implement user dashboard" context = "Add new feature to implement user dashboard"
suggestions = await tools.suggest_labels(context) suggestions = await tools.suggest_labels(context)
@@ -68,7 +200,8 @@ async def test_suggest_labels_feature():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_refactor(): async def test_suggest_labels_refactor():
"""Test label suggestion for refactor context""" """Test label suggestion for refactor context"""
tools = LabelTools(Mock()) labels = ['Type/Refactor', 'Priority/Medium', 'Complexity/Medium', 'Component/Backend']
tools = _create_tools_with_labels(labels)
context = "Refactor architecture to extract service layer" context = "Refactor architecture to extract service layer"
suggestions = await tools.suggest_labels(context) suggestions = await tools.suggest_labels(context)
@@ -80,7 +213,8 @@ async def test_suggest_labels_refactor():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_documentation(): async def test_suggest_labels_documentation():
"""Test label suggestion for documentation context""" """Test label suggestion for documentation context"""
tools = LabelTools(Mock()) labels = ['Type/Documentation', 'Priority/Medium', 'Complexity/Medium', 'Component/API', 'Component/Docs']
tools = _create_tools_with_labels(labels)
context = "Update documentation for API endpoints" context = "Update documentation for API endpoints"
suggestions = await tools.suggest_labels(context) suggestions = await tools.suggest_labels(context)
@@ -92,7 +226,8 @@ async def test_suggest_labels_documentation():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_priority(): async def test_suggest_labels_priority():
"""Test priority detection in suggestions""" """Test priority detection in suggestions"""
tools = LabelTools(Mock()) labels = ['Type/Feature', 'Priority/Critical', 'Priority/High', 'Priority/Medium', 'Priority/Low', 'Complexity/Medium']
tools = _create_tools_with_labels(labels)
# Critical priority # Critical priority
context = "Urgent blocker in production" context = "Urgent blocker in production"
@@ -113,7 +248,8 @@ async def test_suggest_labels_priority():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_complexity(): async def test_suggest_labels_complexity():
"""Test complexity detection in suggestions""" """Test complexity detection in suggestions"""
tools = LabelTools(Mock()) labels = ['Type/Feature', 'Priority/Medium', 'Complexity/Simple', 'Complexity/Medium', 'Complexity/Complex']
tools = _create_tools_with_labels(labels)
# Simple complexity # Simple complexity
context = "Simple quick fix for typo" context = "Simple quick fix for typo"
@@ -129,7 +265,8 @@ async def test_suggest_labels_complexity():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_efforts(): async def test_suggest_labels_efforts():
"""Test efforts detection in suggestions""" """Test efforts detection in suggestions"""
tools = LabelTools(Mock()) labels = ['Type/Feature', 'Priority/Medium', 'Complexity/Medium', 'Efforts/XS', 'Efforts/S', 'Efforts/M', 'Efforts/L', 'Efforts/XL']
tools = _create_tools_with_labels(labels)
# XS effort # XS effort
context = "Tiny fix that takes 1 hour" context = "Tiny fix that takes 1 hour"
@@ -145,7 +282,8 @@ async def test_suggest_labels_efforts():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_components(): async def test_suggest_labels_components():
"""Test component detection in suggestions""" """Test component detection in suggestions"""
tools = LabelTools(Mock()) labels = ['Type/Feature', 'Priority/Medium', 'Complexity/Medium', 'Component/Backend', 'Component/Frontend', 'Component/API', 'Component/Database']
tools = _create_tools_with_labels(labels)
# Backend component # Backend component
context = "Update backend API service" context = "Update backend API service"
@@ -167,7 +305,8 @@ async def test_suggest_labels_components():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_tech_stack(): async def test_suggest_labels_tech_stack():
"""Test tech stack detection in suggestions""" """Test tech stack detection in suggestions"""
tools = LabelTools(Mock()) labels = ['Type/Feature', 'Priority/Medium', 'Complexity/Medium', 'Tech/Python', 'Tech/FastAPI', 'Tech/Docker', 'Tech/PostgreSQL']
tools = _create_tools_with_labels(labels)
# Python # Python
context = "Update Python FastAPI endpoint" context = "Update Python FastAPI endpoint"
@@ -189,7 +328,8 @@ async def test_suggest_labels_tech_stack():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_source(): async def test_suggest_labels_source():
"""Test source detection in suggestions""" """Test source detection in suggestions"""
tools = LabelTools(Mock()) labels = ['Type/Feature', 'Priority/Medium', 'Complexity/Medium', 'Source/Development', 'Source/Staging', 'Source/Production']
tools = _create_tools_with_labels(labels)
# Development # Development
context = "Issue found in development environment" context = "Issue found in development environment"
@@ -205,7 +345,8 @@ async def test_suggest_labels_source():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_risk(): async def test_suggest_labels_risk():
"""Test risk detection in suggestions""" """Test risk detection in suggestions"""
tools = LabelTools(Mock()) labels = ['Type/Feature', 'Priority/Medium', 'Complexity/Medium', 'Risk/High', 'Risk/Low']
tools = _create_tools_with_labels(labels)
# High risk # High risk
context = "Breaking change to major API" context = "Breaking change to major API"
@@ -221,7 +362,15 @@ async def test_suggest_labels_risk():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_suggest_labels_multiple_categories(): async def test_suggest_labels_multiple_categories():
"""Test that suggestions span multiple categories""" """Test that suggestions span multiple categories"""
tools = LabelTools(Mock()) labels = [
'Type/Bug', 'Type/Feature',
'Priority/Critical', 'Priority/Medium',
'Complexity/Complex', 'Complexity/Medium',
'Component/Backend', 'Component/API', 'Component/Auth',
'Tech/FastAPI', 'Tech/PostgreSQL',
'Source/Production'
]
tools = _create_tools_with_labels(labels)
context = """ context = """
Urgent critical bug in production backend API service. Urgent critical bug in production backend API service.
@@ -247,6 +396,33 @@ async def test_suggest_labels_multiple_categories():
assert any('Source/' in label for label in suggestions) assert any('Source/' in label for label in suggestions)
@pytest.mark.asyncio
async def test_suggest_labels_empty_repo():
"""Test suggestions when no repo specified and no labels available"""
mock_client = Mock()
mock_client.repo = None
tools = LabelTools(mock_client)
context = "Fix a bug"
suggestions = await tools.suggest_labels(context)
# Should return empty list when no repo
assert suggestions == []
@pytest.mark.asyncio
async def test_suggest_labels_no_matching_labels():
"""Test suggestions return empty when no matching labels exist"""
labels = ['Custom/Label', 'Other/Thing'] # No standard labels
tools = _create_tools_with_labels(labels)
context = "Fix a bug"
suggestions = await tools.suggest_labels(context)
# Should return empty list since no Type/Bug or similar exists
assert len(suggestions) == 0
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_labels_org_owned_repo(): async def test_get_labels_org_owned_repo():
"""Test getting labels for organization-owned repository""" """Test getting labels for organization-owned repository"""

View File

@@ -6,7 +6,7 @@
"hooks": [ "hooks": [
{ {
"type": "prompt", "type": "prompt",
"prompt": "SECURITY CHECK - Before writing this code, scan for these patterns:\n\n**Critical (BLOCK if found):**\n- eval(), exec() with user input\n- SQL string concatenation (SQL injection)\n- shell=True with user input (command injection)\n- Hardcoded secrets (API keys, passwords, tokens)\n- Pickle/marshal deserialization of untrusted data\n- innerHTML/dangerouslySetInnerHTML with user content (XSS)\n\n**Warning (WARN but allow):**\n- subprocess without input validation\n- File operations without path sanitization\n- HTTP requests without timeout\n- Broad exception catches (except:)\n- Debug/print statements with sensitive data\n\n**Response:**\n- If CRITICAL found: STOP, explain the issue, suggest safe alternative\n- If WARNING found: Note it briefly, proceed with suggestion\n- If clean: Proceed silently (say nothing)\n\nDo NOT announce clean scans. Only speak if issues found." "prompt": "[code-sentinel] SECURITY CHECK - Before writing this code, scan for these patterns:\n\n**Critical (BLOCK if found):**\n- eval(), exec() with user input\n- SQL string concatenation (SQL injection)\n- shell=True with user input (command injection)\n- Hardcoded secrets (API keys, passwords, tokens)\n- Pickle/marshal deserialization of untrusted data\n- innerHTML/dangerouslySetInnerHTML with user content (XSS)\n\n**Warning (WARN but allow):**\n- subprocess without input validation\n- File operations without path sanitization\n- HTTP requests without timeout\n- Broad exception catches (except:)\n- Debug/print statements with sensitive data\n\n**Response:**\n- If CRITICAL found: STOP with '[code-sentinel] BLOCKED:', explain the issue, suggest safe alternative\n- If WARNING found: Note briefly with '[code-sentinel] WARNING:', proceed with suggestion\n- If clean: Proceed silently (say nothing)\n\nDo NOT announce clean scans. Only speak if issues found."
} }
] ]
} }

View File

@@ -11,10 +11,10 @@ Documentation gets outdated. Functions get renamed, configs change, versions bum
## Solution ## Solution
doc-guardian watches your code changes and automatically: doc-guardian monitors your code changes via hooks:
1. Detects when changes affect documentation 1. Detects when changes might affect documentation
2. Queues updates silently (doesn't interrupt your flow) 2. Alerts you to potential drift (doesn't interrupt your flow)
3. Syncs all doc changes in a single commit when ready 3. Provides commands to audit and sync docs when ready
## Commands ## Commands
@@ -25,8 +25,7 @@ doc-guardian watches your code changes and automatically:
## Hooks ## Hooks
- **PostToolUse (Write\|Edit)**: Silently checks if code changes affect docs - **PostToolUse (Write|Edit)**: Silently checks if code changes affect docs
- **Stop**: Reminds you of pending doc updates before session ends
## What It Detects ## What It Detects

View File

@@ -6,18 +6,7 @@
"hooks": [ "hooks": [
{ {
"type": "prompt", "type": "prompt",
"prompt": "A file was just modified. Silently check if this change affects any documentation:\n\n1. If a code file changed: check if README, CLAUDE.md, docstrings, or API docs reference the modified functions/classes/configs\n2. If drift detected: add to internal queue (do NOT interrupt user flow)\n3. At natural breakpoints or when user runs /doc-sync: report pending doc updates\n\nDo NOT announce this check unless drift is found. Work silently." "prompt": "[doc-guardian] QUICK drift check (DO NOT block workflow):\n\n1. ONLY check if the modified file is referenced in README.md, CLAUDE.md, or API docs in the SAME directory\n2. Do NOT read files or perform deep analysis - just note potential drift based on file name/path\n3. If potential drift: output a single line like '[doc-guardian] Note: {filename} changed - may affect {doc}. Run /doc-sync to verify.'\n4. If no obvious drift: say nothing\n\nIMPORTANT: This is notification-only. Do NOT read documentation files, do NOT make changes, do NOT use any tools. Just a quick mental check based on the file path."
}
]
}
],
"Stop": [
{
"matcher": ".*",
"hooks": [
{
"type": "prompt",
"prompt": "Before ending, check if there are pending documentation updates queued by doc-guardian. If yes, ask user: 'I detected documentation drift in X files. Run /doc-sync to update, or skip for now?'"
} }
] ]
} }

View File

@@ -1,12 +1,19 @@
# /branch-cleanup - Clean Merged Branches # /branch-cleanup - Clean Merged and Stale Branches
## Purpose ## Purpose
Remove branches that have been merged, both locally and optionally on remote. Remove branches that have been merged OR whose remote tracking branch no longer exists, both locally and optionally on remote.
## Behavior ## Behavior
### Step 1: Identify Merged Branches ### Step 1: Prune Remote Refs
```bash
# Remove stale remote-tracking references
git fetch --prune
```
### Step 2: Identify Branches for Cleanup
```bash ```bash
# Find merged local branches # Find merged local branches
@@ -14,19 +21,26 @@ git branch --merged <base-branch>
# Find merged remote branches # Find merged remote branches
git branch -r --merged <base-branch> git branch -r --merged <base-branch>
# Find local branches with deleted upstreams (stale)
git branch -vv | grep ': gone]'
``` ```
### Step 2: Present Findings ### Step 3: Present Findings
``` ```
Found 5 merged branches: Found branches for cleanup:
Local: Merged (safe to delete):
- feat/login-page (merged 3 days ago) - feat/login-page (merged 3 days ago)
- fix/typo-header (merged 1 week ago) - fix/typo-header (merged 1 week ago)
- chore/deps-update (merged 2 weeks ago) - chore/deps-update (merged 2 weeks ago)
Remote: Stale (remote deleted):
- feat/old-feature (upstream gone)
- fix/already-merged (upstream gone)
Remote (merged into base):
- origin/feat/login-page - origin/feat/login-page
- origin/fix/typo-header - origin/fix/typo-header
@@ -36,35 +50,40 @@ Protected (won't delete):
- staging - staging
Delete these branches? Delete these branches?
1. Delete all (local + remote) 1. Delete all (local merged + stale + remote)
2. Delete local only 2. Delete merged only (skip stale)
3. Let me pick which ones 3. Delete stale only (upstream gone)
4. Cancel 4. Let me pick which ones
5. Cancel
``` ```
### Step 3: Execute Cleanup ### Step 4: Execute Cleanup
```bash ```bash
# Delete local # Delete merged local branches
git branch -d <branch-name> git branch -d <branch-name>
# Delete remote # Delete stale local branches (force needed since no upstream)
git branch -D <stale-branch-name>
# Delete remote branches
git push origin --delete <branch-name> git push origin --delete <branch-name>
``` ```
### Step 4: Report ### Step 5: Report
``` ```
Cleanup complete: Cleanup complete:
Deleted local: 3 branches Deleted local (merged): 3 branches
Deleted local (stale): 2 branches
Deleted remote: 2 branches Deleted remote: 2 branches
Skipped: 0 branches Skipped: 0 branches
Remaining local branches: Remaining local branches:
- main - main
- development - development
- feat/current-work (not merged) - feat/current-work (not merged, has upstream)
``` ```
## Environment Variables ## Environment Variables
@@ -74,20 +93,24 @@ Remaining local branches:
| `GIT_DEFAULT_BASE` | `development` | Base branch for merge detection | | `GIT_DEFAULT_BASE` | `development` | Base branch for merge detection |
| `GIT_PROTECTED_BRANCHES` | `main,master,development,staging,production` | Never delete these | | `GIT_PROTECTED_BRANCHES` | `main,master,development,staging,production` | Never delete these |
| `GIT_AUTO_DELETE_REMOTE` | `false` | Auto-delete remote branches | | `GIT_AUTO_DELETE_REMOTE` | `false` | Auto-delete remote branches |
| `GIT_CLEANUP_STALE` | `true` | Include stale branches (upstream gone) in cleanup |
## Safety ## Safety
- Never deletes protected branches - Never deletes protected branches
- Warns about unmerged branches - Warns about unmerged branches that still have upstreams
- Confirms before deleting remote branches - Confirms before deleting remote branches
- Uses `-d` (safe delete) not `-D` (force delete) - Uses `-d` (safe delete) for merged branches
- Uses `-D` (force delete) only for stale branches with confirmation
- Stale branches are highlighted separately for review
## Output ## Output
On success: On success:
``` ```
Cleaned up: Cleaned up:
Local: 3 branches deleted Local (merged): 3 branches deleted
Local (stale): 2 branches deleted
Remote: 2 branches deleted Remote: 2 branches deleted
Repository is tidy! Repository is tidy!

View File

@@ -2,7 +2,7 @@
## Purpose ## Purpose
Full sync operation: commit local changes, push to remote, and sync with upstream/base branch. Full sync operation: commit local changes, push to remote, sync with upstream/base branch, and clean up stale remote-tracking branches.
## Behavior ## Behavior
@@ -19,8 +19,8 @@ Push committed changes to remote branch.
Pull latest from base branch and rebase/merge: Pull latest from base branch and rebase/merge:
```bash ```bash
# Fetch all # Fetch all with prune (removes stale remote-tracking refs)
git fetch --all git fetch --all --prune
# Rebase on base branch # Rebase on base branch
git rebase origin/<base-branch> git rebase origin/<base-branch>
@@ -29,7 +29,26 @@ git rebase origin/<base-branch>
git push --force-with-lease git push --force-with-lease
``` ```
### Step 4: Report Status ### Step 4: Detect Stale Local Branches
Check for local branches tracking deleted remotes:
```bash
# Find local branches with gone upstreams
git branch -vv | grep ': gone]'
```
If stale branches found, report them:
```
Stale local branches (remote deleted):
- feat/old-feature (was tracking origin/feat/old-feature)
- fix/merged-bugfix (was tracking origin/fix/merged-bugfix)
Run /branch-cleanup to remove these branches.
```
### Step 5: Report Status
``` ```
Sync complete: Sync complete:
@@ -40,6 +59,10 @@ Base: development @ xyz7890 (synced)
Your branch is up-to-date with development. Your branch is up-to-date with development.
No conflicts detected. No conflicts detected.
Cleanup:
Remote refs pruned: 2
Stale local branches: 2 (run /branch-cleanup to remove)
``` ```
## Environment Variables ## Environment Variables
@@ -48,6 +71,7 @@ No conflicts detected.
|----------|---------|-------------| |----------|---------|-------------|
| `GIT_DEFAULT_BASE` | `development` | Branch to sync with | | `GIT_DEFAULT_BASE` | `development` | Branch to sync with |
| `GIT_SYNC_STRATEGY` | `rebase` | How to incorporate upstream changes | | `GIT_SYNC_STRATEGY` | `rebase` | How to incorporate upstream changes |
| `GIT_AUTO_PRUNE` | `true` | Auto-prune stale remote refs on sync |
## Conflict Handling ## Conflict Handling
@@ -76,4 +100,5 @@ Pushed to: origin/feat/password-reset
Synced with: development (xyz7890) Synced with: development (xyz7890)
Status: Clean, up-to-date Status: Clean, up-to-date
Stale branches: None (or N found - run /branch-cleanup)
``` ```

View File

@@ -3,7 +3,10 @@
"gitea": { "gitea": {
"command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/gitea/.venv/bin/python", "command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/gitea/.venv/bin/python",
"args": ["-m", "mcp_server.server"], "args": ["-m", "mcp_server.server"],
"cwd": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/gitea" "cwd": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/gitea",
"env": {
"PYTHONPATH": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/gitea"
}
} }
} }
} }

View File

@@ -1,14 +1,9 @@
{ {
"hooks": { "hooks": {
"SessionStart": [ "SessionStart": [
{
"matcher": ".*",
"hooks": [
{ {
"type": "prompt", "type": "prompt",
"prompt": "Silently check if the project's git remote matches the .env configuration:\n\n1. Read .env to get GITEA_ORG and GITEA_REPO values\n2. Run: git remote get-url origin\n3. Extract org and repo from the remote URL\n4. Compare with .env values\n\nIf mismatch detected:\n- Display a brief warning: 'Repository location mismatch detected. Git remote shows <org>/<repo> but .env has <old-org>/<old-repo>. Run /project-sync to update.'\n\nIf match or no .env exists: Stay silent, do not interrupt.\n\nThis check should be quick and non-blocking." "prompt": "[pr-review] Perform silent startup checks:\n\n1. Check if MCP venvs exist at the installed marketplace location. If the marketplace is installed but venvs are missing, warn the user: '[pr-review] MCP venvs missing - run setup.sh from installed marketplace location'.\n\n2. Check if the project git remote matches .env configuration (GITEA_ORG/GITEA_REPO). If mismatch, warn: '[pr-review] Git remote mismatch - run /project-sync'.\n\nStay silent if all checks pass or not applicable. Be quick and non-blocking."
}
]
} }
] ]
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "projman", "name": "projman",
"version": "3.0.0", "version": "3.1.0",
"description": "Sprint planning and project management with Gitea integration", "description": "Sprint planning and project management with Gitea integration",
"author": { "author": {
"name": "Leo Miranda", "name": "Leo Miranda",

View File

@@ -178,7 +178,9 @@ Sync configuration with current git remote.
**When to use:** After moving or renaming a repository **When to use:** After moving or renaming a repository
**Note:** A SessionStart hook automatically detects mismatches and warns you to run `/project-sync`. **Note:** A SessionStart hook automatically checks for:
1. Missing MCP venvs at the installed marketplace location (warns to run setup.sh)
2. Repository config mismatches (warns to run `/project-sync`)
### `/review` ### `/review`
Pre-sprint-close code quality review. Pre-sprint-close code quality review.
@@ -232,6 +234,39 @@ Generate tests for specified code.
**When to use:** When adding new code that needs test coverage **When to use:** When adding new code that needs test coverage
## Debug Workflow Commands
These commands enable a cross-repository debugging workflow between your project and the marketplace.
### `/debug-report`
Run diagnostics and create structured issue in marketplace repository.
**What it does:**
- Runs MCP tool diagnostics (validate_repo_org, get_labels, list_issues, etc.)
- Captures error messages and hypothesis
- Creates a structured issue in the marketplace repository
- Tags with `Source: Diagnostic` label
**When to use:** When MCP tools fail in your project, run this to report the issue to the marketplace for investigation.
### `/debug-review`
Investigate diagnostic issues and propose fixes with human approval.
**What it does:**
- Fetches open diagnostic issues from marketplace
- Lets you select which issue to investigate
- Maps errors to relevant source files
- Reads code and analyzes root cause
- Proposes fixes with THREE mandatory approval gates
- Creates PR with fix after approval
**Approval Gates:**
1. Analysis confirmation - Does the investigation match your understanding?
2. Fix approach - Proceed with proposed changes?
3. PR creation - Create pull request?
**When to use:** In the marketplace repo, to investigate and fix issues reported by `/debug-report`.
## Code Quality Commands ## Code Quality Commands
The `/review` and `/test-check` commands complement the Executor agent by catching issues before work is marked complete. Run both commands before `/sprint-close` for a complete quality check. The `/review` and `/test-check` commands complement the Executor agent by catching issues before work is marked complete. Run both commands before `/sprint-close` for a complete quality check.
@@ -447,9 +482,13 @@ projman/
│ ├── sprint-close.md │ ├── sprint-close.md
│ ├── labels-sync.md │ ├── labels-sync.md
│ ├── initial-setup.md │ ├── initial-setup.md
│ ├── project-init.md
│ ├── project-sync.md
│ ├── review.md │ ├── review.md
│ ├── test-check.md │ ├── test-check.md
── test-gen.md ── test-gen.md
│ ├── debug-report.md
│ └── debug-review.md
├── agents/ # Agent prompts ├── agents/ # Agent prompts
│ ├── planner.md │ ├── planner.md
│ ├── orchestrator.md │ ├── orchestrator.md

View File

@@ -0,0 +1,342 @@
---
description: Run diagnostics and create structured issue in marketplace repository
---
# Debug Report
Run diagnostic checks on projman MCP tools and create a structured issue in the marketplace repository for investigation.
## Prerequisites
Your project `.env` must have:
```env
PROJMAN_MARKETPLACE_REPO=personal-projects/leo-claude-mktplace
```
If not configured, ask the user for the marketplace repository path.
## CRITICAL: Execution Steps
You MUST follow these steps in order. Do NOT skip any step.
### Step 1: Gather Project Context
Run these Bash commands to capture project information:
```bash
# Get git remote URL
git remote get-url origin
# Get current branch
git branch --show-current
# Get working directory
pwd
```
Parse the git remote to extract `REPO_NAME` in `owner/repo` format.
Store all values:
- `PROJECT_REPO`: The detected owner/repo
- `GIT_REMOTE`: Full git remote URL
- `CURRENT_BRANCH`: Current branch name
- `WORKING_DIR`: Current working directory
### Step 2: Read Marketplace Configuration
```bash
grep PROJMAN_MARKETPLACE_REPO .env
```
Store as `MARKETPLACE_REPO`. If not found, ask the user.
### Step 3: Run Diagnostic Suite
Run each MCP tool with explicit `repo` parameter. Record success/failure and full response.
**Test 1: validate_repo_org**
```
mcp__plugin_projman_gitea__validate_repo_org(repo=PROJECT_REPO)
```
Expected: `{is_organization: true/false}`
**Test 2: get_labels**
```
mcp__plugin_projman_gitea__get_labels(repo=PROJECT_REPO)
```
Expected: `{organization: [...], repository: [...], total_count: N}`
**Test 3: list_issues**
```
mcp__plugin_projman_gitea__list_issues(repo=PROJECT_REPO, state="open")
```
Expected: Array of issues
**Test 4: list_milestones**
```
mcp__plugin_projman_gitea__list_milestones(repo=PROJECT_REPO)
```
Expected: Array of milestones
**Test 5: suggest_labels**
```
mcp__plugin_projman_gitea__suggest_labels(context="Test bug fix for authentication")
```
Expected: Array of label names matching repo's format
For each test, record:
- Tool name
- Exact parameters used
- Status: PASS or FAIL
- Response or error message
### Step 4: Analyze Results
Count failures and categorize errors:
| Category | Indicators |
|----------|------------|
| Parameter Format | "owner/repo format", "missing parameter" |
| Authentication | "401", "403", "unauthorized" |
| Not Found | "404", "not found" |
| Network | "connection", "timeout", "ECONNREFUSED" |
| Logic | Unexpected response format, wrong data |
For each failure, write a hypothesis about the likely cause.
### Step 5: Generate Issue Content
Use this exact template:
```markdown
## Diagnostic Report
**Generated**: [ISO timestamp]
**Command Tested**: [What the user was trying to run, or "general diagnostic"]
**Reporter**: Claude Code via /debug-report
## Project Context
| Field | Value |
|-------|-------|
| Repository | `[PROJECT_REPO]` |
| Git Remote | `[GIT_REMOTE]` |
| Working Directory | `[WORKING_DIR]` |
| Current Branch | `[CURRENT_BRANCH]` |
## Diagnostic Results
### Test 1: validate_repo_org
**Call**: `validate_repo_org(repo="[PROJECT_REPO]")`
**Status**: [PASS/FAIL]
**Response**:
```json
[full response or error]
```
### Test 2: get_labels
**Call**: `get_labels(repo="[PROJECT_REPO]")`
**Status**: [PASS/FAIL]
**Response**:
```json
[full response or error - truncate if very long]
```
[... repeat for each test ...]
## Summary
- **Total Tests**: 5
- **Passed**: [N]
- **Failed**: [N]
### Failed Tools
[List each failed tool with its error]
### Error Category
[Check applicable categories]
### Hypothesis
[Your analysis of what's wrong and why]
### Suggested Investigation
1. [First file/function to check]
2. [Second file/function to check]
3. [etc.]
## Reproduction Steps
1. Navigate to `[WORKING_DIR]`
2. Run `[command that was being tested]`
3. Observe error at step [X]
---
*Generated by /debug-report - Labels: Type: Bug, Source: Diagnostic, Agent: Claude*
```
### Step 6: Create Issue in Marketplace
**First, check if MCP tools are available.** Attempt to use an MCP tool. If you receive "tool not found", "not in function list", or similar error, the MCP server is not accessible in this session - use the curl fallback.
#### Option A: MCP Available (preferred)
```
mcp__plugin_projman_gitea__create_issue(
repo=MARKETPLACE_REPO,
title="[Diagnostic] [summary of main failure]",
body=[generated content from Step 5],
labels=["Type: Bug", "Source: Diagnostic", "Agent: Claude"]
)
```
If labels don't exist, create issue without labels.
#### Option B: MCP Unavailable - Use curl Fallback
If MCP tools are not available (the very issue you may be diagnosing), use this fallback:
**1. Check for Gitea credentials:**
```bash
if [[ -f ~/.config/claude/gitea.env ]]; then
source ~/.config/claude/gitea.env
echo "Credentials found. API URL: $GITEA_API_URL"
else
echo "No credentials at ~/.config/claude/gitea.env"
fi
```
**2. If credentials exist, create issue via curl with proper JSON escaping:**
Create secure temp files and save content:
```bash
# Create temp files with restrictive permissions
DIAG_TITLE=$(mktemp -p /tmp -m 600 diag-title.XXXXXX)
DIAG_BODY=$(mktemp -p /tmp -m 600 diag-body.XXXXXX)
DIAG_PAYLOAD=$(mktemp -p /tmp -m 600 diag-payload.XXXXXX)
# Save title
echo "[Diagnostic] [summary of main failure]" > "$DIAG_TITLE"
# Save body (paste Step 5 content) - heredoc delimiter prevents shell expansion
cat > "$DIAG_BODY" << 'DIAGNOSTIC_EOF'
[Paste the full issue content from Step 5 here]
DIAGNOSTIC_EOF
```
Construct JSON safely using jq's --rawfile (avoids command substitution):
```bash
# Build JSON payload using jq with --rawfile for safe content handling
jq -n \
--rawfile title "$DIAG_TITLE" \
--rawfile body "$DIAG_BODY" \
'{title: ($title | rtrimstr("\n")), body: $body}' > "$DIAG_PAYLOAD"
# Create issue using the JSON file
curl -s -X POST "${GITEA_API_URL}/repos/${MARKETPLACE_REPO}/issues" \
-H "Authorization: token ${GITEA_API_TOKEN}" \
-H "Content-Type: application/json" \
-d @"$DIAG_PAYLOAD" | jq '.html_url // .'
# Secure cleanup
rm -f "$DIAG_TITLE" "$DIAG_BODY" "$DIAG_PAYLOAD"
```
**3. If no credentials found, save report locally:**
```bash
REPORT_FILE=$(mktemp -p /tmp -m 600 diagnostic-report-XXXXXX.md)
cat > "$REPORT_FILE" << 'DIAGNOSTIC_EOF'
[Paste the full issue content from Step 5 here]
DIAGNOSTIC_EOF
echo "Report saved to: $REPORT_FILE"
```
Then inform the user:
```
MCP tools are unavailable and no Gitea credentials found at ~/.config/claude/gitea.env.
Diagnostic report saved to: [REPORT_FILE]
To create the issue manually:
1. Configure credentials: See docs/CONFIGURATION.md
2. Or create issue directly at: http://gitea.hotserv.cloud/[MARKETPLACE_REPO]/issues/new
```
### Step 7: Report to User
Display summary:
```
Debug Report Complete
=====================
Project: [PROJECT_REPO]
Tests Run: 5
Passed: [N]
Failed: [N]
Failed Tools:
- [tool1]: [brief error]
- [tool2]: [brief error]
Issue Created: [issue URL]
Next Steps:
1. Switch to marketplace repo: cd [marketplace path]
2. Run: /debug-review
3. Select issue #[N] to investigate
```
## DO NOT
- **DO NOT** attempt to fix anything - only report
- **DO NOT** create issues if all tests pass (just report success)
- **DO NOT** skip any diagnostic test
- **DO NOT** call MCP tools without the `repo` parameter
- **DO NOT** ask user questions during execution - run autonomously
## If All Tests Pass
If all 5 tests pass, report success without creating an issue:
```
Debug Report Complete
=====================
Project: [PROJECT_REPO]
Tests Run: 5
Passed: 5
Failed: 0
All diagnostics passed. No issues to report.
If you're experiencing a specific problem, please describe it
and I can create a manual bug report.
```
## Troubleshooting
**PROJMAN_MARKETPLACE_REPO not configured**
- Ask user: "What is the marketplace repository? (e.g., personal-projects/leo-claude-mktplace)"
- Store for this session and remind user to add to .env
**Cannot detect project repository**
- Check if in a git repository: `git rev-parse --git-dir`
- If not a git repo, ask user for the repository path
**MCP tools not available**
- Use the curl fallback in Step 6, Option B
- Requires Gitea credentials at `~/.config/claude/gitea.env`
- If no credentials, report will be saved locally for manual submission

View File

@@ -0,0 +1,394 @@
---
description: Investigate diagnostic issues and propose fixes with human approval
---
# Debug Review
Investigate diagnostic issues created by `/debug-report`, read relevant code, and propose fixes with human approval at each step.
## CRITICAL: This Command Requires Human Approval
This command has THREE mandatory approval gates. You MUST stop and wait for user confirmation at each gate before proceeding.
## Execution Steps
### Step 1: Detect Repository
Run Bash to get the current repository:
```bash
git remote get-url origin
```
Parse to extract `REPO_NAME` in `owner/repo` format.
### Step 2: Fetch Diagnostic Issues
```
mcp__plugin_projman_gitea__list_issues(
repo=REPO_NAME,
state="open",
labels=["Source: Diagnostic"]
)
```
If no issues with that label, try without label filter and look for issues with "[Diagnostic]" in title.
### Step 3: Display Issue List
Show the user available issues:
```
Debug Review
============
Open Diagnostic Issues:
#80 - [Diagnostic] get_labels fails without repo parameter
Created: 2026-01-21 | Labels: Type: Bug, Source: Diagnostic
#77 - [Diagnostic] MCP tools require explicit repo parameter
Created: 2026-01-21 | Labels: Type: Bug, Source: Diagnostic
No diagnostic issues? Showing recent bugs:
#75 - [Bug] Some other issue
Created: 2026-01-20
```
### Step 4: User Selects Issue
Use AskUserQuestion:
```
Which issue would you like to investigate?
Options: [List issue numbers]
```
Wait for user selection.
### Step 5: Fetch Full Issue Details
```
mcp__plugin_projman_gitea__get_issue(repo=REPO_NAME, issue_number=SELECTED)
```
### Step 6: Parse Diagnostic Report
Extract from the issue body:
1. **Failed Tools**: Which MCP tools failed
2. **Error Messages**: Exact error text
3. **Hypothesis**: Reporter's analysis
4. **Suggested Investigation**: Files to check
5. **Project Context**: Repo, branch, cwd where error occurred
If the issue doesn't follow the diagnostic template, extract what information is available.
### Step 7: Map Errors to Code Files
Use this mapping to identify relevant files:
**By Tool Name:**
| Tool | Primary Files |
|------|---------------|
| `validate_repo_org` | `mcp-servers/gitea/mcp_server/gitea_client.py` |
| `get_labels` | `mcp-servers/gitea/mcp_server/tools/labels.py` |
| `suggest_labels` | `mcp-servers/gitea/mcp_server/tools/labels.py` |
| `list_issues` | `mcp-servers/gitea/mcp_server/tools/issues.py` |
| `create_issue` | `mcp-servers/gitea/mcp_server/tools/issues.py` |
| `list_milestones` | `mcp-servers/gitea/mcp_server/gitea_client.py` |
**By Error Pattern:**
| Error Contains | Check Files |
|----------------|-------------|
| "owner/repo format" | `config.py`, `gitea_client.py` |
| "404" + "orgs" | `gitea_client.py` (is_org_repo method) |
| "401", "403" | `config.py` (token loading) |
| "No repository" | Command `.md` file (repo detection step) |
**By Command:**
| Command | Documentation File |
|---------|-------------------|
| `/labels-sync` | `plugins/projman/commands/labels-sync.md` |
| `/sprint-plan` | `plugins/projman/commands/sprint-plan.md` |
| `/sprint-start` | `plugins/projman/commands/sprint-start.md` |
| `/debug-report` | `plugins/projman/commands/debug-report.md` |
### Step 8: Read Relevant Files (MANDATORY)
You MUST read the identified files before proposing any fix.
For each relevant file:
1. Read the file using the Read tool
2. Find the specific function/method mentioned in the error
3. Understand the code path that leads to the error
4. Note any related code that might be affected
Display snippets of relevant code to the user:
```
Reading relevant files...
┌─ mcp-servers/gitea/mcp_server/tools/labels.py (lines 29-40) ────────┐
│ │
│ async def get_labels(self, repo: Optional[str] = None): │
│ target_repo = repo or self.gitea.repo │
│ if not target_repo or '/' not in target_repo: │
│ raise ValueError("Use 'owner/repo' format...") │
│ │
└──────────────────────────────────────────────────────────────────────┘
```
### Step 9: Present Investigation Summary
Summarize what you found:
```
Investigation Summary
=====================
ISSUE: #80 - get_labels fails without repo parameter
FAILED TOOLS:
• get_labels - "Use 'owner/repo' format"
CODE ANALYSIS:
1. labels.py:get_labels() requires repo parameter
- Line 30: `target_repo = repo or self.gitea.repo`
- Line 31-32: Raises ValueError if no repo
2. labels-sync.md documents Step 1 for repo detection
- Lines 13-26: Instructs to run `git remote get-url origin`
- This step may not be followed by executing Claude
ROOT CAUSE HYPOTHESIS:
The command documentation (labels-sync.md) correctly instructs
repo detection, but the executing Claude may be skipping Step 1
and calling MCP tools directly.
Evidence:
• Error indicates repo parameter was not passed
• labels-sync.md has correct instructions
• MCP server cannot auto-detect (sandboxed environment)
LIKELY FIX:
Option A: Make Step 1 more prominent in labels-sync.md
Option B: Add validation that repo was detected before proceeding
Option C: [Other based on analysis]
```
## APPROVAL GATE 1
```
Does this analysis match your understanding of the problem?
[Y] Yes, proceed to propose fix
[N] No, let me clarify
[R] Read more files first
```
**STOP HERE AND WAIT FOR USER RESPONSE**
Do NOT proceed until user approves.
### Step 10: Propose Fix Approach
Based on the analysis, propose a specific fix:
```
Proposed Fix
============
APPROACH: [A/B/C from above]
CHANGES NEEDED:
1. File: plugins/projman/commands/labels-sync.md
Change: Add warning box after Step 1 emphasizing repo must be detected
2. File: [if applicable]
Change: [description]
RATIONALE:
[Explain why this fix addresses the root cause]
RISKS:
[Any potential issues with this approach]
```
## APPROVAL GATE 2
```
Proceed with this fix approach?
[Y] Yes, implement it
[N] No, try different approach
[M] Modify the approach (tell me what to change)
```
**STOP HERE AND WAIT FOR USER RESPONSE**
Do NOT implement until user approves.
### Step 11: Implement Fix
Only after user approves:
1. Create feature branch:
```bash
git checkout -b fix/issue-[NUMBER]-[brief-description]
```
2. Make the code changes using Edit tool
3. Run relevant tests if they exist:
```bash
cd mcp-servers/gitea && .venv/bin/python -m pytest tests/ -v
```
4. Show the changes to user:
```bash
git diff
```
### Step 12: Present Changes
```
Changes Implemented
===================
Branch: fix/issue-80-labels-sync-instructions
Files Modified:
• plugins/projman/commands/labels-sync.md (+15, -3)
Diff Summary:
[Show git diff output]
Test Results:
• 23 passed, 0 failed (or N/A if no tests)
```
## APPROVAL GATE 3
```
Create PR with these changes?
[Y] Yes, create PR
[N] No, I want to modify something
[D] Discard changes
```
**STOP HERE AND WAIT FOR USER RESPONSE**
Do NOT create PR until user approves.
### Step 13: Create PR
Only after user approves:
1. Commit changes:
```bash
git add -A
git commit -m "fix: [description]
[Longer explanation]
Fixes #[ISSUE_NUMBER]
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"
```
2. Push branch:
```bash
git push -u origin fix/issue-[NUMBER]-[brief-description]
```
3. Create PR via API or MCP tools
4. **Switch back to development branch** (required for MCP issue operations):
```bash
git checkout development
```
5. Add comment to original issue:
```
mcp__plugin_projman_gitea__add_comment(
repo=REPO_NAME,
issue_number=ISSUE_NUMBER,
comment="Fix proposed in PR #[PR_NUMBER]\n\nChanges:\n- [summary]\n\nPlease test after merge and report back."
)
```
### Step 14: Report Completion
```
Debug Review Complete
=====================
Issue: #80 - get_labels fails without repo parameter
Status: Fix Proposed
PR Created: #81 - fix: improve labels-sync repo detection instructions
URL: http://gitea.hotserv.cloud/.../pulls/81
Next Steps:
1. Review and merge PR #81
2. In test project, pull latest plugin version
3. Run /debug-report to verify fix
4. If passing, close issue #80
```
## DO NOT
- **DO NOT** skip reading relevant files - this is MANDATORY
- **DO NOT** proceed past approval gates without user confirmation
- **DO NOT** guess at fixes without evidence from code
- **DO NOT** close issues - let user verify fix works first
- **DO NOT** commit directly to development or main branches
## If Investigation Finds No Bug
Sometimes investigation reveals the issue is:
- User error (didn't follow documented steps)
- Configuration issue (missing .env vars)
- Already fixed in a newer version
In this case:
```
Investigation Summary
=====================
FINDING: This does not appear to be a code bug.
ANALYSIS:
[Explanation of what you found]
RECOMMENDATION:
[ ] Close issue as "not a bug" - user error
[ ] Close issue as "duplicate" of #[X]
[ ] Add documentation to prevent confusion
[ ] Other: [specify]
Would you like me to add a comment explaining this finding?
```
## Error-to-File Quick Reference
```
Error Message → File to Check
─────────────────────────────────────────────────────────────────
"Use 'owner/repo' format" → config.py, gitea_client.py
"404 Client Error.*orgs" → gitea_client.py (_is_organization)
"No repository specified" → Command .md file (Step 1)
"401 Unauthorized" → config.py (token loading)
"labels not found" → labels.py, gitea_client.py
"create local.*file" → Command .md file (DO NOT section)
```

View File

@@ -4,45 +4,92 @@ description: Fetch and validate label taxonomy from Gitea, create missing requir
# Sync Label Taxonomy from Gitea # Sync Label Taxonomy from Gitea
This command fetches the current label taxonomy from Gitea (organization + repository labels), validates that required labels exist, and creates any missing ones. Labels are fetched dynamically - no local files are created or modified. This command fetches the current label taxonomy from Gitea (organization + repository labels), validates that required labels exist, and creates any missing ones.
## Why Label Sync Matters ## CRITICAL: Execution Steps
The label taxonomy is **dynamic** - new labels may be added to Gitea over time: You MUST follow these steps in order. Do NOT skip any step.
- Organization-level labels (shared across all repos)
- Repository-specific labels (unique to this project)
**Dynamic approach:** Never hardcode labels. Always fetch from Gitea and adapt suggestions accordingly. ### Step 1: Detect Repository from Git Remote
## What This Command Does Run this Bash command to get the git remote URL:
1. **Auto-Detect Repository** - Automatically detects repo from git remote (or uses explicit `repo` param) ```bash
2. **Check Owner Type** - Determines if owner is organization or user account git remote get-url origin
3. **Fetch Current Labels** - Uses `get_labels` MCP tool to fetch all labels (org + repo) ```
4. **Display Current Taxonomy** - Shows organization and repository label counts
5. **Identify Missing Required Labels** - Checks for required labels (Type/*, Priority/*, etc.)
6. **Create Missing Labels** - Automatically creates any missing required labels
7. **Report Status** - Summarizes what was found and created
**Note:** This command executes autonomously without user prompts. It fetches labels, reports findings, and creates missing labels automatically. Parse the output to extract `owner/repo`:
- SSH format `ssh://git@host:port/owner/repo.git` → extract `owner/repo`
- SSH short `git@host:owner/repo.git` → extract `owner/repo`
- HTTPS `https://host/owner/repo.git` → extract `owner/repo`
## MCP Tools Used Store this as `REPO_NAME` for all subsequent MCP calls.
**Gitea Tools:** ### Step 2: Validate Repository Organization
- `get_labels` - Fetch all labels (organization + repository)
- `create_label` - Create missing required labels
- `validate_repo_org` - Verify repository belongs to organization
## Required Label Categories Call MCP tool with the detected repo:
At minimum, these label categories must exist: ```
mcp__plugin_projman_gitea__validate_repo_org(repo=REPO_NAME)
```
This determines if the owner is an organization or user account.
### Step 3: Fetch Labels from Gitea
Call MCP tool with the detected repo:
```
mcp__plugin_projman_gitea__get_labels(repo=REPO_NAME)
```
This returns both organization labels (if org-owned) and repository labels.
### Step 4: Display Current Taxonomy
Show the user:
- Total organization labels count
- Total repository labels count
- Labels grouped by category (Type/*, Priority/*, etc.)
### Step 5: Check Required Labels
Verify these required label categories exist:
- **Type/***: Bug, Feature, Refactor, Documentation, Test, Chore - **Type/***: Bug, Feature, Refactor, Documentation, Test, Chore
- **Priority/***: Low, Medium, High, Critical - **Priority/***: Low, Medium, High, Critical
- **Complexity/***: Simple, Medium, Complex - **Complexity/***: Simple, Medium, Complex
- **Efforts/***: XS, S, M, L, XL - **Effort/***: XS, S, M, L, XL (note: may be "Effort" or "Efforts")
If any required labels are missing, the command will offer to create them. ### Step 6: Create Missing Labels (if any)
For each missing required label, call:
```
mcp__plugin_projman_gitea__create_label(repo=REPO_NAME, name="Type: Bug", color="d73a4a")
```
Use the label format that matches existing labels in the repo (slash `/` or colon-space `: `).
### Step 7: Report Results
Summarize what was found and created.
## DO NOT
- **DO NOT** call MCP tools without the `repo` parameter - they will fail
- **DO NOT** create any local files - this command only interacts with Gitea
- **DO NOT** ask the user questions - execute autonomously
- **DO NOT** create a "labels reference file" - labels are fetched dynamically from Gitea
## MCP Tools Used
All tools require the `repo` parameter in `owner/repo` format:
| Tool | Purpose |
|------|---------|
| `validate_repo_org(repo=...)` | Check if owner is organization or user |
| `get_labels(repo=...)` | Fetch all labels (org + repo) |
| `create_label(repo=..., name=..., color=...)` | Create missing labels |
## Expected Output ## Expected Output
@@ -50,7 +97,7 @@ If any required labels are missing, the command will offer to create them.
Label Taxonomy Sync Label Taxonomy Sync
=================== ===================
Auto-detecting repository from git remote... Detecting repository from git remote...
Repository: personal-projects/your-repo-name Repository: personal-projects/your-repo-name
Owner type: Organization Owner type: Organization
@@ -62,189 +109,51 @@ Current Label Taxonomy:
- Total: 43 labels - Total: 43 labels
Organization Labels by Category: Organization Labels by Category:
Agent/*: 2 labels
Complexity/*: 3 labels
Efforts/*: 5 labels
Priority/*: 4 labels
Risk/*: 3 labels
Source/*: 4 labels
Type/*: 6 labels Type/*: 6 labels
Priority/*: 4 labels
Complexity/*: 3 labels
Effort/*: 5 labels
...
Repository Labels by Category: Repository Labels by Category:
Component/*: 9 labels Component/*: 9 labels
Tech/*: 7 labels Tech/*: 7 labels
Required Labels Check: Required Labels Check:
Type/*: 6/6 present Type/*: 6/6 present
Priority/*: 4/4 present Priority/*: 4/4 present
Complexity/*: 3/3 present Complexity/*: 3/3 present
Efforts/*: 5/5 present Effort/*: 5/5 present
All required labels present. Label taxonomy is ready for use. All required labels present. Label taxonomy is ready for use.
``` ```
## Label Taxonomy Structure ## Label Format Detection
Labels are organized by namespace: Labels may use different naming conventions:
- Slash format: `Type/Bug`, `Priority/High`
- Colon-space format: `Type: Bug`, `Priority: High`
**Organization Labels (28):** When creating missing labels, match the format used by existing labels in the repository.
- `Agent/*` (2): Agent/Human, Agent/Claude
- `Complexity/*` (3): Simple, Medium, Complex
- `Efforts/*` (5): XS, S, M, L, XL
- `Priority/*` (4): Low, Medium, High, Critical
- `Risk/*` (3): Low, Medium, High
- `Source/*` (4): Development, Staging, Production, Customer
- `Type/*` (6): Bug, Feature, Refactor, Documentation, Test, Chore
**Repository Labels (16):** ## Troubleshooting
- `Component/*` (9): Backend, Frontend, API, Database, Auth, Deploy, Testing, Docs, Infra
- `Tech/*` (7): Python, JavaScript, Docker, PostgreSQL, Redis, Vue, FastAPI
## Label Reference **Error: Use 'owner/repo' format**
- You forgot to pass the `repo` parameter to the MCP tool
- Go back to Step 1 and detect the repo from git remote
The plugin includes a static reference file at `skills/label-taxonomy/labels-reference.md` that documents the expected label taxonomy and suggestion logic. **Empty organization labels**
- If owner is a user account (not org), organization labels will be empty
- This is expected - user accounts only have repository-level labels
**Important:** This reference file is part of the plugin package and serves as documentation. The `/labels-sync` command fetches labels dynamically from Gitea - it does not modify the reference file. **Git remote not found**
- Ensure you're running in a directory with a git repository
**Dynamic Approach:** Labels are always fetched fresh from Gitea using `get_labels`. The reference file provides context for the `suggest_labels` logic but is not the source of truth. - Check that the `origin` remote is configured
Example reference file structure:
```markdown
# Label Taxonomy Reference
Last synced: 2025-01-18 14:30 UTC
Source: Gitea (bandit/your-repo-name)
## Organization Labels (28)
### Agent (2)
- Agent/Human - Work performed by human developers
- Agent/Claude - Work performed by Claude Code
### Type (6)
- Type/Bug - Bug fixes and error corrections
- Type/Feature - New features and enhancements
- Type/Refactor - Code restructuring and architectural changes
- Type/Documentation - Documentation updates
- Type/Test - Testing-related work
- Type/Chore - Maintenance and tooling tasks
...
## Repository Labels (16)
### Component (9)
- Component/Backend - Backend service code
- Component/Frontend - User interface code
- Component/API - API endpoints and contracts
...
## Suggestion Logic
When suggesting labels, consider:
**Type Detection:**
- Keywords "bug", "fix", "error" -> Type/Bug
- Keywords "feature", "add", "implement" -> Type/Feature
- Keywords "refactor", "extract", "restructure" -> Type/Refactor
...
```
## When to Run ## When to Run
Run `/labels-sync` when: Run `/labels-sync` when:
- Setting up the plugin for the first time - Setting up the plugin for the first time
- You notice missing labels in suggestions - You notice missing labels in suggestions
- New labels are added to Gitea (announced by team) - New labels are added to Gitea
- Quarterly maintenance (check for changes)
- After major taxonomy updates - After major taxonomy updates
## Integration with Other Commands
The updated taxonomy is used by:
- `/sprint-plan` - Planner agent uses `suggest_labels` with current taxonomy
- All commands that create or update issues
## Example Usage
**Example 1: All labels present**
```
User: /labels-sync
Auto-detecting repository...
Repository: personal-projects/my-project
Owner type: Organization
Fetching labels from Gitea...
Current Label Taxonomy:
- Organization Labels: 27
- Repository Labels: 16
- Total: 43 labels
Required Labels Check:
Type/*: 6/6 present
Priority/*: 4/4 present
Complexity/*: 3/3 present
Efforts/*: 5/5 present
All required labels present. Label taxonomy is ready for use.
```
**Example 2: Missing required labels (auto-created)**
```
User: /labels-sync
Auto-detecting repository...
Repository: personal-projects/new-project
Owner type: User (no organization labels available)
Fetching labels from Gitea...
Current Label Taxonomy:
- Organization Labels: 0 (user account - no org labels)
- Repository Labels: 3
- Total: 3 labels
Required Labels Check:
Type/*: 0/6 - MISSING: Bug, Feature, Refactor, Documentation, Test, Chore
Priority/*: 0/4 - MISSING: Low, Medium, High, Critical
Complexity/*: 0/3 - MISSING: Simple, Medium, Complex
Efforts/*: 0/5 - MISSING: XS, S, M, L, XL
Creating missing required labels...
Created: Type/Bug (#d73a4a)
Created: Type/Feature (#0075ca)
... (18 total labels created)
Label taxonomy initialized. All required labels now present.
```
## Troubleshooting
**Error: Cannot fetch labels from Gitea**
- Check your Gitea configuration in `~/.config/claude/gitea.env`
- Verify your API token has `read:org` and `repo` permissions
- Ensure you're connected to the network
**Error: Use 'owner/repo' format**
- Ensure you're running from a directory with a git remote configured
- Or pass the `repo` parameter explicitly: `GITEA_REPO=owner/repo` in `.env`
**Organization labels empty for org-owned repo**
- Verify the organization has labels configured in Gitea
- Check if the owner is truly an organization (not a user account)
- User accounts don't have organization-level labels
**User-owned repo (no org labels)**
- This is expected behavior - user accounts can only have repository-level labels
- The plugin will work with repo labels only and create missing required labels
## Best Practices
1. **Sync at sprint start** - Ensure labels are current before planning
2. **Review changes** - Always review what changed before confirming
3. **Create missing required labels** - Don't skip this step
4. **Update planning** - After sync, consider if new labels affect current sprint
5. **Communicate changes** - Let team know when new labels are available

View File

@@ -1,14 +1,9 @@
{ {
"hooks": { "hooks": {
"SessionStart": [ "SessionStart": [
{
"matcher": ".*",
"hooks": [
{ {
"type": "prompt", "type": "prompt",
"prompt": "Silently check if the project's git remote matches the .env configuration:\n\n1. Read .env to get GITEA_ORG and GITEA_REPO values\n2. Run: git remote get-url origin\n3. Extract org and repo from the remote URL\n4. Compare with .env values\n\nIf mismatch detected:\n- Display a brief warning: 'Repository location mismatch detected. Git remote shows <org>/<repo> but .env has <old-org>/<old-repo>. Run /project-sync to update.'\n\nIf match or no .env exists: Stay silent, do not interrupt.\n\nThis check should be quick and non-blocking." "prompt": "[projman] Perform silent startup checks:\n\n1. Check if MCP venvs exist at the installed marketplace location. If the marketplace is installed but venvs are missing, warn the user: '[projman] MCP venvs missing - run setup.sh from installed marketplace location'.\n\n2. Check if the project git remote matches .env configuration (GITEA_ORG/GITEA_REPO). If mismatch, warn: '[projman] Git remote mismatch - run /project-sync'.\n\nStay silent if all checks pass or not applicable. Be quick and non-blocking."
}
]
} }
] ]
} }

37
scripts/check-venv.sh Executable file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
#
# check-venv.sh - Check if MCP server venvs exist in installed marketplace
#
# Usage: ./scripts/check-venv.sh
#
# Exit codes:
# 0 - All venvs exist (or not installed via marketplace)
# 1 - Venvs missing, needs setup
#
# This script is designed to be called from SessionStart hooks
# to enable self-healing MCP server setup.
set -euo pipefail
# Installed marketplace location
MKTPLACE="$HOME/.claude/plugins/marketplaces/leo-claude-mktplace"
# If not installed via marketplace, exit silently
if [[ ! -d "$MKTPLACE" ]]; then
exit 0
fi
# Check if gitea venv exists
if [[ ! -f "$MKTPLACE/mcp-servers/gitea/.venv/bin/python" ]]; then
echo "SETUP_NEEDED"
exit 1
fi
# Check if netbox venv exists
if [[ ! -f "$MKTPLACE/mcp-servers/netbox/.venv/bin/python" ]]; then
echo "SETUP_NEEDED"
exit 1
fi
# All good
exit 0