mirror of
https://github.com/nodnarbnitram/claude-code-extensions.git
synced 2026-09-14 18:36:21 +08:00
1ac816cc29
## 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>
128 lines
3.7 KiB
Python
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() |