125 Commits

Author SHA1 Message Date
3227d2d618 chore: release v3.2.0
Version 3.2.0 includes:
- New features: netbox device params, claude-config-maintainer auto-enforce
- Bug fixes: cmdb-assistant schemas, netbox tool names, API URLs
- All hooks converted to command type
- Versioning workflow with release script

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 13:08:45 -05:00
1dfbffcf38 feat: add versioning workflow with release script
- Add scripts/release.sh for consistent version releases
- Fix CHANGELOG.md: consolidate all changes under [Unreleased]
- Update CLAUDE.md with comprehensive versioning documentation
- Include all commits since v3.1.1 in [Unreleased] section

The release script ensures version consistency across:
- Git tags
- README.md title
- marketplace.json version
- CHANGELOG.md sections

Addresses #143

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 13:08:06 -05:00
98df35a33e Merge pull request 'fix(cmdb-assistant): complete MCP tool schemas for update operations' (#138) from fix/issue-137-update-schema-completeness into development
Reviewed-on: #138
2026-01-24 17:22:00 +00:00
70d6963d0d fix(cmdb-assistant): complete MCP tool schemas for update operations
Expand parameter schemas for 15 update tools that previously only exposed
the 'id' field. The underlying Python implementation already supported
all fields via **kwargs, but Claude couldn't discover available parameters.

Updated tools:
- virtualization: update_virtual_machine, update_cluster
- dcim: update_site, update_location, update_rack, update_manufacturer,
        update_device_type, update_device_role, update_platform,
        update_interface, update_cable
- ipam: update_vrf, update_prefix, update_ip_address, update_vlan

Each tool now exposes all commonly-used optional fields matching the
NetBox API, following the pattern established by dcim_update_device.

Fixes #137

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 12:18:57 -05:00
54c6694117 Merge pull request 'fix(netbox): shorten tool names to meet 64-char API limit' (#134) from fix/netbox-tool-name-length into development
Reviewed-on: #134
2026-01-24 16:47:48 +00:00
2402f88daf fix(netbox): shorten tool names to meet 64-char API limit
Claude API has a 64-character limit on tool names. Claude Code uses a
36-character prefix (mcp__plugin_cmdb-assistant_netbox__), leaving only
28 characters for the actual tool name.

Shortened 33 tools that exceeded this limit:
- virtualization_* -> virt_* (19 tools)
- circuits_*_circuit_type* -> circ_*_type* (3 tools)
- circuits_*_circuit_termination* -> circ_*_termination* (3 tools)
- wireless_*_wireless_* -> wlan_* (8 tools)

Added TOOL_NAME_MAP to route shortened names to original method names.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 11:45:34 -05:00
1cf1dbefb8 Merge pull request 'fix(cmdb-assistant): correct NetBox API URL format in setup wizard' (#132) from fix/issue-126-netbox-url-format into development
Reviewed-on: #132
2026-01-23 22:54:14 +00:00
dafa8db8bb fix(cmdb-assistant): correct NetBox API URL format in setup wizard
The /initial-setup command was generating NETBOX_API_URL without the
/api suffix, causing all MCP tools to fail with JSON parsing errors.

Changes:
- Update example URL to include /api suffix
- Add instruction to auto-append /api if user forgets
- Fix validation test to be consistent with actual usage

Root cause: Setup asked for base URL (https://netbox.company.com) but
NetBoxClient expects API URL (https://netbox.company.com/api). The
validation test incorrectly added /api/ making it pass, while the
actual MCP server failed.

Fixes #126

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 17:52:23 -05:00
65e79efb24 Merge pull request 'fix(gitea,projman): type safety for create_label_smart, curl-based debug-report' (#124) from fix/issue-123-mcp-tool-failures into development
Reviewed-on: #124
2026-01-23 21:47:51 +00:00
5ffc13b635 fix(gitea,projman): type safety for create_label_smart, curl-based debug-report
Fixes multiple issues from diagnostic #123:

1. create_label_smart type safety (labels.py)
   - Add isinstance(result, dict) checks after API calls
   - Return structured error dict if API returns unexpected type
   - Prevents "list indices must be integers" crash

2. debug-report always uses curl with labels
   - Remove MCP option - always use curl for marketplace issues
   - Add label ID fetching step (Source/Diagnostic, Type/Bug)
   - Include labels in curl POST payload
   - Avoids branch protection restrictions on main branch

Fixes #123

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 16:46:37 -05:00
50bfd20fd4 Merge pull request 'fix(netbox): add diagnostic logging for JSON parse errors' (#121) from fix/issue-120-json-parse-diagnostics into development
Reviewed-on: #121
2026-01-23 21:07:52 +00:00
c14f1f46cd fix(netbox): add diagnostic logging for JSON parse errors
When NetBox MCP tools fail with JSON decode errors, the error message
now includes:
- HTTP status code
- Response content length
- Preview of actual content received (first 200 bytes)

This helps diagnose transient issues like network timeouts or
incomplete responses that result in cryptic "Expecting value" errors.

Fixes #120

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 16:06:51 -05:00
52c8371f4a Merge pull request 'feat(netbox): add platform and primary_ip params to device tools' (#118) from feat/netbox-device-update-params into development
Reviewed-on: #118
2026-01-23 19:47:10 +00:00
f8d6d42150 feat(netbox): add platform and primary_ip params to device tools
Expose additional parameters in dcim_create_device and dcim_update_device
MCP tools that were already supported by the backend but not exposed:

dcim_create_device:
- platform, primary_ip4, primary_ip6, asset_tag, description, comments

dcim_update_device:
- platform, primary_ip4, primary_ip6, serial, asset_tag, site, rack,
  position, description, comments

This enables setting the platform (OS) and primary IP address when
creating or updating devices in NetBox.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 14:46:09 -05:00
469487f6ed Merge pull request 'fix(labels): add duplicate check before creating labels' (#116) from fix/labels-duplicate-check into development
Reviewed-on: #116
2026-01-23 17:49:57 +00:00
7a2966367d feat(claude-config-maintainer): auto-enforce mandatory behavior rules
SessionStart hook checks if CLAUDE.md has mandatory rules.
If missing, adds them automatically.

Rules enforced:
- Check everything when user asks
- Believe user when they say something's wrong
- Never say "done" without verification
- Show exactly what user asks for

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:46:18 -05:00
0466b299a7 fix: add mandatory behavior rules and verification
- Add MANDATORY BEHAVIOR RULES to CLAUDE.md (read every session)
- Rules: check everything, believe user, verify before saying done
- post-update.sh now clears plugin cache
- verify-hooks.sh checks all locations for prompt hooks

These rules prevent wasted user time from AI overconfidence.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:44:27 -05:00
b34304ed57 fix: add cache clearing and hook verification scripts
- post-update.sh now clears plugin cache automatically
- verify-hooks.sh checks ALL locations for prompt hooks
- Prevents cached old hooks from overriding fixed hooks

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:41:33 -05:00
96963531fc fix(labels): add duplicate check before creating labels
create_label_smart now checks if label already exists before creating.
- Checks both org and repo labels
- Handles format variations (Type/Bug vs Type: Bug)
- Returns {skipped: true} if label already exists
- Prevents duplicate label creation errors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:36:00 -05:00
4c9a7c55ae Merge pull request 'development' (#115) from development into main
Reviewed-on: #115
2026-01-23 17:25:18 +00:00
8a75203251 Merge pull request 'fix: hooks command type conversion + org-level labels' (#114) from fix/hooks-and-labels-v3.2.0 into development
Reviewed-on: #114
2026-01-23 17:24:08 +00:00
da6e81260e fix(hooks): convert ALL hooks to command type with proper prefixes
ALL hooks now use command type (bash scripts) instead of prompt type.
Prompt hooks are unreliable - model ignores instructions.

Changes:
- projman: SessionStart → startup-check.sh with [projman] prefix
- pr-review: SessionStart → startup-check.sh with [pr-review] prefix
- project-hygiene: cleanup.sh now has [project-hygiene] prefix
- doc-guardian: already fixed (notify.sh with [doc-guardian] prefix)
- code-sentinel: already fixed (security-check.sh with [code-sentinel] prefix)

All hook output now guaranteed to have plugin name prefix.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:18:40 -05:00
e1f1335655 fix(code-sentinel): replace prompt hook with command hook
Same fix as doc-guardian - prompt hooks unreliable.
Command hook guarantees exact behavior.

- Add security-check.sh that skips config/doc files silently
- Only checks code files for hardcoded secrets
- Outputs with [code-sentinel] prefix

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:16:06 -05:00
b017db83a1 feat(gitea): add organization-level label creation
- Add create_org_label() method to gitea_client.py for org-level labels
- Add create_label_smart() to labels.py that auto-detects correct level
- Register both tools in server.py
- Update labels-sync.md to use create_label_smart

Label level detection:
- Organization: Type/*, Priority/*, Complexity/*, Effort/*, Risk/*, Source/*, Agent/*
- Repository: Component/*, Tech/*

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:15:14 -05:00
bc136fab7e docs: update CHANGELOG with actual fix for #110
Prompt hook approach didn't work - Claude ignores instructions.
Real fix was switching to command hook type.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 11:51:31 -05:00
6c24bcbb91 fix(doc-guardian): replace prompt hook with command hook
Prompt hooks are unreliable - Claude ignores instructions and generates
verbose analysis despite explicit FORBIDDEN rules. Command hooks guarantee
the exact output we want.

- Add notify.sh script that only outputs for config file changes
- Change hooks.json from prompt type to command type
- Script exits silently for non-config files (no blocking)

Fixes #110

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 11:48:54 -05:00
11a05799d3 docs: sync documentation with codebase
- CLAUDE.md: Update version 3.0.1 → 3.1.2, projman 3.0.0 → 3.1.0, command count 12 → 13
- README.md: Add debug-report/debug-review to projman commands, add DEBUGGING-CHECKLIST.md to docs table
- CANONICAL-PATHS.md: Update version, remove non-existent docs/workflows/, add COMMANDS-CHEATSHEET.md
- projman/README.md: Fix "Three-Agent" → "Four-Agent", update architecture to show symlink
- pr-review/README.md: Add missing setup commands (initial-setup, project-init, project-sync)
- cmdb-assistant/README.md: Add initial-setup.md to architecture
- project-hygiene/README.md: Fix invalid hook event name (task-completed → PostToolUse)
- doc-guardian/plugin.json: Add missing commands field
- code-sentinel/plugin.json: Add missing commands field

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 11:25:14 -05:00
403271dc0c Merge pull request 'development' (#112) from development into main
Reviewed-on: #112
2026-01-23 16:10:12 +00:00
cc4abf67b9 Merge pull request 'fix: protected branch detection and non-blocking hooks' (#111) from fix/issue-109-110-hooks-and-protected-branch into development
Reviewed-on: #111
2026-01-23 16:08:38 +00:00
35cf20e02d fix: protected branch detection and non-blocking hooks
- Add protected branch detection to /commit command (Step 1)
- Warn users before committing to protected branches
- Offer to create feature branch automatically
- Rewrite doc-guardian hook to be truly non-blocking
- Enforce strict [plugin-name] prefix in all hook outputs
- Add forbidden words list to prevent accidental blocking

Fixes #109, #110

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 11:08:00 -05:00
5209f82efb Merge pull request 'development' (#108) from development into main
Reviewed-on: #108
2026-01-22 23:03:57 +00:00
1f55387e9e Merge pull request 'feat: enhance debug commands with sprint awareness and lessons learned' (#107) from feat/debug-commands-enhancements into development
Reviewed-on: #107
2026-01-22 23:03:34 +00:00
32bbca73ba feat: enhance debug commands with sprint awareness and lessons learned
Debug Report (/debug-report):
- Add Step 1.5: Sprint context detection based on branch and milestone
- Add Step 5: Smart labeling via suggest_labels MCP tool
- Update issue creation to support milestone association

Debug Review (/debug-review):
- Add Step 9.5: Search lessons learned before proposing fixes
- Add Step 15: Verify, close issue, and optionally capture lesson

Hooks:
- Simplify doc-guardian hook to be truly non-blocking (15 words max)
- Update code-sentinel to skip docs/config files entirely

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 18:02:09 -05:00
0e6999ea21 Merge pull request 'development' (#106) from development into main
Reviewed-on: #106
2026-01-22 21:17:18 +00:00
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
6cf3c1830c Merge pull request 'development' (#104) from development into main
Reviewed-on: #104
2026-01-22 20:02:06 +00: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
51ef10633b Merge pull request 'fix: remove conflicting reference file instruction from labels-sync' (#74) from fix/labels-sync-no-reference-file into development 2026-01-21 21:50:14 +00:00
83094598c5 fix: remove conflicting reference file instruction from labels-sync
The command description still said "updates the local reference file"
which caused the agent to prompt about creating a reference file.

Updated to clarify: no local files are created or modified.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:47:57 -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
4f3560d121 Merge pull request 'fix: detect project directory correctly for git remote parsing' (#71) from fix/project-directory-detection into development 2026-01-21 21:38:24 +00:00
d5e521a759 fix: detect project directory correctly for git remote parsing
The MCP server runs with cwd set to the plugin directory, not the
user's project directory. This caused git remote auto-detection to
fail because it was looking at the wrong directory.

Changes:
- Added _find_project_directory() method with multiple strategies:
  1. CLAUDE_PROJECT_DIR environment variable
  2. PWD environment variable (if it has .git or .env)
  3. Current working directory (if it has .git or .env)
- Updated _detect_repo_from_git() to accept project_dir parameter
- Added 3 new tests for project directory detection

Fixes #70

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:25:14 -05: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
71efa1aafa Merge pull request 'docs: clarify labels-sync command behavior' (#68) from docs/labels-sync-clarification into development 2026-01-21 21:14:14 +00:00
aa3ff016e2 docs: clarify labels-sync command behavior and remove misleading prompts
Updates labels-sync.md to:
- Remove [Y/n] prompts that suggested manual confirmation
- Clarify that execution is autonomous
- Document that labels are fetched dynamically (not from reference file)
- Update examples to show actual behavior
- Improve troubleshooting for common issues
- Add user-owned repo handling documentation

Related to #67

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:02:05 -05: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
d282a65fc6 Merge pull request 'fix: add repo auto-detection and improve org validation' (#65) from fix/repo-auto-detection-and-org-validation into development 2026-01-21 20:12:30 +00:00
ad56700059 fix: add repo auto-detection and improve org validation
1. Repo Auto-Detection (config.py):
   - Added _detect_repo_from_git() to parse git remote URL
   - Supports SSH, SSH short, HTTPS, HTTP URL formats
   - Falls back to git remote when GITEA_REPO not set

2. Organization Validation (gitea_client.py):
   - Changed is_org_repo() to use /orgs/{owner} endpoint
   - Added _is_organization() method for reliable org detection
   - Fixes issue where owner.type was null in Gitea API

3. Tests:
   - Added 6 tests for git URL parsing
   - Added 3 tests for org detection

Fixes #64

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 15:10:53 -05: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
feb86b059f Merge pull request 'fix: handle user-owned repos in get_labels (skip org labels)' (#62) from fix/user-owned-repo-labels into development
Merge pull request #62 from fix/user-owned-repo-labels
2026-01-21 19:47:10 +00:00
c23e84f965 fix: handle user-owned repos in get_labels (skip org labels)
The get_labels tool was failing with 404 for user-owned repositories
because it always queried /api/v1/orgs/{owner}/labels, which only
works for organizations.

Changes:
- labels.py: Check is_org_repo() before fetching org labels
- gitea_client.py: Same fix in _resolve_label_ids()
- test_labels.py: Added tests for both org and user-owned repos

Fixes #61

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 14:40:30 -05: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
53f1b9662f Merge pull request 'chore: bump version to v3.0.1' (#59) from chore/version-bump-3.0.1 into development 2026-01-21 17:01:34 +00:00
eeffb9e853 chore: bump version to v3.0.1
Updates version references in:
- README.md title
- CHANGELOG.md (new entry with all changes since v3.0.0)
- marketplace.json metadata
- CLAUDE.md project overview

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 12:00:39 -05:00
6c142a9710 Merge pull request 'development' (#57) from development into main
Reviewed-on: #57
2026-01-21 16:56:12 +00:00
f781c6f7b1 Merge pull request 'fix: rename env vars to match MCP expectations' (#58) from fix/env-variable-names into development 2026-01-21 16:54:59 +00:00
8228c20d47 fix: rename environment variables to match MCP server expectations
Gitea MCP server expects:
- GITEA_API_URL (not GITEA_URL) - must include /api/v1
- GITEA_API_TOKEN (not GITEA_TOKEN)

NetBox MCP server expects:
- NETBOX_API_URL (not NETBOX_URL) - must include /api
- NETBOX_API_TOKEN (not NETBOX_TOKEN)

Changes:
- Update all setup commands to use correct variable names
- Update docs/CONFIGURATION.md with correct variables and URL format
- Update scripts/setup.sh templates
- Update CLAUDE.md hybrid configuration reference
- Remove GITEA_ORG from system-level config (it's project-level)
- Fix API URL paths in curl commands (remove redundant /api/v1)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 11:53:04 -05:00
85953d8e1e Merge pull request 'fix: remove redundant manual setup from README' (#56) from fix/readme-simplify into development 2026-01-21 16:37:18 +00:00
f8b6131bfc fix: remove redundant manual setup from README
Manual setup instructions belong in docs/CONFIGURATION.md, not README.
README should only show the simple wizard path (/initial-setup).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 11:36:58 -05:00
cd3d4c69f0 Merge pull request 'fix: update README.md with new setup commands and correct GITEA_ORG placement' (#55) from fix/readme-setup-commands into development 2026-01-21 16:31:56 +00:00
7f6e0893dd fix: update README.md with new setup commands and correct GITEA_ORG placement
- Add /project-init and /project-sync commands to projman
- Add setup commands (/initial-setup, /project-init, /project-sync) to pr-review
- Add /initial-setup to cmdb-assistant
- Replace manual MCP setup instructions with /initial-setup wizard
- Move GITEA_ORG from system-level to project-level in examples
- Add COMMANDS-CHEATSHEET.md and UPDATING.md to documentation table
- Fix GITEA_ORG validation in projman initial-setup (was checking system config)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 11:30:04 -05:00
39105688a5 Merge pull request 'development' (#54) from development into main
Reviewed-on: #54
2026-01-21 16:24:26 +00:00
2a6b3df8e1 Merge pull request 'feat: add interactive setup wizard with API validation and mismatch detection' (#53) from feat/interactive-setup-wizard into development
Reviewed-on: #53
2026-01-21 16:24:02 +00:00
0c2fc8c0d9 feat: add interactive setup wizard with API validation and mismatch detection
Major improvements to plugin setup experience:

Setup Commands:
- Redesign /initial-setup as interactive wizard (MCP + system + project config)
- Add /project-init for quick project-only setup
- Add /project-sync for handling repository moves/renames
- Add Gitea API validation to auto-fill org/repo when verified

Configuration Changes:
- Move GITEA_ORG from system to project level (supports multi-org users)
- System config now only contains GITEA_URL and GITEA_TOKEN
- Project .env now contains GITEA_ORG and GITEA_REPO

Automation:
- Add SessionStart hook for projman and pr-review
- Automatically detects git remote vs .env mismatch
- Warns user to run /project-sync when mismatch found

Documentation:
- Unify configuration docs (remove duplicate in plugins/projman)
- Add flow diagrams to CONFIGURATION.md
- Add setup script review guidance to UPDATING.md
- Update COMMANDS-CHEATSHEET.md with new commands and hooks

Files added:
- plugins/projman/commands/project-init.md
- plugins/projman/commands/project-sync.md
- plugins/projman/hooks/hooks.json
- plugins/pr-review/commands/initial-setup.md
- plugins/pr-review/commands/project-init.md
- plugins/pr-review/commands/project-sync.md
- plugins/pr-review/hooks/hooks.json
- plugins/cmdb-assistant/commands/initial-setup.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 11:20:36 -05:00
b5144de0cf Merge pull request 'development' (#52) from development into main
Reviewed-on: #52
2026-01-21 15:15:35 +00:00
29c54279a9 Merge pull request 'fix: marketplace schema corrections for Claude Code compatibility' (#51) from fix/marketplace-schema into development
Reviewed-on: #51
2026-01-21 15:15:21 +00:00
178593f355 docs: add plugin installation verification guide
Explains how to verify plugins are installed since /plugin shows
"(no content)" which can confuse users. Includes test commands
for each plugin.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:19:44 -05:00
a70df64cae Merge pull request 'development' (#50) from development into main
Reviewed-on: #50
2026-01-21 02:50:35 +00:00
2a2ac5f85e Merge pull request 'fix: remove unsupported agents field from all plugin manifests' (#49) from fix/marketplace-schema into development
Reviewed-on: #49
2026-01-21 02:50:17 +00:00
e01ba74e84 fix: remove agents field from all plugin.json files
Claude Code v2.0.44 doesn't support the agents field in plugin manifests.
Removed from: clarity-assist, pr-review, projman, claude-config-maintainer, cmdb-assistant

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 21:48:39 -05:00
565540d0ba fix: remove agents field from git-flow plugin.json
Claude Code doesn't support the agents field in plugin manifests.
Commands-only manifest should work.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 21:47:36 -05:00
394c91f8cf Merge pull request 'development' (#48) from development into main
Reviewed-on: #48
2026-01-21 02:13:48 +00:00
89bfd98d9f Merge pull request 'fix: simplify plugin.json schema for commands/agents/skills' (#47) from fix/marketplace-schema into development
Reviewed-on: #47
2026-01-21 02:13:28 +00:00
5c9dd8d6e0 fix: simplify plugin.json schema for commands/agents/skills
Use directory paths instead of object arrays for:
- git-flow
- clarity-assist
- pr-review

Claude Code expects ["./commands/"] format, not detailed object arrays.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 21:11:46 -05:00
374912b463 Merge pull request 'development' (#46) from development into main
Reviewed-on: #46
2026-01-21 01:58:17 +00:00
debb91aa7e Merge pull request 'fix: correct marketplace.json schema for Claude Code compatibility' (#45) from fix/marketplace-schema into development
Reviewed-on: #45
2026-01-21 01:57:48 +00:00
40860c172e fix: correct marketplace.json schema for Claude Code compatibility
- mcpServers: use relative paths (./.mcp.json) instead of names
- hooks: use relative paths (./hooks/hooks.json) instead of event names
- Remove unsupported integrationFile field from all plugins

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 20:55:52 -05:00
50ebe83c0a Merge pull request 'development' (#44) from development into main
Reviewed-on: #44
2026-01-21 01:21:53 +00:00
7295345013 Merge pull request 'chore: rename marketplace to Leo Claude Marketplace' (#43) from chore/rename-marketplace into development
Reviewed-on: #43
2026-01-21 01:18:42 +00:00
a2502c708b chore: rename marketplace to Leo Claude Marketplace
Update all references from old names to new marketplace identity:
- support-claude-mktplace → leo-claude-mktplace (URLs)
- lm-claude-plugins → leo-claude-mktplace (repo name)
- Claude Code Marketplace → Leo Claude Marketplace (display name)

Files updated:
- Core docs (CLAUDE.md, README.md, CHANGELOG.md)
- Documentation (CANONICAL-PATHS, CONFIGURATION, UPDATING, COMMANDS-CHEATSHEET)
- Marketplace manifest and all 9 plugin.json files
- Plugin READMEs and MCP server READMEs
- Setup script and label taxonomy reference

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 20:17:27 -05:00
4ede59e89a Merge pull request 'development' (#42) from development into main
Reviewed-on: #42
2026-01-20 22:31:17 +00:00
3017e4c097 Merge pull request 'Update README.md' (#41) from fix/marketplace-name-change into development
Reviewed-on: #41
2026-01-20 22:30:56 +00:00
de7675a649 Update README.md 2026-01-20 22:30:37 +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
0a8af05f9c Merge pull request 'docs: add plugin commands cheat sheet with workflow examples' (#39) from docs/commands-cheatsheet into development
Reviewed-on: personal-projects/support-claude-mktplace#39
2026-01-20 22:13:57 +00:00
04322732bc docs: add plugin commands cheat sheet with workflow examples
Add comprehensive documentation for all marketplace plugin commands:
- Reference table with all 37 commands across 9 plugins
- Auto vs manual trigger indicators
- 7 dev workflow examples for practical usage
- Hook-based automation summary
- MCP server requirements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 17:12:38 -05: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
50b45f4834 Merge pull request 'feat/v3.0.0-architecture-overhaul' (#37) from feat/v3.0.0-architecture-overhaul into development
Reviewed-on: personal-projects/support-claude-mktplace#37
2026-01-20 22:03:21 +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
69 changed files with 6032 additions and 1503 deletions

View File

@@ -1,27 +1,26 @@
{
"name": "lm-claude-plugins",
"name": "leo-claude-mktplace",
"owner": {
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"metadata": {
"description": "Project management plugins with Gitea and NetBox integrations",
"version": "3.0.0"
"version": "3.2.0"
},
"plugins": [
{
"name": "projman",
"version": "3.0.0",
"version": "3.1.0",
"description": "Sprint planning and project management with Gitea integration",
"source": "./plugins/projman",
"author": {
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/projman/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"mcpServers": ["gitea"],
"integrationFile": "claude-md-integration.md",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/projman/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"mcpServers": ["./.mcp.json"],
"category": "development",
"tags": ["sprint", "agile", "gitea", "project-management"],
"license": "MIT"
@@ -35,11 +34,9 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/doc-guardian/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"mcpServers": [],
"integrationFile": "claude-md-integration.md",
"hooks": ["PostToolUse", "Stop"],
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/doc-guardian/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"hooks": ["./hooks/hooks.json"],
"category": "productivity",
"tags": ["documentation", "drift-detection", "sync"],
"license": "MIT"
@@ -53,11 +50,9 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/code-sentinel/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"mcpServers": [],
"integrationFile": "claude-md-integration.md",
"hooks": ["PreToolUse"],
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/code-sentinel/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"hooks": ["./hooks/hooks.json"],
"category": "security",
"tags": ["security-scan", "refactoring", "vulnerabilities"],
"license": "MIT"
@@ -71,11 +66,9 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/project-hygiene/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"mcpServers": [],
"integrationFile": "claude-md-integration.md",
"hooks": ["PostToolUse"],
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/project-hygiene/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"hooks": ["./hooks/hooks.json"],
"category": "productivity",
"tags": ["cleanup", "automation", "hygiene"],
"license": "MIT"
@@ -89,10 +82,9 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/cmdb-assistant/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"mcpServers": ["netbox"],
"integrationFile": "claude-md-integration.md",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/cmdb-assistant/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"mcpServers": ["./.mcp.json"],
"category": "infrastructure",
"tags": ["cmdb", "netbox", "dcim", "ipam"],
"license": "MIT"
@@ -106,10 +98,8 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/claude-config-maintainer/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"mcpServers": [],
"integrationFile": "claude-md-integration.md",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/claude-config-maintainer/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"category": "development",
"tags": ["claude-md", "configuration", "optimization"],
"license": "MIT"
@@ -123,10 +113,8 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/clarity-assist/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"mcpServers": [],
"integrationFile": "claude-md-integration.md",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/clarity-assist/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"category": "productivity",
"tags": ["prompts", "requirements", "clarification", "nd-friendly"],
"license": "MIT"
@@ -140,10 +128,8 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/git-flow/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"mcpServers": [],
"integrationFile": "claude-md-integration.md",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/git-flow/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"category": "development",
"tags": ["git", "workflow", "commits", "branching"],
"license": "MIT"
@@ -157,24 +143,12 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/pr-review/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"mcpServers": ["gitea"],
"integrationFile": "claude-md-integration.md",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/pr-review/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"mcpServers": ["./.mcp.json"],
"category": "development",
"tags": ["code-review", "pull-requests", "security", "quality"],
"license": "MIT"
}
],
"pluginDetection": {
"mcpServerMapping": {
"gitea": "projman",
"netbox": "cmdb-assistant"
},
"hookMapping": {
"PostToolUse:Write|Edit": "project-hygiene",
"PostToolUse:Write|Edit|MultiEdit": "doc-guardian",
"PreToolUse:Write|Edit|MultiEdit": "code-sentinel"
}
}
]
}

View File

@@ -1,9 +1,136 @@
# Changelog
All notable changes to lm-claude-plugins will be documented in this file.
All notable changes to the Leo Claude Marketplace will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
*Changes staged for the next release*
---
## [3.2.0] - 2026-01-24
### Added
- **git-flow:** `/commit` now detects protected branches before committing
- Warns when on protected branch (main, master, development, staging, production)
- Offers to create feature branch automatically instead of committing directly
- Configurable via `GIT_PROTECTED_BRANCHES` environment variable
- **netbox:** Platform and primary_ip parameters added to device update tools
- **claude-config-maintainer:** Auto-enforce mandatory behavior rules via SessionStart hook
- **scripts:** `release.sh` - Versioning workflow script for consistent releases
- **scripts:** `verify-hooks.sh` - Verify all hooks are command type
### Changed
- **doc-guardian:** Hook switched from `prompt` type to `command` type
- Prompt hooks unreliable - Claude ignores explicit instructions
- New `notify.sh` bash script guarantees exact output behavior
- Only notifies for config file changes (commands/, agents/, skills/, hooks/)
- Silent exit for all other files - no blocking possible
- **All hooks:** Converted to command type with stricter plugin prefix enforcement
- All hooks now mandate `[plugin-name]` prefix with "NO EXCEPTIONS" rule
- Simplified output formats with word limits
- Consistent structure across projman, pr-review, code-sentinel, doc-guardian
- **CLAUDE.md:** Replaced destructive "ALWAYS CLEAR CACHE" rule with "VERIFY AND RESTART"
- Cache clearing mid-session breaks MCP tools
- Added guidance for proper plugin development workflow
### Fixed
- **cmdb-assistant:** Complete MCP tool schemas for update operations (#138)
- **netbox:** Shorten tool names to meet 64-char API limit (#134)
- **cmdb-assistant:** Correct NetBox API URL format in setup wizard (#132)
- **gitea/projman:** Type safety for `create_label_smart`, curl-based debug-report (#124)
- **netbox:** Add diagnostic logging for JSON parse errors (#121)
- **labels:** Add duplicate check before creating labels (#116)
- **hooks:** Convert ALL hooks to command type with proper prefixes (#114)
- Protected branch workflow: Claude no longer commits directly to protected branches (fixes #109)
- doc-guardian hook no longer blocks workflow (fixes #110)
---
## [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
### Added
- `/project-init` command for quick project setup when system is already configured
- `/project-sync` command to sync .env with git remote after repository move/rename
- SessionStart hooks for automatic mismatch detection between git remote and .env
- Interactive setup wizard (`/initial-setup`) redesigned to use Claude tools instead of bash script
### Changed
- `GITEA_ORG` moved from system-level to project-level configuration (different projects may belong to different organizations)
- Environment variables renamed to match MCP server expectations:
- `GITEA_URL``GITEA_API_URL` (must include `/api/v1`)
- `GITEA_TOKEN``GITEA_API_TOKEN`
- `NETBOX_URL``NETBOX_API_URL` (must include `/api`)
- `NETBOX_TOKEN``NETBOX_API_TOKEN`
- Setup commands now validate repository via Gitea API before saving configuration
- README.md simplified to show only wizard setup path (manual setup moved to CONFIGURATION.md)
### Fixed
- API URL paths in curl commands (removed redundant `/api/v1` since it's now in the URL variable)
- Documentation now correctly references environment variable names
---
## [3.0.0] - 2026-01-20
### Added
@@ -45,7 +172,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `docs/CONFIGURATION.md` - Centralized configuration guide for all plugins
### Changed
- **BREAKING:** Marketplace renamed from `claude-code-marketplace` to `lm-claude-plugins`
- **BREAKING:** Marketplace renamed from `claude-code-marketplace` to `leo-claude-mktplace`
- **BREAKING:** MCP servers moved from plugin directories to shared `mcp-servers/` at repository root
- All plugins now have `category`, `tags`, and `license` fields in marketplace.json
- Plugin MCP dependencies now use symlinks to shared servers

188
CLAUDE.md
View File

@@ -1,18 +1,58 @@
# CLAUDE.md
This file provides guidance to Claude Code when working with code in this repository.
## ⛔ MANDATORY BEHAVIOR RULES - READ FIRST
**These rules are NON-NEGOTIABLE. Violating them wastes the user's time and money.**
### 1. WHEN USER ASKS YOU TO CHECK SOMETHING - CHECK EVERYTHING
- Search ALL locations, not just where you think it is
- Check cache directories: `~/.claude/plugins/cache/`
- Check installed: `~/.claude/plugins/marketplaces/`
- Check source: `~/claude-plugins-work/`
- **NEVER say "no" or "that's not the issue" without exhaustive verification**
### 2. WHEN USER SAYS SOMETHING IS WRONG - BELIEVE THEM
- The user knows their system better than you
- Investigate thoroughly before disagreeing
- If user suspects cache, CHECK THE CACHE
- If user suspects a file, READ THE FILE
- **Your confidence is often wrong. User's instincts are often right.**
### 3. NEVER SAY "DONE" WITHOUT VERIFICATION
- Run the actual command/script to verify
- Show the output to the user
- Check ALL affected locations
- **"Done" means VERIFIED WORKING, not "I made changes"**
### 4. SHOW EXACTLY WHAT USER ASKS FOR
- If user asks for messages, show the MESSAGES
- If user asks for code, show the CODE
- If user asks for output, show the OUTPUT
- **Don't interpret or summarize unless asked**
### 5. AFTER PLUGIN UPDATES - ALWAYS CLEAR CACHE
```bash
rm -rf ~/.claude/plugins/cache/leo-claude-mktplace/
./scripts/verify-hooks.sh
```
**FAILURE TO FOLLOW THESE RULES = WASTED USER TIME = UNACCEPTABLE**
---
## Project Overview
**Repository:** lm-claude-plugins
**Version:** 3.0.0
**Repository:** leo-claude-mktplace
**Version:** 3.1.2
**Status:** Production Ready
A Claude Code plugin marketplace containing:
A plugin marketplace for Claude Code containing:
| Plugin | Description | Version |
|--------|-------------|---------|
| `projman` | Sprint planning and project management with Gitea integration | 3.0.0 |
| `projman` | Sprint planning and project management with Gitea integration | 3.1.0 |
| `git-flow` | Git workflow automation with smart commits and branch management | 1.0.0 |
| `pr-review` | Multi-agent PR review with confidence scoring | 1.0.0 |
| `clarity-assist` | Prompt optimization with ND-friendly accommodations | 1.0.0 |
@@ -28,18 +68,27 @@ A Claude Code plugin marketplace containing:
# Validate marketplace compliance
./scripts/validate-marketplace.sh
# Run projman commands (in a target project with plugin installed)
/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
# After updates
./scripts/post-update.sh # Rebuild venvs, verify symlinks
```
### 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
```
lm-claude-plugins/
leo-claude-mktplace/
├── .claude-plugin/
│ └── marketplace.json # Marketplace manifest
├── mcp-servers/ # SHARED MCP servers (v3.0.0+)
@@ -50,18 +99,20 @@ lm-claude-plugins/
│ │ ├── .claude-plugin/plugin.json
│ │ ├── .mcp.json
│ │ ├── mcp-servers/gitea -> ../../../mcp-servers/gitea # SYMLINK
│ │ ├── commands/ # 9 commands
│ │ ├── commands/ # 13 commands (incl. setup, debug)
│ │ ├── hooks/ # SessionStart mismatch detection
│ │ ├── agents/ # 4 agents
│ │ └── skills/label-taxonomy/
│ ├── git-flow/ # Git workflow automation (NEW v3.0.0)
│ ├── git-flow/ # Git workflow automation
│ │ ├── .claude-plugin/plugin.json
│ │ ├── commands/ # 8 commands
│ │ └── agents/
│ ├── pr-review/ # Multi-agent PR review (NEW v3.0.0)
│ ├── pr-review/ # Multi-agent PR review
│ │ ├── .claude-plugin/plugin.json
│ │ ├── .mcp.json
│ │ ├── mcp-servers/gitea -> ../../../mcp-servers/gitea # SYMLINK
│ │ ├── commands/ # 3 commands
│ │ ├── commands/ # 6 commands (incl. setup)
│ │ ├── hooks/ # SessionStart mismatch detection
│ │ └── agents/ # 5 agents
│ ├── clarity-assist/ # Prompt optimization (NEW v3.0.0)
│ │ ├── .claude-plugin/plugin.json
@@ -133,8 +184,10 @@ lm-claude-plugins/
| Level | Location | Purpose |
|-------|----------|---------|
| System | `~/.config/claude/gitea.env` | Credentials (GITEA_URL, GITEA_TOKEN, GITEA_ORG) |
| Project | `.env` in project root | Repository specification (GITEA_REPO) |
| System | `~/.config/claude/gitea.env` | Credentials (GITEA_API_URL, GITEA_API_TOKEN) |
| Project | `.env` in project root | Repository specification (GITEA_ORG, GITEA_REPO) |
**Note:** `GITEA_ORG` is at project level since different projects may belong to different organizations.
### Branch-Aware Security
@@ -199,41 +252,90 @@ Stored in Gitea Wiki under `lessons-learned/sprints/`.
| Document | Purpose |
|----------|---------|
| `docs/CANONICAL-PATHS.md` | **Single source of truth** for paths |
| `docs/COMMANDS-CHEATSHEET.md` | All commands quick reference |
| `docs/CONFIGURATION.md` | Centralized setup guide |
| `docs/DEBUGGING-CHECKLIST.md` | Systematic troubleshooting guide |
| `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 |
## Versioning and Changelog Rules
## Installation Paths
### Version Display
**The marketplace version is displayed ONLY in the main `README.md` title.**
Understanding where files live is critical for debugging:
- Format: `# Claude Code Marketplace - vX.Y.Z`
- Do NOT add version numbers to individual plugin documentation titles
- Do NOT add version numbers to configuration guides
- Do NOT add version numbers to CLAUDE.md or other docs
| Context | Path | Purpose |
|---------|------|---------|
| **Source** | `~/claude-plugins-work/` | Development - edit here |
| **Installed** | `~/.claude/plugins/marketplaces/leo-claude-mktplace/` | Runtime - Claude uses this |
| **Cache** | `~/.claude/` | Plugin metadata and settings |
### Changelog Maintenance (MANDATORY)
**`CHANGELOG.md` is the authoritative source for version history.**
**Key insight:** Edits to source require reinstall/update to take effect at runtime.
When releasing a new version:
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
## Debugging & Troubleshooting
### Version Format
- Follow [Semantic Versioning](https://semver.org/): MAJOR.MINOR.PATCH
- MAJOR: Breaking changes
- MINOR: New features, backward compatible
- PATCH: Bug fixes, minor improvements
See `docs/DEBUGGING-CHECKLIST.md` for systematic troubleshooting.
**Common Issues:**
| Symptom | Likely Cause | Fix |
|---------|--------------|-----|
| "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 Workflow
This project follows [SemVer](https://semver.org/) and [Keep a Changelog](https://keepachangelog.com).
### Version Locations (must stay in sync)
| Location | Format | Example |
|----------|--------|---------|
| Git tags | `vX.Y.Z` | `v3.2.0` |
| README.md title | `# Leo Claude Marketplace - vX.Y.Z` | `v3.2.0` |
| marketplace.json | `"version": "X.Y.Z"` | `3.2.0` |
| CHANGELOG.md | `## [X.Y.Z] - YYYY-MM-DD` | `[3.2.0] - 2026-01-24` |
### During Development
**All changes go under `[Unreleased]` in CHANGELOG.md.** Never create a versioned section until release time.
```markdown
## [Unreleased]
### Added
- New feature description
### Fixed
- Bug fix description
```
### Creating a Release
Use the release script to ensure consistency:
```bash
./scripts/release.sh 3.2.0
```
The script will:
1. Validate `[Unreleased]` section has content
2. Replace `[Unreleased]` with `[3.2.0] - YYYY-MM-DD`
3. Update README.md title
4. Update marketplace.json version
5. Commit and create git tag
### SemVer Guidelines
| Change Type | Version Bump | Example |
|-------------|--------------|---------|
| Bug fixes only | PATCH (x.y.**Z**) | 3.1.1 → 3.1.2 |
| New features (backwards compatible) | MINOR (x.**Y**.0) | 3.1.2 → 3.2.0 |
| Breaking changes | MAJOR (**X**.0.0) | 3.2.0 → 4.0.0 |
---
**Last Updated:** 2026-01-20
**Last Updated:** 2026-01-24

128
README.md
View File

@@ -1,4 +1,4 @@
# Claude Code Marketplace - v3.0.0
# Leo Claude Marketplace - v3.2.0
A collection of Claude Code plugins for project management, infrastructure automation, and development workflows.
@@ -19,7 +19,7 @@ AI-guided sprint planning with full Gitea integration. Transforms a proven 15-sp
- Branch-aware security (development/staging/production)
- Pre-sprint-close code quality review and test verification
**Commands:** `/sprint-plan`, `/sprint-start`, `/sprint-status`, `/sprint-close`, `/labels-sync`, `/initial-setup`, `/review`, `/test-check`, `/test-gen`
**Commands:** `/sprint-plan`, `/sprint-start`, `/sprint-status`, `/sprint-close`, `/labels-sync`, `/initial-setup`, `/project-init`, `/project-sync`, `/review`, `/test-check`, `/test-gen`, `/debug-report`, `/debug-review`
#### [git-flow](./plugins/git-flow/README.md) *NEW in v3.0.0*
**Git Workflow Automation**
@@ -44,7 +44,7 @@ Comprehensive pull request review using specialized agents.
- Actionable feedback with suggested fixes
- Gitea integration for automated review submission
**Commands:** `/pr-review`, `/pr-summary`, `/pr-findings`
**Commands:** `/pr-review`, `/pr-summary`, `/pr-findings`, `/initial-setup`, `/project-init`, `/project-sync`
#### [claude-config-maintainer](./plugins/claude-config-maintainer/README.md)
**CLAUDE.md Optimization and Maintenance**
@@ -94,7 +94,7 @@ Security vulnerability detection and code refactoring tools.
Full CRUD operations for network infrastructure management directly from Claude Code.
**Commands:** `/cmdb-search`, `/cmdb-device`, `/cmdb-ip`, `/cmdb-site`
**Commands:** `/initial-setup`, `/cmdb-search`, `/cmdb-device`, `/cmdb-ip`, `/cmdb-site`
## MCP Servers
@@ -106,11 +106,11 @@ Full Gitea API integration for project management.
| Category | Tools |
|----------|-------|
| Issues | `list_issues`, `get_issue`, `create_issue`, `update_issue`, `add_comment` |
| Labels | `get_labels`, `suggest_labels`, `create_label` |
| Wiki | `list_wiki_pages`, `get_wiki_page`, `create_wiki_page`, `create_lesson`, `search_lessons` |
| Milestones | `list_milestones`, `get_milestone`, `create_milestone`, `update_milestone` |
| Dependencies | `list_issue_dependencies`, `create_issue_dependency`, `get_execution_order` |
| Issues | `list_issues`, `get_issue`, `create_issue`, `update_issue`, `add_comment`, `aggregate_issues` |
| Labels | `get_labels`, `suggest_labels`, `create_label`, `create_label_smart` |
| Wiki | `list_wiki_pages`, `get_wiki_page`, `create_wiki_page`, `update_wiki_page`, `create_lesson`, `search_lessons` |
| Milestones | `list_milestones`, `get_milestone`, `create_milestone`, `update_milestone`, `delete_milestone` |
| Dependencies | `list_issue_dependencies`, `create_issue_dependency`, `remove_issue_dependency`, `get_execution_order` |
| **Pull Requests** | `list_pull_requests`, `get_pull_request`, `get_pr_diff`, `get_pr_comments`, `create_pr_review`, `add_pr_comment` *(NEW in v3.0.0)* |
| Validation | `validate_repo_org`, `get_branch_protection` |
@@ -138,7 +138,7 @@ Comprehensive NetBox REST API integration for infrastructure management.
**Option 1 - CLI command (recommended):**
```bash
/plugin marketplace add https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git
/plugin marketplace add https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git
```
**Option 2 - Settings file (for team distribution):**
@@ -147,62 +147,86 @@ Add to `.claude/settings.json` in your target project:
```json
{
"extraKnownMarketplaces": {
"lm-claude-plugins": {
"leo-claude-mktplace": {
"source": {
"source": "git",
"url": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git"
"url": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git"
}
}
}
}
```
### Configure MCP Server Dependencies
### Run Interactive Setup
Install dependencies for shared MCP servers:
After installing plugins, run the setup wizard:
```bash
# Gitea MCP (for projman, pr-review)
cd mcp-servers/gitea
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
deactivate
# NetBox MCP (for cmdb-assistant)
cd ../netbox
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
deactivate
```
/initial-setup
```
### Configure Credentials
The wizard handles everything:
- Sets up MCP server (Python venv + dependencies)
- Creates system config (`~/.config/claude/gitea.env`)
- Guides you through adding your API token
- Detects and validates your repository via API
- Creates project config (`.env`)
See [docs/CONFIGURATION.md](./docs/CONFIGURATION.md) for complete setup instructions.
**Quick start:**
```bash
mkdir -p ~/.config/claude
# Gitea credentials
cat > ~/.config/claude/gitea.env << 'EOF'
GITEA_URL=https://gitea.example.com
GITEA_TOKEN=your_token
GITEA_ORG=your_org
EOF
chmod 600 ~/.config/claude/gitea.env
# Project-level settings
cat > .env << 'EOF'
GITEA_REPO=your-repository-name
EOF
**For new projects** (when system is already configured):
```
/project-init
```
**After moving a repository:**
```
/project-sync
```
See [docs/CONFIGURATION.md](./docs/CONFIGURATION.md) for manual setup and advanced options.
## Verifying Plugin Installation
After installing plugins, the `/plugin` command may show `(no content)` - this is normal Claude Code behavior and doesn't indicate an error.
**To verify a plugin is installed correctly:**
1. **Check installed plugins list:**
```
/plugin list
```
Look for `✔ plugin-name · Installed`
2. **Test a plugin command directly:**
```
/git-flow:git-status
/projman:sprint-status
/clarity-assist:clarify
```
If the command executes and shows output, the plugin is working.
3. **Check for loading errors:**
```
/plugin list
```
Look for any `Plugin Loading Errors` section - this indicates manifest issues.
**Command format:** All plugin commands use the format `/plugin-name:command-name`
| Plugin | Test Command |
|--------|--------------|
| git-flow | `/git-flow:git-status` |
| projman | `/projman:sprint-status` |
| pr-review | `/pr-review:pr-summary` |
| clarity-assist | `/clarity-assist:clarify` |
| doc-guardian | `/doc-guardian:doc-audit` |
| code-sentinel | `/code-sentinel:security-scan` |
| claude-config-maintainer | `/claude-config-maintainer:config-analyze` |
| cmdb-assistant | `/cmdb-assistant:cmdb-search` |
## Repository Structure
```
lm-claude-plugins/
leo-claude-mktplace/
├── .claude-plugin/ # Marketplace manifest
│ └── marketplace.json
├── mcp-servers/ # SHARED MCP servers (v3.0.0+)
@@ -221,7 +245,8 @@ lm-claude-plugins/
├── docs/ # Documentation
│ ├── CANONICAL-PATHS.md # Path reference
│ └── CONFIGURATION.md # Setup guide
── scripts/ # Setup scripts
── scripts/ # Setup scripts
└── CHANGELOG.md # Version history
```
## Documentation
@@ -230,7 +255,10 @@ lm-claude-plugins/
|----------|-------------|
| [CLAUDE.md](./CLAUDE.md) | Main project instructions |
| [CONFIGURATION.md](./docs/CONFIGURATION.md) | Centralized setup guide |
| [COMMANDS-CHEATSHEET.md](./docs/COMMANDS-CHEATSHEET.md) | All commands quick reference |
| [UPDATING.md](./docs/UPDATING.md) | Update guide for the marketplace |
| [CANONICAL-PATHS.md](./docs/CANONICAL-PATHS.md) | Authoritative path reference |
| [DEBUGGING-CHECKLIST.md](./docs/DEBUGGING-CHECKLIST.md) | Systematic troubleshooting guide |
| [CHANGELOG.md](./CHANGELOG.md) | Version history |
## License
@@ -240,4 +268,4 @@ MIT License
## Support
- **Issues**: Contact repository maintainer
- **Repository**: `https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git`
- **Repository**: `https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git`

View File

@@ -2,14 +2,14 @@
**This file defines ALL valid paths in this repository. No exceptions. No inference. No assumptions.**
Last Updated: 2026-01-20 (v3.0.0)
Last Updated: 2026-01-23 (v3.1.2)
---
## Repository Root Structure
```
lm-claude-plugins/
leo-claude-mktplace/
├── .claude/ # Claude Code local settings
├── .claude-plugin/ # Marketplace manifest
│ └── marketplace.json
@@ -17,9 +17,10 @@ lm-claude-plugins/
├── docs/ # All documentation
│ ├── architecture/ # Draw.io diagrams and specs
│ ├── CANONICAL-PATHS.md # This file - single source of truth
│ ├── COMMANDS-CHEATSHEET.md # All commands quick reference
│ ├── CONFIGURATION.md # Centralized configuration guide
│ ├── UPDATING.md # Update guide
│ └── workflows/ # Workflow documentation
│ ├── DEBUGGING-CHECKLIST.md # Systematic troubleshooting guide
│ └── UPDATING.md # Update guide
├── hooks/ # Shared hooks (if any)
├── mcp-servers/ # SHARED MCP servers (v3.0.0+)
│ ├── gitea/ # Gitea MCP server
@@ -103,6 +104,10 @@ lm-claude-plugins/
│ ├── skills/
│ └── claude-md-integration.md
├── 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
├── README.md
├── LICENSE
@@ -151,10 +156,11 @@ The symlink target is relative: `../../../mcp-servers/{server}`
| Type | Location |
|------|----------|
| Architecture diagrams | `docs/architecture/` |
| Workflow docs | `docs/workflows/` |
| This file | `docs/CANONICAL-PATHS.md` |
| Update guide | `docs/UPDATING.md` |
| Configuration guide | `docs/CONFIGURATION.md` |
| Commands cheat sheet | `docs/COMMANDS-CHEATSHEET.md` |
| Debugging checklist | `docs/DEBUGGING-CHECKLIST.md` |
---
@@ -231,7 +237,7 @@ plugins/pr-review/mcp-servers/gitea -> ../../../mcp-servers/gitea
| 2026-01-20 | v3.0.0: MCP servers moved to root with symlinks | Claude Code |
| 2026-01-20 | v3.0.0: Added clarity-assist, git-flow, pr-review plugins | Claude Code |
| 2026-01-20 | v3.0.0: Added docs/CONFIGURATION.md | Claude Code |
| 2026-01-20 | v3.0.0: Renamed marketplace to lm-claude-plugins | Claude Code |
| 2026-01-20 | v3.0.0: Renamed marketplace to leo-claude-mktplace | Claude Code |
| 2026-01-20 | Removed docs/references/ (obsolete planning docs) | Claude Code |
| 2026-01-19 | Added claude-md-integration.md path pattern | Claude Code |
| 2025-12-15 | Restructured: MCP servers bundled in plugins | Claude Code |

217
docs/COMMANDS-CHEATSHEET.md Normal file
View File

@@ -0,0 +1,217 @@
# Plugin Commands Cheat Sheet
Quick reference for all commands in the Leo Claude Marketplace.
---
## Command Reference Table
| Plugin | Command | Auto | Manual | Description |
|--------|---------|:----:|:------:|-------------|
| **projman** | `/sprint-plan` | | X | Start sprint planning with AI-guided architecture analysis and issue creation |
| **projman** | `/sprint-start` | | X | Begin sprint execution with dependency analysis and parallel task coordination |
| **projman** | `/sprint-status` | | X | Check current sprint progress and identify blockers |
| **projman** | `/review` | | X | Pre-sprint-close code quality review (debug artifacts, security, error handling) |
| **projman** | `/test-check` | | X | Run tests and verify coverage before sprint close |
| **projman** | `/sprint-close` | | X | Complete sprint and capture lessons learned to Gitea Wiki |
| **projman** | `/labels-sync` | | X | Synchronize label taxonomy from Gitea |
| **projman** | `/initial-setup` | | X | Full setup wizard: MCP server + system config + project config |
| **projman** | `/project-init` | | X | Quick project setup (assumes system config exists) |
| **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** | `/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-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-sync` | | X | Full sync: commit, push, and sync with upstream/base branch |
| **git-flow** | `/branch-start` | | X | Create new feature/fix/chore branch with naming conventions |
| **git-flow** | `/branch-cleanup` | | X | Remove merged branches locally and optionally on remote |
| **git-flow** | `/git-status` | | X | Enhanced git status with recommendations |
| **git-flow** | `/git-config` | | X | Configure git-flow settings for the project |
| **pr-review** | `/initial-setup` | | X | Setup wizard for pr-review (shares Gitea MCP with projman) |
| **pr-review** | `/project-init` | | X | Quick project setup for PR reviews |
| **pr-review** | `/project-sync` | | X | Sync config with git remote after repo move/rename |
| **pr-review** | *SessionStart hook* | X | | Detects git remote vs .env mismatch |
| **pr-review** | `/pr-review` | | X | Full multi-agent PR review with confidence scoring |
| **pr-review** | `/pr-summary` | | X | Quick summary of PR changes |
| **pr-review** | `/pr-findings` | | X | List and filter review findings by category/severity |
| **clarity-assist** | `/clarify` | | X | Full 4-D prompt optimization with ND accommodations |
| **clarity-assist** | `/quick-clarify` | | X | Rapid single-pass clarification for simple requests |
| **doc-guardian** | `/doc-audit` | | X | Full documentation audit - scans for doc drift |
| **doc-guardian** | `/doc-sync` | | X | Synchronize pending documentation updates |
| **doc-guardian** | *PostToolUse hook* | X | | Silently detects doc drift on Write/Edit |
| **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-dry` | | X | Preview refactoring without applying changes |
| **code-sentinel** | *PreToolUse hook* | X | | Scans code before writing; blocks critical issues |
| **claude-config-maintainer** | `/config-analyze` | | X | Analyze CLAUDE.md for optimization opportunities |
| **claude-config-maintainer** | `/config-optimize` | | X | Optimize CLAUDE.md structure with preview/backup |
| **claude-config-maintainer** | `/config-init` | | X | Initialize new CLAUDE.md for a project |
| **cmdb-assistant** | `/initial-setup` | | X | Setup wizard for NetBox MCP server |
| **cmdb-assistant** | `/cmdb-search` | | X | Search NetBox for devices, IPs, sites |
| **cmdb-assistant** | `/cmdb-device` | | X | Manage network devices (create, view, update, delete) |
| **cmdb-assistant** | `/cmdb-ip` | | X | Manage IP addresses and prefixes |
| **cmdb-assistant** | `/cmdb-site` | | X | Manage sites, locations, racks, and regions |
| **project-hygiene** | *PostToolUse hook* | X | | Removes temp files, warns about unexpected root files |
---
## Plugins by Category
| Category | Plugins | Primary Use |
|----------|---------|-------------|
| **Setup** | projman, pr-review, cmdb-assistant | `/initial-setup`, `/project-init` |
| **Task Planning** | projman, clarity-assist | Sprint management, requirement clarification |
| **Code Quality** | code-sentinel, pr-review | Security scanning, PR reviews |
| **Documentation** | doc-guardian, claude-config-maintainer | Doc sync, CLAUDE.md maintenance |
| **Git Operations** | git-flow | Commits, branches, workflow automation |
| **Infrastructure** | cmdb-assistant | NetBox CMDB management |
| **Maintenance** | project-hygiene | Automatic cleanup |
---
## Hook-Based Automation Summary
| Plugin | Hook Event | Behavior |
|--------|------------|----------|
| **projman** | 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 |
| **code-sentinel** | PreToolUse (Write/Edit) | Scans for security issues; blocks critical vulnerabilities |
| **project-hygiene** | PostToolUse (Write/Edit) | Cleans temp files, warns about misplaced files |
---
## Dev Workflow Examples
### Example 1: Starting a New Feature Sprint
A typical workflow for planning and executing a feature sprint:
```
1. /clarify # Clarify requirements if vague
2. /sprint-plan # Plan the sprint with architecture analysis
3. /labels-sync # Ensure labels are up-to-date
4. /sprint-start # Begin execution with dependency ordering
5. /branch-start feat/... # Create feature branch
... implement features ...
6. /commit # Commit with conventional message
7. /sprint-status # Check progress mid-sprint
8. /review # Pre-close quality review
9. /test-check # Verify test coverage
10. /sprint-close # Capture lessons learned
```
### Example 2: Daily Development Cycle
Quick daily workflow with git-flow:
```
1. /git-status # Check current state
2. /branch-start fix/... # Start bugfix branch
... make changes ...
3. /commit # Auto-generate commit message
4. /commit-push # Push to remote
5. /branch-cleanup # Clean merged branches
```
### Example 3: Pull Request Review Workflow
Reviewing a PR before merge:
```
1. /pr-summary # Quick overview of changes
2. /pr-review # Full multi-agent review
3. /pr-findings # Filter findings by severity
4. /security-scan # Deep security audit if needed
```
### Example 4: Documentation Maintenance
Keeping docs in sync:
```
1. /doc-audit # Scan for documentation drift
2. /doc-sync # Apply pending updates
3. /config-analyze # Check CLAUDE.md health
4. /config-optimize # Optimize if needed
```
### Example 5: Code Refactoring Session
Safe refactoring with preview:
```
1. /refactor-dry # Preview opportunities
2. /security-scan # Baseline security check
3. /refactor # Apply improvements
4. /test-check # Verify nothing broke
5. /commit # Commit with descriptive message
```
### Example 6: Infrastructure Documentation
Managing infrastructure with CMDB:
```
1. /cmdb-search "server" # Find existing devices
2. /cmdb-device view X # Check device details
3. /cmdb-ip list # List available IPs
4. /cmdb-site view Y # Check site info
```
### Example 7: First-Time Setup (New Machine)
Setting up the marketplace for the first time:
```
1. /initial-setup # Full setup: MCP + system config + project
# → Follow prompts for Gitea URL, org
# → Add token manually when prompted
# → Confirm repository name
2. # Restart Claude Code session
3. /labels-sync # Sync Gitea labels
4. /sprint-plan # Plan first sprint
```
### Example 8: New Project Setup (System Already Configured)
Adding a new project when system config exists:
```
1. /project-init # Quick project setup
# → Confirms detected repo name
# → Creates .env
2. /labels-sync # Sync Gitea labels
3. /sprint-plan # Plan first sprint
```
---
## Quick Tips
- **Hooks run automatically** - doc-guardian and code-sentinel protect you without manual invocation
- **Use `/commit` over `git commit`** - generates better commit messages following conventions
- **Run `/review` before `/sprint-close`** - catches issues before closing the sprint
- **Use `/clarify` for vague requests** - especially helpful for complex requirements
- **`/refactor-dry` is safe** - always preview before applying refactoring changes
---
## MCP Server Requirements
Some plugins require MCP server connectivity:
| Plugin | MCP Server | Purpose |
|--------|------------|---------|
| projman | Gitea | Issues, PRs, wiki, labels, milestones |
| pr-review | Gitea | PR operations and reviews |
| cmdb-assistant | NetBox | Infrastructure CMDB |
Ensure credentials are configured in `~/.config/claude/gitea.env` or `~/.config/claude/netbox.env`.
---
*Last Updated: 2026-01-22*

View File

@@ -1,105 +1,341 @@
# Configuration Guide
Centralized configuration documentation for all plugins and MCP servers in the lm-claude-plugins marketplace.
Centralized configuration documentation for all plugins and MCP servers in the Leo Claude Marketplace.
## Overview
---
## Quick Start
**After installing the marketplace and plugins via Claude Code:**
```
/initial-setup
```
The interactive wizard handles everything except manually adding your API tokens.
---
## Setup Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ FIRST TIME SETUP │
│ (once per machine) │
└─────────────────────────────────────────────────────────────────────────────┘
/initial-setup
┌──────────────────────────────┼──────────────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ PHASE 1 │ │ PHASE 2 │ │ PHASE 3 │
│ Automated │───────────▶│ Automated │───────────▶│ Interactive │
│ │ │ │ │ │
│ • Check │ │ • Find MCP path │ │ • Ask Gitea URL │
│ Python │ │ • Create venv │ │ • Ask Org name │
│ version │ │ • Install deps │ │ • Create config │
└─────────────┘ └─────────────────┘ └─────────────────┘
┌───────────────────────────┐
│ PHASE 4 │
│ USER ACTION │
│ │
│ Edit config file to add │
│ API token (for security) │
│ │
│ nano ~/.config/claude/ │
│ gitea.env │
└───────────────────────────┘
┌──────────────────────────────┬──────────────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ PHASE 5 │ │ PHASE 6 │ │ PHASE 7 │
│ Interactive │ │ Automated │ │ Automated │
│ │ │ │ │ │
│ • Confirm │ │ • Create .env │ │ • Test API │
│ repo name │ │ • Check │ │ • Show summary │
│ from git │ │ .gitignore │ │ • Restart note │
└─────────────┘ └─────────────────┘ └─────────────────┘
┌───────────────────────────┐
│ RESTART SESSION │
│ │
│ MCP tools available │
│ after restart │
└───────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ NEW PROJECT SETUP │
│ (once per project) │
└─────────────────────────────────────────────────────────────────────────────┘
┌───────────────┴───────────────┐
▼ ▼
/project-init /initial-setup
(direct path) (smart detection)
│ │
│ ┌──────────┴──────────┐
│ ▼ ▼
│ "Quick setup" "Full setup"
│ (skips to (re-runs
│ project config) everything)
│ │ │
└────────────────────┴─────────────────────┘
┌─────────────────────┐
│ PROJECT CONFIG │
│ │
│ • Detect repo from │
│ git remote │
│ • Confirm with user │
│ • Create .env │
│ • Check .gitignore │
└─────────────────────┘
Done!
```
---
## What Runs Automatically vs User Interaction
### `/initial-setup` - Full Setup
| Phase | Type | What Happens |
|-------|------|--------------|
| **1. Environment Check** | Automated | Verifies Python 3.10+ is installed |
| **2. MCP Server Setup** | Automated | Finds plugin path, creates venv, installs dependencies |
| **3. System Config Creation** | Interactive | Asks for Gitea URL and organization name |
| **4. Token Entry** | **User Action** | User manually edits config file to add API token |
| **5. Project Detection** | Interactive | Shows detected repo name, asks for confirmation |
| **6. Project Config** | Automated | Creates `.env` file, checks `.gitignore` |
| **7. Validation** | Automated | Tests API connectivity, shows summary |
### `/project-init` - Quick Project Setup
| Phase | Type | What Happens |
|-------|------|--------------|
| **1. Pre-flight Check** | Automated | Verifies system config exists |
| **2. Project Detection** | Interactive | Shows detected repo name, asks for confirmation |
| **3. Project Config** | Automated | Creates/updates `.env` file |
| **4. Gitignore Check** | Interactive | Asks to add `.env` to `.gitignore` if missing |
---
## Three Commands for Different Scenarios
| Command | When to Use | What It Does |
|---------|-------------|--------------|
| `/initial-setup` | First time on a machine | Full setup: MCP server + system config + project config |
| `/project-init` | Starting a new project | Quick setup: project config only (assumes system is ready) |
| `/project-sync` | After repo move/rename | Updates .env to match current git remote |
**Typical workflow:**
1. Install plugin → run `/initial-setup` (once per machine)
2. Start new project → run `/project-init` (once per project)
3. Repository moved? → run `/project-sync` (updates config)
**Smart features:**
- `/initial-setup` detects existing system config and offers quick project setup
- All commands validate org/repo via Gitea API before saving (auto-fills if verified)
- SessionStart hook automatically detects git remote vs .env mismatches
---
## Configuration Architecture
This marketplace uses a **hybrid configuration** approach:
- **System-level:** Credentials and service configuration (stored once per machine)
- **Project-level:** Repository-specific settings (stored per project)
```
┌─────────────────────────────────────────────────────────────────┐
│ SYSTEM-LEVEL (once per machine) │
│ ~/.config/claude/ │
├─────────────────────────────────────────────────────────────────┤
│ gitea.env │ GITEA_API_URL, GITEA_API_TOKEN │
│ netbox.env │ NETBOX_API_URL, NETBOX_API_TOKEN │
│ git-flow.env │ GIT_WORKFLOW_STYLE, GIT_DEFAULT_BASE, etc. │
└─────────────────────────────────────────────────────────────────┘
│ Shared across all projects
┌─────────────────────────────────────────────────────────────────┐
│ PROJECT-LEVEL (once per project) │
│ <project-root>/.env │
├─────────────────────────────────────────────────────────────────┤
│ GITEA_ORG │ Organization for this project │
│ GITEA_REPO │ Repository name for this project │
│ GIT_WORKFLOW_STYLE │ (optional) Override system default │
│ PR_REVIEW_* │ (optional) PR review settings │
└─────────────────────────────────────────────────────────────────┘
```
**Benefits:**
- Single token per service (update once, use everywhere)
- Easy multi-project setup (just add `.env` per project)
- Security (tokens never committed to git)
- Project isolation (each project has its own scope)
- Easy multi-project setup (just run `/project-init` in each project)
- Security (tokens never committed to git, never typed into AI chat)
- Project isolation (each project can override defaults)
---
## Prerequisites
Before configuring any plugin:
Before running `/initial-setup`:
1. **Python 3.10+** installed
```bash
python3 --version # Should be 3.10.0 or higher
```
2. **Git repository** initialized
2. **Git repository** initialized (for project setup)
```bash
git status # Should show initialized repository
```
3. **Claude Code** installed and working
3. **Claude Code** installed and working with the marketplace
---
## System-Level Configuration
## Setup Methods
Configuration files stored in `~/.config/claude/`:
### Method 1: Interactive Wizard (Recommended)
Run the setup wizard in Claude Code:
```
/initial-setup
```
The wizard will guide you through each step interactively.
**Note:** After first-time setup, you'll need to restart your Claude Code session for MCP tools to become available.
### Method 2: Manual Setup
If you prefer to set up manually or need to troubleshoot:
#### Step 1: MCP Server Setup
```bash
# Navigate to marketplace directory
cd /path/to/leo-claude-mktplace
# Set up Gitea MCP server
cd mcp-servers/gitea
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
deactivate
# (Optional) Set up NetBox MCP server
cd ../netbox
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
deactivate
```
#### Step 2: System Configuration
```bash
mkdir -p ~/.config/claude
```
### Gitea Configuration
Required by: `projman`, `pr-review`
```bash
# Gitea configuration (credentials only)
cat > ~/.config/claude/gitea.env << 'EOF'
# Gitea API Configuration
GITEA_URL=https://gitea.example.com
GITEA_TOKEN=your_gitea_token_here
GITEA_ORG=your_organization
GITEA_API_URL=https://gitea.example.com
GITEA_API_TOKEN=your_token_here
EOF
# Secure the file
chmod 600 ~/.config/claude/gitea.env
```
**Variables:**
#### Step 3: Project Configuration
In each project root:
```bash
cat > .env << 'EOF'
GITEA_ORG=your-organization
GITEA_REPO=your-repo-name
EOF
```
Add `.env` to `.gitignore` if not already there.
### Method 3: Automation Script (CI/Scripting)
For automated setups or CI environments:
```bash
cd /path/to/leo-claude-mktplace
./scripts/setup.sh
```
This script is useful for CI/CD pipelines and bulk provisioning.
---
## Configuration Reference
### System-Level Files
Located in `~/.config/claude/`:
| File | Required By | Purpose |
|------|-------------|---------|
| `gitea.env` | projman, pr-review | Gitea API credentials |
| `netbox.env` | cmdb-assistant | NetBox API credentials |
| `git-flow.env` | git-flow | Default git workflow settings |
### Gitea Configuration
```bash
# ~/.config/claude/gitea.env
GITEA_API_URL=https://gitea.example.com/api/v1
GITEA_API_TOKEN=your_gitea_token_here
```
| Variable | Description | Example |
|----------|-------------|---------|
| `GITEA_URL` | Gitea base URL (no `/api/v1`) | `https://gitea.example.com` |
| `GITEA_TOKEN` | Personal access token | `glpat-xxx...` |
| `GITEA_ORG` | Organization name | `bandit` |
| `GITEA_API_URL` | Gitea API endpoint (with `/api/v1`) | `https://gitea.example.com/api/v1` |
| `GITEA_API_TOKEN` | Personal access token | `abc123...` |
**Generating Gitea Token:**
**Note:** `GITEA_ORG` is configured at the project level (see below) since different projects may belong to different organizations.
**Generating a Gitea Token:**
1. Log into Gitea → **User Icon** → **Settings**
2. **Applications** tab → **Manage Access Tokens**
3. **Generate New Token** with permissions:
- `repo` (all sub-permissions)
- `read:org`
- `read:user`
- `write:repo` (for wiki)
- `write:repo` (for wiki access)
4. Copy token immediately (shown only once)
### NetBox Configuration
Required by: `cmdb-assistant`
```bash
cat > ~/.config/claude/netbox.env << 'EOF'
# NetBox API Configuration
NETBOX_URL=https://netbox.example.com
NETBOX_TOKEN=your_netbox_token_here
EOF
chmod 600 ~/.config/claude/netbox.env
# ~/.config/claude/netbox.env
NETBOX_API_URL=https://netbox.example.com
NETBOX_API_TOKEN=your_netbox_token_here
```
**Variables:**
| Variable | Description | Example |
|----------|-------------|---------|
| `NETBOX_URL` | NetBox base URL | `https://netbox.example.com` |
| `NETBOX_TOKEN` | API token | `abc123...` |
| `NETBOX_API_URL` | NetBox base URL | `https://netbox.example.com` |
| `NETBOX_API_TOKEN` | API token | `abc123...` |
### Git-Flow Configuration
Optional system defaults for: `git-flow`
```bash
cat > ~/.config/claude/git-flow.env << 'EOF'
# Git-Flow Default Configuration
# ~/.config/claude/git-flow.env
GIT_WORKFLOW_STYLE=feature-branch
GIT_DEFAULT_BASE=development
GIT_AUTO_DELETE_MERGED=true
@@ -107,170 +343,110 @@ GIT_AUTO_PUSH=false
GIT_PROTECTED_BRANCHES=main,master,development,staging,production
GIT_COMMIT_STYLE=conventional
GIT_CO_AUTHOR=true
EOF
```
| Variable | Default | Description |
|----------|---------|-------------|
| `GIT_WORKFLOW_STYLE` | `feature-branch` | Branching strategy |
| `GIT_DEFAULT_BASE` | `development` | Default base branch |
| `GIT_AUTO_DELETE_MERGED` | `true` | Delete merged branches |
| `GIT_AUTO_PUSH` | `false` | Auto-push after commit |
| `GIT_PROTECTED_BRANCHES` | `main,master,...` | Protected branches |
| `GIT_COMMIT_STYLE` | `conventional` | Commit message style |
| `GIT_CO_AUTHOR` | `true` | Include Claude co-author |
---
## Project-Level Configuration
Create `.env` in each project root:
### Gitea Repository (projman, pr-review)
```bash
# .env in project root
# Required for projman, pr-review
GITEA_ORG=your-organization
GITEA_REPO=your-repo-name
```
### Git-Flow (project overrides)
```bash
# .env in project root
# Optional: Override git-flow defaults
GIT_WORKFLOW_STYLE=pr-required
GIT_DEFAULT_BASE=main
```
### PR Review
```bash
# .env in project root
# Optional: PR review settings
PR_REVIEW_CONFIDENCE_THRESHOLD=0.5
PR_REVIEW_AUTO_SUBMIT=false
```
---
## MCP Server Installation
MCP servers are located at repository root: `mcp-servers/`
### Gitea MCP Server
```bash
cd mcp-servers/gitea
# Create virtual environment
python3 -m venv .venv
# Activate
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Verify
python -c "from mcp_server import server; print('OK')"
deactivate
```
### NetBox MCP Server
```bash
cd mcp-servers/netbox
# Create virtual environment
python3 -m venv .venv
# Activate
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Verify
python -c "from mcp_server import server; print('OK')"
deactivate
```
| Variable | Required | Description |
|----------|----------|-------------|
| `GITEA_ORG` | Yes | Gitea organization for this project |
| `GITEA_REPO` | Yes | Repository name (must match Gitea exactly) |
| `GIT_WORKFLOW_STYLE` | No | Override system default |
| `PR_REVIEW_*` | No | PR review settings |
---
## Plugin Configuration Reference
## Plugin Configuration Summary
### projman
| Level | Variable | Default | Description |
|-------|----------|---------|-------------|
| System | `GITEA_URL` | (required) | Gitea API base URL |
| System | `GITEA_TOKEN` | (required) | API token |
| System | `GITEA_ORG` | (required) | Organization name |
| Project | `GITEA_REPO` | (required) | Repository name |
**Commands:** `/sprint-plan`, `/sprint-start`, `/sprint-status`, `/sprint-close`, `/labels-sync`, `/initial-setup`, `/review`, `/test-check`, `/test-gen`
### pr-review
| Level | Variable | Default | Description |
|-------|----------|---------|-------------|
| System | `GITEA_URL` | (required) | Gitea API base URL |
| System | `GITEA_TOKEN` | (required) | API token |
| Project | `GITEA_REPO` | (required) | Repository name |
| Project | `PR_REVIEW_CONFIDENCE_THRESHOLD` | `0.5` | Minimum confidence |
| Project | `PR_REVIEW_AUTO_SUBMIT` | `false` | Auto-submit reviews |
**Commands:** `/pr-review`, `/pr-summary`, `/pr-findings`
### git-flow
| Level | Variable | Default | Description |
|-------|----------|---------|-------------|
| System/Project | `GIT_WORKFLOW_STYLE` | `feature-branch` | Branching strategy |
| System/Project | `GIT_DEFAULT_BASE` | `development` | Default base branch |
| System/Project | `GIT_AUTO_DELETE_MERGED` | `true` | Delete merged branches |
| System/Project | `GIT_AUTO_PUSH` | `false` | Auto-push after commit |
| System/Project | `GIT_PROTECTED_BRANCHES` | `main,master,...` | Protected branches |
| System/Project | `GIT_COMMIT_STYLE` | `conventional` | Commit message style |
| System/Project | `GIT_CO_AUTHOR` | `true` | Include Claude co-author |
**Commands:** `/commit`, `/commit-push`, `/commit-merge`, `/commit-sync`, `/branch-start`, `/branch-cleanup`, `/git-status`, `/git-config`
### clarity-assist
No configuration required. Uses sensible defaults.
**Commands:** `/clarify`, `/quick-clarify`
### cmdb-assistant
| Level | Variable | Default | Description |
|-------|----------|---------|-------------|
| System | `NETBOX_URL` | (required) | NetBox API base URL |
| System | `NETBOX_TOKEN` | (required) | API token |
### doc-guardian
No configuration required. Hook-based plugin.
### code-sentinel
No configuration required. Hook-based plugin.
### project-hygiene
No configuration required. Hook-based plugin.
### claude-config-maintainer
No configuration required.
| Plugin | System Config | Project Config | Setup Commands |
|--------|---------------|----------------|----------------|
| **projman** | gitea.env | .env (GITEA_ORG, GITEA_REPO) | `/initial-setup`, `/project-init`, `/project-sync` |
| **pr-review** | gitea.env | .env (GITEA_ORG, GITEA_REPO) | `/initial-setup`, `/project-init`, `/project-sync` |
| **git-flow** | git-flow.env (optional) | .env (optional) | None needed |
| **clarity-assist** | None | None | None needed |
| **cmdb-assistant** | netbox.env | None | `/initial-setup` |
| **doc-guardian** | None | None | None needed |
| **code-sentinel** | None | None | None needed |
| **project-hygiene** | None | None | None needed |
| **claude-config-maintainer** | None | None | None needed |
---
## Multi-Project Setup
## Multi-Project Workflow
1. **System config:** Set up once (credentials)
2. **Project config:** Create `.env` in each project root
Once system-level config is set up, adding new projects is simple:
**Example:**
```bash
# ~/projects/my-app/.env
GITEA_REPO=my-app
# ~/projects/another-app/.env
GITEA_REPO=another-app
GIT_WORKFLOW_STYLE=trunk-based
**Option 1: Use `/project-init` (faster)**
```
cd ~/projects/new-project
/project-init
```
**Option 2: Use `/initial-setup` (auto-detects)**
```
cd ~/projects/new-project
/initial-setup
# → Detects system config exists
# → Offers "Quick project setup" option
```
Both approaches work. Use `/project-init` when you know the system is already configured.
---
## Automatic Validation Features
### API Validation
When running `/initial-setup`, `/project-init`, or `/project-sync`, the commands:
1. **Detect** organization and repository from git remote URL
2. **Validate** via Gitea API: `GET /api/v1/repos/{org}/{repo}`
3. **Auto-fill** if repository exists and is accessible (no confirmation needed)
4. **Ask for confirmation** only if validation fails (404 or permission error)
This catches typos and permission issues before saving configuration.
### Mismatch Detection (SessionStart Hook)
When you start a Claude Code session, a hook automatically:
1. Reads `GITEA_ORG` and `GITEA_REPO` from `.env`
2. Compares with current `git remote get-url origin`
3. **Warns** if mismatch detected: "Repository location mismatch. Run `/project-sync` to update."
This helps when you:
- Move a repository to a different organization
- Rename a repository
- Clone a repo but forget to update `.env`
---
@@ -279,31 +455,29 @@ GIT_WORKFLOW_STYLE=trunk-based
### Test Gitea Connection
```bash
curl -H "Authorization: token YOUR_TOKEN" \
https://gitea.example.com/api/v1/user
source ~/.config/claude/gitea.env
curl -H "Authorization: token $GITEA_API_TOKEN" "$GITEA_API_URL/user"
```
### Test MCP Server
### Verify Project Setup
```bash
cd mcp-servers/gitea
source .venv/bin/activate
python -c "from mcp_server import server; print('OK')"
In Claude Code, after restarting your session:
```
### Run Plugin Setup
```bash
# For projman
/initial-setup
/labels-sync
```
If this works, your setup is complete.
---
## Troubleshooting
### Configuration not found
### MCP tools not available
**Cause:** Session wasn't restarted after setup.
**Solution:** Exit Claude Code and start a new session.
### "Configuration not found" error
```bash
# Check system config exists
@@ -317,23 +491,25 @@ stat ~/.config/claude/gitea.env
```bash
# Test token directly
curl -H "Authorization: token YOUR_TOKEN" \
https://gitea.example.com/api/v1/user
# Regenerate if invalid
source ~/.config/claude/gitea.env
curl -H "Authorization: token $GITEA_API_TOKEN" "$GITEA_API_URL/user"
```
### MCP server not starting
If you get 401, regenerate your token in Gitea.
### MCP server won't start
```bash
# Check venv exists
ls mcp-servers/gitea/.venv
ls /path/to/mcp-servers/gitea/.venv
# Reinstall if missing
cd mcp-servers/gitea
cd /path/to/mcp-servers/gitea
rm -rf .venv
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
deactivate
```
### Wrong repository
@@ -342,7 +518,7 @@ pip install -r requirements.txt
# Check project .env
cat .env
# Verify GITEA_REPO matches exactly
# Verify GITEA_REPO matches the Gitea repository name exactly
```
---
@@ -358,23 +534,14 @@ cat .env
chmod 600 ~/.config/claude/*.env
```
3. **Rotate tokens periodically**
3. **Never type tokens into AI chat**
- Always edit config files directly in your editor
- The `/initial-setup` wizard respects this
4. **Rotate tokens periodically**
- Every 6-12 months
- Immediately if compromised
4. **Minimum permissions**
5. **Minimum permissions**
- Only grant required token permissions
- Use separate tokens for different environments
---
## Quick Setup Checklist
- [ ] Python 3.10+ installed
- [ ] `~/.config/claude/` directory created
- [ ] Service credentials configured (gitea.env, netbox.env)
- [ ] Configuration files secured (chmod 600)
- [ ] MCP servers installed with venv
- [ ] Project `.env` created with repository settings
- [ ] Connections tested
- [ ] Plugin commands verified

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

@@ -1,24 +1,71 @@
# Updating support-claude-mktplace
# Updating Leo Claude Marketplace
This guide covers how to update your local installation when new versions are released.
## 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
# 1. Pull latest changes
cd /path/to/support-claude-mktplace
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
git pull origin main
# 2. Run post-update script
# 2. Run post-update script (updates source repo venvs)
./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.
---
## What the Post-Update Script Does
1. **Updates Python dependencies** for MCP servers
1. **Updates Python dependencies** for MCP servers (gitea, netbox)
2. **Shows recent changelog entries** so you know what changed
3. **Validates your configuration** is still compatible
---
## After Updating: Re-run Setup if Needed
### When to Re-run `/initial-setup`
You typically **don't need** to re-run setup after updates. However, re-run if:
- Changelog mentions **new required environment variables**
- Changelog mentions **breaking changes** to configuration
- MCP tools stop working after update
### For Existing Projects
If an update requires new project-level configuration:
```
/project-init
```
This will detect existing settings and only add what's missing.
---
## Manual Steps After Update
Some updates may require manual configuration changes:
@@ -29,7 +76,7 @@ If the changelog mentions new environment variables:
1. Check the variable name and purpose in the changelog
2. Add it to the appropriate config file:
- Gitea variables → `~/.config/claude/gitea.env`
- System variables → `~/.config/claude/gitea.env` or `netbox.env`
- Project variables → `.env` in your project root
### New MCP Server Features
@@ -37,20 +84,56 @@ If the changelog mentions new environment variables:
If a new MCP server tool is added:
1. The post-update script handles dependency installation
2. Check `plugins/projman/README.md` for usage documentation
3. New tools are available immediately after update
2. Check plugin documentation for usage
3. New tools are available immediately after session restart
### Breaking Changes
Breaking changes will be clearly marked in CHANGELOG.md with migration instructions.
## Troubleshooting
### Setup Script and Configuration Workflow Changes
When updating, review if changes affect the setup workflow:
1. **Check for setup command changes:**
```bash
git diff HEAD~1 plugins/*/commands/initial-setup.md
git diff HEAD~1 plugins/*/commands/project-init.md
git diff HEAD~1 plugins/*/commands/project-sync.md
```
2. **Check for hook changes:**
```bash
git diff HEAD~1 plugins/*/hooks/hooks.json
```
3. **Check for configuration structure changes:**
```bash
git diff HEAD~1 docs/CONFIGURATION.md
```
**If setup commands changed:**
- Review what's new (new validation steps, new prompts, etc.)
- Consider re-running `/initial-setup` or `/project-init` to benefit from improvements
- Existing configurations remain valid unless changelog notes breaking changes
**If hooks changed:**
- Restart your Claude Code session to load new hooks
- New hooks (like SessionStart validation) activate automatically
**If configuration structure changed:**
- Check if new variables are required
- Run `/project-sync` if repository detection logic improved
---
## Troubleshooting Updates
### Dependencies fail to install
```bash
# Rebuild virtual environment
cd plugins/projman/mcp-servers/gitea
cd mcp-servers/gitea
rm -rf .venv
python3 -m venv .venv
source .venv/bin/activate
@@ -61,14 +144,47 @@ deactivate
### Configuration no longer works
1. Check CHANGELOG.md for breaking changes
2. Compare your config files with updated `.env.example` (if provided)
3. Run `./scripts/setup.sh` to validate configuration
2. Run `/initial-setup` to re-validate and fix configuration
3. Compare your config files with documentation in `docs/CONFIGURATION.md`
### MCP server won't start
### 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+)
2. Verify venv exists: `ls plugins/projman/mcp-servers/gitea/.venv`
3. Check logs for specific errors
2. Verify venv exists in INSTALLED location:
```bash
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
1. Restart your Claude Code session
2. Verify the plugin is still installed
3. Check if the command requires additional setup
---
## Version Pinning
@@ -79,15 +195,28 @@ To stay on a specific version:
git tag
# Checkout specific version
git checkout v2.2.0
git checkout v3.0.0
# Run post-update
./scripts/post-update.sh
```
---
## Checking Current Version
The version is displayed in the main README.md title and in `CHANGELOG.md`.
```bash
# Check version from changelog
head -20 CHANGELOG.md
```
---
## Getting Help
- Check `plugins/projman/README.md` for projman documentation
- Check `plugins/projman/CONFIGURATION.md` for setup guide
- Review CHANGELOG.md for recent changes
- Check `docs/CONFIGURATION.md` for setup guide
- Check `docs/COMMANDS-CHEATSHEET.md` for command reference
- Review `CHANGELOG.md` for recent changes
- Search existing issues in Gitea

View File

@@ -389,7 +389,7 @@ def list_issues(self, state='open', labels=None, repo=None):
## License
MIT License - Part of the Claude Code Marketplace project.
MIT License - Part of the Leo Claude Marketplace project.
## Related Documentation
@@ -406,7 +406,7 @@ For issues or questions:
---
**Built for**: Claude Code Marketplace - Project Management Plugins
**Built for**: Leo Claude Marketplace - Project Management Plugins
**Phase**: 1 (Complete)
**Status**: ✅ Production Ready
**Last Updated**: 2025-01-06

View File

@@ -4,10 +4,13 @@ Configuration loader for Gitea MCP Server.
Implements hybrid configuration system:
- System-level: ~/.config/claude/gitea.env (credentials)
- Project-level: .env (repository specification)
- Auto-detection: Falls back to git remote URL parsing
"""
from pathlib import Path
from dotenv import load_dotenv
import os
import re
import subprocess
import logging
from typing import Dict, Optional
@@ -48,8 +51,12 @@ class GiteaConfig:
"cat > ~/.config/claude/gitea.env"
)
# Find project directory (MCP server cwd is plugin dir, not project dir)
project_dir = self._find_project_directory()
# Load project config (overrides system)
project_config = Path.cwd() / '.env'
if project_dir:
project_config = project_dir / '.env'
if project_config.exists():
load_dotenv(project_config, override=True)
logger.info(f"Loaded project configuration from {project_config}")
@@ -59,6 +66,12 @@ class GiteaConfig:
self.api_token = os.getenv('GITEA_API_TOKEN')
self.repo = os.getenv('GITEA_REPO') # Optional, must be owner/repo format
# Auto-detect repo from git remote if not specified
if not self.repo and project_dir:
self.repo = self._detect_repo_from_git(project_dir)
if self.repo:
logger.info(f"Auto-detected repository from git remote: {self.repo}")
# Detect mode
if self.repo:
self.mode = 'project'
@@ -96,3 +109,119 @@ class GiteaConfig:
f"Missing required configuration: {', '.join(missing)}\n"
"Check your ~/.config/claude/gitea.env file"
)
def _find_project_directory(self) -> Optional[Path]:
"""
Find the user's project directory.
The MCP server runs with cwd set to the plugin directory, not the
user's project. We need to find the actual project directory using
various heuristics.
Returns:
Path to project directory, or None if not found
"""
# Strategy 1: Check CLAUDE_PROJECT_DIR environment variable
project_dir = os.getenv('CLAUDE_PROJECT_DIR')
if project_dir:
path = Path(project_dir)
if path.exists():
logger.info(f"Found project directory from CLAUDE_PROJECT_DIR: {path}")
return path
# Strategy 2: Check PWD (original working directory before cwd override)
pwd = os.getenv('PWD')
if pwd:
path = Path(pwd)
# Verify it has .git or .env (indicates a project)
if path.exists() and ((path / '.git').exists() or (path / '.env').exists()):
logger.info(f"Found project directory from PWD: {path}")
return path
# Strategy 3: Check current working directory
# This handles test scenarios and cases where cwd is actually the project
cwd = Path.cwd()
if (cwd / '.git').exists() or (cwd / '.env').exists():
logger.info(f"Found project directory from cwd: {cwd}")
return cwd
# Strategy 4: Check if GITEA_REPO is already set (user configured it)
# If so, we don't need to find the project directory for git detection
if os.getenv('GITEA_REPO'):
logger.debug("GITEA_REPO already set, skipping project directory detection")
return None
logger.debug("Could not determine project directory")
return None
def _detect_repo_from_git(self, project_dir: Optional[Path] = None) -> Optional[str]:
"""
Auto-detect repository from git remote origin URL.
Args:
project_dir: Directory to run git command from (defaults to cwd)
Supports URL formats:
- SSH: ssh://git@host:port/owner/repo.git
- SSH short: git@host:owner/repo.git
- HTTPS: https://host/owner/repo.git
- HTTP: http://host/owner/repo.git
Returns:
Repository in 'owner/repo' format, or None if detection fails
"""
try:
result = subprocess.run(
['git', 'remote', 'get-url', 'origin'],
capture_output=True,
text=True,
timeout=5,
cwd=str(project_dir) if project_dir else None
)
if result.returncode != 0:
logger.debug("No git remote 'origin' found")
return None
url = result.stdout.strip()
return self._parse_git_url(url)
except subprocess.TimeoutExpired:
logger.warning("Git command timed out")
return None
except FileNotFoundError:
logger.debug("Git not available")
return None
except Exception as e:
logger.debug(f"Failed to detect repo from git: {e}")
return None
def _parse_git_url(self, url: str) -> Optional[str]:
"""
Parse git URL to extract owner/repo.
Args:
url: Git remote URL
Returns:
Repository in 'owner/repo' format, or None if parsing fails
"""
# Remove .git suffix if present
url = re.sub(r'\.git$', '', url)
# SSH format: ssh://git@host:port/owner/repo
ssh_match = re.match(r'ssh://[^/]+/(.+/.+)$', url)
if ssh_match:
return ssh_match.group(1)
# SSH short format: git@host:owner/repo
ssh_short_match = re.match(r'git@[^:]+:(.+/.+)$', url)
if ssh_short_match:
return ssh_short_match.group(1)
# HTTPS/HTTP format: https://host/owner/repo
http_match = re.match(r'https?://[^/]+/(.+/.+)$', url)
if http_match:
return http_match.group(1)
logger.warning(f"Could not parse git URL: {url}")
return None

View File

@@ -110,8 +110,14 @@ class GiteaClient:
def _resolve_label_ids(self, label_names: List[str], owner: str, repo: str) -> List[int]:
"""Convert label names to label IDs."""
full_repo = f"{owner}/{repo}"
# Only fetch org labels if repo belongs to an organization
org_labels = []
if self.is_org_repo(full_repo):
org_labels = self.get_org_labels(owner)
repo_labels = self.get_labels(f"{owner}/{repo}")
repo_labels = self.get_labels(full_repo)
all_labels = org_labels + repo_labels
label_map = {label['name']: label['id'] for label in all_labels}
label_ids = []
@@ -548,10 +554,33 @@ class GiteaClient:
return response.json()
def is_org_repo(self, repo: Optional[str] = None) -> bool:
"""Check if repository belongs to an organization (not a user)."""
info = self.get_repo_info(repo)
owner_type = info.get('owner', {}).get('type', '')
return owner_type.lower() == 'organization'
"""
Check if repository belongs to an organization (not a user).
Uses the /orgs/{owner} endpoint to reliably detect organizations,
as the owner.type field in repo info may be null in some Gitea versions.
"""
owner, _ = self._parse_repo(repo)
return self._is_organization(owner)
def _is_organization(self, owner: str) -> bool:
"""
Check if an owner is an organization by querying the orgs endpoint.
Args:
owner: The owner name to check
Returns:
True if owner is an organization, False if user or unknown
"""
url = f"{self.base_url}/orgs/{owner}"
try:
response = self.session.get(url)
# 200 = organization exists, 404 = not an organization (user account)
return response.status_code == 200
except Exception as e:
logger.warning(f"Failed to check if {owner} is organization: {e}")
return False
def get_branch_protection(
self,
@@ -592,6 +621,40 @@ class GiteaClient:
response.raise_for_status()
return response.json()
def create_org_label(
self,
org: str,
name: str,
color: str,
description: Optional[str] = None
) -> Dict:
"""
Create a new label at the organization level.
Organization labels are shared across all repositories in the org.
Use this for workflow labels (Type, Priority, Complexity, Effort, etc.)
Args:
org: Organization name
name: Label name (e.g., 'Type/Bug', 'Priority/High')
color: Hex color code (with or without #)
description: Optional label description
Returns:
Created label dictionary
"""
url = f"{self.base_url}/orgs/{org}/labels"
data = {
'name': name,
'color': color.lstrip('#') # Remove # if present
}
if description:
data['description'] = description
logger.info(f"Creating organization label '{name}' in {org}")
response = self.session.post(url, json=data)
response.raise_for_status()
return response.json()
# ========================================
# PULL REQUEST OPERATIONS
# ========================================

View File

@@ -220,6 +220,10 @@ class GiteaMCPServer:
"context": {
"type": "string",
"description": "Issue title + description or sprint context"
},
"repo": {
"type": "string",
"description": "Repository name (owner/repo format)"
}
},
"required": ["context"]
@@ -618,13 +622,65 @@ class GiteaMCPServer:
),
Tool(
name="create_label",
description="Create a new label in the repository",
description="Create a new label in the repository (for repo-specific labels like Component/*, Tech/*)",
inputSchema={
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Label name"
"description": "Label name (e.g., 'Component/Backend', 'Tech/Python')"
},
"color": {
"type": "string",
"description": "Label color (hex code)"
},
"description": {
"type": "string",
"description": "Label description"
},
"repo": {
"type": "string",
"description": "Repository name (owner/repo format)"
}
},
"required": ["name", "color"]
}
),
Tool(
name="create_org_label",
description="Create a new label at organization level (for workflow labels like Type/*, Priority/*, Complexity/*, Effort/*)",
inputSchema={
"type": "object",
"properties": {
"org": {
"type": "string",
"description": "Organization name"
},
"name": {
"type": "string",
"description": "Label name (e.g., 'Type/Bug', 'Priority/High')"
},
"color": {
"type": "string",
"description": "Label color (hex code)"
},
"description": {
"type": "string",
"description": "Label description"
}
},
"required": ["org", "name", "color"]
}
),
Tool(
name="create_label_smart",
description="Create a label at the appropriate level (org or repo) based on category. Org: Type/*, Priority/*, Complexity/*, Effort/*, Risk/*, Source/*, Agent/*. Repo: Component/*, Tech/*",
inputSchema={
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Label name (e.g., 'Type/Bug', 'Component/Backend')"
},
"color": {
"type": "string",
@@ -876,6 +932,20 @@ class GiteaMCPServer:
arguments.get('description'),
arguments.get('repo')
)
elif name == "create_org_label":
result = self.client.create_org_label(
arguments['org'],
arguments['name'],
arguments['color'],
arguments.get('description')
)
elif name == "create_label_smart":
result = await self.label_tools.create_label_smart(
arguments['name'],
arguments['color'],
arguments.get('description'),
arguments.get('repo')
)
# Pull Request tools
elif name == "list_pull_requests":
result = await self.pr_tools.list_pull_requests(**arguments)

View File

@@ -8,6 +8,7 @@ Provides async wrappers for label operations with:
"""
import asyncio
import logging
import re
from typing import List, Dict, Optional
logging.basicConfig(level=logging.INFO)
@@ -27,15 +28,22 @@ class LabelTools:
self.gitea = gitea_client
async def get_labels(self, repo: Optional[str] = None) -> Dict[str, List[Dict]]:
"""Get all labels (org + repo). Repo must be 'owner/repo' format."""
"""Get all labels (org + repo if org-owned, repo-only if user-owned)."""
loop = asyncio.get_event_loop()
target_repo = repo or self.gitea.repo
if not target_repo or '/' not in target_repo:
raise ValueError("Use 'owner/repo' format (e.g. 'org/repo-name')")
org = target_repo.split('/')[0]
# Check if repo belongs to an organization or user
is_org = await loop.run_in_executor(
None,
lambda: self.gitea.is_org_repo(target_repo)
)
org_labels = []
if is_org:
org = target_repo.split('/')[0]
org_labels = await loop.run_in_executor(
None,
lambda: self.gitea.get_org_labels(org)
@@ -52,107 +60,318 @@ class LabelTools:
'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:
context: Issue title + description or sprint context
repo: Repository in 'owner/repo' format (optional, uses default if not provided)
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 = []
context_lower = context.lower()
# Type detection (exclusive - only one)
type_label = None
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']):
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']):
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']):
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']):
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']):
suggested.append('Type/Chore')
type_label = self._find_label(label_lookup, 'type', 'chore')
if type_label:
suggested.append(type_label)
# Priority detection
priority_label = None
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']):
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']):
suggested.append('Priority/Low')
priority_label = self._find_label(label_lookup, 'priority', 'low')
else:
suggested.append('Priority/Medium')
priority_label = self._find_label(label_lookup, 'priority', 'medium')
if priority_label:
suggested.append(priority_label)
# Complexity detection
complexity_label = None
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']):
suggested.append('Complexity/Complex')
complexity_label = self._find_label(label_lookup, 'complexity', 'complex')
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']):
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']):
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']):
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']):
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']):
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_keywords = {
'Component/Backend': ['backend', 'server', 'api', 'database', 'service'],
'Component/Frontend': ['frontend', 'ui', 'interface', 'react', 'vue', 'component'],
'Component/API': ['api', 'endpoint', 'rest', 'graphql', 'route'],
'Component/Database': ['database', 'db', 'sql', 'migration', 'schema', 'postgres'],
'Component/Auth': ['auth', 'authentication', 'login', 'oauth', 'token', 'session'],
'Component/Deploy': ['deploy', 'deployment', 'docker', 'kubernetes', 'ci/cd'],
'Component/Testing': ['test', 'testing', 'spec', 'jest', 'pytest', 'coverage'],
'Component/Docs': ['docs', 'documentation', 'readme', 'guide', 'wiki']
component_mappings = {
'backend': ['backend', 'server', 'api', 'database', 'service'],
'frontend': ['frontend', 'ui', 'interface', 'react', 'vue', 'component'],
'api': ['api', 'endpoint', 'rest', 'graphql', 'route'],
'database': ['database', 'db', 'sql', 'migration', 'schema', 'postgres'],
'auth': ['auth', 'authentication', 'login', 'oauth', 'token', 'session'],
'deploy': ['deploy', 'deployment', 'docker', 'kubernetes', 'ci/cd'],
'testing': ['test', 'testing', 'spec', 'jest', 'pytest', 'coverage'],
'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):
label = self._find_label(label_lookup, 'component', component)
if label and label not in suggested:
suggested.append(label)
# Tech stack detection
tech_keywords = {
'Tech/Python': ['python', 'fastapi', 'django', 'flask', 'pytest'],
'Tech/JavaScript': ['javascript', 'js', 'node', 'npm', 'yarn'],
'Tech/Docker': ['docker', 'dockerfile', 'container', 'compose'],
'Tech/PostgreSQL': ['postgres', 'postgresql', 'psql', 'sql'],
'Tech/Redis': ['redis', 'cache', 'session store'],
'Tech/Vue': ['vue', 'vuejs', 'nuxt'],
'Tech/FastAPI': ['fastapi', 'pydantic', 'starlette']
tech_mappings = {
'python': ['python', 'fastapi', 'django', 'flask', 'pytest'],
'javascript': ['javascript', 'js', 'node', 'npm', 'yarn'],
'docker': ['docker', 'dockerfile', 'container', 'compose'],
'postgresql': ['postgres', 'postgresql', 'psql', 'sql'],
'redis': ['redis', 'cache', 'session store'],
'vue': ['vue', 'vuejs', 'nuxt'],
'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):
label = self._find_label(label_lookup, 'tech', tech)
if label and label not in suggested:
suggested.append(label)
# Source detection (based on git branch or context)
source_label = None
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:
suggested.append('Source/Staging')
source_label = self._find_label(label_lookup, 'source', 'staging')
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_label = None
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']):
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
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
# Organization-level label categories (workflow labels shared across repos)
ORG_LABEL_CATEGORIES = {'agent', 'complexity', 'effort', 'efforts', 'priority', 'risk', 'source', 'type'}
# Repository-level label categories (project-specific labels)
REPO_LABEL_CATEGORIES = {'component', 'tech'}
async def create_label_smart(
self,
name: str,
color: str,
description: Optional[str] = None,
repo: Optional[str] = None
) -> Dict:
"""
Create a label at the appropriate level (org or repo) based on category.
Skips if label already exists (checks both org and repo levels).
Organization labels: Agent, Complexity, Effort, Priority, Risk, Source, Type
Repository labels: Component, Tech
Args:
name: Label name (e.g., 'Type/Bug', 'Component/Backend')
color: Hex color code
description: Optional label description
repo: Repository in 'owner/repo' format
Returns:
Created label dictionary with 'level' key, or 'skipped' if already exists
"""
loop = asyncio.get_event_loop()
target_repo = repo or self.gitea.repo
if not target_repo or '/' not in target_repo:
raise ValueError("Use 'owner/repo' format (e.g. 'org/repo-name')")
owner = target_repo.split('/')[0]
is_org = await loop.run_in_executor(
None,
lambda: self.gitea.is_org_repo(target_repo)
)
# Fetch existing labels to check for duplicates
existing_labels = await self.get_labels(target_repo)
all_existing = existing_labels.get('organization', []) + existing_labels.get('repository', [])
existing_names = [label['name'].lower() for label in all_existing]
# Normalize the new label name for comparison
name_normalized = name.lower()
# Also check for format variations (Type/Bug vs Type: Bug)
name_variations = [name_normalized]
if '/' in name:
name_variations.append(name.replace('/', ': ').lower())
name_variations.append(name.replace('/', ':').lower())
elif ': ' in name:
name_variations.append(name.replace(': ', '/').lower())
elif ':' in name:
name_variations.append(name.replace(':', '/').lower())
# Check if label already exists in any format
for variation in name_variations:
if variation in existing_names:
logger.info(f"Label '{name}' already exists (found as '{variation}'), skipping")
return {
'name': name,
'skipped': True,
'reason': f"Label already exists",
'level': 'existing'
}
# Parse category from label name
category = None
if '/' in name:
category = name.split('/')[0].lower().rstrip('s')
elif ':' in name:
category = name.split(':')[0].strip().lower().rstrip('s')
# If it's an org repo and the category is an org-level category, create at org level
if is_org and category in self.ORG_LABEL_CATEGORIES:
result = await loop.run_in_executor(
None,
lambda: self.gitea.create_org_label(owner, name, color, description)
)
# Handle unexpected response types (API may return list or non-dict)
if not isinstance(result, dict):
logger.error(f"Unexpected API response type for org label: {type(result)} - {result}")
return {
'name': name,
'error': True,
'reason': f"API returned {type(result).__name__} instead of dict: {result}",
'level': 'organization'
}
result['level'] = 'organization'
result['skipped'] = False
logger.info(f"Created organization label '{name}' in {owner}")
else:
# Create at repo level
result = await loop.run_in_executor(
None,
lambda: self.gitea.create_label(name, color, description, target_repo)
)
# Handle unexpected response types (API may return list or non-dict)
if not isinstance(result, dict):
logger.error(f"Unexpected API response type for repo label: {type(result)} - {result}")
return {
'name': name,
'error': True,
'reason': f"API returned {type(result).__name__} instead of dict: {result}",
'level': 'repository'
}
result['level'] = 'repository'
result['skipped'] = False
logger.info(f"Created repository label '{name}' in {target_repo}")
return result

View File

@@ -149,3 +149,112 @@ def test_mode_detection_company(tmp_path, monkeypatch):
assert result['mode'] == 'company'
assert result['repo'] is None
# ========================================
# GIT URL PARSING TESTS
# ========================================
def test_parse_git_url_ssh_format():
"""Test parsing SSH format git URL"""
config = GiteaConfig()
# SSH with port: ssh://git@host:port/owner/repo.git
url = "ssh://git@hotserv.tailc9b278.ts.net:2222/personal-projects/personal-portfolio.git"
result = config._parse_git_url(url)
assert result == "personal-projects/personal-portfolio"
def test_parse_git_url_ssh_short_format():
"""Test parsing SSH short format git URL"""
config = GiteaConfig()
# SSH short: git@host:owner/repo.git
url = "git@github.com:owner/repo.git"
result = config._parse_git_url(url)
assert result == "owner/repo"
def test_parse_git_url_https_format():
"""Test parsing HTTPS format git URL"""
config = GiteaConfig()
# HTTPS: https://host/owner/repo.git
url = "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git"
result = config._parse_git_url(url)
assert result == "personal-projects/leo-claude-mktplace"
def test_parse_git_url_http_format():
"""Test parsing HTTP format git URL"""
config = GiteaConfig()
# HTTP: http://host/owner/repo.git
url = "http://gitea.hotserv.cloud/personal-projects/repo.git"
result = config._parse_git_url(url)
assert result == "personal-projects/repo"
def test_parse_git_url_without_git_suffix():
"""Test parsing git URL without .git suffix"""
config = GiteaConfig()
url = "https://github.com/owner/repo"
result = config._parse_git_url(url)
assert result == "owner/repo"
def test_parse_git_url_invalid_format():
"""Test parsing invalid git URL returns None"""
config = GiteaConfig()
url = "not-a-valid-url"
result = config._parse_git_url(url)
assert result is None
def test_find_project_directory_from_env(tmp_path, monkeypatch):
"""Test finding project directory from CLAUDE_PROJECT_DIR env var"""
project_dir = tmp_path / 'my-project'
project_dir.mkdir()
(project_dir / '.git').mkdir()
monkeypatch.setenv('CLAUDE_PROJECT_DIR', str(project_dir))
config = GiteaConfig()
result = config._find_project_directory()
assert result == project_dir
def test_find_project_directory_from_cwd(tmp_path, monkeypatch):
"""Test finding project directory from cwd with .env file"""
project_dir = tmp_path / 'project'
project_dir.mkdir()
(project_dir / '.env').write_text("GITEA_REPO=test/repo")
monkeypatch.chdir(project_dir)
# Clear env vars that might interfere
monkeypatch.delenv('CLAUDE_PROJECT_DIR', raising=False)
monkeypatch.delenv('PWD', raising=False)
config = GiteaConfig()
result = config._find_project_directory()
assert result == project_dir
def test_find_project_directory_none_when_no_markers(tmp_path, monkeypatch):
"""Test returns None when no project markers found"""
empty_dir = tmp_path / 'empty'
empty_dir.mkdir()
monkeypatch.chdir(empty_dir)
monkeypatch.delenv('CLAUDE_PROJECT_DIR', raising=False)
monkeypatch.delenv('PWD', raising=False)
monkeypatch.delenv('GITEA_REPO', raising=False)
config = GiteaConfig()
result = config._find_project_directory()
assert result is None

View File

@@ -222,3 +222,47 @@ def test_no_repo_specified_error(gitea_client):
client.list_issues()
assert "Repository not specified" in str(exc_info.value)
# ========================================
# ORGANIZATION DETECTION TESTS
# ========================================
def test_is_organization_true(gitea_client):
"""Test _is_organization returns True for valid organization"""
mock_response = Mock()
mock_response.status_code = 200
with patch.object(gitea_client.session, 'get', return_value=mock_response):
result = gitea_client._is_organization('personal-projects')
assert result is True
gitea_client.session.get.assert_called_once_with(
'https://test.com/api/v1/orgs/personal-projects'
)
def test_is_organization_false(gitea_client):
"""Test _is_organization returns False for user account"""
mock_response = Mock()
mock_response.status_code = 404
with patch.object(gitea_client.session, 'get', return_value=mock_response):
result = gitea_client._is_organization('lmiranda')
assert result is False
def test_is_org_repo_uses_orgs_endpoint(gitea_client):
"""Test is_org_repo uses /orgs endpoint instead of owner.type"""
mock_response = Mock()
mock_response.status_code = 200
with patch.object(gitea_client.session, 'get', return_value=mock_response):
result = gitea_client.is_org_repo('personal-projects/repo')
assert result is True
# Should call /orgs/personal-projects, not /repos/.../
gitea_client.session.get.assert_called_once_with(
'https://test.com/api/v1/orgs/personal-projects'
)

View File

@@ -10,7 +10,8 @@ from mcp_server.tools.labels import LabelTools
def mock_gitea_client():
"""Fixture providing mocked Gitea client"""
client = Mock()
client.repo = 'test_repo'
client.repo = 'test_org/test_repo'
client.is_org_repo = Mock(return_value=True)
return client
@@ -39,10 +40,141 @@ async def test_get_labels(label_tools):
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
async def test_suggest_labels_bug():
"""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"
suggestions = await tools.suggest_labels(context)
@@ -55,7 +187,8 @@ async def test_suggest_labels_bug():
@pytest.mark.asyncio
async def test_suggest_labels_feature():
"""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"
suggestions = await tools.suggest_labels(context)
@@ -67,7 +200,8 @@ async def test_suggest_labels_feature():
@pytest.mark.asyncio
async def test_suggest_labels_refactor():
"""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"
suggestions = await tools.suggest_labels(context)
@@ -79,7 +213,8 @@ async def test_suggest_labels_refactor():
@pytest.mark.asyncio
async def test_suggest_labels_documentation():
"""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"
suggestions = await tools.suggest_labels(context)
@@ -91,7 +226,8 @@ async def test_suggest_labels_documentation():
@pytest.mark.asyncio
async def test_suggest_labels_priority():
"""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
context = "Urgent blocker in production"
@@ -112,7 +248,8 @@ async def test_suggest_labels_priority():
@pytest.mark.asyncio
async def test_suggest_labels_complexity():
"""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
context = "Simple quick fix for typo"
@@ -128,7 +265,8 @@ async def test_suggest_labels_complexity():
@pytest.mark.asyncio
async def test_suggest_labels_efforts():
"""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
context = "Tiny fix that takes 1 hour"
@@ -144,7 +282,8 @@ async def test_suggest_labels_efforts():
@pytest.mark.asyncio
async def test_suggest_labels_components():
"""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
context = "Update backend API service"
@@ -166,7 +305,8 @@ async def test_suggest_labels_components():
@pytest.mark.asyncio
async def test_suggest_labels_tech_stack():
"""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
context = "Update Python FastAPI endpoint"
@@ -188,7 +328,8 @@ async def test_suggest_labels_tech_stack():
@pytest.mark.asyncio
async def test_suggest_labels_source():
"""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
context = "Issue found in development environment"
@@ -204,7 +345,8 @@ async def test_suggest_labels_source():
@pytest.mark.asyncio
async def test_suggest_labels_risk():
"""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
context = "Breaking change to major API"
@@ -220,7 +362,15 @@ async def test_suggest_labels_risk():
@pytest.mark.asyncio
async def test_suggest_labels_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 = """
Urgent critical bug in production backend API service.
@@ -244,3 +394,85 @@ async def test_suggest_labels_multiple_categories():
# Should have Source
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
async def test_get_labels_org_owned_repo():
"""Test getting labels for organization-owned repository"""
mock_client = Mock()
mock_client.repo = 'myorg/myrepo'
mock_client.is_org_repo = Mock(return_value=True)
mock_client.get_org_labels = Mock(return_value=[
{'name': 'Type/Bug', 'id': 1},
{'name': 'Type/Feature', 'id': 2}
])
mock_client.get_labels = Mock(return_value=[
{'name': 'Component/Backend', 'id': 3}
])
tools = LabelTools(mock_client)
result = await tools.get_labels()
# Should fetch both org and repo labels
mock_client.is_org_repo.assert_called_once_with('myorg/myrepo')
mock_client.get_org_labels.assert_called_once_with('myorg')
mock_client.get_labels.assert_called_once_with('myorg/myrepo')
assert len(result['organization']) == 2
assert len(result['repository']) == 1
assert result['total_count'] == 3
@pytest.mark.asyncio
async def test_get_labels_user_owned_repo():
"""Test getting labels for user-owned repository (no org labels)"""
mock_client = Mock()
mock_client.repo = 'lmiranda/personal-portfolio'
mock_client.is_org_repo = Mock(return_value=False)
mock_client.get_labels = Mock(return_value=[
{'name': 'bug', 'id': 1},
{'name': 'enhancement', 'id': 2}
])
tools = LabelTools(mock_client)
result = await tools.get_labels()
# Should check if org repo
mock_client.is_org_repo.assert_called_once_with('lmiranda/personal-portfolio')
# Should NOT call get_org_labels for user-owned repos
mock_client.get_org_labels.assert_not_called()
# Should still get repo labels
mock_client.get_labels.assert_called_once_with('lmiranda/personal-portfolio')
assert len(result['organization']) == 0
assert len(result['repository']) == 2
assert result['total_count'] == 2

View File

@@ -294,4 +294,4 @@ logging.basicConfig(level=logging.DEBUG)
## License
MIT License - Part of the Claude Code Marketplace (`support-claude-mktplace`).
MIT License - Part of the Leo Claude Marketplace.

View File

@@ -4,6 +4,7 @@ NetBox API client for interacting with NetBox REST API.
Provides a generic HTTP client with methods for all standard REST operations.
Individual tool modules use this client for their specific endpoints.
"""
import json
import requests
import logging
from typing import List, Dict, Optional, Any, Union
@@ -83,7 +84,20 @@ class NetBoxClient:
if response.status_code == 204 or not response.content:
return None
# Parse JSON with diagnostic error handling
try:
return response.json()
except json.JSONDecodeError as e:
logger.error(
f"JSON decode failed. Status: {response.status_code}, "
f"Content-Length: {len(response.content)}, "
f"Content preview: {response.content[:200]!r}"
)
raise ValueError(
f"Invalid JSON response from NetBox: {e}. "
f"Status code: {response.status_code}, "
f"Content length: {len(response.content)} bytes"
) from e
def list(
self,

View File

@@ -103,7 +103,19 @@ TOOL_DEFINITIONS = {
'properties': {
'id': {'type': 'integer', 'description': 'Site ID'},
'name': {'type': 'string', 'description': 'New name'},
'status': {'type': 'string', 'description': 'New status'}
'slug': {'type': 'string', 'description': 'New slug'},
'status': {'type': 'string', 'description': 'Status'},
'region': {'type': 'integer', 'description': 'Region ID'},
'group': {'type': 'integer', 'description': 'Site group ID'},
'tenant': {'type': 'integer', 'description': 'Tenant ID'},
'facility': {'type': 'string', 'description': 'Facility name'},
'time_zone': {'type': 'string', 'description': 'Time zone'},
'description': {'type': 'string', 'description': 'Description'},
'physical_address': {'type': 'string', 'description': 'Physical address'},
'shipping_address': {'type': 'string', 'description': 'Shipping address'},
'latitude': {'type': 'number', 'description': 'Latitude'},
'longitude': {'type': 'number', 'description': 'Longitude'},
'comments': {'type': 'string', 'description': 'Comments'}
},
'required': ['id']
},
@@ -136,7 +148,14 @@ TOOL_DEFINITIONS = {
},
'dcim_update_location': {
'description': 'Update an existing location',
'properties': {'id': {'type': 'integer', 'description': 'Location ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'Location ID'},
'name': {'type': 'string', 'description': 'New name'},
'slug': {'type': 'string', 'description': 'New slug'},
'site': {'type': 'integer', 'description': 'Site ID'},
'parent': {'type': 'integer', 'description': 'Parent location ID'},
'description': {'type': 'string', 'description': 'Description'}
},
'required': ['id']
},
'dcim_delete_location': {
@@ -171,7 +190,18 @@ TOOL_DEFINITIONS = {
},
'dcim_update_rack': {
'description': 'Update an existing rack',
'properties': {'id': {'type': 'integer', 'description': 'Rack ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'Rack ID'},
'name': {'type': 'string', 'description': 'New name'},
'site': {'type': 'integer', 'description': 'Site ID'},
'location': {'type': 'integer', 'description': 'Location ID'},
'status': {'type': 'string', 'description': 'Status'},
'role': {'type': 'integer', 'description': 'Role ID'},
'tenant': {'type': 'integer', 'description': 'Tenant ID'},
'u_height': {'type': 'integer', 'description': 'Rack height in U'},
'description': {'type': 'string', 'description': 'Description'},
'comments': {'type': 'string', 'description': 'Comments'}
},
'required': ['id']
},
'dcim_delete_rack': {
@@ -198,7 +228,12 @@ TOOL_DEFINITIONS = {
},
'dcim_update_manufacturer': {
'description': 'Update an existing manufacturer',
'properties': {'id': {'type': 'integer', 'description': 'Manufacturer ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'Manufacturer ID'},
'name': {'type': 'string', 'description': 'New name'},
'slug': {'type': 'string', 'description': 'New slug'},
'description': {'type': 'string', 'description': 'Description'}
},
'required': ['id']
},
'dcim_delete_manufacturer': {
@@ -230,7 +265,16 @@ TOOL_DEFINITIONS = {
},
'dcim_update_device_type': {
'description': 'Update an existing device type',
'properties': {'id': {'type': 'integer', 'description': 'Device type ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'Device type ID'},
'manufacturer': {'type': 'integer', 'description': 'Manufacturer ID'},
'model': {'type': 'string', 'description': 'Model name'},
'slug': {'type': 'string', 'description': 'New slug'},
'u_height': {'type': 'number', 'description': 'Height in rack units'},
'is_full_depth': {'type': 'boolean', 'description': 'Is full depth'},
'description': {'type': 'string', 'description': 'Description'},
'comments': {'type': 'string', 'description': 'Comments'}
},
'required': ['id']
},
'dcim_delete_device_type': {
@@ -259,7 +303,14 @@ TOOL_DEFINITIONS = {
},
'dcim_update_device_role': {
'description': 'Update an existing device role',
'properties': {'id': {'type': 'integer', 'description': 'Device role ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'Device role ID'},
'name': {'type': 'string', 'description': 'New name'},
'slug': {'type': 'string', 'description': 'New slug'},
'color': {'type': 'string', 'description': 'Hex color code'},
'vm_role': {'type': 'boolean', 'description': 'Can be assigned to VMs'},
'description': {'type': 'string', 'description': 'Description'}
},
'required': ['id']
},
'dcim_delete_device_role': {
@@ -290,7 +341,13 @@ TOOL_DEFINITIONS = {
},
'dcim_update_platform': {
'description': 'Update an existing platform',
'properties': {'id': {'type': 'integer', 'description': 'Platform ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'Platform ID'},
'name': {'type': 'string', 'description': 'New name'},
'slug': {'type': 'string', 'description': 'New slug'},
'manufacturer': {'type': 'integer', 'description': 'Manufacturer ID'},
'description': {'type': 'string', 'description': 'Description'}
},
'required': ['id']
},
'dcim_delete_platform': {
@@ -326,7 +383,13 @@ TOOL_DEFINITIONS = {
'status': {'type': 'string', 'description': 'Device status'},
'rack': {'type': 'integer', 'description': 'Rack ID'},
'position': {'type': 'number', 'description': 'Position in rack'},
'serial': {'type': 'string', 'description': 'Serial number'}
'serial': {'type': 'string', 'description': 'Serial number'},
'platform': {'type': 'integer', 'description': 'Platform ID'},
'primary_ip4': {'type': 'integer', 'description': 'Primary IPv4 address ID'},
'primary_ip6': {'type': 'integer', 'description': 'Primary IPv6 address ID'},
'asset_tag': {'type': 'string', 'description': 'Asset tag'},
'description': {'type': 'string', 'description': 'Description'},
'comments': {'type': 'string', 'description': 'Comments'}
},
'required': ['name', 'device_type', 'role', 'site']
},
@@ -335,7 +398,17 @@ TOOL_DEFINITIONS = {
'properties': {
'id': {'type': 'integer', 'description': 'Device ID'},
'name': {'type': 'string', 'description': 'New name'},
'status': {'type': 'string', 'description': 'New status'}
'status': {'type': 'string', 'description': 'New status'},
'platform': {'type': 'integer', 'description': 'Platform ID'},
'primary_ip4': {'type': 'integer', 'description': 'Primary IPv4 address ID'},
'primary_ip6': {'type': 'integer', 'description': 'Primary IPv6 address ID'},
'serial': {'type': 'string', 'description': 'Serial number'},
'asset_tag': {'type': 'string', 'description': 'Asset tag'},
'site': {'type': 'integer', 'description': 'Site ID'},
'rack': {'type': 'integer', 'description': 'Rack ID'},
'position': {'type': 'number', 'description': 'Position in rack'},
'description': {'type': 'string', 'description': 'Description'},
'comments': {'type': 'string', 'description': 'Comments'}
},
'required': ['id']
},
@@ -370,7 +443,18 @@ TOOL_DEFINITIONS = {
},
'dcim_update_interface': {
'description': 'Update an existing interface',
'properties': {'id': {'type': 'integer', 'description': 'Interface ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'Interface ID'},
'name': {'type': 'string', 'description': 'New name'},
'type': {'type': 'string', 'description': 'Interface type'},
'enabled': {'type': 'boolean', 'description': 'Interface enabled'},
'mtu': {'type': 'integer', 'description': 'MTU'},
'mac_address': {'type': 'string', 'description': 'MAC address'},
'description': {'type': 'string', 'description': 'Description'},
'mode': {'type': 'string', 'description': 'VLAN mode'},
'untagged_vlan': {'type': 'integer', 'description': 'Untagged VLAN ID'},
'tagged_vlans': {'type': 'array', 'description': 'Tagged VLAN IDs'}
},
'required': ['id']
},
'dcim_delete_interface': {
@@ -404,7 +488,15 @@ TOOL_DEFINITIONS = {
},
'dcim_update_cable': {
'description': 'Update an existing cable',
'properties': {'id': {'type': 'integer', 'description': 'Cable ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'Cable ID'},
'type': {'type': 'string', 'description': 'Cable type'},
'status': {'type': 'string', 'description': 'Cable status'},
'label': {'type': 'string', 'description': 'Cable label'},
'color': {'type': 'string', 'description': 'Cable color'},
'length': {'type': 'number', 'description': 'Cable length'},
'length_unit': {'type': 'string', 'description': 'Length unit'}
},
'required': ['id']
},
'dcim_delete_cable': {
@@ -492,7 +584,15 @@ TOOL_DEFINITIONS = {
},
'ipam_update_vrf': {
'description': 'Update an existing VRF',
'properties': {'id': {'type': 'integer', 'description': 'VRF ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'VRF ID'},
'name': {'type': 'string', 'description': 'New name'},
'rd': {'type': 'string', 'description': 'Route distinguisher'},
'tenant': {'type': 'integer', 'description': 'Tenant ID'},
'enforce_unique': {'type': 'boolean', 'description': 'Enforce unique IPs'},
'description': {'type': 'string', 'description': 'Description'},
'comments': {'type': 'string', 'description': 'Comments'}
},
'required': ['id']
},
'ipam_delete_vrf': {
@@ -531,7 +631,19 @@ TOOL_DEFINITIONS = {
},
'ipam_update_prefix': {
'description': 'Update an existing prefix',
'properties': {'id': {'type': 'integer', 'description': 'Prefix ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'Prefix ID'},
'prefix': {'type': 'string', 'description': 'Prefix in CIDR notation'},
'status': {'type': 'string', 'description': 'Status'},
'site': {'type': 'integer', 'description': 'Site ID'},
'vrf': {'type': 'integer', 'description': 'VRF ID'},
'vlan': {'type': 'integer', 'description': 'VLAN ID'},
'role': {'type': 'integer', 'description': 'Role ID'},
'tenant': {'type': 'integer', 'description': 'Tenant ID'},
'is_pool': {'type': 'boolean', 'description': 'Is a pool'},
'description': {'type': 'string', 'description': 'Description'},
'comments': {'type': 'string', 'description': 'Comments'}
},
'required': ['id']
},
'ipam_delete_prefix': {
@@ -582,7 +694,18 @@ TOOL_DEFINITIONS = {
},
'ipam_update_ip_address': {
'description': 'Update an existing IP address',
'properties': {'id': {'type': 'integer', 'description': 'IP address ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'IP address ID'},
'address': {'type': 'string', 'description': 'IP address with prefix length'},
'status': {'type': 'string', 'description': 'Status'},
'vrf': {'type': 'integer', 'description': 'VRF ID'},
'tenant': {'type': 'integer', 'description': 'Tenant ID'},
'dns_name': {'type': 'string', 'description': 'DNS name'},
'description': {'type': 'string', 'description': 'Description'},
'comments': {'type': 'string', 'description': 'Comments'},
'assigned_object_type': {'type': 'string', 'description': 'Object type to assign to'},
'assigned_object_id': {'type': 'integer', 'description': 'Object ID to assign to'}
},
'required': ['id']
},
'ipam_delete_ip_address': {
@@ -647,7 +770,18 @@ TOOL_DEFINITIONS = {
},
'ipam_update_vlan': {
'description': 'Update an existing VLAN',
'properties': {'id': {'type': 'integer', 'description': 'VLAN ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'VLAN ID'},
'vid': {'type': 'integer', 'description': 'VLAN ID number'},
'name': {'type': 'string', 'description': 'VLAN name'},
'status': {'type': 'string', 'description': 'Status'},
'site': {'type': 'integer', 'description': 'Site ID'},
'group': {'type': 'integer', 'description': 'VLAN group ID'},
'tenant': {'type': 'integer', 'description': 'Tenant ID'},
'role': {'type': 'integer', 'description': 'Role ID'},
'description': {'type': 'string', 'description': 'Description'},
'comments': {'type': 'string', 'description': 'Comments'}
},
'required': ['id']
},
'ipam_delete_vlan': {
@@ -757,16 +891,17 @@ TOOL_DEFINITIONS = {
'properties': {'id': {'type': 'integer', 'description': 'Provider ID'}},
'required': ['id']
},
'circuits_list_circuit_types': {
# NOTE: circuit_types tools shortened to meet 28-char limit
'circ_list_types': {
'description': 'List all circuit types in NetBox',
'properties': {'name': {'type': 'string', 'description': 'Filter by name'}}
},
'circuits_get_circuit_type': {
'circ_get_type': {
'description': 'Get a specific circuit type by ID',
'properties': {'id': {'type': 'integer', 'description': 'Circuit type ID'}},
'required': ['id']
},
'circuits_create_circuit_type': {
'circ_create_type': {
'description': 'Create a new circuit type',
'properties': {
'name': {'type': 'string', 'description': 'Type name'},
@@ -809,19 +944,20 @@ TOOL_DEFINITIONS = {
'properties': {'id': {'type': 'integer', 'description': 'Circuit ID'}},
'required': ['id']
},
'circuits_list_circuit_terminations': {
# NOTE: circuit_terminations tools shortened to meet 28-char limit
'circ_list_terminations': {
'description': 'List all circuit terminations in NetBox',
'properties': {
'circuit_id': {'type': 'integer', 'description': 'Filter by circuit ID'},
'site_id': {'type': 'integer', 'description': 'Filter by site ID'}
}
},
'circuits_get_circuit_termination': {
'circ_get_termination': {
'description': 'Get a specific circuit termination by ID',
'properties': {'id': {'type': 'integer', 'description': 'Termination ID'}},
'required': ['id']
},
'circuits_create_circuit_termination': {
'circ_create_termination': {
'description': 'Create a new circuit termination',
'properties': {
'circuit': {'type': 'integer', 'description': 'Circuit ID'},
@@ -832,16 +968,18 @@ TOOL_DEFINITIONS = {
},
# ==================== Virtualization Tools ====================
'virtualization_list_cluster_types': {
# NOTE: Tool names shortened from 'virtualization_' to 'virt_' to meet
# 28-char limit (Claude API 64-char limit minus 36-char prefix)
'virt_list_cluster_types': {
'description': 'List all cluster types in NetBox',
'properties': {'name': {'type': 'string', 'description': 'Filter by name'}}
},
'virtualization_get_cluster_type': {
'virt_get_cluster_type': {
'description': 'Get a specific cluster type by ID',
'properties': {'id': {'type': 'integer', 'description': 'Cluster type ID'}},
'required': ['id']
},
'virtualization_create_cluster_type': {
'virt_create_cluster_type': {
'description': 'Create a new cluster type',
'properties': {
'name': {'type': 'string', 'description': 'Type name'},
@@ -849,16 +987,16 @@ TOOL_DEFINITIONS = {
},
'required': ['name', 'slug']
},
'virtualization_list_cluster_groups': {
'virt_list_cluster_groups': {
'description': 'List all cluster groups in NetBox',
'properties': {'name': {'type': 'string', 'description': 'Filter by name'}}
},
'virtualization_get_cluster_group': {
'virt_get_cluster_group': {
'description': 'Get a specific cluster group by ID',
'properties': {'id': {'type': 'integer', 'description': 'Cluster group ID'}},
'required': ['id']
},
'virtualization_create_cluster_group': {
'virt_create_cluster_group': {
'description': 'Create a new cluster group',
'properties': {
'name': {'type': 'string', 'description': 'Group name'},
@@ -866,7 +1004,7 @@ TOOL_DEFINITIONS = {
},
'required': ['name', 'slug']
},
'virtualization_list_clusters': {
'virt_list_clusters': {
'description': 'List all clusters in NetBox',
'properties': {
'name': {'type': 'string', 'description': 'Filter by name'},
@@ -875,12 +1013,12 @@ TOOL_DEFINITIONS = {
'site_id': {'type': 'integer', 'description': 'Filter by site ID'}
}
},
'virtualization_get_cluster': {
'virt_get_cluster': {
'description': 'Get a specific cluster by ID',
'properties': {'id': {'type': 'integer', 'description': 'Cluster ID'}},
'required': ['id']
},
'virtualization_create_cluster': {
'virt_create_cluster': {
'description': 'Create a new cluster',
'properties': {
'name': {'type': 'string', 'description': 'Cluster name'},
@@ -891,17 +1029,27 @@ TOOL_DEFINITIONS = {
},
'required': ['name', 'type']
},
'virtualization_update_cluster': {
'virt_update_cluster': {
'description': 'Update an existing cluster',
'properties': {'id': {'type': 'integer', 'description': 'Cluster ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'Cluster ID'},
'name': {'type': 'string', 'description': 'New name'},
'type': {'type': 'integer', 'description': 'Cluster type ID'},
'group': {'type': 'integer', 'description': 'Cluster group ID'},
'site': {'type': 'integer', 'description': 'Site ID'},
'status': {'type': 'string', 'description': 'Status'},
'tenant': {'type': 'integer', 'description': 'Tenant ID'},
'description': {'type': 'string', 'description': 'Description'},
'comments': {'type': 'string', 'description': 'Comments'}
},
'required': ['id']
},
'virtualization_delete_cluster': {
'virt_delete_cluster': {
'description': 'Delete a cluster',
'properties': {'id': {'type': 'integer', 'description': 'Cluster ID'}},
'required': ['id']
},
'virtualization_list_virtual_machines': {
'virt_list_vms': {
'description': 'List all virtual machines in NetBox',
'properties': {
'name': {'type': 'string', 'description': 'Filter by name'},
@@ -910,12 +1058,12 @@ TOOL_DEFINITIONS = {
'status': {'type': 'string', 'description': 'Filter by status'}
}
},
'virtualization_get_virtual_machine': {
'virt_get_vm': {
'description': 'Get a specific virtual machine by ID',
'properties': {'id': {'type': 'integer', 'description': 'VM ID'}},
'required': ['id']
},
'virtualization_create_virtual_machine': {
'virt_create_vm': {
'description': 'Create a new virtual machine',
'properties': {
'name': {'type': 'string', 'description': 'VM name'},
@@ -928,29 +1076,45 @@ TOOL_DEFINITIONS = {
},
'required': ['name']
},
'virtualization_update_virtual_machine': {
'virt_update_vm': {
'description': 'Update an existing virtual machine',
'properties': {'id': {'type': 'integer', 'description': 'VM ID'}},
'properties': {
'id': {'type': 'integer', 'description': 'VM ID'},
'name': {'type': 'string', 'description': 'New name'},
'status': {'type': 'string', 'description': 'Status'},
'cluster': {'type': 'integer', 'description': 'Cluster ID'},
'site': {'type': 'integer', 'description': 'Site ID'},
'role': {'type': 'integer', 'description': 'Role ID'},
'tenant': {'type': 'integer', 'description': 'Tenant ID'},
'platform': {'type': 'integer', 'description': 'Platform ID'},
'vcpus': {'type': 'number', 'description': 'Number of vCPUs'},
'memory': {'type': 'integer', 'description': 'Memory in MB'},
'disk': {'type': 'integer', 'description': 'Disk in GB'},
'primary_ip4': {'type': 'integer', 'description': 'Primary IPv4 address ID'},
'primary_ip6': {'type': 'integer', 'description': 'Primary IPv6 address ID'},
'description': {'type': 'string', 'description': 'Description'},
'comments': {'type': 'string', 'description': 'Comments'}
},
'required': ['id']
},
'virtualization_delete_virtual_machine': {
'virt_delete_vm': {
'description': 'Delete a virtual machine',
'properties': {'id': {'type': 'integer', 'description': 'VM ID'}},
'required': ['id']
},
'virtualization_list_vm_interfaces': {
'virt_list_vm_ifaces': {
'description': 'List all VM interfaces in NetBox',
'properties': {
'virtual_machine_id': {'type': 'integer', 'description': 'Filter by VM ID'},
'name': {'type': 'string', 'description': 'Filter by name'}
}
},
'virtualization_get_vm_interface': {
'virt_get_vm_iface': {
'description': 'Get a specific VM interface by ID',
'properties': {'id': {'type': 'integer', 'description': 'Interface ID'}},
'required': ['id']
},
'virtualization_create_vm_interface': {
'virt_create_vm_iface': {
'description': 'Create a new VM interface',
'properties': {
'virtual_machine': {'type': 'integer', 'description': 'VM ID'},
@@ -1088,16 +1252,18 @@ TOOL_DEFINITIONS = {
},
# ==================== Wireless Tools ====================
'wireless_list_wireless_lan_groups': {
# NOTE: Tool names shortened from 'wireless_' to 'wlan_' to meet
# 28-char limit (Claude API 64-char limit minus 36-char prefix)
'wlan_list_groups': {
'description': 'List all wireless LAN groups in NetBox',
'properties': {'name': {'type': 'string', 'description': 'Filter by name'}}
},
'wireless_get_wireless_lan_group': {
'wlan_get_group': {
'description': 'Get a specific wireless LAN group by ID',
'properties': {'id': {'type': 'integer', 'description': 'WLAN group ID'}},
'required': ['id']
},
'wireless_create_wireless_lan_group': {
'wlan_create_group': {
'description': 'Create a new wireless LAN group',
'properties': {
'name': {'type': 'string', 'description': 'Group name'},
@@ -1105,7 +1271,7 @@ TOOL_DEFINITIONS = {
},
'required': ['name', 'slug']
},
'wireless_list_wireless_lans': {
'wlan_list_lans': {
'description': 'List all wireless LANs in NetBox',
'properties': {
'ssid': {'type': 'string', 'description': 'Filter by SSID'},
@@ -1113,12 +1279,12 @@ TOOL_DEFINITIONS = {
'status': {'type': 'string', 'description': 'Filter by status'}
}
},
'wireless_get_wireless_lan': {
'wlan_get_lan': {
'description': 'Get a specific wireless LAN by ID',
'properties': {'id': {'type': 'integer', 'description': 'WLAN ID'}},
'required': ['id']
},
'wireless_create_wireless_lan': {
'wlan_create_lan': {
'description': 'Create a new wireless LAN',
'properties': {
'ssid': {'type': 'string', 'description': 'SSID'},
@@ -1128,14 +1294,14 @@ TOOL_DEFINITIONS = {
},
'required': ['ssid']
},
'wireless_list_wireless_links': {
'wlan_list_links': {
'description': 'List all wireless links in NetBox',
'properties': {
'ssid': {'type': 'string', 'description': 'Filter by SSID'},
'status': {'type': 'string', 'description': 'Filter by status'}
}
},
'wireless_get_wireless_link': {
'wlan_get_link': {
'description': 'Get a specific wireless link by ID',
'properties': {'id': {'type': 'integer', 'description': 'Link ID'}},
'required': ['id']
@@ -1241,6 +1407,52 @@ TOOL_DEFINITIONS = {
}
# Map shortened tool names to (category, method_name) for routing.
# This is necessary because tool names were shortened to meet the 28-character
# limit imposed by Claude API's 64-character tool name limit minus the
# 36-character prefix used by Claude Code for MCP tools.
TOOL_NAME_MAP = {
# Virtualization tools (virt_ -> virtualization category)
'virt_list_cluster_types': ('virtualization', 'list_cluster_types'),
'virt_get_cluster_type': ('virtualization', 'get_cluster_type'),
'virt_create_cluster_type': ('virtualization', 'create_cluster_type'),
'virt_list_cluster_groups': ('virtualization', 'list_cluster_groups'),
'virt_get_cluster_group': ('virtualization', 'get_cluster_group'),
'virt_create_cluster_group': ('virtualization', 'create_cluster_group'),
'virt_list_clusters': ('virtualization', 'list_clusters'),
'virt_get_cluster': ('virtualization', 'get_cluster'),
'virt_create_cluster': ('virtualization', 'create_cluster'),
'virt_update_cluster': ('virtualization', 'update_cluster'),
'virt_delete_cluster': ('virtualization', 'delete_cluster'),
'virt_list_vms': ('virtualization', 'list_virtual_machines'),
'virt_get_vm': ('virtualization', 'get_virtual_machine'),
'virt_create_vm': ('virtualization', 'create_virtual_machine'),
'virt_update_vm': ('virtualization', 'update_virtual_machine'),
'virt_delete_vm': ('virtualization', 'delete_virtual_machine'),
'virt_list_vm_ifaces': ('virtualization', 'list_vm_interfaces'),
'virt_get_vm_iface': ('virtualization', 'get_vm_interface'),
'virt_create_vm_iface': ('virtualization', 'create_vm_interface'),
# Circuits tools (circ_ -> circuits category, for shortened names only)
'circ_list_types': ('circuits', 'list_circuit_types'),
'circ_get_type': ('circuits', 'get_circuit_type'),
'circ_create_type': ('circuits', 'create_circuit_type'),
'circ_list_terminations': ('circuits', 'list_circuit_terminations'),
'circ_get_termination': ('circuits', 'get_circuit_termination'),
'circ_create_termination': ('circuits', 'create_circuit_termination'),
# Wireless tools (wlan_ -> wireless category)
'wlan_list_groups': ('wireless', 'list_wireless_lan_groups'),
'wlan_get_group': ('wireless', 'get_wireless_lan_group'),
'wlan_create_group': ('wireless', 'create_wireless_lan_group'),
'wlan_list_lans': ('wireless', 'list_wireless_lans'),
'wlan_get_lan': ('wireless', 'get_wireless_lan'),
'wlan_create_lan': ('wireless', 'create_wireless_lan'),
'wlan_list_links': ('wireless', 'list_wireless_links'),
'wlan_get_link': ('wireless', 'get_wireless_link'),
}
class NetBoxMCPServer:
"""MCP Server for NetBox integration"""
@@ -1314,11 +1526,20 @@ class NetBoxMCPServer:
)]
async def _route_tool(self, name: str, arguments: dict):
"""Route tool call to appropriate handler."""
"""Route tool call to appropriate handler.
Tool names may be shortened (e.g., 'virt_list_vms' instead of
'virtualization_list_virtual_machines') to meet the 28-character
limit. TOOL_NAME_MAP handles the translation to actual method names.
"""
# Check if this is a mapped short name
if name in TOOL_NAME_MAP:
category, method_name = TOOL_NAME_MAP[name]
else:
# Fall back to original logic for unchanged tools
parts = name.split('_', 1)
if len(parts) != 2:
raise ValueError(f"Invalid tool name format: {name}")
category, method_name = parts[0], parts[1]
# Map category to tool class

View File

@@ -6,8 +6,8 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/clarity-assist/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/clarity-assist/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"license": "MIT",
"keywords": [
"prompt-optimization",
@@ -16,30 +16,5 @@
"requirements",
"methodology"
],
"commands": [
{
"name": "clarify",
"description": "Full 4-D prompt optimization (Deconstruct, Diagnose, Develop, Deliver)",
"file": "commands/clarify.md"
},
{
"name": "quick-clarify",
"description": "Rapid mode - single-pass clarification for simple requests",
"file": "commands/quick-clarify.md"
}
],
"agents": [
{
"name": "clarity-coach",
"description": "ND-friendly coach for structured requirement gathering",
"file": "agents/clarity-coach.md"
}
],
"skills": [
{
"name": "prompt-patterns",
"description": "Optimization rules and patterns for effective prompts",
"path": "skills/prompt-patterns"
}
]
"commands": ["./commands/"]
}

View File

@@ -6,8 +6,8 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/claude-config-maintainer/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/claude-config-maintainer/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"license": "MIT",
"keywords": [
"claude-code",
@@ -16,7 +16,5 @@
"claude-md",
"developer-tools"
],
"entryPoint": "agents/maintainer.md",
"commands": ["./commands/"],
"agents": ["./agents/"]
"commands": ["./commands/"]
}

View File

@@ -12,7 +12,7 @@ CLAUDE.md files provide instructions to Claude Code when working with a project.
## Installation
This plugin is part of the support-claude-mktplace collection. Install the marketplace and the plugin will be available.
This plugin is part of the Leo Claude Marketplace. Install the marketplace and the plugin will be available.
## Commands
@@ -96,4 +96,4 @@ Target score: **70+** for effective Claude Code usage.
## Contributing
This plugin is part of the personal-projects/support-claude-mktplace repository.
This plugin is part of the personal-projects/leo-claude-mktplace repository.

View File

@@ -0,0 +1,68 @@
#!/bin/bash
# claude-config-maintainer: enforce mandatory behavior rules
# Checks if CLAUDE.md has the rules, adds them if missing
PREFIX="[claude-config-maintainer]"
# Find CLAUDE.md in current directory or parent
CLAUDE_MD=""
if [ -f "./CLAUDE.md" ]; then
CLAUDE_MD="./CLAUDE.md"
elif [ -f "../CLAUDE.md" ]; then
CLAUDE_MD="../CLAUDE.md"
fi
# If no CLAUDE.md found, exit silently
if [ -z "$CLAUDE_MD" ]; then
exit 0
fi
# Check if mandatory rules exist
if grep -q "MANDATORY BEHAVIOR RULES" "$CLAUDE_MD" 2>/dev/null; then
# Rules exist, all good
exit 0
fi
# Rules missing - add them
RULES='## ⛔ MANDATORY BEHAVIOR RULES - READ FIRST
**These rules are NON-NEGOTIABLE. Violating them wastes the user'\''s time and money.**
### 1. WHEN USER ASKS YOU TO CHECK SOMETHING - CHECK EVERYTHING
- Search ALL locations, not just where you think it is
- Check cache directories: `~/.claude/plugins/cache/`
- Check installed: `~/.claude/plugins/marketplaces/`
- Check source directories
- **NEVER say "no" or "that'\''s not the issue" without exhaustive verification**
### 2. WHEN USER SAYS SOMETHING IS WRONG - BELIEVE THEM
- The user knows their system better than you
- Investigate thoroughly before disagreeing
- **Your confidence is often wrong. User'\''s instincts are often right.**
### 3. NEVER SAY "DONE" WITHOUT VERIFICATION
- Run the actual command/script to verify
- Show the output to the user
- **"Done" means VERIFIED WORKING, not "I made changes"**
### 4. SHOW EXACTLY WHAT USER ASKS FOR
- If user asks for messages, show the MESSAGES
- If user asks for code, show the CODE
- **Do not interpret or summarize unless asked**
**FAILURE TO FOLLOW THESE RULES = WASTED USER TIME = UNACCEPTABLE**
---
'
# Create temp file with rules + existing content
{
head -1 "$CLAUDE_MD"
echo ""
echo "$RULES"
tail -n +2 "$CLAUDE_MD"
} > "${CLAUDE_MD}.tmp"
mv "${CLAUDE_MD}.tmp" "$CLAUDE_MD"
echo "$PREFIX Added mandatory behavior rules to CLAUDE.md"

View File

@@ -0,0 +1,10 @@
{
"hooks": {
"SessionStart": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-rules.sh"
}
]
}
}

View File

@@ -6,8 +6,8 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/cmdb-assistant/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/cmdb-assistant/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"license": "MIT",
"keywords": [
"netbox",
@@ -18,6 +18,5 @@
"dcim"
],
"commands": ["./commands/"],
"agents": ["./agents/"],
"mcpServers": "./.mcp.json"
"mcpServers": ["./.mcp.json"]
}

View File

@@ -111,6 +111,7 @@ cmdb-assistant/
│ └── plugin.json # Plugin manifest
├── .mcp.json # MCP server configuration
├── commands/
│ ├── initial-setup.md # Setup wizard
│ ├── cmdb-search.md # Search command
│ ├── cmdb-device.md # Device management
│ ├── cmdb-ip.md # IP management
@@ -167,4 +168,4 @@ The plugin uses the shared NetBox MCP server at `../mcp-servers/netbox/`.
## License
MIT License - Part of the Claude Code Marketplace.
MIT License - Part of the Leo Claude Marketplace.

View File

@@ -0,0 +1,164 @@
---
description: Interactive setup wizard for cmdb-assistant plugin - configures NetBox MCP server
---
# CMDB Assistant Setup Wizard
This command sets up the cmdb-assistant plugin with NetBox integration.
## Important Context
- **This command uses Bash, Read, Write, and AskUserQuestion tools** - NOT MCP tools
- **MCP tools won't work until after setup + session restart**
- **Uses NetBox MCP server (separate from Gitea MCP)**
---
## Phase 1: Environment Validation
### Step 1.1: Check Python Version
```bash
python3 --version
```
If below 3.10, stop setup and inform user.
---
## Phase 2: MCP Server Setup
### Step 2.1: Locate NetBox MCP Server
```bash
find ~/.claude ~/.config/claude -name "mcp_server" -path "*netbox*" 2>/dev/null | head -5
```
If not found, ask user for marketplace location.
### Step 2.2: Check Virtual Environment
```bash
ls -la /path/to/mcp-servers/netbox/.venv/bin/python 2>/dev/null && echo "VENV_EXISTS" || echo "VENV_MISSING"
```
### Step 2.3: Create Virtual Environment (if missing)
```bash
cd /path/to/mcp-servers/netbox && python3 -m venv .venv && source .venv/bin/activate && pip install --upgrade pip && pip install -r requirements.txt && deactivate
```
---
## Phase 3: System Configuration
### Step 3.1: Create Config Directory
```bash
mkdir -p ~/.config/claude
```
### Step 3.2: Check NetBox Configuration
```bash
cat ~/.config/claude/netbox.env 2>/dev/null || echo "FILE_NOT_FOUND"
```
**If file exists with valid values:** Skip to Phase 4.
**If missing or has placeholders:** Continue.
### Step 3.3: Gather NetBox Information
Use AskUserQuestion:
- Question: "What is your NetBox API URL? (e.g., https://netbox.company.com/api)"
- Header: "NetBox URL"
- Options:
- "Other (I'll provide the URL)"
Ask user to provide the URL.
**Important:** The URL must include `/api` at the end. If the user provides a URL without `/api`, append it automatically.
### Step 3.4: Create Configuration File
```bash
cat > ~/.config/claude/netbox.env << 'EOF'
# NetBox API Configuration
# Generated by cmdb-assistant /initial-setup
NETBOX_API_URL=<USER_PROVIDED_URL>
NETBOX_API_TOKEN=PASTE_YOUR_TOKEN_HERE
EOF
chmod 600 ~/.config/claude/netbox.env
```
### Step 3.5: Token Instructions
---
**Action Required: Add Your NetBox API Token**
I've created `~/.config/claude/netbox.env` but you need to add your API token manually.
**Steps:**
1. Open: `nano ~/.config/claude/netbox.env`
2. Generate token in NetBox: Admin → API Tokens → Add Token
3. Replace `PASTE_YOUR_TOKEN_HERE` with your token
4. Save the file
---
Use AskUserQuestion:
- Question: "Have you added your NetBox token?"
- Header: "Token"
- Options:
- "Yes, I've added the token"
- "Skip for now"
---
## Phase 4: Validation
### Step 4.1: Test Configuration (if token was added)
```bash
source ~/.config/claude/netbox.env && curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Token $NETBOX_API_TOKEN" "$NETBOX_API_URL/"
```
**Note:** The URL already includes `/api`, so we just append `/` for the root API endpoint.
Report result:
- 200: Success
- 403: Invalid token
- Other: Connection issue
### Step 4.2: Summary
```
╔════════════════════════════════════════════════════════════╗
║ CMDB-ASSISTANT SETUP COMPLETE ║
╠════════════════════════════════════════════════════════════╣
║ MCP Server (NetBox): ✓ Ready ║
║ System Config: ✓ ~/.config/claude/netbox.env ║
╚════════════════════════════════════════════════════════════╝
```
### Step 4.3: Session Restart Notice
---
**⚠️ Session Restart Required**
Restart your Claude Code session for MCP tools to become available.
**After restart, you can:**
- Run `/cmdb-device <hostname>` to look up a device
- Run `/cmdb-ip <address>` to look up an IP address
- Run `/cmdb-site <name>` to look up a site
- Run `/cmdb-search <query>` for general search
---
## Note on Project Configuration
cmdb-assistant does not require project-level configuration. The NetBox connection is system-wide and not tied to specific repositories.

View File

@@ -6,8 +6,9 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/code-sentinel/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/code-sentinel/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"license": "MIT",
"keywords": ["security", "refactoring", "code-quality", "static-analysis", "hooks"]
"keywords": ["security", "refactoring", "code-quality", "static-analysis", "hooks"],
"commands": ["./commands/"]
}

View File

@@ -38,7 +38,7 @@ Security scanning and code refactoring tools for Claude Code projects.
## Installation
```bash
/plugin marketplace add https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git
/plugin marketplace add https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git
/plugin install code-sentinel
```

View File

@@ -5,8 +5,8 @@
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"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."
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/security-check.sh"
}
]
}

View File

@@ -0,0 +1,62 @@
#!/bin/bash
# code-sentinel security check hook
# Checks for obvious security issues in code files, skips config/docs
# Command hook - guaranteed predictable behavior
# Read tool input from stdin
INPUT=$(cat)
# Extract file_path from JSON input
FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
# If no file_path, exit silently
if [ -z "$FILE_PATH" ]; then
exit 0
fi
# SKIP config/doc files entirely - exit silently
case "$FILE_PATH" in
*.md|*.json|*.yml|*.yaml|*.txt|*.toml|*.ini|*.cfg|*.conf)
exit 0
;;
*/docs/*|*/README*|*/CHANGELOG*|*/LICENSE*)
exit 0
;;
*/.claude/*|*/.github/*|*/.vscode/*)
exit 0
;;
esac
# For code files, extract content to check
# For Edit tool: check new_string
# For Write tool: check content
CONTENT=$(echo "$INPUT" | grep -o '"new_string"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"new_string"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
if [ -z "$CONTENT" ]; then
CONTENT=$(echo "$INPUT" | grep -o '"content"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"content"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
fi
# If no content to check, exit silently
if [ -z "$CONTENT" ]; then
exit 0
fi
# Check for hardcoded secrets patterns (obvious cases only)
if echo "$CONTENT" | grep -qiE '(api[_-]?key|api[_-]?secret|password|passwd|secret[_-]?key|auth[_-]?token)[[:space:]]*[=:][[:space:]]*["\x27][A-Za-z0-9+/=_-]{20,}["\x27]'; then
echo "[code-sentinel] BLOCKED: Hardcoded secret detected"
exit 1
fi
# Check for AWS keys pattern
if echo "$CONTENT" | grep -qE 'AKIA[A-Z0-9]{16}'; then
echo "[code-sentinel] BLOCKED: AWS access key detected"
exit 1
fi
# Check for private key headers
if echo "$CONTENT" | grep -qE '\-\-\-\-\-BEGIN (RSA |DSA |EC |OPENSSH )?PRIVATE KEY\-\-\-\-\-'; then
echo "[code-sentinel] BLOCKED: Private key detected"
exit 1
fi
# All other cases: exit silently (allow the edit)
exit 0

View File

@@ -6,8 +6,9 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/doc-guardian/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/doc-guardian/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"license": "MIT",
"keywords": ["documentation", "sync", "drift-detection", "automation", "hooks"]
"keywords": ["documentation", "sync", "drift-detection", "automation", "hooks"],
"commands": ["./commands/"]
}

View File

@@ -11,10 +11,10 @@ Documentation gets outdated. Functions get renamed, configs change, versions bum
## Solution
doc-guardian watches your code changes and automatically:
1. Detects when changes affect documentation
2. Queues updates silently (doesn't interrupt your flow)
3. Syncs all doc changes in a single commit when ready
doc-guardian monitors your code changes via hooks:
1. Detects when changes might affect documentation
2. Alerts you to potential drift (doesn't interrupt your flow)
3. Provides commands to audit and sync docs when ready
## Commands
@@ -25,8 +25,7 @@ doc-guardian watches your code changes and automatically:
## Hooks
- **PostToolUse (Write\|Edit)**: Silently checks if code changes affect docs
- **Stop**: Reminds you of pending doc updates before session ends
- **PostToolUse (Write|Edit)**: Silently checks if code changes affect docs
## What It Detects
@@ -37,10 +36,10 @@ doc-guardian watches your code changes and automatically:
## Installation
This plugin is part of the claude-code-marketplace.
This plugin is part of the Leo Claude Marketplace.
```bash
/plugin marketplace add https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git
/plugin marketplace add https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git
/plugin install doc-guardian
```

View File

@@ -5,19 +5,8 @@
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"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."
}
]
}
],
"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?'"
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/notify.sh"
}
]
}

View File

@@ -0,0 +1,23 @@
#!/bin/bash
# doc-guardian notification hook
# Outputs a single notification for config file changes, nothing otherwise
# This is a command hook - guaranteed not to block workflow
# Read tool input from stdin (JSON with file_path)
INPUT=$(cat)
# Extract file_path from JSON input
FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
# If no file_path found, exit silently
if [ -z "$FILE_PATH" ]; then
exit 0
fi
# Check if file is in a config directory (commands/, agents/, skills/, hooks/)
if echo "$FILE_PATH" | grep -qE '/(commands|agents|skills|hooks)/'; then
echo "[doc-guardian] Config file modified. Run /doc-sync when ready."
fi
# Exit silently for all other files (no output = no blocking)
exit 0

View File

@@ -6,8 +6,8 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/git-flow/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/git-flow/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"license": "MIT",
"keywords": [
"git",
@@ -16,60 +16,5 @@
"branch",
"automation"
],
"commands": [
{
"name": "commit",
"description": "Create a commit with auto-generated conventional message",
"file": "commands/commit.md"
},
{
"name": "commit-push",
"description": "Commit and push to remote in one operation",
"file": "commands/commit-push.md"
},
{
"name": "commit-merge",
"description": "Commit current changes and merge into target branch",
"file": "commands/commit-merge.md"
},
{
"name": "commit-sync",
"description": "Commit, push, and sync with upstream",
"file": "commands/commit-sync.md"
},
{
"name": "branch-start",
"description": "Start a new feature/fix/chore branch with naming convention",
"file": "commands/branch-start.md"
},
{
"name": "branch-cleanup",
"description": "Clean up merged branches locally and optionally remotely",
"file": "commands/branch-cleanup.md"
},
{
"name": "git-status",
"description": "Enhanced git status with recommendations",
"file": "commands/git-status.md"
},
{
"name": "git-config",
"description": "Configure git-flow settings for this project",
"file": "commands/git-config.md"
}
],
"agents": [
{
"name": "git-assistant",
"description": "Git workflow assistant for complex operations",
"file": "agents/git-assistant.md"
}
],
"skills": [
{
"name": "workflow-patterns",
"description": "Git branching strategies and workflow patterns",
"path": "skills/workflow-patterns"
}
]
"commands": ["./commands/"]
}

View File

@@ -10,7 +10,7 @@ git-flow streamlines common git operations with smart defaults, conventional com
| Command | Description |
|---------|-------------|
| `/commit` | Create commit with auto-generated conventional message |
| `/commit` | Create commit with auto-generated conventional message (with protected branch detection) |
| `/commit-push` | Commit and push in one operation |
| `/commit-merge` | Commit and merge into target branch |
| `/commit-sync` | Full sync: commit, push, and rebase on base branch |
@@ -79,7 +79,7 @@ chore/update-dependencies
### Safety Checks
- Warns before commits to protected branches
- **Protected branch detection**: Before committing, checks if you're on a protected branch (main, master, development, staging, production by default). Offers to create a feature branch automatically instead of committing directly to protected branches.
- Confirms force push operations
- Prevents accidental branch deletion

View File

@@ -1,12 +1,19 @@
# /branch-cleanup - Clean Merged Branches
# /branch-cleanup - Clean Merged and Stale Branches
## 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
### 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
# Find merged local branches
@@ -14,19 +21,26 @@ git branch --merged <base-branch>
# Find merged remote branches
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)
- fix/typo-header (merged 1 week 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/fix/typo-header
@@ -36,35 +50,40 @@ Protected (won't delete):
- staging
Delete these branches?
1. Delete all (local + remote)
2. Delete local only
3. Let me pick which ones
4. Cancel
1. Delete all (local merged + stale + remote)
2. Delete merged only (skip stale)
3. Delete stale only (upstream gone)
4. Let me pick which ones
5. Cancel
```
### Step 3: Execute Cleanup
### Step 4: Execute Cleanup
```bash
# Delete local
# Delete merged local branches
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>
```
### Step 4: Report
### Step 5: Report
```
Cleanup complete:
Deleted local: 3 branches
Deleted local (merged): 3 branches
Deleted local (stale): 2 branches
Deleted remote: 2 branches
Skipped: 0 branches
Remaining local branches:
- main
- development
- feat/current-work (not merged)
- feat/current-work (not merged, has upstream)
```
## Environment Variables
@@ -74,20 +93,24 @@ Remaining local branches:
| `GIT_DEFAULT_BASE` | `development` | Base branch for merge detection |
| `GIT_PROTECTED_BRANCHES` | `main,master,development,staging,production` | Never delete these |
| `GIT_AUTO_DELETE_REMOTE` | `false` | Auto-delete remote branches |
| `GIT_CLEANUP_STALE` | `true` | Include stale branches (upstream gone) in cleanup |
## Safety
- Never deletes protected branches
- Warns about unmerged branches
- Warns about unmerged branches that still have upstreams
- 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
On success:
```
Cleaned up:
Local: 3 branches deleted
Local (merged): 3 branches deleted
Local (stale): 2 branches deleted
Remote: 2 branches deleted
Repository is tidy!

View File

@@ -2,7 +2,7 @@
## 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
@@ -19,8 +19,8 @@ Push committed changes to remote branch.
Pull latest from base branch and rebase/merge:
```bash
# Fetch all
git fetch --all
# Fetch all with prune (removes stale remote-tracking refs)
git fetch --all --prune
# Rebase on base branch
git rebase origin/<base-branch>
@@ -29,7 +29,26 @@ git rebase origin/<base-branch>
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:
@@ -40,6 +59,10 @@ Base: development @ xyz7890 (synced)
Your branch is up-to-date with development.
No conflicts detected.
Cleanup:
Remote refs pruned: 2
Stale local branches: 2 (run /branch-cleanup to remove)
```
## Environment Variables
@@ -48,6 +71,7 @@ No conflicts detected.
|----------|---------|-------------|
| `GIT_DEFAULT_BASE` | `development` | Branch to sync with |
| `GIT_SYNC_STRATEGY` | `rebase` | How to incorporate upstream changes |
| `GIT_AUTO_PRUNE` | `true` | Auto-prune stale remote refs on sync |
## Conflict Handling
@@ -76,4 +100,5 @@ Pushed to: origin/feat/password-reset
Synced with: development (xyz7890)
Status: Clean, up-to-date
Stale branches: None (or N found - run /branch-cleanup)
```

View File

@@ -6,13 +6,44 @@ Create a git commit with an auto-generated conventional commit message based on
## Behavior
### Step 1: Analyze Changes
### Step 1: Check for Protected Branch
Before any commit operation, check if the current branch is protected:
1. Get current branch: `git branch --show-current`
2. Check against `GIT_PROTECTED_BRANCHES` (default: `main,master,development,staging,production`)
If on a protected branch, warn the user:
```
⚠️ You are on a protected branch: development
Protected branches typically have push restrictions that will prevent
direct commits from being pushed to the remote.
Options:
1. Create a feature branch and continue (Recommended)
2. Continue on this branch anyway (may fail on push)
3. Cancel
```
**If option 1 (create feature branch):**
- Prompt for branch type (feat/fix/chore/docs/refactor)
- Prompt for brief description
- Create branch using `/branch-start` naming conventions
- Continue with commit on the new branch
**If option 2 (continue anyway):**
- Proceed with commit (user accepts risk of push rejection)
- Display reminder: "Remember: push may be rejected by remote protection rules"
### Step 2: Analyze Changes
1. Run `git status` to see staged and unstaged changes
2. Run `git diff --staged` to examine staged changes
3. If nothing staged, prompt user to stage changes
### Step 2: Generate Commit Message
### Step 3: Generate Commit Message
Analyze the changes and generate a conventional commit message:
@@ -38,7 +69,7 @@ Analyze the changes and generate a conventional commit message:
**Scope:** Determined from changed files (e.g., `auth`, `api`, `ui`)
### Step 3: Confirm or Edit
### Step 4: Confirm or Edit
Present the generated message:
@@ -58,7 +89,7 @@ Options:
4. Cancel
```
### Step 4: Execute Commit
### Step 5: Execute Commit
If confirmed, run:
@@ -75,6 +106,7 @@ EOF
| Variable | Default | Description |
|----------|---------|-------------|
| `GIT_PROTECTED_BRANCHES` | `main,master,development,staging,production` | Branches that trigger protection warning |
| `GIT_COMMIT_STYLE` | `conventional` | Message style (conventional, simple, detailed) |
| `GIT_SIGN_COMMITS` | `false` | Use GPG signing |
| `GIT_CO_AUTHOR` | `true` | Include Claude co-author footer |

View File

@@ -6,8 +6,8 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/pr-review/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/pr-review/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"license": "MIT",
"keywords": [
"pull-request",
@@ -16,56 +16,6 @@
"performance",
"multi-agent"
],
"commands": [
{
"name": "pr-review",
"description": "Full multi-agent PR review (security, performance, maintainability, tests)",
"file": "commands/pr-review.md"
},
{
"name": "pr-summary",
"description": "Quick summary of PR changes without full review",
"file": "commands/pr-summary.md"
},
{
"name": "pr-findings",
"description": "List and filter review findings by category or confidence",
"file": "commands/pr-findings.md"
}
],
"agents": [
{
"name": "coordinator",
"description": "Orchestrates the multi-agent review process",
"file": "agents/coordinator.md"
},
{
"name": "security-reviewer",
"description": "Analyzes code for security vulnerabilities",
"file": "agents/security-reviewer.md"
},
{
"name": "performance-analyst",
"description": "Identifies performance issues and optimization opportunities",
"file": "agents/performance-analyst.md"
},
{
"name": "maintainability-auditor",
"description": "Reviews code quality, patterns, and maintainability",
"file": "agents/maintainability-auditor.md"
},
{
"name": "test-validator",
"description": "Validates test coverage and test quality",
"file": "agents/test-validator.md"
}
],
"skills": [
{
"name": "review-patterns",
"description": "Code review patterns and confidence scoring rules",
"path": "skills/review-patterns"
}
],
"mcpServers": ["gitea"]
"commands": ["./commands/"],
"mcpServers": ["./.mcp.json"]
}

View File

@@ -3,7 +3,10 @@
"gitea": {
"command": "${CLAUDE_PLUGIN_ROOT}/mcp-servers/gitea/.venv/bin/python",
"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

@@ -13,6 +13,9 @@ pr-review conducts comprehensive code reviews using specialized agents for secur
| `/pr-review <pr#>` | Full multi-agent review |
| `/pr-summary <pr#>` | Quick summary without full review |
| `/pr-findings <pr#>` | Filter findings by category/confidence |
| `/initial-setup` | Full interactive setup wizard |
| `/project-init` | Quick project setup (system already configured) |
| `/project-sync` | Sync configuration with current git remote |
## Review Agents

View File

@@ -0,0 +1,273 @@
---
description: Interactive setup wizard for pr-review plugin - configures Gitea MCP and project settings
---
# PR Review Setup Wizard
This command sets up the pr-review plugin. It shares the Gitea MCP server with projman, so if you've already run `/initial-setup` for projman, most of the work is done.
## Important Context
- **This command uses Bash, Read, Write, and AskUserQuestion tools** - NOT MCP tools
- **MCP tools won't work until after setup + session restart**
- **Shares Gitea MCP server with projman plugin**
---
## Phase 1: Check Existing Setup
### Step 1.1: Check if Gitea MCP is Already Configured
First, check if the system configuration already exists (from projman or previous setup):
```bash
cat ~/.config/claude/gitea.env 2>/dev/null || echo "FILE_NOT_FOUND"
```
**If file exists with valid values (no placeholders):**
- Skip to Phase 3 (Project Configuration)
- Inform user: "Gitea configuration found. Skipping system setup."
**If file doesn't exist or has placeholders:**
- Continue to Phase 2
### Step 1.2: Check if projman is Installed
Check if projman plugin exists (they share MCP server):
```bash
find ~/.claude ~/.config/claude -name "projman" -type d 2>/dev/null | head -1
```
**If projman exists:**
- Suggest: "The projman plugin is installed and shares the same Gitea MCP server. Consider running `/initial-setup` from projman for the full setup wizard."
Use AskUserQuestion:
- Question: "How would you like to proceed with setup?"
- Header: "Setup"
- Options:
- "Continue with pr-review setup (Recommended if not using projman)"
- "I'll use projman's /initial-setup instead"
**If user chooses projman setup:** End here with instructions to run projman's setup.
---
## Phase 2: System Setup (if needed)
This is a condensed version focusing on what pr-review needs.
### Step 2.1: Python and MCP Server
Check Python version:
```bash
python3 --version
```
If below 3.10, stop and inform user.
Locate and set up the MCP server:
```bash
find ~/.claude ~/.config/claude -name "mcp_server" -path "*gitea*" 2>/dev/null | head -5
```
If venv doesn't exist, create it:
```bash
cd /path/to/mcp-servers/gitea && python3 -m venv .venv && source .venv/bin/activate && pip install --upgrade pip && pip install -r requirements.txt && deactivate
```
### Step 2.2: Gitea Configuration
Create config directory:
```bash
mkdir -p ~/.config/claude
```
Use AskUserQuestion:
- Question: "What is your Gitea server URL?"
- Header: "Gitea URL"
- Options:
- "https://gitea.hotserv.cloud"
- "Other (I'll provide the URL)"
Create configuration file (credentials only, org is per-project):
```bash
cat > ~/.config/claude/gitea.env << 'EOF'
# Gitea API Configuration
# Generated by pr-review /initial-setup
# Note: GITEA_ORG is configured per-project in .env
GITEA_API_URL=<USER_PROVIDED_URL>
GITEA_API_TOKEN=PASTE_YOUR_TOKEN_HERE
EOF
chmod 600 ~/.config/claude/gitea.env
```
### Step 2.3: Token Instructions
Display these instructions:
---
**Action Required: Add Your Gitea API Token**
I've created `~/.config/claude/gitea.env` but you need to add your API token manually.
**Steps:**
1. Open: `nano ~/.config/claude/gitea.env`
2. Generate token in Gitea: Settings → Applications → Generate New Token
- Permissions needed: `repo`, `read:org`, `read:user`
3. Replace `PASTE_YOUR_TOKEN_HERE` with your token
4. Save the file
---
Use AskUserQuestion:
- Question: "Have you added your Gitea token?"
- Header: "Token"
- Options:
- "Yes, I've added the token"
- "Skip for now"
---
## Phase 3: Project Configuration
### Step 3.1: Check Current Directory
```bash
pwd && git rev-parse --show-toplevel 2>/dev/null || echo "NOT_A_GIT_REPO"
```
### Step 3.2: Check Existing Project Config
```bash
cat .env 2>/dev/null | grep GITEA_REPO || echo "NOT_FOUND"
```
If `GITEA_REPO` is already set, skip to Phase 4.
### Step 3.3: Detect Organization and Repository
Extract organization:
```bash
git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\)\/[^/]*$/\1/'
```
Extract repository:
```bash
git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\)\.git$/\1/' | sed 's/.*\/\([^/]*\)$/\1/'
```
### Step 3.4: Validate Repository via Gitea API
```bash
source ~/.config/claude/gitea.env
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_API_TOKEN" "$GITEA_API_URL/repos/<detected-org>/<detected-repo>"
```
| HTTP Code | Action |
|-----------|--------|
| **200** | Auto-fill - "Verified: <org>/<repo> exists" - skip to Step 3.7 |
| **404** | Not found - proceed to Step 3.5 |
| **401/403** | Permission issue - warn, proceed to Step 3.5 |
### Step 3.5: Confirm Organization (only if validation failed)
Use AskUserQuestion:
- Question: "Repository not found. Is '<detected-org>' the correct organization?"
- Header: "Organization"
- Options:
- "Yes, that's correct"
- "No, let me specify"
### Step 3.6: Confirm Repository (only if validation failed)
Use AskUserQuestion:
- Question: "Is '<detected-repo-name>' the correct repository?"
- Header: "Repository"
- Options:
- "Yes, that's correct"
- "No, let me specify"
**After corrections, re-validate via API (Step 3.4).**
### Step 3.7: Create/Update Project Config
If `.env` exists, append:
```bash
echo "GITEA_ORG=<ORG_NAME>" >> .env
echo "GITEA_REPO=<REPO_NAME>" >> .env
```
If `.env` doesn't exist:
```bash
cat > .env << 'EOF'
# Project Configuration
GITEA_ORG=<ORG_NAME>
GITEA_REPO=<REPO_NAME>
EOF
```
### Step 3.5: PR Review Settings (Optional)
Use AskUserQuestion:
- Question: "Do you want to configure PR review settings?"
- Header: "Settings"
- Options:
- "Use defaults (Recommended)"
- "Let me customize"
If customize, ask about:
- `PR_REVIEW_CONFIDENCE_THRESHOLD` (default: 0.5)
- `PR_REVIEW_AUTO_SUBMIT` (default: false)
---
## Phase 4: Validation and Next Steps
### Step 4.1: Test Configuration (if token was added)
```bash
source ~/.config/claude/gitea.env && curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_API_TOKEN" "$GITEA_API_URL/user"
```
Report result:
- 200: Success
- 401: Invalid token
- Other: Connection issue
### Step 4.2: Summary
```
╔════════════════════════════════════════════════════════════╗
║ PR-REVIEW SETUP COMPLETE ║
╠════════════════════════════════════════════════════════════╣
║ MCP Server (Gitea): ✓ Ready ║
║ System Config: ✓ ~/.config/claude/gitea.env ║
║ Project Config: ✓ ./.env ║
╚════════════════════════════════════════════════════════════╝
```
### Step 4.3: Session Restart Notice
---
**⚠️ Session Restart Required**
Restart your Claude Code session for MCP tools to become available.
**After restart, you can:**
- Run `/pr-review <PR_NUMBER>` to review a pull request
- Run `/pr-summary <PR_NUMBER>` for a quick summary
- Run `/pr-findings <PR_NUMBER>` to list actionable findings
---
## Available Commands After Setup
| Command | Description |
|---------|-------------|
| `/pr-review <number>` | Full multi-agent PR review with confidence scoring |
| `/pr-summary <number>` | Quick PR summary |
| `/pr-findings <number>` | List findings with severity and line numbers |

View File

@@ -0,0 +1,136 @@
---
description: Quick project setup - configures only project-level settings for PR review
---
# Project Initialization (PR Review)
Fast setup for a new project when system-level configuration is already complete.
**Use this when:**
- You've already run `/initial-setup` on this machine
- You're starting work on a new project/repository
- You just need to configure this project for PR reviews
---
## Pre-Flight Check
### Step 1: Verify System Configuration
```bash
cat ~/.config/claude/gitea.env 2>/dev/null | grep -v "^#" | grep -v "PASTE_YOUR" | grep "GITEA_API_TOKEN=" && echo "SYSTEM_OK" || echo "SYSTEM_MISSING"
```
**If SYSTEM_MISSING:**
---
**System configuration not found.**
Please run `/initial-setup` first to configure Gitea credentials.
---
**If SYSTEM_OK:** Continue.
---
## Project Setup
### Step 2: Verify Current Directory
```bash
pwd && git rev-parse --show-toplevel 2>/dev/null || echo "NOT_A_GIT_REPO"
```
### Step 3: Check Existing Configuration
```bash
cat .env 2>/dev/null | grep "GITEA_REPO=" || echo "NOT_CONFIGURED"
```
If already configured, ask if user wants to keep or reconfigure.
### Step 4: Detect Organization and Repository
Extract organization:
```bash
git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\)\/[^/]*$/\1/'
```
Extract repository:
```bash
git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\)\.git$/\1/' | sed 's/.*\/\([^/]*\)$/\1/'
```
### Step 5: Validate Repository via Gitea API
```bash
source ~/.config/claude/gitea.env
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_API_TOKEN" "$GITEA_API_URL/repos/<detected-org>/<detected-repo>"
```
| HTTP Code | Action |
|-----------|--------|
| **200** | Auto-fill - "Verified: <org>/<repo> exists" - skip to Step 8 |
| **404** | Not found - proceed to Step 6 |
| **401/403** | Permission issue - warn, proceed to Step 6 |
### Step 6: Confirm Organization (only if validation failed)
Use AskUserQuestion:
- Question: "Repository not found. Is '<detected-org>' the correct organization?"
- Header: "Organization"
- Options:
- "Yes, that's correct"
- "No, let me specify"
### Step 7: Confirm Repository (only if validation failed)
Use AskUserQuestion:
- Question: "Is '<detected-repo-name>' the correct repository?"
- Header: "Repository"
- Options:
- "Yes, that's correct"
- "No, let me specify"
**After corrections, re-validate via API (Step 5).**
### Step 8: Create/Update .env
```bash
echo "GITEA_ORG=<ORG_NAME>" >> .env
echo "GITEA_REPO=<REPO_NAME>" >> .env
```
### Step 9: Optional PR Review Settings
Use AskUserQuestion:
- Question: "Configure PR review settings?"
- Header: "Settings"
- Options:
- "Use defaults (Recommended)"
- "Customize settings"
If customize:
- `PR_REVIEW_CONFIDENCE_THRESHOLD` (default: 0.5)
- `PR_REVIEW_AUTO_SUBMIT` (default: false)
---
## Complete
```
╔══════════════════════════════════════════════════════════════╗
║ PROJECT CONFIGURED ║
╠══════════════════════════════════════════════════════════════╣
║ Organization: <ORG_NAME> ║
║ Repository: <REPO_NAME> ║
║ Config file: ./.env ║
╚══════════════════════════════════════════════════════════════╝
Ready to review PRs:
• /pr-review <number> - Full multi-agent review
• /pr-summary <number> - Quick summary
• /pr-findings <number> - List findings
```

View File

@@ -0,0 +1,93 @@
---
description: Sync project configuration with current git remote - use after changing repository location
---
# Project Sync (PR Review)
Updates project configuration when the git remote URL has changed.
**Use this when:**
- Repository was moved to a different organization
- Repository was renamed
- Git remote URL changed
- SessionStart hook detected a mismatch
---
## Step 1: Verify System Configuration
```bash
cat ~/.config/claude/gitea.env 2>/dev/null | grep -v "^#" | grep -v "PASTE_YOUR" | grep "GITEA_API_TOKEN=" && echo "SYSTEM_OK" || echo "SYSTEM_MISSING"
```
**If SYSTEM_MISSING:** Run `/initial-setup` first.
---
## Step 2: Read Current .env
```bash
cat .env 2>/dev/null
```
Extract `GITEA_ORG` and `GITEA_REPO` values.
**If missing:** Redirect to `/project-init`.
---
## Step 3: Detect Git Remote
```bash
git remote get-url origin 2>/dev/null
```
Extract organization and repository from URL.
---
## Step 4: Compare Values
| Scenario | Action |
|----------|--------|
| **Match** | "Configuration in sync" - exit |
| **Mismatch** | Show diff, proceed to validation |
---
## Step 5: Validate via Gitea API
```bash
source ~/.config/claude/gitea.env
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_API_TOKEN" "$GITEA_API_URL/repos/<NEW_ORG>/<NEW_REPO>"
```
| Code | Action |
|------|--------|
| **200** | Verified - proceed to update |
| **404** | Not found - ask to confirm |
| **401/403** | Permission issue - warn |
---
## Step 6: Confirm and Update
Use AskUserQuestion to confirm, then update .env:
```bash
sed -i 's/^GITEA_ORG=.*/GITEA_ORG=<NEW_ORG>/' .env
sed -i 's/^GITEA_REPO=.*/GITEA_REPO=<NEW_REPO>/' .env
```
---
## Step 7: Confirm Success
```
╔══════════════════════════════════════════════════════════════╗
║ CONFIGURATION UPDATED ║
╠══════════════════════════════════════════════════════════════╣
║ Organization: <NEW_ORG> ║
║ Repository: <NEW_REPO> ║
╚══════════════════════════════════════════════════════════════╝
```

View File

@@ -0,0 +1,10 @@
{
"hooks": {
"SessionStart": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/startup-check.sh"
}
]
}
}

View File

@@ -0,0 +1,30 @@
#!/bin/bash
# pr-review startup check hook
# Checks for common issues at session start
# All output MUST have [pr-review] prefix
PREFIX="[pr-review]"
# Check if MCP venv exists
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$(dirname "$(realpath "$0")")")}"
VENV_PATH="$PLUGIN_ROOT/mcp-servers/gitea/.venv/bin/python"
if [[ ! -f "$VENV_PATH" ]]; then
echo "$PREFIX MCP venvs missing - run setup.sh from installed marketplace"
exit 0
fi
# Check git remote vs .env config (only if .env exists)
if [[ -f ".env" ]]; then
CONFIGURED_REPO=$(grep -E "^GITEA_REPO=" .env 2>/dev/null | cut -d'=' -f2 | tr -d '"' || true)
if [[ -n "$CONFIGURED_REPO" ]]; then
CURRENT_REMOTE=$(git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\/[^.]*\).*/\1/' || true)
if [[ -n "$CURRENT_REMOTE" && "$CONFIGURED_REPO" != "$CURRENT_REMOTE" ]]; then
echo "$PREFIX Git remote mismatch - run /pr-review:project-sync"
exit 0
fi
fi
fi
# All checks passed - say nothing
exit 0

View File

@@ -6,8 +6,8 @@
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/project-hygiene/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/project-hygiene/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"license": "MIT",
"keywords": [
"cleanup",

View File

@@ -20,7 +20,7 @@ claude plugin install project-hygiene
## How It Works
The plugin registers a `task-completed` hook that runs after Claude completes any task. It:
The plugin registers a `PostToolUse` hook (on Write and Edit tools) that runs after Claude modifies files. It:
1. Scans for and deletes known temporary file patterns
2. Removes temporary directories (`__pycache__`, `.pytest_cache`, etc.)

View File

@@ -1,365 +1,28 @@
#!/bin/bash
# project-hygiene cleanup hook
# Runs after task completion to clean up temp files and manage orphans
# Runs after file edits to clean up temp files
# All output MUST have [project-hygiene] prefix
set -euo pipefail
# Configuration
PREFIX="[project-hygiene]"
# Read tool input from stdin (discard - we don't need it for cleanup)
cat > /dev/null
PROJECT_ROOT="${PROJECT_ROOT:-.}"
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$(dirname "$(realpath "$0")")")}"
CONFIG_FILE="${PROJECT_ROOT}/.hygiene.json"
LOG_DIR="${PROJECT_ROOT}/.dev/logs"
SCRATCH_DIR="${PROJECT_ROOT}/.dev/scratch"
LOG_FILE="${LOG_DIR}/hygiene-$(date +%Y%m%d-%H%M%S).log"
# Default allowed root files (can be overridden by .hygiene.json)
DEFAULT_ALLOWED_ROOT=(
".git"
".gitignore"
".gitattributes"
".editorconfig"
".env"
".env.example"
".env.local"
".nvmrc"
".node-version"
".python-version"
".ruby-version"
".tool-versions"
"README.md"
"LICENSE"
"CHANGELOG.md"
"CONTRIBUTING.md"
"CLAUDE.md"
"package.json"
"package-lock.json"
"yarn.lock"
"pnpm-lock.yaml"
"Makefile"
"Dockerfile"
"docker-compose.yml"
"docker-compose.yaml"
"Cargo.toml"
"Cargo.lock"
"go.mod"
"go.sum"
"requirements.txt"
"setup.py"
"pyproject.toml"
"poetry.lock"
"Gemfile"
"Gemfile.lock"
"tsconfig.json"
"jsconfig.json"
".eslintrc*"
".prettierrc*"
"vite.config.*"
"webpack.config.*"
"rollup.config.*"
".hygiene.json"
)
# Temp file patterns to delete
TEMP_PATTERNS=(
"*.tmp"
"*.bak"
"*.swp"
"*.swo"
"*~"
".DS_Store"
"Thumbs.db"
"*.log"
"*.orig"
"*.pyc"
"*.pyo"
)
# Directory patterns to delete
TEMP_DIRS=(
"__pycache__"
".pytest_cache"
".mypy_cache"
".ruff_cache"
"node_modules/.cache"
".next/cache"
".nuxt/.cache"
".turbo"
"*.egg-info"
".eggs"
"dist"
"build"
)
# Orphan patterns to identify
ORPHAN_PATTERNS=(
"test_*.py"
"debug_*"
"*_backup.*"
"*_old.*"
"*_bak.*"
"*.backup"
"temp_*"
"tmp_*"
)
# Initialize
DELETED_COUNT=0
WARNED_COUNT=0
ORPHAN_COUNT=0
MOVE_ORPHANS=false
# Logging function
log() {
local msg="[$(date +%H:%M:%S)] $1"
echo "$msg"
if [[ -f "$LOG_FILE" ]]; then
echo "$msg" >> "$LOG_FILE"
fi
}
log_action() {
local action="$1"
local target="$2"
log " $action: $target"
}
# Load project-local config if exists
load_config() {
if [[ -f "$CONFIG_FILE" ]]; then
log "Loading config from $CONFIG_FILE"
# Check if move_orphans is enabled
if command -v jq &>/dev/null; then
MOVE_ORPHANS=$(jq -r '.move_orphans // false' "$CONFIG_FILE" 2>/dev/null || echo "false")
# Load additional allowed root files
local extra_allowed
extra_allowed=$(jq -r '.allowed_root_files // [] | .[]' "$CONFIG_FILE" 2>/dev/null || true)
if [[ -n "$extra_allowed" ]]; then
while IFS= read -r file; do
DEFAULT_ALLOWED_ROOT+=("$file")
done <<< "$extra_allowed"
fi
# Load additional temp patterns
local extra_temp
extra_temp=$(jq -r '.temp_patterns // [] | .[]' "$CONFIG_FILE" 2>/dev/null || true)
if [[ -n "$extra_temp" ]]; then
while IFS= read -r pattern; do
TEMP_PATTERNS+=("$pattern")
done <<< "$extra_temp"
fi
# Load ignore patterns (files to never touch)
IGNORE_PATTERNS=()
local ignore
ignore=$(jq -r '.ignore_patterns // [] | .[]' "$CONFIG_FILE" 2>/dev/null || true)
if [[ -n "$ignore" ]]; then
while IFS= read -r pattern; do
IGNORE_PATTERNS+=("$pattern")
done <<< "$ignore"
fi
else
log "Warning: jq not installed, using default config"
fi
fi
}
# Check if file should be ignored
should_ignore() {
local file="$1"
local basename
basename=$(basename "$file")
for pattern in "${IGNORE_PATTERNS[@]:-}"; do
if [[ "$basename" == $pattern ]] || [[ "$file" == $pattern ]]; then
return 0
fi
done
return 1
}
# Check if file is in allowed root list
is_allowed_root() {
local file="$1"
local basename
basename=$(basename "$file")
for allowed in "${DEFAULT_ALLOWED_ROOT[@]}"; do
# Support wildcards in allowed patterns
if [[ "$basename" == $allowed ]]; then
return 0
fi
done
return 1
}
# Check if file matches orphan pattern
is_orphan() {
local file="$1"
local basename
basename=$(basename "$file")
for pattern in "${ORPHAN_PATTERNS[@]}"; do
if [[ "$basename" == $pattern ]]; then
return 0
fi
done
return 1
}
# Setup directories
setup_dirs() {
mkdir -p "$LOG_DIR"
if [[ "$MOVE_ORPHANS" == "true" ]]; then
mkdir -p "$SCRATCH_DIR"
fi
# Start log file
echo "=== Project Hygiene Cleanup ===" > "$LOG_FILE"
echo "Started: $(date)" >> "$LOG_FILE"
echo "Project: $PROJECT_ROOT" >> "$LOG_FILE"
echo "" >> "$LOG_FILE"
}
# Delete temp files
cleanup_temp_files() {
log "Cleaning temp files..."
for pattern in "${TEMP_PATTERNS[@]}"; do
# Silently delete temp files
for pattern in "*.tmp" "*.bak" "*.swp" "*~" ".DS_Store"; do
while IFS= read -r -d '' file; do
if should_ignore "$file"; then
continue
fi
rm -f "$file"
log_action "DELETED" "$file"
((DELETED_COUNT++))
done < <(find "$PROJECT_ROOT" -name "$pattern" -type f -print0 2>/dev/null || true)
done
}
# Delete temp directories
cleanup_temp_dirs() {
log "Cleaning temp directories..."
for pattern in "${TEMP_DIRS[@]}"; do
while IFS= read -r -d '' dir; do
if should_ignore "$dir"; then
continue
fi
rm -rf "$dir"
log_action "DELETED DIR" "$dir"
((DELETED_COUNT++))
done < <(find "$PROJECT_ROOT" -name "$pattern" -type d -print0 2>/dev/null || true)
done
}
# Warn about unexpected root files
check_root_files() {
log "Checking root files..."
local unexpected_files=()
while IFS= read -r -d '' file; do
local basename
basename=$(basename "$file")
# Skip directories
[[ -d "$file" ]] && continue
# Skip if in allowed list
is_allowed_root "$basename" && continue
# Skip if should be ignored
should_ignore "$basename" && continue
unexpected_files+=("$basename")
log_action "WARNING" "Unexpected root file: $basename"
((WARNED_COUNT++))
done < <(find "$PROJECT_ROOT" -maxdepth 1 -print0 2>/dev/null || true)
if [[ ${#unexpected_files[@]} -gt 0 ]]; then
log ""
log "⚠️ Unexpected files in project root:"
for f in "${unexpected_files[@]}"; do
log " - $f"
done
fi
}
# Identify and handle orphaned files
handle_orphans() {
log "Checking for orphaned files..."
local orphan_files=()
for pattern in "${ORPHAN_PATTERNS[@]}"; do
while IFS= read -r -d '' file; do
if should_ignore "$file"; then
continue
fi
orphan_files+=("$file")
if [[ "$MOVE_ORPHANS" == "true" ]]; then
local dest="${SCRATCH_DIR}/$(basename "$file")"
# Handle duplicates
if [[ -f "$dest" ]]; then
dest="${SCRATCH_DIR}/$(date +%Y%m%d%H%M%S)_$(basename "$file")"
fi
mv "$file" "$dest"
log_action "MOVED" "$file -> $dest"
else
log_action "ORPHAN" "$file"
fi
((ORPHAN_COUNT++))
rm -f "$file" 2>/dev/null && ((DELETED_COUNT++)) || true
done < <(find "$PROJECT_ROOT" -name "$pattern" -type f -print0 2>/dev/null || true)
done
if [[ ${#orphan_files[@]} -gt 0 && "$MOVE_ORPHANS" != "true" ]]; then
log ""
log "📦 Orphaned files found (enable move_orphans in .hygiene.json to auto-move):"
for f in "${orphan_files[@]}"; do
log " - $f"
done
# Only output if we deleted something
if [[ $DELETED_COUNT -gt 0 ]]; then
echo "$PREFIX Cleaned $DELETED_COUNT temp files"
fi
}
# Summary
print_summary() {
log ""
log "=== Cleanup Summary ==="
log " Deleted: $DELETED_COUNT items"
log " Warnings: $WARNED_COUNT unexpected root files"
log " Orphans: $ORPHAN_COUNT files"
if [[ "$MOVE_ORPHANS" == "true" ]]; then
log " Orphans moved to: $SCRATCH_DIR"
fi
log " Log file: $LOG_FILE"
log ""
}
# Main
main() {
cd "$PROJECT_ROOT" || exit 1
load_config
setup_dirs
log "Starting project hygiene cleanup..."
log ""
cleanup_temp_files
cleanup_temp_dirs
check_root_files
handle_orphans
print_summary
# Exit with warning code if issues found
if [[ $WARNED_COUNT -gt 0 || $ORPHAN_COUNT -gt 0 ]]; then
exit 0 # Still success, but logged warnings
fi
}
main "$@"
exit 0

View File

@@ -1,13 +1,13 @@
{
"name": "projman",
"version": "3.0.0",
"version": "3.1.0",
"description": "Sprint planning and project management with Gitea integration",
"author": {
"name": "Leo Miranda",
"email": "leobmiranda@gmail.com"
},
"homepage": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace/src/branch/main/plugins/projman/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/support-claude-mktplace.git",
"homepage": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/src/branch/main/plugins/projman/README.md",
"repository": "https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git",
"license": "MIT",
"keywords": [
"project-management",
@@ -17,5 +17,5 @@
"lessons-learned"
],
"commands": ["./commands/"],
"agents": ["./agents/"]
"mcpServers": ["./.mcp.json"]
}

View File

@@ -1,39 +1,10 @@
# Configuration Guide - Projman Plugin
# Configuration - Projman Plugin
For comprehensive configuration instructions, see the **[Centralized Configuration Guide](../../docs/CONFIGURATION.md)**.
See **[docs/CONFIGURATION.md](../../docs/CONFIGURATION.md)** for complete setup instructions.
## Quick Reference
## Quick Commands
### Required Configuration
**System-level** (`~/.config/claude/gitea.env`):
```bash
GITEA_URL=https://gitea.example.com
GITEA_TOKEN=your_token
GITEA_ORG=your_organization
```
**Project-level** (`.env` in project root):
```bash
GITEA_REPO=your-repo-name
/initial-setup # First time on this machine
/project-init # New project (system already configured)
```
### MCP Server Installation
```bash
cd mcp-servers/gitea
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
### Verification
```bash
/initial-setup
/labels-sync
```
---
For detailed setup instructions, troubleshooting, and security best practices, see [docs/CONFIGURATION.md](../../docs/CONFIGURATION.md).

View File

@@ -13,7 +13,7 @@ Projman transforms a proven 15-sprint workflow into a distributable Claude Code
- **Milestones** - Sprint milestone management and tracking
- **Lessons Learned** - Systematic capture and search via Gitea Wiki
- **Branch-Aware Security** - Prevents accidental changes on production branches
- **Three-Agent Model** - Planner, Orchestrator, and Executor agents
- **Four-Agent Model** - Planner, Orchestrator, Executor, and Code Reviewer agents
- **CLI Tools Blocked** - All operations via MCP tools only (no `tea` or `gh`)
## Quick Start
@@ -25,57 +25,38 @@ Projman transforms a proven 15-sprint workflow into a distributable Claude Code
- Python 3.10+ installed
- Git repository initialized
### 2. Install MCP Server
### 2. Run Interactive Setup
The plugin bundles the Gitea MCP server:
The setup wizard handles everything:
```bash
cd plugins/projman/mcp-servers/gitea
python3 -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -r requirements.txt
```
See [CONFIGURATION.md](./CONFIGURATION.md) for detailed setup instructions.
### 3. Configure System-Level Settings
Create system-wide configuration with your Gitea credentials:
```bash
mkdir -p ~/.config/claude
# Gitea configuration
cat > ~/.config/claude/gitea.env << 'EOF'
GITEA_URL=https://gitea.example.com
GITEA_TOKEN=your_gitea_token_here
GITEA_ORG=your_organization
EOF
# Secure the file
chmod 600 ~/.config/claude/gitea.env
```
### 4. Configure Project-Level Settings
In your project root directory, create a `.env` file:
```bash
# In your project directory
cat > .env << 'EOF'
GITEA_REPO=your-repo-name
EOF
```
### 5. Run Initial Setup
```bash
/initial-setup
```
### 6. Start Planning!
This will:
- Set up the MCP server (Python venv + dependencies)
- Create system config (`~/.config/claude/gitea.env`)
- Guide you through adding your Gitea token
- Detect and validate your repository via API
- Create project config (`.env`)
```bash
**For new projects** (when system is already configured):
```
/project-init
```
**After moving a repository:**
```
/project-sync
```
See [docs/CONFIGURATION.md](../../docs/CONFIGURATION.md) for detailed instructions.
### 3. Start Planning!
```
/sprint-plan
```
@@ -164,15 +145,42 @@ Synchronize label taxonomy from Gitea.
- When new labels are added to Gitea
### `/initial-setup`
Run initial setup for a new project.
Full interactive setup wizard.
**What it does:**
- Validates Gitea MCP server connection
- Tests credential configuration
- Syncs label taxonomy
- Creates required directory structure
- Checks Python version (requires 3.10+)
- Sets up MCP server virtual environment
- Creates system-level config (`~/.config/claude/gitea.env`)
- Guides token setup (manual entry for security)
- Detects and validates repository via Gitea API
- Creates project-level config (`.env` with GITEA_ORG, GITEA_REPO)
**When to use:** First time setting up projman for a project
**When to use:** First time on a new machine
### `/project-init`
Quick project setup (assumes system config exists).
**What it does:**
- Verifies system configuration exists
- Detects organization and repository from git remote
- Validates via Gitea API
- Creates project `.env` file
**When to use:** Starting work on a new project
### `/project-sync`
Sync configuration with current git remote.
**What it does:**
- Compares .env values with git remote URL
- Validates new repository via Gitea API
- Updates .env if mismatch detected
**When to use:** After moving or renaming a repository
**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`
Pre-sprint-close code quality review.
@@ -226,6 +234,39 @@ Generate tests for specified code.
**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
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.
@@ -420,20 +461,8 @@ projman/
├── .claude-plugin/
│ └── plugin.json # Plugin manifest
├── .mcp.json # MCP server configuration
├── mcp-servers/ # Bundled MCP server
│ └── gitea/
│ ├── .venv/
│ ├── requirements.txt
│ ├── mcp_server/
│ │ ├── server.py
│ │ ├── gitea_client.py
│ │ └── tools/
│ │ ├── issues.py
│ │ ├── labels.py
│ │ ├── wiki.py
│ │ ├── milestones.py
│ │ └── dependencies.py
│ └── tests/
├── mcp-servers/
│ └── gitea -> ../../../mcp-servers/gitea # SYMLINK to shared MCP server
├── commands/ # Slash commands
│ ├── sprint-plan.md
│ ├── sprint-start.md
@@ -441,9 +470,13 @@ projman/
│ ├── sprint-close.md
│ ├── labels-sync.md
│ ├── initial-setup.md
│ ├── project-init.md
│ ├── project-sync.md
│ ├── review.md
│ ├── test-check.md
── test-gen.md
── test-gen.md
│ ├── debug-report.md
│ └── debug-review.md
├── agents/ # Agent prompts
│ ├── planner.md
│ ├── orchestrator.md

View File

@@ -0,0 +1,432 @@
---
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 1.5: Detect Sprint Context
Determine if this debug issue should be associated with an active sprint.
**1. Check for active sprint milestone:**
```
mcp__plugin_projman_gitea__list_milestones(repo=PROJECT_REPO, state="open")
```
Store the first open milestone as `ACTIVE_SPRINT` (if any).
**2. Analyze branch context:**
| Branch Pattern | Context |
|----------------|---------|
| `feat/*`, `fix/*`, `issue-*` | Sprint work - likely related to current sprint |
| `main`, `master`, `development` | Production/standalone - not sprint-related |
| Other | Unknown - ask user |
**3. Determine sprint association:**
```
IF ACTIVE_SPRINT exists AND CURRENT_BRANCH matches sprint pattern (feat/*, fix/*, issue-*):
→ SPRINT_CONTEXT = "detected"
→ Ask user: "Active sprint detected: [SPRINT_NAME]. Is this bug related to sprint work?"
Options:
- Yes, add to sprint (will associate with milestone)
- No, standalone fix (no milestone)
→ Store choice as ASSOCIATE_WITH_SPRINT (true/false)
ELSE IF ACTIVE_SPRINT exists AND CURRENT_BRANCH is main/development:
→ SPRINT_CONTEXT = "production"
→ ASSOCIATE_WITH_SPRINT = false (standalone fix, no question needed)
ELSE:
→ SPRINT_CONTEXT = "none"
→ ASSOCIATE_WITH_SPRINT = false
```
### 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 Smart Labels
Generate appropriate labels based on the diagnostic results.
**1. Build context string for label suggestion:**
```
LABEL_CONTEXT = "Bug fix: " + [summary of main failure] + ". " +
"Failed tools: " + [list of failed tool names] + ". " +
"Error category: " + [detected error category from Step 4]
```
**2. Get suggested labels:**
```
mcp__plugin_projman_gitea__suggest_labels(
repo=PROJECT_REPO,
context=LABEL_CONTEXT
)
```
**3. Merge with base labels:**
```
BASE_LABELS = ["Type: Bug", "Source: Diagnostic", "Agent: Claude"]
SUGGESTED_LABELS = [result from suggest_labels]
# Combine, avoiding duplicates
FINAL_LABELS = BASE_LABELS + [label for label in SUGGESTED_LABELS if label not in BASE_LABELS]
```
The final label set should include:
- **Always**: `Type: Bug`, `Source: Diagnostic`, `Agent: Claude`
- **If detected**: `Component: *`, `Complexity: *`, `Risk: *`, `Priority: *`
### Step 6: 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 7: Create Issue in Marketplace
**IMPORTANT:** Always use curl to create issues in the marketplace repo. This avoids branch protection restrictions and MCP context issues that can block issue creation when working on protected branches.
**1. Load Gitea credentials:**
```bash
if [[ -f ~/.config/claude/gitea.env ]]; then
source ~/.config/claude/gitea.env
echo "Credentials loaded. API URL: $GITEA_API_URL"
else
echo "ERROR: No credentials at ~/.config/claude/gitea.env"
fi
```
**2. Fetch label IDs from marketplace repo:**
The diagnostic labels to apply are:
- `Source/Diagnostic` (always)
- `Type/Bug` (always)
```bash
# Fetch all labels and extract IDs for our target labels
LABELS_JSON=$(curl -s "${GITEA_API_URL}/repos/${MARKETPLACE_REPO}/labels" \
-H "Authorization: token ${GITEA_API_TOKEN}")
# Extract label IDs (handles both org and repo labels)
SOURCE_DIAG_ID=$(echo "$LABELS_JSON" | jq -r '.[] | select(.name == "Source/Diagnostic") | .id')
TYPE_BUG_ID=$(echo "$LABELS_JSON" | jq -r '.[] | select(.name == "Type/Bug") | .id')
# Build label array (only include IDs that were found)
LABEL_IDS="[]"
if [[ -n "$SOURCE_DIAG_ID" && -n "$TYPE_BUG_ID" ]]; then
LABEL_IDS="[$SOURCE_DIAG_ID, $TYPE_BUG_ID]"
elif [[ -n "$SOURCE_DIAG_ID" ]]; then
LABEL_IDS="[$SOURCE_DIAG_ID]"
elif [[ -n "$TYPE_BUG_ID" ]]; then
LABEL_IDS="[$TYPE_BUG_ID]"
fi
echo "Label IDs to apply: $LABEL_IDS"
```
**3. Create issue with labels via curl:**
```bash
# Create temp files with restrictive permissions
DIAG_TITLE=$(mktemp -t diag-title.XXXXXX)
DIAG_BODY=$(mktemp -t diag-body.XXXXXX)
DIAG_PAYLOAD=$(mktemp -t diag-payload.XXXXXX)
# Save title
echo "[Diagnostic] [summary of main failure]" > "$DIAG_TITLE"
# Save body (paste Step 6 content) - heredoc delimiter prevents shell expansion
cat > "$DIAG_BODY" << 'DIAGNOSTIC_EOF'
[Paste the full issue content from Step 6 here]
DIAGNOSTIC_EOF
# Build JSON payload with labels using jq
jq -n \
--rawfile title "$DIAG_TITLE" \
--rawfile body "$DIAG_BODY" \
--argjson labels "$LABEL_IDS" \
'{title: ($title | rtrimstr("\n")), body: $body, labels: $labels}' > "$DIAG_PAYLOAD"
# Create issue using the JSON file
RESULT=$(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")
# Extract and display the issue URL
echo "$RESULT" | jq -r '.html_url // "Error: " + (.message // "Unknown error")'
# Secure cleanup
rm -f "$DIAG_TITLE" "$DIAG_BODY" "$DIAG_PAYLOAD"
```
**4. If no credentials found, save report locally:**
```bash
REPORT_FILE=$(mktemp -t diagnostic-report-XXXXXX.md)
cat > "$REPORT_FILE" << 'DIAGNOSTIC_EOF'
[Paste the full issue content from Step 6 here]
DIAGNOSTIC_EOF
echo "Report saved to: $REPORT_FILE"
```
Then inform the user:
```
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 8: 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
- **DO NOT** use MCP tools to create issues in the marketplace - always use curl (avoids branch restrictions)
## 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
**Gitea credentials not found**
- Credentials must be at `~/.config/claude/gitea.env`
- If missing, the report will be saved locally for manual submission
- See docs/CONFIGURATION.md for setup instructions
**Labels not applied to issue**
- Verify labels exist in the marketplace repo: `Source/Diagnostic`, `Type/Bug`
- Check the label fetch output in Step 7.2 for errors
- If labels don't exist, create them first with `/labels-sync` in the marketplace repo

View File

@@ -0,0 +1,574 @@
---
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 9.5: Search Lessons Learned
Before proposing a fix, search for relevant lessons from past fixes.
**1. Extract search tags from the issue:**
```
SEARCH_TAGS = []
# Add tool names
for each failed_tool in issue:
SEARCH_TAGS.append(tool_name) # e.g., "get_labels", "validate_repo_org"
# Add error category
SEARCH_TAGS.append(error_category) # e.g., "parameter-format", "authentication"
# Add component if identifiable
if error relates to MCP server:
SEARCH_TAGS.append("mcp")
if error relates to command:
SEARCH_TAGS.append("command")
```
**2. Search lessons learned:**
```
mcp__plugin_projman_gitea__search_lessons(
repo=REPO_NAME,
tags=SEARCH_TAGS,
limit=5
)
```
**3. Also search by error keywords:**
```
mcp__plugin_projman_gitea__search_lessons(
repo=REPO_NAME,
query=[key error message words],
limit=5
)
```
**4. Display relevant lessons (if any):**
```
Related Lessons Learned
=======================
Found [N] relevant lessons from past fixes:
📚 Lesson: "Sprint 14 - Parameter validation in MCP tools"
Tags: mcp, get_labels, parameter-format
Summary: Always validate repo parameter format before API calls
Prevention: Add format check at function entry
📚 Lesson: "Sprint 12 - Graceful fallback for missing config"
Tags: configuration, fallback
Summary: Commands should work even without .env
Prevention: Check for env vars, use sensible defaults
These lessons may inform your fix approach.
```
If no lessons found, display:
```
No related lessons found. This may be a new type of issue.
```
### 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. Come back and run Step 15 to close issue and capture lesson
```
### Step 15: Verify, Close, and Capture Lesson
**This step runs AFTER the user has verified the fix works.**
When user returns and confirms the fix is working:
**1. Close the issue:**
```
mcp__plugin_projman_gitea__update_issue(
repo=REPO_NAME,
issue_number=ISSUE_NUMBER,
state="closed"
)
```
**2. Ask about lesson capture:**
Use AskUserQuestion:
```
This fix addressed [ERROR_TYPE] in [COMPONENT].
Would you like to capture this as a lesson learned?
Options:
- Yes, capture lesson (helps avoid similar issues in future)
- No, skip (trivial fix or already documented)
```
**3. If user chooses Yes, auto-generate lesson content:**
```
LESSON_TITLE = "Sprint [N] - [Brief description of fix]"
# Example: "Sprint 17 - MCP parameter validation"
LESSON_CONTENT = """
## Context
[What was happening when the issue occurred]
- Command/tool being used: [FAILED_TOOL]
- Error encountered: [ERROR_MESSAGE]
## Problem
[Root cause identified during investigation]
## Solution
[What was changed to fix it]
- Files modified: [LIST]
- PR: #[PR_NUMBER]
## Prevention
[How to avoid this in the future]
## Related
- Issue: #[ISSUE_NUMBER]
- PR: #[PR_NUMBER]
"""
LESSON_TAGS = [
tool_name, # e.g., "get_labels"
error_category, # e.g., "parameter-format"
component, # e.g., "mcp", "command"
"bug-fix"
]
```
**4. Show lesson preview and ask for approval:**
```
Lesson Preview
==============
Title: [LESSON_TITLE]
Tags: [LESSON_TAGS]
Content:
[LESSON_CONTENT]
Save this lesson? (Y/N/Edit)
```
**5. If approved, create the lesson:**
```
mcp__plugin_projman_gitea__create_lesson(
repo=REPO_NAME,
title=LESSON_TITLE,
content=LESSON_CONTENT,
tags=LESSON_TAGS,
category="sprints"
)
```
**6. Report completion:**
```
Issue Closed & Lesson Captured
==============================
Issue #[N]: CLOSED
Lesson: "[LESSON_TITLE]" saved to wiki
This lesson will be surfaced in future /debug-review
sessions when similar errors are encountered.
```
## 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 until user confirms fix works (Step 15)
- **DO NOT** commit directly to development or main branches
- **DO NOT** skip the lessons learned search - past fixes inform better solutions
## 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

@@ -1,114 +1,463 @@
---
description: Run initial setup for projman plugin
description: Interactive setup wizard for projman plugin - configures MCP servers, credentials, and project settings
---
# Initial Setup
# Initial Setup Wizard
Run the installation script to set up the projman plugin.
This command guides the user through complete projman setup interactively. It handles both first-time marketplace setup and per-project configuration.
## What This Does
## Important Context
1. Creates Python virtual environments for MCP servers
2. Installs all dependencies
3. Creates configuration file templates
4. Validates existing configuration
5. Validates repository organization
6. Syncs label taxonomy
7. Reports remaining manual steps
- **This command uses Bash, Read, Write, and AskUserQuestion tools** - NOT MCP tools
- **MCP tools won't work until after setup + session restart**
- **Tokens must be entered manually by the user** for security (not typed into chat)
## Execution
---
## Quick Path Detection
**FIRST**, check if system-level setup is already complete:
```bash
cd ${PROJECT_ROOT}
./scripts/setup.sh
cat ~/.config/claude/gitea.env 2>/dev/null | grep -v "^#" | grep -v "PASTE_YOUR" | grep -v "example.com" | grep "GITEA_API_TOKEN=" && echo "SYSTEM_CONFIGURED" || echo "SYSTEM_NEEDS_SETUP"
```
## Configuration Structure
**If SYSTEM_CONFIGURED:**
The plugin uses a hybrid configuration approach:
The system-level configuration already exists. Offer the user a choice:
**System-Level (credentials):**
```
~/.config/claude/gitea.env
```
Contains API credentials that work across all projects.
Use AskUserQuestion:
- Question: "System configuration found. What would you like to do?"
- Header: "Setup Mode"
- Options:
- "Quick project setup only (Recommended for new projects)"
- "Full setup (re-check everything)"
**Project-Level (repository/paths):**
```
project-root/.env
```
Contains project-specific settings like repository name.
**If "Quick project setup":**
- Skip directly to **Phase 4: Project-Level Configuration**
- This is equivalent to running `/project-init`
## After Running
**If "Full setup":**
- Continue with Phase 1 below
Review the output for any manual steps required:
**If SYSTEM_NEEDS_SETUP:**
- Continue with Phase 1 (full setup required)
1. **Configure API credentials** in `~/.config/claude/gitea.env`:
```
GITEA_URL=https://gitea.your-company.com
GITEA_TOKEN=your-api-token
GITEA_ORG=your-organization
---
## Phase 1: Environment Validation
### Step 1.1: Check Python Version
Run this command to verify Python 3.10+ is available:
```bash
python3 --version
```
2. **Configure project settings** in `.env`:
```
GITEA_REPO=your-repo-name
WIKIJS_PROJECT=your-project
**If version is below 3.10:**
- Stop setup and inform user: "Python 3.10 or higher is required. Please install it and run /initial-setup again."
- Provide installation hints based on OS (apt, brew, etc.)
**If successful:** Continue to next step.
---
## Phase 2: MCP Server Setup
### Step 2.1: Locate the Plugin Installation
The plugin is installed somewhere on the system. Find the MCP server directory by resolving the path.
First, identify where this plugin is installed. The MCP server should be accessible via the symlink structure. Look for the gitea MCP server:
```bash
# Find the plugin's mcp-servers directory (search common locations)
find ~/.claude ~/.config/claude -name "mcp_server" -path "*gitea*" 2>/dev/null | head -5
```
3. **Run `/labels-sync`** to sync Gitea labels
If found, extract the parent path (the gitea MCP server root).
4. **Verify Gitea Wiki** is accessible and has proper structure
**Alternative:** If the user knows the marketplace location, ask them:
## Pre-Flight Checks
Use AskUserQuestion:
- Question: "Where is the Leo Claude Marketplace installed?"
- Options:
- "~/.claude/plugins/leo-claude-mktplace" (default Claude plugins location)
- "Let me find it automatically"
- Other (user provides path)
The setup script validates:
### Step 2.2: Check if Virtual Environment Exists
- Repository belongs to an organization (required)
- Required label categories exist
- API credentials are valid
- Network connectivity to Gitea
Once you have the MCP server path (e.g., `/path/to/mcp-servers/gitea`), check for the venv:
## Re-Running
This command is safe to run multiple times. It will skip already-completed steps.
## MCP Server Structure
The plugin bundles these MCP servers:
```
plugins/projman/mcp-servers/
└── gitea/
├── .venv/
├── requirements.txt
└── mcp_server/
├── server.py
├── gitea_client.py
└── tools/
├── issues.py
├── labels.py
├── wiki.py
├── milestones.py
└── dependencies.py
```bash
ls -la /path/to/mcp-servers/gitea/.venv/bin/python 2>/dev/null && echo "VENV_EXISTS" || echo "VENV_MISSING"
```
## Troubleshooting
### Step 2.3: Create Virtual Environment (if missing)
**Error: Repository not under organization**
- This plugin requires repositories to belong to a Gitea organization
- Transfer your repository to an organization or create one
If venv doesn't exist:
**Error: Missing required labels**
- Run `/labels-sync` to create missing labels
- Or create them manually in Gitea
```bash
cd /path/to/mcp-servers/gitea && python3 -m venv .venv
```
**Error: Cannot connect to Gitea**
- Verify `GITEA_URL` in `~/.config/claude/gitea.env`
- Check your API token has proper permissions
- Ensure network connectivity
Then install dependencies:
**Error: Virtual environment creation failed**
- Ensure Python 3.8+ is installed
- Check disk space
- Try running `python -m venv .venv` manually in the MCP server directory
```bash
cd /path/to/mcp-servers/gitea && source .venv/bin/activate && pip install --upgrade pip && pip install -r requirements.txt && deactivate
```
**If pip install fails:**
- Show the error to the user
- Suggest: "Check your internet connection and try again. You can also manually run: `cd /path/to/mcp-servers/gitea && source .venv/bin/activate && pip install -r requirements.txt`"
### Step 2.4: NetBox MCP Server (Optional)
Check if the user wants to set up the NetBox MCP server (for cmdb-assistant plugin):
Use AskUserQuestion:
- Question: "Do you want to set up the NetBox MCP server for infrastructure management?"
- Options:
- "Yes, set up NetBox MCP"
- "No, skip NetBox (Recommended if not using cmdb-assistant)"
If yes, repeat steps 2.2-2.3 for `/path/to/mcp-servers/netbox`.
---
## Phase 3: System-Level Configuration
System configuration is stored in `~/.config/claude/` and contains credentials that work across all projects.
### Step 3.1: Create Config Directory
```bash
mkdir -p ~/.config/claude
```
### Step 3.2: Check Gitea Configuration
```bash
cat ~/.config/claude/gitea.env 2>/dev/null || echo "FILE_NOT_FOUND"
```
**If file doesn't exist:** Go to Step 3.3 (Create Template)
**If file exists:** Read it and check if values are placeholders (contain "example.com" or "your_" or "token_here"). If placeholders, go to Step 3.4 (Update Existing).
**If file has real values:** Go to Step 3.5 (Validate).
### Step 3.3: Create Gitea Configuration Template
Gather the Gitea server URL from the user.
Use AskUserQuestion:
- Question: "What is your Gitea server URL? (e.g., https://gitea.company.com)"
- Header: "Gitea URL"
- Options:
- "https://gitea.hotserv.cloud" (if this is Leo's setup)
- "Other (I'll provide the URL)"
If "Other", ask the user to type the URL.
Now create the configuration file with a placeholder for the token:
```bash
cat > ~/.config/claude/gitea.env << 'EOF'
# Gitea API Configuration
# Generated by /initial-setup
# Note: GITEA_ORG is configured per-project in .env
GITEA_API_URL=<USER_PROVIDED_URL>
GITEA_API_TOKEN=PASTE_YOUR_TOKEN_HERE
EOF
chmod 600 ~/.config/claude/gitea.env
```
Replace `<USER_PROVIDED_URL>` with the actual value from the user.
### Step 3.4: Token Entry Instructions
**CRITICAL: Do not ask the user to type the token into chat.**
Display these instructions clearly:
---
**Action Required: Add Your Gitea API Token**
I've created the configuration file at `~/.config/claude/gitea.env` but you need to add your API token manually for security.
**Steps:**
1. Open the file in your editor:
```
nano ~/.config/claude/gitea.env
```
Or use any editor you prefer.
2. Generate a Gitea token (if you don't have one):
- Go to your Gitea instance → User Icon → Settings
- Click "Applications" tab
- Under "Manage Access Tokens", click "Generate New Token"
- Name it something like "claude-code"
- Select permissions: `repo` (all), `read:org`, `read:user`
- Click "Generate Token" and copy it immediately
3. Replace `PASTE_YOUR_TOKEN_HERE` with your actual token
4. Save the file
---
Use AskUserQuestion:
- Question: "Have you added your Gitea token to ~/.config/claude/gitea.env?"
- Header: "Token Added?"
- Options:
- "Yes, I've added the token"
- "I need help generating a token"
- "Skip for now (I'll do it later)"
**If "I need help":** Provide detailed instructions for their specific Gitea instance.
**If "Skip":** Warn that MCP tools won't work until configured, but continue with project setup.
### Step 3.5: Validate Gitea Configuration
Read the config file and verify it has non-placeholder values:
```bash
source ~/.config/claude/gitea.env && echo "URL: $GITEA_API_URL" && echo "TOKEN_LENGTH: ${#GITEA_API_TOKEN}"
```
If TOKEN_LENGTH is less than 10 or contains "PASTE" or "your_", the token hasn't been set properly.
**Note:** GITEA_ORG is configured per-project in `.env`, not in the system-level config.
**Test connectivity (optional but recommended):**
```bash
source ~/.config/claude/gitea.env && curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_API_TOKEN" "$GITEA_API_URL/user"
```
- **200:** Success! Credentials are valid.
- **401:** Invalid token.
- **404/Connection error:** Invalid URL or network issue.
Report the result to the user.
### Step 3.6: Git-Flow Configuration (Optional)
Use AskUserQuestion:
- Question: "Do you want to configure git-flow defaults for smart commits?"
- Header: "Git-Flow"
- Options:
- "Yes, use recommended defaults (Recommended)"
- "Yes, let me customize"
- "No, skip git-flow configuration"
If yes with defaults:
```bash
cat > ~/.config/claude/git-flow.env << 'EOF'
# Git-Flow Default Configuration
GIT_WORKFLOW_STYLE=feature-branch
GIT_DEFAULT_BASE=development
GIT_AUTO_DELETE_MERGED=true
GIT_AUTO_PUSH=false
GIT_PROTECTED_BRANCHES=main,master,development,staging,production
GIT_COMMIT_STYLE=conventional
GIT_CO_AUTHOR=true
EOF
chmod 600 ~/.config/claude/git-flow.env
```
If customize, use AskUserQuestion for each setting.
---
## Phase 4: Project-Level Configuration
Project configuration is stored in `.env` in the current project root.
### Step 4.1: Verify Current Directory
Confirm we're in the right place:
```bash
pwd && git rev-parse --show-toplevel 2>/dev/null || echo "NOT_A_GIT_REPO"
```
If not a git repo, ask the user:
Use AskUserQuestion:
- Question: "The current directory doesn't appear to be a git repository. Where should I create the project configuration?"
- Options:
- "This directory is correct, continue anyway"
- "Let me navigate to the right directory first"
### Step 4.2: Check Existing Project Configuration
```bash
cat .env 2>/dev/null || echo "FILE_NOT_FOUND"
```
If `.env` exists and has `GITEA_REPO=` set to a non-placeholder value, skip to Phase 5.
### Step 4.3: Infer Organization and Repository from Git Remote
Try to detect org and repo name automatically:
```bash
git remote get-url origin 2>/dev/null
```
Parse the output to extract both organization and repository:
- `git@gitea.example.com:org/repo-name.git` → org: `org`, repo: `repo-name`
- `https://gitea.example.com/org/repo-name.git` → org: `org`, repo: `repo-name`
Extract organization:
```bash
git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\)\/[^/]*$/\1/'
```
Extract repository:
```bash
git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\)\.git$/\1/' | sed 's/.*\/\([^/]*\)$/\1/'
```
### Step 4.4: Validate Repository via Gitea API
**Before asking for confirmation**, verify the repository exists and is accessible:
```bash
source ~/.config/claude/gitea.env
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_API_TOKEN" "$GITEA_API_URL/repos/<detected-org>/<detected-repo>"
```
**Based on response:**
| HTTP Code | Meaning | Action |
|-----------|---------|--------|
| **200** | Repository exists and accessible | **Auto-fill without asking** - skip to Step 4.7 |
| **404** | Repository not found | Ask user to confirm/correct (Step 4.5) |
| **401/403** | Token issue or no access | Warn about permissions, ask to confirm |
| **Other** | Network/server issue | Warn, ask to confirm manually |
**If 200 OK:** Display confirmation message and skip to Step 4.7:
"Verified: Repository '<detected-org>/<detected-repo>' exists and is accessible."
### Step 4.5: Confirm Organization (only if API validation failed)
Use AskUserQuestion:
- Question: "Repository '<detected-org>/<detected-repo>' was not found. Is '<detected-org>' the correct organization?"
- Header: "Organization"
- Options:
- "Yes, that's correct"
- "No, let me specify the correct organization"
If no, ask user to provide the correct organization name.
### Step 4.6: Confirm Repository Name (only if API validation failed)
Use AskUserQuestion:
- Question: "Is '<detected-repo-name>' the correct Gitea repository name?"
- Header: "Repository"
- Options:
- "Yes, that's correct"
- "No, let me specify the correct name"
If no, ask user to provide the correct name.
**After user provides corrections, re-validate via API (Step 4.4)** to ensure the corrected values are valid.
### Step 4.7: Create Project Configuration
```bash
cat > .env << 'EOF'
# Project Configuration for projman
# Generated by /initial-setup
GITEA_ORG=<ORG_NAME>
GITEA_REPO=<REPO_NAME>
EOF
```
Replace `<REPO_NAME>` with the confirmed value.
**Important:** Check if `.env` is in `.gitignore`:
```bash
grep -q "^\.env$" .gitignore 2>/dev/null && echo "GITIGNORE_OK" || echo "GITIGNORE_MISSING"
```
If not in `.gitignore`, warn the user:
"Warning: `.env` is not in your `.gitignore`. Consider adding it to prevent accidentally committing credentials."
---
## Phase 5: Final Validation and Next Steps
### Step 5.1: Summary Report
Display a summary of what was configured:
```
╔══════════════════════════════════════════════════════════════╗
║ SETUP COMPLETE ║
╠══════════════════════════════════════════════════════════════╣
║ MCP Server (Gitea): ✓ Installed ║
║ System Config: ✓ ~/.config/claude/gitea.env ║
║ Project Config: ✓ ./.env ║
║ Gitea Connection: ✓ Verified (or ⚠ Not tested) ║
╚══════════════════════════════════════════════════════════════╝
```
### Step 5.2: Session Restart Notice
**IMPORTANT:** Display this notice clearly:
---
**⚠️ Session Restart Required**
The MCP server has been configured but won't be available until you restart your Claude Code session.
**To complete setup:**
1. Exit this Claude Code session (type `/exit` or close the terminal)
2. Start a new Claude Code session in this project
3. The Gitea MCP tools will now be available
**After restart, you can:**
- Run `/labels-sync` to sync your label taxonomy
- Run `/sprint-plan` to start planning
- Use MCP tools like `list_issues`, `create_issue`, etc.
---
### Step 5.3: Troubleshooting Checklist
If something isn't working after restart, check:
1. **MCP server not found:** Verify venv exists at the expected path
2. **Authentication failed:** Re-check token in `~/.config/claude/gitea.env`
3. **Wrong repository:** Verify `GITEA_REPO` in `./.env` matches Gitea exactly
4. **Network error:** Ensure Gitea URL is accessible from this machine
---
## Re-Running This Command
This command is safe to run multiple times:
- Existing venvs are skipped (not recreated)
- Existing config files are checked for validity
- Only missing or placeholder values are updated
- Project config can be regenerated for new projects
---
## Quick Reference: Files Created
| File | Purpose | Contains |
|------|---------|----------|
| `~/.config/claude/gitea.env` | System credentials | URL, token, org |
| `~/.config/claude/git-flow.env` | Git defaults | Workflow settings |
| `./.env` | Project settings | Repository name |
| `<mcp-server>/.venv/` | Python environment | Dependencies |

View File

@@ -1,47 +1,103 @@
---
description: Synchronize label taxonomy from Gitea and update suggestion logic
description: Fetch and validate label taxonomy from Gitea, create missing required labels
---
# Sync Label Taxonomy from Gitea
This command synchronizes the label taxonomy from Gitea (organization + repository labels) and updates the local reference file used by the label suggestion logic.
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:
- Organization-level labels (shared across all repos)
- Repository-specific labels (unique to this project)
You MUST follow these steps in order. Do NOT skip any step.
**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. **Validate Repository** - Verify repo belongs to an organization using `validate_repo_org`
2. **Fetch Current Labels** - Uses `get_labels` MCP tool to fetch all labels (org + repo)
3. **Compare with Local Reference** - Checks against `skills/label-taxonomy/labels-reference.md`
4. **Detect Changes** - Identifies new, removed, or modified labels
5. **Explain Changes** - Shows what changed and why it matters
6. **Create Missing Labels** - Uses `create_label` for required labels that don't exist
7. **Update Reference** - Updates the local labels-reference.md file
8. **Confirm Update** - Asks for user confirmation before updating
```bash
git remote get-url origin
```
## MCP Tools Used
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`
**Gitea Tools:**
- `get_labels` - Fetch all labels (organization + repository)
- `create_label` - Create missing required labels
- `validate_repo_org` - Verify repository belongs to organization
Store this as `REPO_NAME` for all subsequent MCP calls.
## Required Label Categories
### Step 2: Validate Repository Organization
At minimum, these label categories must exist:
Call MCP tool with the detected repo:
```
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
- **Priority/***: Low, Medium, High, Critical
- **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)
Use `create_label_smart` which automatically creates labels at the correct level:
- **Organization level**: Type/*, Priority/*, Complexity/*, Effort/*, Risk/*, Source/*, Agent/*
- **Repository level**: Component/*, Tech/*
```
mcp__plugin_projman_gitea__create_label_smart(repo=REPO_NAME, name="Type/Bug", color="d73a4a")
```
This automatically detects whether to create at org or repo level based on the category.
**Alternative (explicit control):**
- Org labels: `create_org_label(org="org-name", name="Type/Bug", color="d73a4a")`
- Repo labels: `create_label(repo=REPO_NAME, name="Component/Backend", color="5319e7")`
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
@@ -49,212 +105,63 @@ If any required labels are missing, the command will offer to create them.
Label Taxonomy Sync
===================
Validating repository organization...
Repository: bandit/your-repo-name
Organization: bandit
Detecting repository from git remote...
Repository: personal-projects/your-repo-name
Owner type: Organization
Fetching labels from Gitea...
Current Label Taxonomy:
- Organization Labels: 28
- Organization Labels: 27
- Repository Labels: 16
- Total: 44 labels
- Total: 43 labels
Comparing with local reference...
Organization Labels by Category:
Type/*: 6 labels
Priority/*: 4 labels
Complexity/*: 3 labels
Effort/*: 5 labels
...
Changes Detected:
NEW: Type/Performance (org-level)
Description: Performance optimization tasks
Color: #FF6B6B
Suggestion: Add to suggestion logic for performance-related work
NEW: Tech/Redis (repo-level)
Description: Redis-related technology
Color: #DC143C
Suggestion: Add to suggestion logic for caching and data store work
MODIFIED: Priority/Critical
Change: Color updated from #D73A4A to #FF0000
Impact: Visual only, no logic change needed
REMOVED: Component/Legacy
Reason: Component deprecated and removed from codebase
Impact: Remove from suggestion logic
Repository Labels by Category:
Component/*: 9 labels
Tech/*: 7 labels
Required Labels Check:
Type/*: 6/6 present
Priority/*: 4/4 present
Complexity/*: 3/3 present
Efforts/*: 5/5 present
Type/*: 6/6 present
Priority/*: 4/4 present
Complexity/*: 3/3 present
Effort/*: 5/5 present
Summary:
- 2 new labels added
- 1 label modified (color only)
- 1 label removed
- Total labels: 44 -> 45
- All required labels present
Update local reference file?
[Y/n]
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):**
- `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
When creating missing labels, match the format used by existing labels in the repository.
**Repository Labels (16):**
- `Component/*` (9): Backend, Frontend, API, Database, Auth, Deploy, Testing, Docs, Infra
- `Tech/*` (7): Python, JavaScript, Docker, PostgreSQL, Redis, Vue, FastAPI
## Troubleshooting
## Local Reference File
**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 command updates `skills/label-taxonomy/labels-reference.md` with:
**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
```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
...
```
**Git remote not found**
- Ensure you're running in a directory with a git repository
- Check that the `origin` remote is configured
## When to Run
Run `/labels-sync` when:
- Setting up the plugin for the first time
- You notice missing labels in suggestions
- New labels are added to Gitea (announced by team)
- Quarterly maintenance (check for changes)
- New labels are added to Gitea
- 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
```
User: /labels-sync
Validating repository organization...
Repository: bandit/your-repo-name
Fetching labels from Gitea...
Current Label Taxonomy:
- Organization Labels: 28
- Repository Labels: 16
- Total: 44 labels
Comparing with local reference...
No changes detected. Label taxonomy is up to date.
Last synced: 2025-01-18 14:30 UTC
```
```
User: /labels-sync
Fetching labels from Gitea...
Changes Detected:
NEW: Type/Performance
NEW: Tech/Redis
Required Labels Check:
MISSING: Complexity/Simple
MISSING: Complexity/Medium
MISSING: Complexity/Complex
Would you like me to create the missing required labels? [Y/n] y
Creating missing labels...
Created: Complexity/Simple
Created: Complexity/Medium
Created: Complexity/Complex
Update local reference file? [Y/n] y
Label taxonomy updated successfully!
Suggestion logic updated with new labels
New labels available for use:
- Type/Performance
- Tech/Redis
- Complexity/Simple
- Complexity/Medium
- Complexity/Complex
```
## 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: Repository is not under an organization**
- This plugin requires repositories to belong to an organization
- Transfer the repository to an organization or create one
**Error: Permission denied to update reference file**
- Check file permissions on `skills/label-taxonomy/labels-reference.md`
- Ensure you have write access to the plugin directory
**No changes detected but labels seem wrong**
- The reference file may be manually edited - review it
- Try forcing a re-sync by deleting the reference file first
- Check if you're comparing against the correct repository
## 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

@@ -0,0 +1,218 @@
---
description: Quick project setup - configures only project-level settings (assumes system setup is complete)
---
# Project Initialization
Fast setup for a new project when system-level configuration is already complete.
**Use this when:**
- You've already run `/initial-setup` on this machine
- You're starting work on a new project/repository
- You just need to create the project `.env` file
**Use `/initial-setup` instead if:**
- This is your first time using the plugin
- MCP tools aren't working (might need system setup)
---
## Pre-Flight Check
### Step 1: Verify System Configuration Exists
Quickly verify system setup is complete:
```bash
cat ~/.config/claude/gitea.env 2>/dev/null | grep -v "^#" | grep -v "PASTE_YOUR" | grep "GITEA_API_TOKEN=" && echo "SYSTEM_OK" || echo "SYSTEM_MISSING"
```
**If SYSTEM_MISSING:**
Display this message and stop:
---
**System configuration not found or incomplete.**
It looks like the system-level setup hasn't been completed yet. Please run:
```
/initial-setup
```
This will configure both system credentials and this project.
---
**If SYSTEM_OK:** Continue to project setup.
---
## Project Setup
### Step 2: Verify Current Directory
```bash
pwd && git rev-parse --show-toplevel 2>/dev/null || echo "NOT_A_GIT_REPO"
```
If not a git repo, ask:
Use AskUserQuestion:
- Question: "This doesn't appear to be a git repository. Continue anyway?"
- Header: "Directory"
- Options:
- "Yes, continue here"
- "No, I'll navigate to the correct directory"
### Step 3: Check for Existing Configuration
```bash
cat .env 2>/dev/null | grep "GITEA_REPO=" || echo "NOT_CONFIGURED"
```
**If already configured:**
Use AskUserQuestion:
- Question: "This project already has GITEA_ORG and GITEA_REPO configured. What would you like to do?"
- Header: "Existing"
- Options:
- "Keep existing configuration"
- "Reconfigure (replace current settings)"
**If "Keep":** End with success message.
### Step 4: Detect Organization and Repository
Try to auto-detect from git remote:
```bash
git remote get-url origin 2>/dev/null
```
Extract organization:
```bash
git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\)\/[^/]*$/\1/'
```
Extract repository:
```bash
git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\)\.git$/\1/' | sed 's/.*\/\([^/]*\)$/\1/'
```
### Step 5: Validate Repository via Gitea API
Verify the repository exists and is accessible:
```bash
source ~/.config/claude/gitea.env
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_API_TOKEN" "$GITEA_API_URL/repos/<detected-org>/<detected-repo>"
```
| HTTP Code | Action |
|-----------|--------|
| **200** | Auto-fill - display "Verified: <org>/<repo> exists" and skip to Step 8 |
| **404** | Repository not found - proceed to Step 6 |
| **401/403** | Permission issue - warn and proceed to Step 6 |
### Step 6: Confirm Organization (only if API validation failed)
Use AskUserQuestion:
- Question: "Repository not found. Is '<detected-org>' the correct organization?"
- Header: "Organization"
- Options:
- "Yes, that's correct"
- "No, let me specify"
If "No", ask user to type the correct organization name.
### Step 7: Confirm Repository Name (only if API validation failed)
Use AskUserQuestion:
- Question: "Is '<detected-repo-name>' the correct repository name?"
- Header: "Repository"
- Options:
- "Yes, that's correct"
- "No, let me specify"
If "No", ask user to type the correct name.
**After corrections, re-validate via API (Step 5).**
### Step 8: Create/Update Project Configuration
**If `.env` exists:**
Check if it already has other content and append:
```bash
echo "" >> .env
echo "# Added by /project-init" >> .env
echo "GITEA_ORG=<ORG_NAME>" >> .env
echo "GITEA_REPO=<REPO_NAME>" >> .env
```
**If `.env` doesn't exist:**
```bash
cat > .env << 'EOF'
# Project Configuration for projman
# Generated by /project-init
GITEA_ORG=<ORG_NAME>
GITEA_REPO=<REPO_NAME>
EOF
```
### Step 9: Check .gitignore
```bash
grep -q "^\.env$" .gitignore 2>/dev/null && echo "GITIGNORE_OK" || echo "GITIGNORE_MISSING"
```
**If GITIGNORE_MISSING:**
Use AskUserQuestion:
- Question: "`.env` is not in `.gitignore`. Add it to prevent committing secrets?"
- Header: "gitignore"
- Options:
- "Yes, add .env to .gitignore (Recommended)"
- "No, I'll handle it manually"
If yes:
```bash
echo ".env" >> .gitignore
```
---
## Complete
Display success message:
```
╔══════════════════════════════════════════════════════════════╗
║ PROJECT CONFIGURED ║
╠══════════════════════════════════════════════════════════════╣
║ Organization: <ORG_NAME> ║
║ Repository: <REPO_NAME> ║
║ Config file: ./.env ║
╚══════════════════════════════════════════════════════════════╝
You're ready to use projman commands:
• /sprint-plan - Start sprint planning
• /sprint-status - Check progress
• /labels-sync - Sync label taxonomy
```
---
## Troubleshooting
**MCP tools not working?**
- Run `/initial-setup` for full setup including MCP server
- Restart your Claude Code session after setup
**Wrong repository configured?**
- Edit `.env` directly: `nano .env`
- Or run `/project-init` again and choose "Reconfigure"

View File

@@ -0,0 +1,196 @@
---
description: Sync project configuration with current git remote - use after changing repository location or organization
---
# Project Sync
Updates project configuration when the git remote URL has changed (repository moved, renamed, or organization changed).
**Use this when:**
- You moved the repository to a different organization
- You renamed the repository
- You changed the git remote URL
- The SessionStart hook detected a mismatch
---
## Step 1: Verify System Configuration
```bash
cat ~/.config/claude/gitea.env 2>/dev/null | grep -v "^#" | grep -v "PASTE_YOUR" | grep "GITEA_API_TOKEN=" && echo "SYSTEM_OK" || echo "SYSTEM_MISSING"
```
**If SYSTEM_MISSING:** Stop and instruct user to run `/initial-setup` first.
---
## Step 2: Read Current Configuration
Read the current .env values:
```bash
cat .env 2>/dev/null
```
Extract current values:
- `CURRENT_ORG` from `GITEA_ORG=...`
- `CURRENT_REPO` from `GITEA_REPO=...`
**If .env doesn't exist or has no GITEA values:** Redirect to `/project-init`.
---
## Step 3: Detect Git Remote Values
Get the current git remote:
```bash
git remote get-url origin 2>/dev/null
```
Extract organization:
```bash
git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\)\/[^/]*$/\1/'
```
Extract repository:
```bash
git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\)\.git$/\1/' | sed 's/.*\/\([^/]*\)$/\1/'
```
---
## Step 4: Compare Values
Compare current .env values with detected git remote values:
| Scenario | Action |
|----------|--------|
| **Both match** | Display "Configuration is in sync" and exit |
| **Organization changed** | Proceed to Step 5 |
| **Repository changed** | Proceed to Step 5 |
| **Both changed** | Proceed to Step 5 |
**If already in sync:**
```
╔══════════════════════════════════════════════════════════════╗
║ CONFIGURATION IN SYNC ║
╠══════════════════════════════════════════════════════════════╣
║ Organization: <ORG_NAME> ║
║ Repository: <REPO_NAME> ║
║ Git Remote: matches .env ║
╚══════════════════════════════════════════════════════════════╝
```
Exit here if in sync.
---
## Step 5: Show Detected Changes
Display the detected changes to the user:
```
╔══════════════════════════════════════════════════════════════╗
║ REPOSITORY CHANGE DETECTED ║
╠══════════════════════════════════════════════════════════════╣
║ Current .env │ Git Remote ║
║ Organization: <OLD_ORG> │ <NEW_ORG> ║
║ Repository: <OLD_REPO> │ <NEW_REPO> ║
╚══════════════════════════════════════════════════════════════╝
```
---
## Step 6: Validate New Repository via Gitea API
Verify the new repository exists and is accessible:
```bash
source ~/.config/claude/gitea.env
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_API_TOKEN" "$GITEA_API_URL/repos/<NEW_ORG>/<NEW_REPO>"
```
| HTTP Code | Action |
|-----------|--------|
| **200** | Repository verified - proceed to Step 7 |
| **404** | Repository not found - ask user to confirm (Step 6a) |
| **401/403** | Permission issue - warn and ask to confirm |
### Step 6a: Confirm if API Validation Failed
Use AskUserQuestion:
- Question: "The new repository '<NEW_ORG>/<NEW_REPO>' was not found via API. Update configuration anyway?"
- Header: "Not Found"
- Options:
- "Yes, update anyway (I'll fix the remote later)"
- "No, let me fix the git remote first"
- "Let me specify different values"
**If "specify different values":** Ask for correct org and repo, then re-validate.
---
## Step 7: Confirm Update
Use AskUserQuestion:
- Question: "Update project configuration to match git remote?"
- Header: "Confirm"
- Options:
- "Yes, update .env (Recommended)"
- "No, keep current configuration"
**If "No":** Exit without changes.
---
## Step 8: Update Configuration
Update the .env file with new values:
```bash
# Update GITEA_ORG
sed -i 's/^GITEA_ORG=.*/GITEA_ORG=<NEW_ORG>/' .env
# Update GITEA_REPO
sed -i 's/^GITEA_REPO=.*/GITEA_REPO=<NEW_REPO>/' .env
```
Alternatively, if sed doesn't work well, read the file, replace values, and write back.
---
## Step 9: Verify Update
Read the updated .env and display confirmation:
```
╔══════════════════════════════════════════════════════════════╗
║ CONFIGURATION UPDATED ║
╠══════════════════════════════════════════════════════════════╣
║ Organization: <NEW_ORG> ║
║ Repository: <NEW_REPO> ║
║ Status: In sync with git remote ║
╚══════════════════════════════════════════════════════════════╝
Your project configuration has been updated.
MCP tools will now use the new repository.
```
---
## Troubleshooting
**"Repository not found" but it exists:**
- Check your Gitea token has access to the new organization
- Verify the repository name matches exactly (case-sensitive)
- Ensure your token has `repo` permissions
**Git remote URL is wrong:**
- Fix it first: `git remote set-url origin <correct-url>`
- Then run `/project-sync` again
**Want to revert the change:**
- Edit `.env` manually: `nano .env`
- Or run `/project-sync` after fixing the git remote

View File

@@ -0,0 +1,10 @@
{
"hooks": {
"SessionStart": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/startup-check.sh"
}
]
}
}

View File

@@ -0,0 +1,30 @@
#!/bin/bash
# projman startup check hook
# Checks for common issues at session start
# All output MUST have [projman] prefix
PREFIX="[projman]"
# Check if MCP venv exists
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$(dirname "$(realpath "$0")")")}"
VENV_PATH="$PLUGIN_ROOT/mcp-servers/gitea/.venv/bin/python"
if [[ ! -f "$VENV_PATH" ]]; then
echo "$PREFIX MCP venvs missing - run setup.sh from installed marketplace"
exit 0
fi
# Check git remote vs .env config (only if .env exists)
if [[ -f ".env" ]]; then
CONFIGURED_REPO=$(grep -E "^GITEA_REPO=" .env 2>/dev/null | cut -d'=' -f2 | tr -d '"' || true)
if [[ -n "$CONFIGURED_REPO" ]]; then
CURRENT_REMOTE=$(git remote get-url origin 2>/dev/null | sed 's/.*[:/]\([^/]*\/[^.]*\).*/\1/' || true)
if [[ -n "$CURRENT_REMOTE" && "$CONFIGURED_REPO" != "$CURRENT_REMOTE" ]]; then
echo "$PREFIX Git remote mismatch - run /project-sync"
exit 0
fi
fi
fi
# All checks passed - say nothing
exit 0

View File

@@ -7,7 +7,7 @@ description: Dynamic reference for Gitea label taxonomy (organization + reposito
**Status:** ✅ Synced with Gitea
**Last synced:** 2025-11-21 (via automated testing)
**Source:** Gitea (personal-projects/support-claude-mktplace)
**Source:** Gitea (personal-projects/leo-claude-mktplace)
## Overview

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

View File

@@ -78,3 +78,8 @@ main() {
}
main "$@"
# Clear plugin cache to ensure fresh hooks are loaded
echo "Clearing plugin cache..."
rm -rf ~/.claude/plugins/cache/leo-claude-mktplace/
echo "Cache cleared"

172
scripts/release.sh Executable file
View File

@@ -0,0 +1,172 @@
#!/bin/bash
# release.sh - Create a new release with version consistency
#
# Usage: ./scripts/release.sh X.Y.Z
#
# This script ensures all version references are updated consistently:
# 1. CHANGELOG.md - [Unreleased] becomes [X.Y.Z] - YYYY-MM-DD
# 2. README.md - Title updated to vX.Y.Z
# 3. marketplace.json - version field updated
# 4. Git commit and tag created
#
# Prerequisites:
# - Clean working directory (no uncommitted changes)
# - [Unreleased] section in CHANGELOG.md with content
# - On development branch
set -e
VERSION=$1
DATE=$(date +%Y-%m-%d)
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
error() { echo -e "${RED}ERROR: $1${NC}" >&2; exit 1; }
warn() { echo -e "${YELLOW}WARNING: $1${NC}"; }
success() { echo -e "${GREEN}$1${NC}"; }
info() { echo -e "$1"; }
# Validate arguments
if [ -z "$VERSION" ]; then
echo "Usage: ./scripts/release.sh X.Y.Z"
echo ""
echo "Example: ./scripts/release.sh 3.2.0"
echo ""
echo "This will:"
echo " 1. Update CHANGELOG.md [Unreleased] -> [X.Y.Z] - $(date +%Y-%m-%d)"
echo " 2. Update README.md title to vX.Y.Z"
echo " 3. Update marketplace.json version to X.Y.Z"
echo " 4. Commit with message 'chore: release vX.Y.Z'"
echo " 5. Create git tag vX.Y.Z"
exit 1
fi
# Validate version format
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
error "Invalid version format. Use X.Y.Z (e.g., 3.2.0)"
fi
# Check we're in the right directory
if [ ! -f "CHANGELOG.md" ] || [ ! -f "README.md" ] || [ ! -f ".claude-plugin/marketplace.json" ]; then
error "Must run from repository root (CHANGELOG.md, README.md, .claude-plugin/marketplace.json must exist)"
fi
# Check for clean working directory
if [ -n "$(git status --porcelain)" ]; then
warn "Working directory has uncommitted changes"
echo ""
git status --short
echo ""
read -p "Continue anyway? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
# Check current branch
BRANCH=$(git branch --show-current)
if [ "$BRANCH" != "development" ] && [ "$BRANCH" != "main" ]; then
warn "Not on development or main branch (current: $BRANCH)"
read -p "Continue anyway? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
# Check [Unreleased] section has content
if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then
error "CHANGELOG.md missing [Unreleased] section"
fi
# Check if tag already exists
if git tag -l | grep -q "^v$VERSION$"; then
error "Tag v$VERSION already exists"
fi
info ""
info "=== Release v$VERSION ==="
info ""
# Show what will change
info "Changes to be made:"
info " CHANGELOG.md: [Unreleased] -> [$VERSION] - $DATE"
info " README.md: title -> v$VERSION"
info " marketplace.json: version -> $VERSION"
info " Git: commit + tag v$VERSION"
info ""
# Preview CHANGELOG [Unreleased] content
info "Current [Unreleased] content:"
info "---"
sed -n '/^## \[Unreleased\]/,/^## \[/p' CHANGELOG.md | head -30
info "---"
info ""
read -p "Proceed with release? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
info "Aborted"
exit 0
fi
info ""
info "Updating files..."
# 1. Update CHANGELOG.md
# Replace [Unreleased] with [X.Y.Z] - DATE and add new [Unreleased] section
sed -i "s/^## \[Unreleased\]$/## [Unreleased]\n\n*Changes staged for the next release*\n\n---\n\n## [$VERSION] - $DATE/" CHANGELOG.md
# Remove the placeholder text if it exists after the new [Unreleased]
sed -i '/^\*Changes staged for the next release\*$/d' CHANGELOG.md
# Clean up any double blank lines
sed -i '/^$/N;/^\n$/d' CHANGELOG.md
success " CHANGELOG.md updated"
# 2. Update README.md title
sed -i "s/^# Leo Claude Marketplace - v[0-9]\+\.[0-9]\+\.[0-9]\+$/# Leo Claude Marketplace - v$VERSION/" README.md
success " README.md updated"
# 3. Update marketplace.json version
sed -i "s/\"version\": \"[0-9]\+\.[0-9]\+\.[0-9]\+\"/\"version\": \"$VERSION\"/" .claude-plugin/marketplace.json
success " marketplace.json updated"
info ""
info "Files updated. Review changes:"
info ""
git diff --stat
info ""
git diff CHANGELOG.md | head -40
info ""
read -p "Commit and tag? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
warn "Changes made but not committed. Run 'git checkout -- .' to revert."
exit 0
fi
# Commit
git add CHANGELOG.md README.md .claude-plugin/marketplace.json
git commit -m "chore: release v$VERSION"
success " Committed"
# Tag
git tag "v$VERSION"
success " Tagged v$VERSION"
info ""
success "=== Release v$VERSION created ==="
info ""
info "Next steps:"
info " 1. Review the commit: git show HEAD"
info " 2. Push to remote: git push && git push --tags"
info " 3. Merge to main if on development branch"
info ""

View File

@@ -1,6 +1,17 @@
#!/usr/bin/env bash
#
# setup.sh - Initial setup for lm-claude-plugins
# setup.sh - Automated setup for Leo Claude Marketplace
#
# ============================================================================
# NOTE: For interactive setup, use the /initial-setup command in Claude Code
# instead of this script. The command provides a guided wizard experience.
#
# This script is intended for:
# - CI/CD pipelines
# - Automated deployments
# - Bulk machine provisioning
# - Fallback when Claude Code is not available
# ============================================================================
#
# Usage: ./scripts/setup.sh
#
@@ -123,10 +134,10 @@ setup_config_templates() {
cat > "$config_dir/gitea.env" << 'EOF'
# Gitea API Configuration
# Update these values with your Gitea instance details
# Note: GITEA_ORG is configured per-project in .env
GITEA_URL=https://gitea.example.com
GITEA_TOKEN=your_gitea_token_here
GITEA_ORG=your_organization_name
GITEA_API_URL=https://gitea.example.com/api/v1
GITEA_API_TOKEN=your_gitea_token_here
EOF
chmod 600 "$config_dir/gitea.env"
log_success "gitea.env template created"
@@ -141,8 +152,8 @@ EOF
# NetBox API Configuration
# Update these values with your NetBox instance details
NETBOX_URL=https://netbox.example.com
NETBOX_TOKEN=your_netbox_token_here
NETBOX_API_URL=https://netbox.example.com/api
NETBOX_API_TOKEN=your_netbox_token_here
EOF
chmod 600 "$config_dir/netbox.env"
log_success "netbox.env template created"
@@ -178,8 +189,8 @@ validate_config() {
# Check Gitea config has real values
if [[ -f "$config_dir/gitea.env" ]]; then
source "$config_dir/gitea.env"
if [[ "${GITEA_TOKEN:-}" == "your_gitea_token_here" ]] || [[ -z "${GITEA_TOKEN:-}" ]]; then
log_todo "Update GITEA_TOKEN in ~/.config/claude/gitea.env"
if [[ "${GITEA_API_TOKEN:-}" == "your_gitea_token_here" ]] || [[ -z "${GITEA_API_TOKEN:-}" ]]; then
log_todo "Update GITEA_API_TOKEN in ~/.config/claude/gitea.env"
else
log_success "Gitea configuration appears valid"
fi
@@ -188,8 +199,8 @@ validate_config() {
# Check NetBox config has real values
if [[ -f "$config_dir/netbox.env" ]]; then
source "$config_dir/netbox.env"
if [[ "${NETBOX_TOKEN:-}" == "your_netbox_token_here" ]] || [[ -z "${NETBOX_TOKEN:-}" ]]; then
log_todo "Update NETBOX_TOKEN in ~/.config/claude/netbox.env"
if [[ "${NETBOX_API_TOKEN:-}" == "your_netbox_token_here" ]] || [[ -z "${NETBOX_API_TOKEN:-}" ]]; then
log_todo "Update NETBOX_API_TOKEN in ~/.config/claude/netbox.env"
else
log_success "NetBox configuration appears valid"
fi
@@ -265,7 +276,7 @@ print_report() {
# --- Main ---
main() {
echo "=============================================="
echo " lm-claude-plugins Setup (v3.0.0)"
echo " Leo Claude Marketplace Setup (v3.0.0)"
echo "=============================================="
echo ""

44
scripts/verify-hooks.sh Executable file
View File

@@ -0,0 +1,44 @@
#!/bin/bash
# Verify all hooks are command type (not prompt)
# Run this after any plugin update
echo "=== HOOK VERIFICATION ==="
echo ""
FAILED=0
# Check ALL hooks.json files in .claude directory
for f in $(find ~/.claude -name "hooks.json" 2>/dev/null); do
if grep -q '"type": "prompt"' "$f" || grep -q '"type":"prompt"' "$f"; then
echo "❌ PROMPT HOOK FOUND: $f"
FAILED=1
fi
done
# Check cache specifically
if [ -d ~/.claude/plugins/cache/leo-claude-mktplace ]; then
echo "❌ CACHE EXISTS: ~/.claude/plugins/cache/leo-claude-mktplace"
echo " Run: rm -rf ~/.claude/plugins/cache/leo-claude-mktplace/"
FAILED=1
fi
# Verify installed hooks are command type
for plugin in doc-guardian code-sentinel projman pr-review project-hygiene; do
HOOK_FILE=~/.claude/plugins/marketplaces/leo-claude-mktplace/plugins/$plugin/hooks/hooks.json
if [ -f "$HOOK_FILE" ]; then
if grep -q '"type": "command"' "$HOOK_FILE" || grep -q '"type":"command"' "$HOOK_FILE"; then
echo "$plugin: command type"
else
echo "$plugin: NOT command type"
FAILED=1
fi
fi
done
echo ""
if [ $FAILED -eq 0 ]; then
echo "✓ All hooks verified OK"
else
echo "❌ ISSUES FOUND - fix before using"
exit 1
fi