diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index d39da8d..f441b5e 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -5,7 +5,7 @@ "url": "https://github.com/ShaheerKhawaja" }, "metadata": { - "description": "ProductionOS — AI engineering OS for Claude Code and Codex. 80 agents, 41 commands, 17 hooks.", + "description": "ProductionOS — AI engineering OS for Claude Code and Codex. 80 agents, 41 commands, 18 hooks.", "version": "1.2.0-beta.1", "homepage": "https://github.com/ShaheerKhawaja/ProductionOS", "repository": "https://github.com/ShaheerKhawaja/ProductionOS", @@ -15,7 +15,7 @@ { "name": "productionos", "source": "./", - "description": "Dual-target AI engineering OS — 80 agents, 41 commands, 17 hooks, one shared workflow registry.", + "description": "Dual-target AI engineering OS — 80 agents, 41 commands, 18 hooks, one shared workflow registry.", "version": "1.2.0-beta.1", "author": { "name": "Shaheer Khawaja", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 3c65f12..734227e 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "productionos", - "description": "AI engineering OS for Claude Code and Codex — 80 agents, 41 commands, 17 hooks.", + "description": "AI engineering OS for Claude Code and Codex — 80 agents, 41 commands, 18 hooks.", "version": "1.2.0-beta.1", "author": { "name": "Shaheer Khawaja", diff --git a/.claude/skills/productionos/SKILL.md b/.claude/skills/productionos/SKILL.md index 40d4db0..79873fb 100644 --- a/.claude/skills/productionos/SKILL.md +++ b/.claude/skills/productionos/SKILL.md @@ -23,7 +23,7 @@ metadata: # ProductionOS -ProductionOS is a dual-target AI engineering operating system with 80 agents, 41 commands, and 17 hooks. +ProductionOS is a dual-target AI engineering operating system with 80 agents, 41 commands, and 18 hooks. Use this skill to translate the Claude-oriented workflow specs in this repo into Codex-native execution. diff --git a/SKILL.md b/SKILL.md index 701e134..d8bf14d 100644 --- a/SKILL.md +++ b/SKILL.md @@ -6,7 +6,7 @@ argument-hint: "[goal, command name, or repo path]" # ProductionOS -ProductionOS is a dual-target AI engineering operating system with 80 agents, 41 commands, and 17 hooks. +ProductionOS is a dual-target AI engineering operating system with 80 agents, 41 commands, and 18 hooks. Use this skill to translate the Claude-oriented workflow specs in this repo into Codex-native execution. diff --git a/docs/CODEX-PARITY-HANDOFF.md b/docs/CODEX-PARITY-HANDOFF.md index 354985c..52340e8 100644 --- a/docs/CODEX-PARITY-HANDOFF.md +++ b/docs/CODEX-PARITY-HANDOFF.md @@ -2,7 +2,7 @@ This document is generated from the runtime-neutral registry in [scripts/lib/runtime-targets.ts](../scripts/lib/runtime-targets.ts). -Current snapshot: 80 agents, 41 commands, 17 hooks, 14 templates, 28 tests. +Current snapshot: 80 agents, 41 commands, 18 hooks, 14 templates, 28 tests. ## Runtime Targets diff --git a/hooks/README.md b/hooks/README.md new file mode 100644 index 0000000..79caef2 --- /dev/null +++ b/hooks/README.md @@ -0,0 +1,87 @@ +# ProductionOS Hooks + +15 lifecycle hooks across 4 events. Auto-enforced via `hooks/hooks.json`. All hooks degrade gracefully if dependencies are missing. + +## Hook Inventory + +### SessionStart (1 hook) + +| Hook | What | Dependencies | +|------|------|-------------| +| `session-start.sh` | Init state, display banner, load instincts, context recovery | python3, bun | + +### PreToolUse (5 hooks) + +| Hook | Matcher | What | Dependencies | +|------|---------|------|-------------| +| `scope-enforcement.sh` | Edit/Write/Bash | Block out-of-scope file edits | python3, jq | +| `repo-boundary-guard.sh` | Edit/Write/Bash | Block cross-repo operations | python3, jq | +| `protected-file-guard.sh` | Edit/Write/Bash | Block writes to .env, keys, certs | python3, jq | +| `pre-edit-security.sh` | Edit/Write/Bash | Advisory on auth/payment file edits | python3, jq | +| `pre-commit-gitleaks.sh` | Bash | Secret detection in git commits | python3, jq, gitleaks (optional) | + +### PostToolUse (4 hooks) + +| Hook | Matcher | What | Dependencies | +|------|---------|------|-------------| +| `self-learn.sh` | Edit/Write | Cross-session pattern capture | python3, jq | +| `post-edit-telemetry.sh` | Edit/Write | Log file edits to analytics | python3, jq | +| `post-edit-review-hint.sh` | Edit/Write | Suggest review after 10+ edits | python3 | +| `eval-gate.sh` | Edit/Write | Quality gate (tests + tsc + eval) | bun, jq | + +### Stop (3 hooks) + +| Hook | What | Dependencies | +|------|------|-------------| +| `stop-session-handoff.sh` | Generate session summary + Obsidian log | python3, jq | +| `stop-extract-instincts.sh` | Extract patterns to instincts directory | python3 | +| `stop-eval-gate.sh` | Session-end evaluation score | bun | + +### Non-Lifecycle (1 hook) + +| Hook | What | +|------|------| +| `pre-push-gate.sh` | Git pre-push validation (tests + tsc) | + +## Error Handling + +All hooks include: +- `set -euo pipefail` for strict error handling +- Binary availability checks (`_HAS_BUN`, `_HAS_PYTHON`, `_HAS_JQ`) +- `_log_error()` function writing to `~/.productionos/logs/hook-errors.log` +- Graceful degradation when dependencies are missing (skip, don't crash) + +## Error Log + +Hook failures are logged to: +``` +~/.productionos/logs/hook-errors.log +``` + +Format: `[ISO8601] ERROR hook-name.sh: description` + +## Configuration + +Hooks are registered in `hooks/hooks.json`. The file is read by Claude Code on session start. + +### Disable a Specific Hook + +Remove the hook entry from `hooks/hooks.json`. Do not delete the `.sh` file. + +### Disable All Hooks + +Remove the `hooks` key from `hooks/hooks.json` or rename the file. + +### Eval-Gate Frequency + +The eval-gate runs tests after every 10 edits (configurable): +- Fast gate (tests + tsc): every 10 edits +- Full eval: every 20 edits + +## Adding a New Hook + +1. Create `hooks/my-hook.sh` with `#!/usr/bin/env bash` + `set -euo pipefail` +2. Include the binary check preamble (copy from any existing hook) +3. Add entry to `hooks/hooks.json` under the appropriate event +4. Make executable: `chmod +x hooks/my-hook.sh` +5. Test: `echo '{}' | bash hooks/my-hook.sh` diff --git a/hooks/eval-gate.sh b/hooks/eval-gate.sh index 32217c8..4c67e04 100755 --- a/hooks/eval-gate.sh +++ b/hooks/eval-gate.sh @@ -4,6 +4,18 @@ # Enforces 8/10 quality standard — blocks session completion if below threshold. set -euo pipefail +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} + PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" STATE_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}" EDIT_COUNT_FILE="$STATE_DIR/sessions/edit-count-$$" @@ -38,7 +50,12 @@ if [ "$CURRENT_COUNT" -gt 0 ] && [ $((CURRENT_COUNT % EVAL_INTERVAL)) -eq 0 ]; t fi # Run full eval (slower, only at 20+ edits) - if [ "$CURRENT_COUNT" -ge 20 ]; then + if [ "$CURRENT_COUNT" -ge 20 ] && [ "$_HAS_BUN" = "1" ]; then + # Verify eval script exists before running + if [ ! -f "$PLUGIN_ROOT/scripts/eval-runner.ts" ] && [ ! -f "$PLUGIN_ROOT/package.json" ]; then + _log_error "eval-runner.ts not found, skipping full eval" + exit 0 + fi EVAL_SCORE=$(bun run eval 2>&1 | grep "OVERALL" | grep -oE '[0-9]+\.[0-9]+' || echo "0") # Validate EVAL_SCORE is a plain decimal number before use if [[ "$EVAL_SCORE" =~ ^[0-9]+\.[0-9]+$ ]]; then diff --git a/hooks/post-bash-telemetry.sh b/hooks/post-bash-telemetry.sh index 78aca8a..4535399 100755 --- a/hooks/post-bash-telemetry.sh +++ b/hooks/post-bash-telemetry.sh @@ -1,6 +1,18 @@ #!/usr/bin/env bash # ProductionOS PostToolUse — log bash command telemetry set -euo pipefail + +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} # Resolve plugin root — works for both marketplace install and git clone PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" STATE_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}" diff --git a/hooks/post-edit-review-hint.sh b/hooks/post-edit-review-hint.sh index cc8ce52..42b00df 100755 --- a/hooks/post-edit-review-hint.sh +++ b/hooks/post-edit-review-hint.sh @@ -2,6 +2,18 @@ # ProductionOS PostToolUse — auto code review hint after edits # Tracks edit count per session. After 10+ edits, suggests running review. set -euo pipefail + +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} # Resolve plugin root — works for both marketplace install and git clone PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" STATE_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}" diff --git a/hooks/post-edit-telemetry.sh b/hooks/post-edit-telemetry.sh index 96c9e71..e7a017c 100755 --- a/hooks/post-edit-telemetry.sh +++ b/hooks/post-edit-telemetry.sh @@ -1,6 +1,18 @@ #!/usr/bin/env bash # ProductionOS PostToolUse — log edit telemetry (project-scoped) set -euo pipefail + +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} # Resolve plugin root — works for both marketplace install and git clone PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" STATE_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}" diff --git a/hooks/pre-commit-gitleaks.sh b/hooks/pre-commit-gitleaks.sh index 5d1bc05..dd613e6 100755 --- a/hooks/pre-commit-gitleaks.sh +++ b/hooks/pre-commit-gitleaks.sh @@ -4,6 +4,18 @@ # Only triggers on Bash tool invocations containing 'git commit' set -euo pipefail +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} + STATE_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}" INPUT=$(cat) diff --git a/hooks/pre-edit-security.sh b/hooks/pre-edit-security.sh index a35c7e6..8cb7b76 100755 --- a/hooks/pre-edit-security.sh +++ b/hooks/pre-edit-security.sh @@ -1,6 +1,18 @@ #!/usr/bin/env bash # ProductionOS PreToolUse — security advisory on sensitive file edits set -euo pipefail + +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} # Resolve plugin root — works for both marketplace install and git clone PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" diff --git a/hooks/pre-push-gate.sh b/hooks/pre-push-gate.sh index dcd23c4..5addf62 100755 --- a/hooks/pre-push-gate.sh +++ b/hooks/pre-push-gate.sh @@ -4,6 +4,18 @@ # Also called by the Stop hook for session-end validation. set -euo pipefail +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} + PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" # Handle being called from .git/hooks/ (symlink target) if [ ! -f "$PLUGIN_ROOT/package.json" ]; then diff --git a/hooks/protected-file-guard.sh b/hooks/protected-file-guard.sh index edcb391..6a49f8f 100755 --- a/hooks/protected-file-guard.sh +++ b/hooks/protected-file-guard.sh @@ -3,6 +3,18 @@ # Returns JSON: {"decision":"block","reason":"..."} or {"decision":"allow"} # Handles Edit, Write, AND Bash tools (detects file writes in shell commands) set -euo pipefail + +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} # Resolve plugin root — works for both marketplace install and git clone PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" diff --git a/hooks/repo-boundary-guard.sh b/hooks/repo-boundary-guard.sh index b96ede4..53c9379 100755 --- a/hooks/repo-boundary-guard.sh +++ b/hooks/repo-boundary-guard.sh @@ -7,6 +7,18 @@ # - No active project detected → ALLOW everything set -euo pipefail +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} + STATE_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}" INPUT=$(cat) diff --git a/hooks/scope-enforcement.sh b/hooks/scope-enforcement.sh index 5ed7afa..3e24d7c 100755 --- a/hooks/scope-enforcement.sh +++ b/hooks/scope-enforcement.sh @@ -7,6 +7,18 @@ # GAP-22 (git apply/am) set -euo pipefail +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} + # ─── Fast Exit: No scope enforcement active ───────────────── SCOPE_FILE="${PRODUCTIONOS_AGENT_SCOPE:-}" if [ -z "$SCOPE_FILE" ] || [ ! -f "$SCOPE_FILE" ]; then diff --git a/hooks/self-learn.sh b/hooks/self-learn.sh index 7e2b4c2..fa4cac8 100755 --- a/hooks/self-learn.sh +++ b/hooks/self-learn.sh @@ -5,6 +5,18 @@ # v2: Cross-session pattern aggregation set -euo pipefail + +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} # Resolve plugin root — works for both marketplace install and git clone PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" diff --git a/hooks/session-start.sh b/hooks/session-start.sh index d5d0d72..155235f 100755 --- a/hooks/session-start.sh +++ b/hooks/session-start.sh @@ -1,6 +1,18 @@ #!/usr/bin/env bash # ProductionOS session start — init state, track session, detect project, show status set -euo pipefail + +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} # Resolve plugin root — works for both marketplace install and git clone PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" STATE_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}" diff --git a/hooks/stop-eval-gate.sh b/hooks/stop-eval-gate.sh index fc8b65c..1c6a548 100755 --- a/hooks/stop-eval-gate.sh +++ b/hooks/stop-eval-gate.sh @@ -3,6 +3,18 @@ # Enforces 10/10 standard by writing warnings to handoff document. set -euo pipefail +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} + PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" STATE_DIR="${HOME}/.productionos" diff --git a/hooks/stop-extract-instincts.sh b/hooks/stop-extract-instincts.sh index 9bd75e7..aaac3ef 100755 --- a/hooks/stop-extract-instincts.sh +++ b/hooks/stop-extract-instincts.sh @@ -1,6 +1,18 @@ #!/usr/bin/env bash # ProductionOS Stop hook — extract instincts from session analytics set -euo pipefail + +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} # Resolve plugin root — works for both marketplace install and git clone PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" STATE_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}" diff --git a/hooks/stop-session-handoff.sh b/hooks/stop-session-handoff.sh index f9ba90b..ba8452a 100755 --- a/hooks/stop-session-handoff.sh +++ b/hooks/stop-session-handoff.sh @@ -1,6 +1,18 @@ #!/usr/bin/env bash # ProductionOS Stop hook — session cleanup, project-scoped handoff, mini-retro, instinct extraction set -euo pipefail + +# === Binary availability (degrade gracefully if missing) === +_HAS_BUN=$(command -v bun >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_PYTHON=$(command -v python3 >/dev/null 2>&1 && echo "1" || echo "0") +_HAS_JQ=$(command -v jq >/dev/null 2>&1 && echo "1" || echo "0") +_LOG_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}/logs" +mkdir -p "$_LOG_DIR" 2>/dev/null || true + +_log_error() { + local msg="$1" + echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR $(basename "$0"): $msg" >> "$_LOG_DIR/hook-errors.log" 2>/dev/null || true +} STATE_DIR="${PRODUCTIONOS_HOME:-$HOME/.productionos}" # Read active project before cleanup diff --git a/skills/productionos/SKILL.md b/skills/productionos/SKILL.md index 701e134..d8bf14d 100644 --- a/skills/productionos/SKILL.md +++ b/skills/productionos/SKILL.md @@ -6,7 +6,7 @@ argument-hint: "[goal, command name, or repo path]" # ProductionOS -ProductionOS is a dual-target AI engineering operating system with 80 agents, 41 commands, and 17 hooks. +ProductionOS is a dual-target AI engineering operating system with 80 agents, 41 commands, and 18 hooks. Use this skill to translate the Claude-oriented workflow specs in this repo into Codex-native execution.