Files
Brandon Martin 1ac816cc29 refactor(plugins): package self-contained plugin installs (#5)
## Summary
- convert the marketplace packages into self-contained plugin installs
with local `agents/`, `skills/`, `commands/`, and `hooks/` content
- replace the old `triage-workflow` path with the new `linear` skill and
ship a dedicated `cce-linear` plugin for Linear-backed ticket and PR
workflows
- remove the unfinished `cce-auto-blog` plugin so this PR stays focused
on the plugin infrastructure redo

## What Changed
- added `scripts/sync_plugin_packages.py` to generate package manifests
and sync plugin-local assets from the shared `.claude/` source tree
- updated `.claude-plugin/marketplace.json` and per-plugin manifests for
19 packages, including new `cce-linear` and `cce-tauri` entries
- materialized packaged plugin assets under `plugins/*` so marketplace
installs no longer depend on repo-root `.claude` paths
- rewired Linear commands to use the `linear` skill wrapper scripts and
removed hardcoded workspace-specific Linear URLs
- refreshed root and plugin README files to match the packaged layout
and current plugin inventory

## Verification
- `python3 -m py_compile scripts/sync_plugin_packages.py
install_extensions.py .claude/skills/linear/scripts/*.py`
- `python3 scripts/sync_plugin_packages.py`
- validated plugin JSON/manifests and `cce-linear` package wiring
- confirmed no `axios-hq` or `triage-workflow` references remain in the
`.claude` Linear sources

---------

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-01 21:51:29 -05:00

128 lines
3.7 KiB
Python

#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
import json
import sys
import subprocess
import shutil
from pathlib import Path
def check_command_exists(command):
"""Check if a command exists in PATH."""
return shutil.which(command) is not None
def run_linter(linter_cmd, file_path, linter_name):
"""Run a linter command and report results."""
try:
result = subprocess.run(
linter_cmd,
capture_output=True,
text=True
)
# Print output if there are issues
if result.returncode != 0:
print(f"🔍 {linter_name} found issues in {file_path}:")
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
return result.returncode
except Exception as e:
print(f"Error running {linter_name}: {e}", file=sys.stderr)
return 0
def lint_python(file_path):
"""Lint Python files with ruff."""
if not check_command_exists('ruff'):
return 0
return run_linter(['ruff', 'check', file_path], file_path, 'Ruff')
def lint_go(file_path):
"""Lint Go files with golangci-lint."""
if not check_command_exists('golangci-lint'):
return 0
return run_linter(['golangci-lint', 'run', file_path], file_path, 'golangci-lint')
def lint_js_ts(file_path):
"""Lint JS/TS files with ultracite (via npx)."""
# Check for npx (comes with Node.js/npm)
if check_command_exists('npx'):
# Use ultracite for comprehensive linting and security checks
result = subprocess.run(
['npx', 'ultracite', 'check', file_path],
capture_output=True,
text=True
)
if result.returncode != 0:
print(f"🔍 Ultracite found issues in {file_path}:")
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
return result.returncode
# Fall back to prettier if npx is not available
if check_command_exists('prettier'):
result = subprocess.run(
['prettier', '--check', file_path],
capture_output=True,
text=True
)
if result.returncode != 0:
print(f"🔍 Prettier found formatting issues in {file_path}:")
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
return result.returncode
return 0
def main():
try:
# Read JSON input from stdin
input_data = json.load(sys.stdin)
# Extract file path
file_path = input_data.get('tool_input', {}).get('file_path', '')
# Check if file exists
if not file_path or not Path(file_path).exists():
sys.exit(0)
file_extension = Path(file_path).suffix.lower()
# Route to appropriate linter based on file extension
exit_code = 0
if file_extension == '.py':
exit_code = lint_python(file_path)
elif file_extension == '.go':
exit_code = lint_go(file_path)
elif file_extension in ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs']:
exit_code = lint_js_ts(file_path)
# Exit with 2 to block the operation if linting failed
if exit_code != 0:
print(f"\nLinting failed for {file_path}. Please fix the issues above.", file=sys.stderr)
sys.exit(2)
sys.exit(0)
except Exception as e:
# Fail gracefully
print(f"Error in lint hook: {e}", file=sys.stderr)
sys.exit(0)
if __name__ == '__main__':
main()