feat: migrate CI commands to Forge API, add hooks.json, fix prefer-core heredoc handling
- Update CI skill commands (status, fix, run, workflow) to detect Forge vs GitHub and use Forge API with curl + FORGE_TOKEN instead of gh CLI - Add detect-forge.sh script for CI provider detection from git remote - Add hooks.json with PreToolUse, PostToolUse, PreCompact, SessionStart hooks - Fix prefer-core.sh false positives: strip heredoc content before checking commands, tighten wildcard matching for mv/cp to only block bare wildcards - Update plugin.json: rename to "code", bump to v0.1.3, point URLs to Forge - Update block-docs.sh with improved blocking rules Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
bdc617d48e
commit
e3eac36b05
11 changed files with 284 additions and 260 deletions
|
|
@ -6,7 +6,7 @@ args: [status|run|logs|fix]
|
|||
|
||||
# CI Integration
|
||||
|
||||
Check GitHub Actions status and manage CI workflows.
|
||||
Check CI status and manage workflows using the `core` CLI (supports Forgejo and GitHub).
|
||||
|
||||
## Commands
|
||||
|
||||
|
|
@ -15,66 +15,40 @@ Check GitHub Actions status and manage CI workflows.
|
|||
/ci:ci
|
||||
/ci:ci status
|
||||
```
|
||||
|
||||
Check current CI status for the repo/branch.
|
||||
|
||||
### Run workflow
|
||||
```
|
||||
/ci:ci run
|
||||
/ci:ci run tests
|
||||
```bash
|
||||
core dev ci
|
||||
core dev ci --branch $(git branch --show-current)
|
||||
core dev ci --failed
|
||||
```
|
||||
|
||||
Trigger a workflow run.
|
||||
|
||||
### View logs
|
||||
### List workflows
|
||||
```
|
||||
/ci:ci logs
|
||||
/ci:ci logs 12345
|
||||
/ci:ci workflows
|
||||
```
|
||||
```bash
|
||||
core dev workflow list
|
||||
```
|
||||
|
||||
View logs from a workflow run.
|
||||
### Issues
|
||||
```
|
||||
/ci:ci issues
|
||||
```
|
||||
```bash
|
||||
core dev issues
|
||||
core dev issues --assignee @me
|
||||
```
|
||||
|
||||
### Reviews / PRs
|
||||
```
|
||||
/ci:ci reviews
|
||||
```
|
||||
```bash
|
||||
core dev reviews
|
||||
core dev reviews --all
|
||||
```
|
||||
|
||||
### Fix failing CI
|
||||
```
|
||||
/ci:ci fix
|
||||
```
|
||||
|
||||
Analyse failing CI and suggest fixes.
|
||||
|
||||
## Implementation
|
||||
|
||||
### Check status
|
||||
```bash
|
||||
gh run list --limit 5
|
||||
gh run view --log-failed
|
||||
```
|
||||
|
||||
### Trigger workflow
|
||||
```bash
|
||||
gh workflow run tests.yml
|
||||
```
|
||||
|
||||
### View logs
|
||||
```bash
|
||||
gh run view 12345 --log
|
||||
```
|
||||
|
||||
## CI Status Report
|
||||
|
||||
```markdown
|
||||
## CI Status: main
|
||||
|
||||
| Workflow | Status | Duration | Commit |
|
||||
|----------|--------|----------|--------|
|
||||
| Tests | ✓ passing | 2m 34s | abc123 |
|
||||
| Lint | ✓ passing | 45s | abc123 |
|
||||
| Build | ✗ failed | 1m 12s | abc123 |
|
||||
|
||||
### Failing: Build
|
||||
```
|
||||
Error: go build failed
|
||||
pkg/api/handler.go:42: undefined: ErrNotFound
|
||||
```
|
||||
|
||||
**Suggested fix**: Add missing error definition
|
||||
```
|
||||
Analyse failing CI and suggest fixes. See `/ci:fix`.
|
||||
|
|
|
|||
|
|
@ -9,89 +9,39 @@ Analyse failing CI runs and suggest/apply fixes.
|
|||
|
||||
## Process
|
||||
|
||||
1. **Get failing run**
|
||||
1. **Get failing runs**
|
||||
```bash
|
||||
gh run list --status failure --limit 1
|
||||
gh run view <id> --log-failed
|
||||
core dev ci --failed
|
||||
```
|
||||
|
||||
2. **Analyse failure**
|
||||
- Parse error messages
|
||||
- Parse error messages from CI output
|
||||
- Identify root cause
|
||||
- Check if local issue or CI-specific
|
||||
|
||||
3. **Suggest fix**
|
||||
3. **Reproduce locally**
|
||||
```bash
|
||||
core go test
|
||||
core go lint
|
||||
core go vet
|
||||
```
|
||||
|
||||
4. **Suggest fix**
|
||||
- Code changes if needed
|
||||
- CI config changes if needed
|
||||
|
||||
4. **Apply fix** (if approved)
|
||||
5. **Apply fix** (if approved)
|
||||
|
||||
## Common CI Failures
|
||||
|
||||
### Test Failures
|
||||
```
|
||||
Error: go test failed
|
||||
--- FAIL: TestFoo
|
||||
```
|
||||
→ Fix the failing test locally, then push
|
||||
→ Run `core go test --run TestFoo`, fix the test, push
|
||||
|
||||
### Lint Failures
|
||||
```
|
||||
Error: golangci-lint failed
|
||||
file.go:42: undefined: X
|
||||
```
|
||||
→ Fix lint issue locally
|
||||
→ Run `core go lint`, fix lint issues
|
||||
|
||||
### Build Failures
|
||||
```
|
||||
Error: go build failed
|
||||
cannot find package
|
||||
```
|
||||
→ Run `go mod tidy`, check imports
|
||||
→ Run `core build`, check imports, run `core go fmt`
|
||||
|
||||
### Dependency Issues
|
||||
```
|
||||
Error: go mod download failed
|
||||
```
|
||||
→ Check go.mod, clear cache, retry
|
||||
|
||||
### Timeout
|
||||
```
|
||||
Error: Job exceeded time limit
|
||||
```
|
||||
→ Optimise tests or increase timeout in workflow
|
||||
|
||||
## Output
|
||||
|
||||
```markdown
|
||||
## CI Failure Analysis
|
||||
|
||||
**Run**: #12345
|
||||
**Workflow**: Tests
|
||||
**Failed at**: 2024-01-15 14:30
|
||||
|
||||
### Error
|
||||
```
|
||||
--- FAIL: TestCreateUser (0.02s)
|
||||
handler_test.go:45: expected 200, got 500
|
||||
```
|
||||
|
||||
### Analysis
|
||||
The test expects a 200 response but gets 500. This indicates the handler is returning an error.
|
||||
|
||||
### Root Cause
|
||||
Looking at recent changes, `ErrNotFound` was removed but still referenced.
|
||||
|
||||
### Fix
|
||||
Add the missing error definition:
|
||||
```go
|
||||
var ErrNotFound = errors.New("not found")
|
||||
```
|
||||
|
||||
### Commands
|
||||
```bash
|
||||
# Apply fix and push
|
||||
git add . && git commit -m "fix: add missing ErrNotFound"
|
||||
git push
|
||||
```
|
||||
```
|
||||
→ Check go.mod, `core go fmt`, retry
|
||||
|
|
|
|||
|
|
@ -6,71 +6,28 @@ args: [workflow-name]
|
|||
|
||||
# Run Workflow
|
||||
|
||||
Manually trigger a GitHub Actions workflow.
|
||||
Trigger a CI workflow or view available workflows.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/ci:run # Run default workflow
|
||||
/ci:run tests # Run specific workflow
|
||||
/ci:run release # Trigger release workflow
|
||||
/ci:run # List available workflows
|
||||
/ci:run tests # Trigger specific workflow
|
||||
```
|
||||
|
||||
## Process
|
||||
|
||||
1. **List available workflows**
|
||||
```bash
|
||||
gh workflow list
|
||||
```
|
||||
|
||||
2. **Trigger workflow**
|
||||
```bash
|
||||
gh workflow run tests.yml
|
||||
gh workflow run tests.yml --ref feature-branch
|
||||
```
|
||||
|
||||
3. **Watch progress**
|
||||
```bash
|
||||
gh run watch
|
||||
```
|
||||
|
||||
## Common Workflows
|
||||
|
||||
| Workflow | Trigger | Purpose |
|
||||
|----------|---------|---------|
|
||||
| `tests.yml` | Push, PR | Run test suite |
|
||||
| `lint.yml` | Push, PR | Run linters |
|
||||
| `build.yml` | Push | Build artifacts |
|
||||
| `release.yml` | Tag | Create release |
|
||||
| `deploy.yml` | Manual | Deploy to environment |
|
||||
|
||||
## Output
|
||||
|
||||
```markdown
|
||||
## Workflow Triggered
|
||||
|
||||
**Workflow**: tests.yml
|
||||
**Branch**: feature/add-auth
|
||||
**Run ID**: 12345
|
||||
|
||||
Watching progress...
|
||||
|
||||
```
|
||||
⠋ Tests running...
|
||||
✓ Setup (12s)
|
||||
✓ Install dependencies (45s)
|
||||
⠋ Run tests (running)
|
||||
```
|
||||
|
||||
**Run completed in 2m 34s** ✓
|
||||
```
|
||||
|
||||
## Options
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Run with inputs (for workflows that accept them)
|
||||
gh workflow run deploy.yml -f environment=staging
|
||||
# List available workflows
|
||||
core dev workflow list
|
||||
|
||||
# Run on specific ref
|
||||
gh workflow run tests.yml --ref main
|
||||
# Sync workflows across repos
|
||||
core dev workflow sync
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Forgejo Actions uses `.forgejo/workflows/` or `.github/workflows/` (both supported)
|
||||
- Workflows are triggered automatically on push/PR/tag
|
||||
- Manual dispatch: use the Forgejo web UI at `forge.lthn.ai/{owner}/{repo}/actions`
|
||||
- Runner: `build-noc` on the noc server
|
||||
|
|
|
|||
|
|
@ -5,59 +5,59 @@ description: Show CI status for current branch
|
|||
|
||||
# CI Status
|
||||
|
||||
Show GitHub Actions status for the current branch.
|
||||
Show CI status for the current branch.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/ci:status
|
||||
/ci:status --all # All recent runs
|
||||
/ci:status --branch X # Specific branch
|
||||
```
|
||||
|
||||
## Commands
|
||||
## Detection
|
||||
|
||||
Detect the CI provider from git remote, then use the appropriate method:
|
||||
|
||||
```bash
|
||||
# Current branch status
|
||||
gh run list --branch $(git branch --show-current) --limit 5
|
||||
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
|
||||
```
|
||||
|
||||
# Get details of latest run
|
||||
gh run view --log-failed
|
||||
### Forgejo (forge.lthn.ai)
|
||||
|
||||
# Watch running workflow
|
||||
gh run watch
|
||||
```bash
|
||||
# Extract owner/repo from remote
|
||||
OWNER_REPO=$(git remote get-url origin 2>/dev/null | sed -E 's#.*forge\.lthn\.ai[:/]+([0-9]+/)?##; s#\.git$##')
|
||||
|
||||
# List recent workflow runs (requires FORGEJO_TOKEN or use web UI)
|
||||
curl -s "https://forge.lthn.ai/api/v1/repos/${OWNER_REPO}/actions/tasks?limit=10&state=running"
|
||||
|
||||
# Or just open the Actions page
|
||||
echo "https://forge.lthn.ai/${OWNER_REPO}/actions"
|
||||
```
|
||||
|
||||
### GitHub (fallback, requires gh CLI)
|
||||
|
||||
```bash
|
||||
core dev ci
|
||||
core dev ci --branch $(git branch --show-current)
|
||||
core dev ci --failed
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Present results as a status table:
|
||||
|
||||
```markdown
|
||||
## CI Status: feature/add-auth
|
||||
## CI Status: main
|
||||
|
||||
| Workflow | Status | Duration | Commit | When |
|
||||
|----------|--------|----------|--------|------|
|
||||
| Tests | ✓ pass | 2m 34s | abc123 | 5m ago |
|
||||
| Lint | ✓ pass | 45s | abc123 | 5m ago |
|
||||
| Build | ✓ pass | 1m 12s | abc123 | 5m ago |
|
||||
| Workflow | Status | When |
|
||||
|----------|--------|------|
|
||||
| Tests | pass | 5m ago |
|
||||
| Build | pass | 5m ago |
|
||||
|
||||
**All checks passing** ✓
|
||||
|
||||
---
|
||||
|
||||
Or if failing:
|
||||
|
||||
| Workflow | Status | Duration | Commit | When |
|
||||
|----------|--------|----------|--------|------|
|
||||
| Tests | ✗ fail | 1m 45s | abc123 | 5m ago |
|
||||
| Lint | ✓ pass | 45s | abc123 | 5m ago |
|
||||
| Build | - skip | - | abc123 | 5m ago |
|
||||
|
||||
**1 workflow failing**
|
||||
|
||||
### Tests Failure
|
||||
```
|
||||
--- FAIL: TestCreateUser
|
||||
expected 200, got 500
|
||||
**All checks passing**
|
||||
```
|
||||
|
||||
Run `/ci:fix` to analyse and fix.
|
||||
If no API token available, output the web URL:
|
||||
```
|
||||
View CI status: https://forge.lthn.ai/{owner}/{repo}/actions
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,26 +1,12 @@
|
|||
---
|
||||
name: workflow
|
||||
description: Create or update GitHub Actions workflow
|
||||
description: Create or update CI workflow
|
||||
args: <workflow-type>
|
||||
---
|
||||
|
||||
# Workflow Generator
|
||||
|
||||
Create or update GitHub Actions workflows.
|
||||
|
||||
## Workflow Types
|
||||
|
||||
### test
|
||||
Standard test workflow for Go/PHP projects.
|
||||
|
||||
### lint
|
||||
Linting workflow with golangci-lint or PHPStan.
|
||||
|
||||
### release
|
||||
Release workflow with goreleaser or similar.
|
||||
|
||||
### deploy
|
||||
Deployment workflow (requires configuration).
|
||||
Create or update CI workflows. Forgejo Actions uses the same YAML format as GitHub Actions.
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
@ -30,6 +16,24 @@ Deployment workflow (requires configuration).
|
|||
/ci:workflow release
|
||||
```
|
||||
|
||||
## List existing workflows
|
||||
|
||||
```bash
|
||||
core dev workflow list
|
||||
```
|
||||
|
||||
## Sync workflows across repos
|
||||
|
||||
```bash
|
||||
core dev workflow sync
|
||||
```
|
||||
|
||||
## Workflow directory
|
||||
|
||||
Forgejo supports both:
|
||||
- `.forgejo/workflows/` (preferred)
|
||||
- `.github/workflows/` (also works)
|
||||
|
||||
## Templates
|
||||
|
||||
### Go Test Workflow
|
||||
|
|
@ -49,28 +53,35 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22'
|
||||
- run: go test -v ./...
|
||||
go-version-file: go.mod
|
||||
- run: go test -v -race ./...
|
||||
```
|
||||
|
||||
### PHP Test Workflow
|
||||
### Go Release Workflow (core build)
|
||||
```yaml
|
||||
name: Tests
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
tags: ['v*']
|
||||
|
||||
jobs:
|
||||
test:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.3'
|
||||
- run: composer install
|
||||
- run: composer test
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
- name: Build and release
|
||||
run: core build release --we-are-go-for-launch
|
||||
```
|
||||
|
||||
## Forgejo Notes
|
||||
|
||||
- Runner label: `ubuntu-latest` (maps to Forgejo runner labels)
|
||||
- Secrets: Set via repo Settings → Actions → Secrets
|
||||
- Runner: `build-noc` on the noc server
|
||||
- Web UI: `forge.lthn.ai/{owner}/{repo}/actions`
|
||||
|
|
|
|||
21
claude/ci/scripts/detect-forge.sh
Executable file
21
claude/ci/scripts/detect-forge.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
# Detect CI provider from git remote
|
||||
# Outputs: "forgejo" or "github" or "unknown"
|
||||
# Also exports FORGE_API, FORGE_OWNER, FORGE_REPO
|
||||
|
||||
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
|
||||
|
||||
if echo "$REMOTE_URL" | grep -q "forge.lthn.ai"; then
|
||||
echo "forgejo"
|
||||
# Extract owner/repo from SSH or HTTPS URL
|
||||
# SSH: ssh://git@forge.lthn.ai:2223/core/go.git
|
||||
# HTTPS: https://forge.lthn.ai/core/go.git
|
||||
OWNER_REPO=$(echo "$REMOTE_URL" | sed -E 's#.*forge\.lthn\.ai[:/]+([0-9]+/)?##; s#\.git$##')
|
||||
export FORGE_API="https://forge.lthn.ai/api/v1"
|
||||
export FORGE_OWNER=$(echo "$OWNER_REPO" | cut -d'/' -f1)
|
||||
export FORGE_REPO=$(echo "$OWNER_REPO" | cut -d'/' -f2)
|
||||
elif echo "$REMOTE_URL" | grep -qE "github\.com"; then
|
||||
echo "github"
|
||||
else
|
||||
echo "unknown"
|
||||
fi
|
||||
|
|
@ -5,13 +5,13 @@ read -r input
|
|||
EXIT_CODE=$(echo "$input" | jq -r '.tool_response.exit_code // 0')
|
||||
|
||||
if [ "$EXIT_CODE" = "0" ]; then
|
||||
# Check if repo has workflows
|
||||
if [ -d ".github/workflows" ]; then
|
||||
# Check if repo has workflows (Forgejo or GitHub)
|
||||
if [ -d ".forgejo/workflows" ] || [ -d ".github/workflows" ]; then
|
||||
cat << 'EOF'
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PostToolUse",
|
||||
"additionalContext": "Push successful. CI workflows will run shortly.\n\nRun `/ci:status` to check progress or `gh run watch` to follow live."
|
||||
"additionalContext": "Push successful. CI workflows will run shortly.\n\nRun `/ci:status` to check progress or `core dev ci` to view status."
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
{
|
||||
"name": "core-agent",
|
||||
"name": "code",
|
||||
"description": "Advanced Claude Code plugin for Host UK monorepo - core CLI integration, data collection skills, and autonomous workflows",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.3",
|
||||
"author": {
|
||||
"name": "Lethean",
|
||||
"email": "hello@host.uk.com"
|
||||
},
|
||||
"homepage": "https://github.com/host-uk/core-agent",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/host-uk/core-agent.git"
|
||||
},
|
||||
"homepage": "https://forge.lthn.ai/core/agent",
|
||||
"repository": "ssh://git@forge.lthn.ai:2223/core/agent.git",
|
||||
"license": "EUPL-1.2",
|
||||
"keywords": [
|
||||
"devops",
|
||||
|
|
|
|||
93
claude/code/hooks/hooks.json
Normal file
93
claude/code/hooks/hooks.json
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"$schema": "https://claude.ai/schemas/hooks.json",
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/prefer-core.sh"
|
||||
}
|
||||
],
|
||||
"description": "Block destructive commands (rm -rf, sed -i, xargs rm) and enforce core CLI"
|
||||
},
|
||||
{
|
||||
"matcher": "Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/block-docs.sh"
|
||||
}
|
||||
],
|
||||
"description": "Block random .md file creation"
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\.go$\"",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/go-format.sh"
|
||||
}
|
||||
],
|
||||
"description": "Auto-format Go files after edits"
|
||||
},
|
||||
{
|
||||
"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\.php$\"",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/php-format.sh"
|
||||
}
|
||||
],
|
||||
"description": "Auto-format PHP files after edits"
|
||||
},
|
||||
{
|
||||
"matcher": "tool == \"Edit\"",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/check-debug.sh"
|
||||
}
|
||||
],
|
||||
"description": "Warn about debug statements (dd, dump, fmt.Println)"
|
||||
},
|
||||
{
|
||||
"matcher": "tool == \"Bash\" && tool_input.command matches \"^git commit\"",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/post-commit-check.sh"
|
||||
}
|
||||
],
|
||||
"description": "Warn about uncommitted work after git commit"
|
||||
}
|
||||
],
|
||||
"PreCompact": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/pre-compact.sh"
|
||||
}
|
||||
],
|
||||
"description": "Save state before auto-compact to prevent amnesia"
|
||||
}
|
||||
],
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/session-start.sh"
|
||||
}
|
||||
],
|
||||
"description": "Restore recent session context on startup"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -3,14 +3,24 @@
|
|||
#
|
||||
# BLOCKS:
|
||||
# - Raw go commands (use core go *)
|
||||
# - Destructive grep patterns (sed -i, xargs rm, etc.)
|
||||
# - Destructive patterns (sed -i, xargs rm, etc.)
|
||||
# - Mass file operations (rm -rf, mv/cp with wildcards)
|
||||
# - Any sed outside of safe patterns
|
||||
#
|
||||
# This prevents "efficient shortcuts" that nuke codebases
|
||||
|
||||
read -r input
|
||||
command=$(echo "$input" | jq -r '.tool_input.command // empty')
|
||||
full_command=$(echo "$input" | jq -r '.tool_input.command // empty')
|
||||
|
||||
# Strip heredoc content — only check the actual command, not embedded text
|
||||
# This prevents false positives from code/docs inside heredocs
|
||||
command=$(echo "$full_command" | sed -n '1p')
|
||||
if echo "$command" | grep -qE "<<\s*['\"]?[A-Z_]+"; then
|
||||
# First line has heredoc marker — only check the command portion before <<
|
||||
command=$(echo "$command" | sed -E 's/\s*<<.*$//')
|
||||
fi
|
||||
|
||||
# For multi-line commands joined with && or ;, check each segment
|
||||
# But still only the first line (not heredoc body)
|
||||
|
||||
# === HARD BLOCKS - Never allow these ===
|
||||
|
||||
|
|
@ -23,9 +33,10 @@ if echo "$command" | grep -qE 'rm\s+(-[a-zA-Z]*r[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*r|
|
|||
fi
|
||||
fi
|
||||
|
||||
# Block mv/cp with wildcards (mass file moves)
|
||||
if echo "$command" | grep -qE '(mv|cp)\s+.*\*'; then
|
||||
echo '{"decision": "block", "message": "BLOCKED: Mass file move/copy with wildcards is not allowed. Move files individually."}'
|
||||
# Block mv/cp with dangerous wildcards (e.g. `cp * /tmp`, `mv ./* /dest`)
|
||||
# Allow specific file copies that happen to use glob in a for loop or path
|
||||
if echo "$command" | grep -qE '(mv|cp)\s+(\.\/)?\*\s'; then
|
||||
echo '{"decision": "block", "message": "BLOCKED: Mass file move/copy with bare wildcards is not allowed. Copy files individually."}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
|
@ -47,7 +58,7 @@ if echo "$command" | grep -qE 'sed\s+(-[a-zA-Z]*i|--in-place)'; then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
# Block sed piped to file operations
|
||||
# Block sed piped to file operations (but not inside heredocs)
|
||||
if echo "$command" | grep -qE 'sed.*\|.*tee|sed.*>'; then
|
||||
echo '{"decision": "block", "message": "BLOCKED: sed with file output is not allowed. Use the Edit tool for file changes."}'
|
||||
exit 0
|
||||
|
|
@ -67,14 +78,14 @@ fi
|
|||
|
||||
# === REQUIRE CORE CLI ===
|
||||
|
||||
# Block raw go commands
|
||||
# Block raw go commands (only check first line, not heredoc content)
|
||||
case "$command" in
|
||||
"go test"*|"go build"*|"go fmt"*|"go mod tidy"*|"go vet"*|"go run"*)
|
||||
echo '{"decision": "block", "message": "Use `core go test`, `core build`, `core go fmt --fix`, etc. Raw go commands are not allowed."}'
|
||||
exit 0
|
||||
;;
|
||||
"go "*)
|
||||
# Other go commands - warn but allow
|
||||
# Other go commands - block
|
||||
echo '{"decision": "block", "message": "Prefer `core go *` commands. If core does not have this command, ask the user."}'
|
||||
exit 0
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,16 @@ if [[ -n "$FILE_PATH" ]]; then
|
|||
echo "$input"
|
||||
exit 0
|
||||
;;
|
||||
# Allow Claude memory and plan files
|
||||
*/.claude/*.md|*/.claude/**/*.md)
|
||||
echo "$input"
|
||||
exit 0
|
||||
;;
|
||||
# Allow plugin development (commands, skills)
|
||||
*/commands/*.md|*/skills/*.md|*/skills/**/*.md)
|
||||
echo "$input"
|
||||
exit 0
|
||||
;;
|
||||
# Block other .md files
|
||||
*.md)
|
||||
echo '{"decision": "block", "message": "Use README.md or docs/ for documentation. Random .md files clutter the repo."}'
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue