@orchestrator-claude/cli 3.17.1 → 3.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/base/CLAUDE.md.hbs +45 -28
- package/dist/templates/base/claude/agents/orchestrator.md +84 -117
- package/dist/templates/base/claude/hooks/dangling-guard.ts +53 -0
- package/dist/templates/base/claude/hooks/gate-guardian.ts +102 -0
- package/dist/templates/base/claude/hooks/lib/api-client.ts +293 -0
- package/dist/templates/base/claude/hooks/lib/git-checkpoint.ts +91 -0
- package/dist/templates/base/claude/hooks/package.json +13 -0
- package/dist/templates/base/claude/hooks/post-compact.ts +44 -0
- package/dist/templates/base/claude/hooks/session-start.ts +97 -0
- package/dist/templates/base/claude/hooks/subagent-start.ts +50 -0
- package/dist/templates/base/claude/hooks/subagent-stop.ts +57 -0
- package/dist/templates/base/claude/hooks/tsconfig.json +18 -0
- package/dist/templates/base/claude/hooks/user-prompt.ts +95 -0
- package/dist/templates/base/claude/hooks/workflow-guard.ts +120 -0
- package/dist/templates/base/claude/settings.json +23 -22
- package/dist/templates/base/claude/skills/orchestrator/SKILL.md +108 -0
- package/package.json +1 -1
- package/templates/base/CLAUDE.md.hbs +45 -28
- package/templates/base/claude/agents/orchestrator.md +84 -117
- package/templates/base/claude/hooks/dangling-guard.ts +53 -0
- package/templates/base/claude/hooks/gate-guardian.ts +102 -0
- package/templates/base/claude/hooks/lib/api-client.ts +293 -0
- package/templates/base/claude/hooks/lib/git-checkpoint.ts +91 -0
- package/templates/base/claude/hooks/package.json +13 -0
- package/templates/base/claude/hooks/post-compact.ts +44 -0
- package/templates/base/claude/hooks/session-start.ts +97 -0
- package/templates/base/claude/hooks/subagent-start.ts +50 -0
- package/templates/base/claude/hooks/subagent-stop.ts +57 -0
- package/templates/base/claude/hooks/tsconfig.json +18 -0
- package/templates/base/claude/hooks/user-prompt.ts +95 -0
- package/templates/base/claude/hooks/workflow-guard.ts +120 -0
- package/templates/base/claude/settings.json +23 -22
- package/templates/base/claude/skills/orchestrator/SKILL.md +108 -0
- package/dist/templates/base/claude/hooks/approval-guardian.sh +0 -62
- package/dist/templates/base/claude/hooks/dangling-workflow-guard.sh +0 -57
- package/dist/templates/base/claude/hooks/gate-guardian.sh +0 -84
- package/dist/templates/base/claude/hooks/orch-helpers.sh +0 -135
- package/dist/templates/base/claude/hooks/ping-pong-enforcer.sh +0 -58
- package/dist/templates/base/claude/hooks/post-phase-checkpoint.sh +0 -203
- package/dist/templates/base/claude/hooks/prompt-orchestrator.sh +0 -41
- package/dist/templates/base/claude/hooks/session-orchestrator.sh +0 -54
- package/dist/templates/base/claude/hooks/track-agent-invocation.sh +0 -230
- package/dist/templates/base/claude/hooks/workflow-guard.sh +0 -79
- package/templates/base/claude/hooks/approval-guardian.sh +0 -62
- package/templates/base/claude/hooks/dangling-workflow-guard.sh +0 -57
- package/templates/base/claude/hooks/gate-guardian.sh +0 -84
- package/templates/base/claude/hooks/orch-helpers.sh +0 -135
- package/templates/base/claude/hooks/ping-pong-enforcer.sh +0 -58
- package/templates/base/claude/hooks/post-phase-checkpoint.sh +0 -203
- package/templates/base/claude/hooks/prompt-orchestrator.sh +0 -41
- package/templates/base/claude/hooks/session-orchestrator.sh +0 -54
- package/templates/base/claude/hooks/track-agent-invocation.sh +0 -230
- package/templates/base/claude/hooks/workflow-guard.sh +0 -79
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Post-Phase Checkpoint Hook (RFC-012 compatible)
|
|
3
|
-
# Registers a checkpoint in PostgreSQL via the Orchestrator REST API.
|
|
4
|
-
# Replaces the deprecated orchestrator-index.json approach (TD-105 F-04).
|
|
5
|
-
#
|
|
6
|
-
# Triggered by: PostToolUse on Task tool (after agent completes)
|
|
7
|
-
# Behavior: Infers the just-completed phase from the active workflow state
|
|
8
|
-
# and registers a checkpoint via the REST API.
|
|
9
|
-
#
|
|
10
|
-
# Required env vars:
|
|
11
|
-
# ORCHESTRATOR_API_URL - Base URL (e.g. http://localhost:3000)
|
|
12
|
-
# ORCHESTRATOR_PROJECT_TOKEN - Bearer token for API authentication
|
|
13
|
-
#
|
|
14
|
-
# Optional env vars:
|
|
15
|
-
# ORCHESTRATOR_PROJECT_ID - Project ID for project-scoped workflow query
|
|
16
|
-
# ORCH_CHECKPOINT_DEBUG=1 - Enable verbose logging
|
|
17
|
-
#
|
|
18
|
-
# Always exits 0 (non-blocking).
|
|
19
|
-
|
|
20
|
-
set -euo pipefail
|
|
21
|
-
|
|
22
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
-
STATE_DIR="$SCRIPT_DIR/../../.orchestrator/.state"
|
|
24
|
-
LOG_FILE="${STATE_DIR}/checkpoint-hook.log"
|
|
25
|
-
LAST_PHASE_FILE="${STATE_DIR}/last-checkpointed-phase"
|
|
26
|
-
DEBUG="${ORCH_CHECKPOINT_DEBUG:-0}"
|
|
27
|
-
|
|
28
|
-
mkdir -p "$STATE_DIR"
|
|
29
|
-
|
|
30
|
-
log_info() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [INFO] $1" >> "$LOG_FILE"; }
|
|
31
|
-
log_warn() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [WARN] $1" >> "$LOG_FILE"; echo "[WARN] $1" >&2; }
|
|
32
|
-
log_debug() { [ "$DEBUG" = "1" ] && echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [DEBUG] $1" >> "$LOG_FILE" || true; }
|
|
33
|
-
|
|
34
|
-
# ── prerequisites ─────────────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
check_prerequisites() {
|
|
37
|
-
if ! command -v curl &>/dev/null; then
|
|
38
|
-
log_warn "curl not found. Checkpoints disabled."
|
|
39
|
-
return 1
|
|
40
|
-
fi
|
|
41
|
-
if ! command -v jq &>/dev/null; then
|
|
42
|
-
log_warn "jq not found. Checkpoints disabled."
|
|
43
|
-
return 1
|
|
44
|
-
fi
|
|
45
|
-
if [ -z "${ORCHESTRATOR_API_URL:-}" ]; then
|
|
46
|
-
log_debug "ORCHESTRATOR_API_URL not set. Checkpoints disabled."
|
|
47
|
-
return 1
|
|
48
|
-
fi
|
|
49
|
-
if [ -z "${ORCHESTRATOR_PROJECT_TOKEN:-}" ]; then
|
|
50
|
-
log_debug "ORCHESTRATOR_PROJECT_TOKEN not set. Checkpoints disabled."
|
|
51
|
-
return 1
|
|
52
|
-
fi
|
|
53
|
-
return 0
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
# ── API helpers ────────────────────────────────────────────────────────────────
|
|
57
|
-
|
|
58
|
-
get_active_workflow() {
|
|
59
|
-
local project_id="${ORCHESTRATOR_PROJECT_ID:-}"
|
|
60
|
-
local url="${ORCHESTRATOR_API_URL}/api/v1/workflows?status=in_progress&limit=1"
|
|
61
|
-
[ -n "$project_id" ] && url="${url}&projectId=${project_id}"
|
|
62
|
-
|
|
63
|
-
local tmpfile
|
|
64
|
-
tmpfile=$(mktemp 2>/dev/null) || return 1
|
|
65
|
-
|
|
66
|
-
curl -sf \
|
|
67
|
-
-H "Authorization: Bearer ${ORCHESTRATOR_PROJECT_TOKEN}" \
|
|
68
|
-
-H "Content-Type: application/json" \
|
|
69
|
-
"${url}" \
|
|
70
|
-
-o "$tmpfile" 2>/dev/null || { rm -f "$tmpfile"; return 1; }
|
|
71
|
-
|
|
72
|
-
local result
|
|
73
|
-
result=$(node -e "
|
|
74
|
-
const fs=require('fs');
|
|
75
|
-
try {
|
|
76
|
-
const d=fs.readFileSync('${tmpfile}','utf8');
|
|
77
|
-
const j=JSON.parse(d);
|
|
78
|
-
const wf=Array.isArray(j)?j[0]:j;
|
|
79
|
-
if(wf&&wf.id){ process.stdout.write(JSON.stringify(wf)); process.exit(0); }
|
|
80
|
-
process.exit(1);
|
|
81
|
-
} catch { process.exit(1); }" 2>/dev/null)
|
|
82
|
-
local rc=$?
|
|
83
|
-
rm -f "$tmpfile"
|
|
84
|
-
[ $rc -eq 0 ] && echo "$result" || return 1
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
create_checkpoint() {
|
|
88
|
-
local workflow_id="$1"
|
|
89
|
-
local phase="$2"
|
|
90
|
-
local description="$3"
|
|
91
|
-
|
|
92
|
-
local body
|
|
93
|
-
body=$(jq -n \
|
|
94
|
-
--arg phase "$phase" \
|
|
95
|
-
--arg desc "$description" \
|
|
96
|
-
'{ phase: $phase, description: $desc }')
|
|
97
|
-
|
|
98
|
-
local http_code
|
|
99
|
-
http_code=$(curl -sf -o /dev/null -w "%{http_code}" \
|
|
100
|
-
-X POST \
|
|
101
|
-
-H "Authorization: Bearer ${ORCHESTRATOR_PROJECT_TOKEN}" \
|
|
102
|
-
-H "Content-Type: application/json" \
|
|
103
|
-
-d "$body" \
|
|
104
|
-
"${ORCHESTRATOR_API_URL}/api/v1/workflows/${workflow_id}/checkpoints" 2>/dev/null) || echo ""
|
|
105
|
-
|
|
106
|
-
echo "$http_code"
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
# ── dedup guard ────────────────────────────────────────────────────────────────
|
|
110
|
-
|
|
111
|
-
get_saved_last_phase() {
|
|
112
|
-
[ -f "$LAST_PHASE_FILE" ] && cat "$LAST_PHASE_FILE" || echo ""
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
save_last_phase() {
|
|
116
|
-
echo "$1" > "$LAST_PHASE_FILE"
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
# ── phase inference ────────────────────────────────────────────────────────────
|
|
120
|
-
|
|
121
|
-
# When workflow is at PLAN, it means SPECIFY just completed, etc.
|
|
122
|
-
get_completed_phase() {
|
|
123
|
-
local current="$1"
|
|
124
|
-
local status="$2"
|
|
125
|
-
case "$status" in
|
|
126
|
-
"completed") echo "implement"; return ;;
|
|
127
|
-
esac
|
|
128
|
-
case "${current,,}" in
|
|
129
|
-
"plan") echo "specify" ;;
|
|
130
|
-
"tasks") echo "plan" ;;
|
|
131
|
-
"implement") echo "tasks" ;;
|
|
132
|
-
*) echo "" ;;
|
|
133
|
-
esac
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
# ── main ───────────────────────────────────────────────────────────────────────
|
|
137
|
-
|
|
138
|
-
main() {
|
|
139
|
-
log_debug "Hook triggered"
|
|
140
|
-
|
|
141
|
-
if ! check_prerequisites; then
|
|
142
|
-
exit 0
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
local workflow_json
|
|
146
|
-
workflow_json=$(get_active_workflow 2>/dev/null) || workflow_json=""
|
|
147
|
-
|
|
148
|
-
if [ -z "$workflow_json" ]; then
|
|
149
|
-
log_debug "No active workflow found via API."
|
|
150
|
-
exit 0
|
|
151
|
-
fi
|
|
152
|
-
|
|
153
|
-
local workflow_id current_phase workflow_status
|
|
154
|
-
workflow_id=$(echo "$workflow_json" | jq -r '.id // empty' 2>/dev/null || echo "")
|
|
155
|
-
current_phase=$(echo "$workflow_json" | jq -r '.currentPhase // empty' 2>/dev/null || echo "")
|
|
156
|
-
workflow_status=$(echo "$workflow_json" | jq -r '.status // empty' 2>/dev/null || echo "")
|
|
157
|
-
|
|
158
|
-
if [ -z "$workflow_id" ] || [ -z "$current_phase" ]; then
|
|
159
|
-
log_debug "Could not parse workflow ID or phase."
|
|
160
|
-
exit 0
|
|
161
|
-
fi
|
|
162
|
-
|
|
163
|
-
local completed_phase
|
|
164
|
-
completed_phase=$(get_completed_phase "$current_phase" "$workflow_status")
|
|
165
|
-
|
|
166
|
-
if [ -z "$completed_phase" ]; then
|
|
167
|
-
log_debug "No completed phase inferred from current_phase=${current_phase}."
|
|
168
|
-
exit 0
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
|
-
# Dedup guard — skip if we already checkpointed this phase
|
|
172
|
-
local saved_last
|
|
173
|
-
saved_last=$(get_saved_last_phase)
|
|
174
|
-
if [ "$completed_phase" = "$saved_last" ]; then
|
|
175
|
-
log_debug "Phase ${completed_phase} already checkpointed. Skipping."
|
|
176
|
-
exit 0
|
|
177
|
-
fi
|
|
178
|
-
|
|
179
|
-
local description="Auto-checkpoint after ${completed_phase} phase"
|
|
180
|
-
local http_code
|
|
181
|
-
http_code=$(create_checkpoint "$workflow_id" "$completed_phase" "$description")
|
|
182
|
-
|
|
183
|
-
case "$http_code" in
|
|
184
|
-
201|200)
|
|
185
|
-
save_last_phase "$completed_phase"
|
|
186
|
-
log_info "Checkpoint registered: workflow=${workflow_id}, phase=${completed_phase}"
|
|
187
|
-
;;
|
|
188
|
-
409)
|
|
189
|
-
save_last_phase "$completed_phase"
|
|
190
|
-
log_info "Checkpoint already exists (409): phase=${completed_phase}"
|
|
191
|
-
;;
|
|
192
|
-
"")
|
|
193
|
-
log_warn "API unreachable. Checkpoint skipped for phase=${completed_phase}."
|
|
194
|
-
;;
|
|
195
|
-
*)
|
|
196
|
-
log_warn "Unexpected HTTP ${http_code} creating checkpoint for phase=${completed_phase}."
|
|
197
|
-
;;
|
|
198
|
-
esac
|
|
199
|
-
|
|
200
|
-
exit 0
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
main "$@"
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# prompt-orchestrator.sh — ADR-013 Phase 5 Hook (JSON Structured Output)
|
|
3
|
-
# Trigger: UserPromptSubmit
|
|
4
|
-
# Purpose: On every user prompt, inject orchestrator context.
|
|
5
|
-
# If no workflow active, remind about workflow requirement.
|
|
6
|
-
# If workflow active, inject current state.
|
|
7
|
-
#
|
|
8
|
-
# Output: JSON with additionalContext (never blocks prompts)
|
|
9
|
-
|
|
10
|
-
set -euo pipefail
|
|
11
|
-
|
|
12
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
-
source "$SCRIPT_DIR/orch-helpers.sh"
|
|
14
|
-
|
|
15
|
-
orch_log "PROMPT-ORCH: UserPromptSubmit triggered"
|
|
16
|
-
|
|
17
|
-
# Find active workflow
|
|
18
|
-
WORKFLOW_ID=$(orch_get_active_workflow 2>/dev/null) || WORKFLOW_ID=""
|
|
19
|
-
|
|
20
|
-
if [ -z "$WORKFLOW_ID" ]; then
|
|
21
|
-
# No workflow — inject reminder (lightweight, no API calls)
|
|
22
|
-
orch_log "PROMPT-ORCH: No active workflow, injecting reminder"
|
|
23
|
-
echo '{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"[ORCHESTRATOR] No active workflow. If this request involves creating, modifying, or fixing code, you must start a workflow first (detectWorkflow → startWorkflow). The workflow-guard hook will block writes to src/ without a workflow."}}'
|
|
24
|
-
exit 0
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
# Active workflow — inject state
|
|
28
|
-
NEXT_ACTION=$(orch_get_next_action "$WORKFLOW_ID" 2>/dev/null) || NEXT_ACTION=""
|
|
29
|
-
HAS_ACTION=$(orch_json_field "$NEXT_ACTION" "hasAction")
|
|
30
|
-
AGENT=$(orch_json_field "$NEXT_ACTION" "pendingAction.agent")
|
|
31
|
-
PA_STATUS=$(orch_json_field "$NEXT_ACTION" "pendingAction.status")
|
|
32
|
-
|
|
33
|
-
if [ "$HAS_ACTION" = "true" ]; then
|
|
34
|
-
CONTEXT="[ORCHESTRATOR] Active workflow: ${WORKFLOW_ID}. Next: invoke '${AGENT}' (${PA_STATUS})."
|
|
35
|
-
else
|
|
36
|
-
CONTEXT="[ORCHESTRATOR] Active workflow: ${WORKFLOW_ID}. No pending actions."
|
|
37
|
-
fi
|
|
38
|
-
|
|
39
|
-
orch_log "PROMPT-ORCH: workflow=$WORKFLOW_ID hasAction=$HAS_ACTION"
|
|
40
|
-
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"UserPromptSubmit\",\"additionalContext\":$(echo "$CONTEXT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>console.log(JSON.stringify(d)))" 2>/dev/null)}}"
|
|
41
|
-
exit 0
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# session-orchestrator.sh — ADR-013 Phase 5 Hook (JSON Structured Output)
|
|
3
|
-
# Trigger: SessionStart
|
|
4
|
-
# Purpose: On session start, check for active workflow and inject context.
|
|
5
|
-
# Helps the LLM resume work from where it left off.
|
|
6
|
-
#
|
|
7
|
-
# Output: JSON with additionalContext containing workflow state
|
|
8
|
-
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
-
source "$SCRIPT_DIR/orch-helpers.sh"
|
|
13
|
-
|
|
14
|
-
orch_log "SESSION-ORCH: SessionStart hook triggered"
|
|
15
|
-
|
|
16
|
-
# Find active workflow
|
|
17
|
-
WORKFLOW_ID=$(orch_get_active_workflow 2>/dev/null) || WORKFLOW_ID=""
|
|
18
|
-
|
|
19
|
-
if [ -z "$WORKFLOW_ID" ]; then
|
|
20
|
-
orch_log "SESSION-ORCH: No active workflow"
|
|
21
|
-
echo '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"[ORCHESTRATOR] No active workflow. For feature requests, bug fixes, or refactoring, start with:\n1. mcp__orchestrator-tools__detectWorkflow\n2. mcp__orchestrator-tools__startWorkflow\n3. Follow the nextStep returned\n\nDirect implementation is blocked by the workflow-guard hook."}}'
|
|
22
|
-
exit 0
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# Get workflow details
|
|
26
|
-
TOKEN=$(orch_get_token 2>/dev/null) || TOKEN=""
|
|
27
|
-
if [ -z "$TOKEN" ]; then
|
|
28
|
-
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"SessionStart\",\"additionalContext\":\"[ORCHESTRATOR] Active workflow: ${WORKFLOW_ID} (could not fetch details — auth failed).\"}}"
|
|
29
|
-
exit 0
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
STATUS_RESP=$(curl -sf --max-time 5 "${API_URL}/api/v1/workflows/${WORKFLOW_ID}/status" \
|
|
33
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
34
|
-
-H "X-Project-ID: $PROJECT_ID" 2>/dev/null) || STATUS_RESP=""
|
|
35
|
-
|
|
36
|
-
PHASE=$(orch_json_field "$STATUS_RESP" "currentPhase")
|
|
37
|
-
STATUS=$(orch_json_field "$STATUS_RESP" "status")
|
|
38
|
-
WF_TYPE=$(orch_json_field "$STATUS_RESP" "type")
|
|
39
|
-
|
|
40
|
-
# Get next action
|
|
41
|
-
NEXT_ACTION=$(orch_get_next_action "$WORKFLOW_ID" 2>/dev/null) || NEXT_ACTION=""
|
|
42
|
-
HAS_ACTION=$(orch_json_field "$NEXT_ACTION" "hasAction")
|
|
43
|
-
AGENT=$(orch_json_field "$NEXT_ACTION" "pendingAction.agent")
|
|
44
|
-
PA_STATUS=$(orch_json_field "$NEXT_ACTION" "pendingAction.status")
|
|
45
|
-
|
|
46
|
-
CONTEXT="[ORCHESTRATOR] Active workflow: ${WORKFLOW_ID}\nType: ${WF_TYPE}\nPhase: ${PHASE}\nStatus: ${STATUS}"
|
|
47
|
-
|
|
48
|
-
if [ "$HAS_ACTION" = "true" ]; then
|
|
49
|
-
CONTEXT="${CONTEXT}\nNext action: invoke '${AGENT}' (${PA_STATUS})"
|
|
50
|
-
fi
|
|
51
|
-
|
|
52
|
-
orch_log "SESSION-ORCH: Injecting workflow context (wf=$WORKFLOW_ID phase=$PHASE)"
|
|
53
|
-
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"SessionStart\",\"additionalContext\":$(echo "$CONTEXT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>console.log(JSON.stringify(d)))" 2>/dev/null)}}"
|
|
54
|
-
exit 0
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Track Agent Invocation Hook
|
|
3
|
-
# Registers agent invocations in orchestrator-index.json
|
|
4
|
-
#
|
|
5
|
-
# Usage (called by Claude Code hooks):
|
|
6
|
-
# track-agent-invocation.sh start (reads stdin JSON)
|
|
7
|
-
# track-agent-invocation.sh complete (reads stdin JSON)
|
|
8
|
-
#
|
|
9
|
-
# Claude Code passes JSON via stdin with structure:
|
|
10
|
-
# { "tool_name": "Task", "tool_input": { "subagent_type": "...", ... } }
|
|
11
|
-
|
|
12
|
-
set -e
|
|
13
|
-
|
|
14
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
-
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
16
|
-
INDEX_FILE="$PROJECT_ROOT/.orchestrator/orchestrator-index.json"
|
|
17
|
-
STATE_DIR="$PROJECT_ROOT/.orchestrator/.state"
|
|
18
|
-
INVOCATION_FILE="$STATE_DIR/current-invocation"
|
|
19
|
-
DEBUG_LOG="$STATE_DIR/hook-debug.log"
|
|
20
|
-
|
|
21
|
-
# Ensure state directory exists
|
|
22
|
-
mkdir -p "$STATE_DIR"
|
|
23
|
-
|
|
24
|
-
ACTION="${1:-}"
|
|
25
|
-
|
|
26
|
-
# Read stdin into variable (Claude Code passes JSON via stdin)
|
|
27
|
-
STDIN_DATA=""
|
|
28
|
-
if [ ! -t 0 ]; then
|
|
29
|
-
STDIN_DATA=$(cat)
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
case "$ACTION" in
|
|
33
|
-
start)
|
|
34
|
-
# Extract agent info from stdin JSON
|
|
35
|
-
# Structure: { "tool_name": "Task", "tool_input": { "subagent_type": "...", ... } }
|
|
36
|
-
if [ -n "$STDIN_DATA" ]; then
|
|
37
|
-
# Debug: log received data
|
|
38
|
-
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] START stdin: $STDIN_DATA" >> "$DEBUG_LOG"
|
|
39
|
-
|
|
40
|
-
AGENT_NAME=$(echo "$STDIN_DATA" | node -e "
|
|
41
|
-
let data = '';
|
|
42
|
-
process.stdin.on('data', chunk => data += chunk);
|
|
43
|
-
process.stdin.on('end', () => {
|
|
44
|
-
try {
|
|
45
|
-
const json = JSON.parse(data);
|
|
46
|
-
// Try different structures
|
|
47
|
-
const type = json.tool_input?.subagent_type
|
|
48
|
-
|| json.subagent_type
|
|
49
|
-
|| json.tool_input?.type
|
|
50
|
-
|| 'unknown';
|
|
51
|
-
console.log(type);
|
|
52
|
-
} catch { console.log('unknown'); }
|
|
53
|
-
});
|
|
54
|
-
")
|
|
55
|
-
PHASE=$(echo "$STDIN_DATA" | node -e "
|
|
56
|
-
let data = '';
|
|
57
|
-
process.stdin.on('data', chunk => data += chunk);
|
|
58
|
-
process.stdin.on('end', () => {
|
|
59
|
-
try {
|
|
60
|
-
const json = JSON.parse(data);
|
|
61
|
-
const type = json.tool_input?.subagent_type
|
|
62
|
-
|| json.subagent_type
|
|
63
|
-
|| json.tool_input?.type
|
|
64
|
-
|| 'unknown';
|
|
65
|
-
const phaseMap = {
|
|
66
|
-
'specifier': 'specify',
|
|
67
|
-
'planner': 'plan',
|
|
68
|
-
'task-generator': 'tasks',
|
|
69
|
-
'implementer': 'implement',
|
|
70
|
-
'researcher': 'research',
|
|
71
|
-
'reviewer': 'review',
|
|
72
|
-
'orchestrator': 'orchestrate'
|
|
73
|
-
};
|
|
74
|
-
console.log(phaseMap[type] || type);
|
|
75
|
-
} catch { console.log('unknown'); }
|
|
76
|
-
});
|
|
77
|
-
")
|
|
78
|
-
elif [ -n "$CLAUDE_TOOL_INPUT" ]; then
|
|
79
|
-
# Fallback to environment variable
|
|
80
|
-
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] START env: $CLAUDE_TOOL_INPUT" >> "$DEBUG_LOG"
|
|
81
|
-
|
|
82
|
-
AGENT_NAME=$(echo "$CLAUDE_TOOL_INPUT" | node -e "
|
|
83
|
-
let data = '';
|
|
84
|
-
process.stdin.on('data', chunk => data += chunk);
|
|
85
|
-
process.stdin.on('end', () => {
|
|
86
|
-
try {
|
|
87
|
-
const json = JSON.parse(data);
|
|
88
|
-
const type = json.subagent_type || json.tool_input?.subagent_type || 'unknown';
|
|
89
|
-
console.log(type);
|
|
90
|
-
} catch { console.log('unknown'); }
|
|
91
|
-
});
|
|
92
|
-
")
|
|
93
|
-
PHASE="unknown"
|
|
94
|
-
else
|
|
95
|
-
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] START no input received" >> "$DEBUG_LOG"
|
|
96
|
-
AGENT_NAME="unknown"
|
|
97
|
-
PHASE="unknown"
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
# Generate invocation ID
|
|
101
|
-
INVOCATION_ID="inv-$(date +%s)-$(head -c 4 /dev/urandom | xxd -p)"
|
|
102
|
-
STARTED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
103
|
-
|
|
104
|
-
# Get workflow ID from index
|
|
105
|
-
WORKFLOW_ID=""
|
|
106
|
-
if [ -f "$INDEX_FILE" ]; then
|
|
107
|
-
WORKFLOW_ID=$(node -e "
|
|
108
|
-
const fs = require('fs');
|
|
109
|
-
try {
|
|
110
|
-
const idx = JSON.parse(fs.readFileSync('$INDEX_FILE', 'utf8'));
|
|
111
|
-
console.log(idx.activeWorkflow?.id || 'wf-standalone-' + Date.now());
|
|
112
|
-
} catch { console.log('wf-standalone-' + Date.now()); }
|
|
113
|
-
")
|
|
114
|
-
fi
|
|
115
|
-
|
|
116
|
-
# Save invocation state
|
|
117
|
-
echo "$INVOCATION_ID" > "$INVOCATION_FILE"
|
|
118
|
-
|
|
119
|
-
# Update orchestrator-index.json
|
|
120
|
-
if [ -f "$INDEX_FILE" ]; then
|
|
121
|
-
node -e "
|
|
122
|
-
const fs = require('fs');
|
|
123
|
-
const indexPath = '$INDEX_FILE';
|
|
124
|
-
const idx = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
|
125
|
-
|
|
126
|
-
if (!idx.agentInvocations) idx.agentInvocations = [];
|
|
127
|
-
|
|
128
|
-
idx.agentInvocations.push({
|
|
129
|
-
id: '$INVOCATION_ID',
|
|
130
|
-
agentName: '$AGENT_NAME',
|
|
131
|
-
phase: '$PHASE',
|
|
132
|
-
workflowId: '$WORKFLOW_ID',
|
|
133
|
-
startedAt: '$STARTED_AT',
|
|
134
|
-
completedAt: null,
|
|
135
|
-
status: 'running',
|
|
136
|
-
durationMs: 0
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
fs.writeFileSync(indexPath, JSON.stringify(idx, null, 2));
|
|
140
|
-
console.log(JSON.stringify({ invocationId: '$INVOCATION_ID', startedAt: '$STARTED_AT' }));
|
|
141
|
-
"
|
|
142
|
-
else
|
|
143
|
-
echo '{"invocationId": "'$INVOCATION_ID'", "startedAt": "'$STARTED_AT'", "warning": "No index file found"}'
|
|
144
|
-
fi
|
|
145
|
-
;;
|
|
146
|
-
|
|
147
|
-
complete)
|
|
148
|
-
# Get invocation ID from state file
|
|
149
|
-
INVOCATION_ID=""
|
|
150
|
-
if [ -f "$INVOCATION_FILE" ]; then
|
|
151
|
-
INVOCATION_ID=$(cat "$INVOCATION_FILE")
|
|
152
|
-
fi
|
|
153
|
-
|
|
154
|
-
if [ -z "$INVOCATION_ID" ]; then
|
|
155
|
-
echo '{"success": false, "error": "No invocation ID found"}'
|
|
156
|
-
exit 0 # Don't fail the hook
|
|
157
|
-
fi
|
|
158
|
-
|
|
159
|
-
# Debug log
|
|
160
|
-
if [ -n "$STDIN_DATA" ]; then
|
|
161
|
-
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] COMPLETE stdin: $STDIN_DATA" >> "$DEBUG_LOG"
|
|
162
|
-
fi
|
|
163
|
-
|
|
164
|
-
# Determine status - default to success
|
|
165
|
-
STATUS="success"
|
|
166
|
-
SUMMARY="Completed"
|
|
167
|
-
|
|
168
|
-
COMPLETED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
169
|
-
|
|
170
|
-
# Update orchestrator-index.json
|
|
171
|
-
if [ -f "$INDEX_FILE" ]; then
|
|
172
|
-
node -e "
|
|
173
|
-
const fs = require('fs');
|
|
174
|
-
const indexPath = '$INDEX_FILE';
|
|
175
|
-
const idx = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
|
176
|
-
|
|
177
|
-
const invocations = idx.agentInvocations || [];
|
|
178
|
-
const invIdx = invocations.findIndex(i => i.id === '$INVOCATION_ID');
|
|
179
|
-
|
|
180
|
-
if (invIdx === -1) {
|
|
181
|
-
console.log(JSON.stringify({ success: false, error: 'Invocation not found' }));
|
|
182
|
-
process.exit(0);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const inv = invocations[invIdx];
|
|
186
|
-
const startTime = new Date(inv.startedAt).getTime();
|
|
187
|
-
const endTime = new Date('$COMPLETED_AT').getTime();
|
|
188
|
-
const durationMs = endTime - startTime;
|
|
189
|
-
|
|
190
|
-
invocations[invIdx] = {
|
|
191
|
-
...inv,
|
|
192
|
-
completedAt: '$COMPLETED_AT',
|
|
193
|
-
status: '$STATUS' === 'success' ? 'completed' : 'failed',
|
|
194
|
-
durationMs,
|
|
195
|
-
result: {
|
|
196
|
-
status: '$STATUS',
|
|
197
|
-
summary: '$SUMMARY'
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
idx.agentInvocations = invocations;
|
|
202
|
-
|
|
203
|
-
// Update statistics
|
|
204
|
-
if (idx.statistics) {
|
|
205
|
-
idx.statistics.totalInvocations = invocations.length;
|
|
206
|
-
idx.statistics.lastActivity = '$COMPLETED_AT';
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
fs.writeFileSync(indexPath, JSON.stringify(idx, null, 2));
|
|
210
|
-
console.log(JSON.stringify({ success: true, durationMs }));
|
|
211
|
-
"
|
|
212
|
-
else
|
|
213
|
-
echo '{"success": false, "error": "No index file found"}'
|
|
214
|
-
fi
|
|
215
|
-
|
|
216
|
-
# Clean up state file
|
|
217
|
-
rm -f "$INVOCATION_FILE"
|
|
218
|
-
;;
|
|
219
|
-
|
|
220
|
-
*)
|
|
221
|
-
echo "Usage: $0 {start|complete}"
|
|
222
|
-
echo ""
|
|
223
|
-
echo "This script is called by Claude Code hooks."
|
|
224
|
-
echo "It reads JSON from stdin with structure:"
|
|
225
|
-
echo ' { "tool_name": "Task", "tool_input": { "subagent_type": "...", ... } }'
|
|
226
|
-
echo ""
|
|
227
|
-
echo "Debug log: $DEBUG_LOG"
|
|
228
|
-
exit 0
|
|
229
|
-
;;
|
|
230
|
-
esac
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# workflow-guard.sh — ADR-013 Phase 5 Hook (JSON Structured Output)
|
|
3
|
-
# Trigger: PreToolUse on Write|Edit
|
|
4
|
-
# Purpose: Block writes to src/ and tests/ when no active workflow exists.
|
|
5
|
-
#
|
|
6
|
-
# Output: JSON with permissionDecision (deny/allow) + additionalContext
|
|
7
|
-
# Exit 0 with JSON = structured decision
|
|
8
|
-
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
-
source "$SCRIPT_DIR/orch-helpers.sh"
|
|
13
|
-
|
|
14
|
-
STDIN_DATA=$(orch_read_stdin)
|
|
15
|
-
|
|
16
|
-
# Extract file path (Claude Code wraps in tool_input)
|
|
17
|
-
FILE_PATH=$(orch_json_field "$STDIN_DATA" "tool_input.file_path")
|
|
18
|
-
[ -z "$FILE_PATH" ] && FILE_PATH=$(orch_json_field "$STDIN_DATA" "file_path")
|
|
19
|
-
[ -z "$FILE_PATH" ] && FILE_PATH=$(orch_json_field "$STDIN_DATA" "input.file_path")
|
|
20
|
-
|
|
21
|
-
orch_log "workflow-guard: file_path=$FILE_PATH"
|
|
22
|
-
|
|
23
|
-
# Explicit bypass via env var (for direct implementation without dogfooding)
|
|
24
|
-
if [ "${SKIP_WORKFLOW_GUARD:-}" = "true" ]; then
|
|
25
|
-
orch_log "workflow-guard: ALLOW (SKIP_WORKFLOW_GUARD=true)"
|
|
26
|
-
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","additionalContext":"Workflow guard bypassed via SKIP_WORKFLOW_GUARD=true."}}'
|
|
27
|
-
exit 0
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
# Only guard src/ and tests/ paths (production code)
|
|
31
|
-
case "$FILE_PATH" in
|
|
32
|
-
*/src/*|*/tests/*)
|
|
33
|
-
;;
|
|
34
|
-
*)
|
|
35
|
-
orch_log "workflow-guard: ALLOW (non-guarded path)"
|
|
36
|
-
exit 0
|
|
37
|
-
;;
|
|
38
|
-
esac
|
|
39
|
-
|
|
40
|
-
# Allow config and non-code files
|
|
41
|
-
case "$FILE_PATH" in
|
|
42
|
-
*.json|*.yml|*.yaml|*.md|*.env|*.env.*)
|
|
43
|
-
orch_log "workflow-guard: ALLOW (config/doc file)"
|
|
44
|
-
exit 0
|
|
45
|
-
;;
|
|
46
|
-
esac
|
|
47
|
-
|
|
48
|
-
# Check for active workflow
|
|
49
|
-
WORKFLOW_ID=$(orch_get_active_workflow 2>/dev/null) || WORKFLOW_ID=""
|
|
50
|
-
|
|
51
|
-
if [ -n "$WORKFLOW_ID" ]; then
|
|
52
|
-
# Check if running inside a sub-agent (agent_id present) or main agent (absent)
|
|
53
|
-
AGENT_ID=$(orch_json_field "$STDIN_DATA" "agent_id")
|
|
54
|
-
|
|
55
|
-
if [ -n "$AGENT_ID" ]; then
|
|
56
|
-
# Sub-agent writing — ALLOW
|
|
57
|
-
AGENT_TYPE=$(orch_json_field "$STDIN_DATA" "agent_type")
|
|
58
|
-
orch_log "workflow-guard: ALLOW (sub-agent: ${AGENT_TYPE:-unknown}, workflow: $WORKFLOW_ID)"
|
|
59
|
-
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"additionalContext\":\"Active workflow: ${WORKFLOW_ID}. Sub-agent ${AGENT_TYPE:-unknown} write allowed.\"}}"
|
|
60
|
-
exit 0
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
# Main agent writing directly — check if SKIP_SUBAGENT_GUARD allows it
|
|
64
|
-
if [ "${SKIP_SUBAGENT_GUARD:-}" = "true" ]; then
|
|
65
|
-
orch_log "workflow-guard: ALLOW (SKIP_SUBAGENT_GUARD=true, workflow: $WORKFLOW_ID)"
|
|
66
|
-
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"additionalContext\":\"Active workflow: ${WORKFLOW_ID}. Direct write allowed via SKIP_SUBAGENT_GUARD.\"}}"
|
|
67
|
-
exit 0
|
|
68
|
-
fi
|
|
69
|
-
|
|
70
|
-
# Main agent writing directly — DENY, must use sub-agent
|
|
71
|
-
orch_log "workflow-guard: DENY (main agent direct write, no sub-agent invocation)"
|
|
72
|
-
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Workflow Guard: Direct code writes are blocked. You must invoke a sub-agent (e.g. implementer) to write code. The sub-agent will have write access.\",\"additionalContext\":\"Use the Agent tool to spawn an implementer sub-agent for code changes. The workflow-guard allows writes only from sub-agents (identified by agent_id in hook input).\"}}"
|
|
73
|
-
exit 0
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
# No active workflow — DENY with structured instructions
|
|
77
|
-
orch_log "workflow-guard: DENY (no active workflow)"
|
|
78
|
-
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Workflow Guard: No active workflow. Direct implementation is not allowed.","additionalContext":"You MUST start a workflow before writing code:\n1. mcp__orchestrator-tools__detectWorkflow({ prompt: \"...\" })\n2. mcp__orchestrator-tools__startWorkflow({ workflowType: \"...\", prompt: \"...\" })\n3. Follow the nextStep returned by startWorkflow\n\nThe workflow-guard hook blocks all writes to src/ and tests/ without an active workflow."}}'
|
|
79
|
-
exit 0
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# approval-guardian.sh — TD-128 F-09 Hook
|
|
3
|
-
# Trigger: PreToolUse on mcp__orchestrator-tools__approveAction and mcp__orchestrator-extended__completeWorkflow
|
|
4
|
-
# Purpose: Block auto-approve and auto-complete when workflow is awaiting_approval.
|
|
5
|
-
# Requires explicit human confirmation before proceeding.
|
|
6
|
-
#
|
|
7
|
-
# Output: JSON with permissionDecision (deny/allow)
|
|
8
|
-
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
-
source "$SCRIPT_DIR/orch-helpers.sh"
|
|
13
|
-
|
|
14
|
-
STDIN_DATA=$(orch_read_stdin)
|
|
15
|
-
TOOL_NAME=$(orch_json_field "$STDIN_DATA" "tool_name")
|
|
16
|
-
orch_log "APPROVAL-GUARDIAN: PreToolUse $TOOL_NAME triggered"
|
|
17
|
-
|
|
18
|
-
# Extract workflow ID from tool_input
|
|
19
|
-
WORKFLOW_ID=$(orch_json_field "$STDIN_DATA" "tool_input.workflowId")
|
|
20
|
-
[ -z "$WORKFLOW_ID" ] && WORKFLOW_ID=$(orch_json_field "$STDIN_DATA" "workflowId")
|
|
21
|
-
|
|
22
|
-
if [ -z "$WORKFLOW_ID" ]; then
|
|
23
|
-
# No workflow ID — allow (fail-open for non-workflow calls)
|
|
24
|
-
orch_log "APPROVAL-GUARDIAN: ALLOW (no workflowId)"
|
|
25
|
-
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","additionalContext":"No workflowId found, allowing."}}'
|
|
26
|
-
exit 0
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
# Get auth token
|
|
30
|
-
TOKEN=$(orch_get_token 2>/dev/null) || TOKEN=""
|
|
31
|
-
if [ -z "$TOKEN" ]; then
|
|
32
|
-
# Can't check status — fail-open (auth issues shouldn't block workflow completion)
|
|
33
|
-
orch_log "APPROVAL-GUARDIAN: ALLOW (auth failed, fail-open)"
|
|
34
|
-
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","additionalContext":"Auth failed, allowing."}}'
|
|
35
|
-
exit 0
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
# Get workflow status
|
|
39
|
-
STATUS=$(curl -sf --max-time 5 "${API_URL}/api/v1/workflows/${WORKFLOW_ID}" \
|
|
40
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
41
|
-
-H "X-Project-ID: $PROJECT_ID" 2>/dev/null) || STATUS=""
|
|
42
|
-
|
|
43
|
-
if [ -z "$STATUS" ]; then
|
|
44
|
-
orch_log "APPROVAL-GUARDIAN: ALLOW (could not fetch workflow status)"
|
|
45
|
-
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","additionalContext":"Could not fetch workflow status, allowing."}}'
|
|
46
|
-
exit 0
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
WORKFLOW_STATUS=$(orch_json_field "$STATUS" "status")
|
|
50
|
-
orch_log "APPROVAL-GUARDIAN: workflow=$WORKFLOW_ID status=$WORKFLOW_STATUS tool=$TOOL_NAME"
|
|
51
|
-
|
|
52
|
-
# Block approveAction AND completeWorkflow when awaiting_approval
|
|
53
|
-
if [ "$WORKFLOW_STATUS" = "awaiting_approval" ]; then
|
|
54
|
-
orch_log "APPROVAL-GUARDIAN: DENY (workflow awaiting_approval — human confirmation required for $TOOL_NAME)"
|
|
55
|
-
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Approval Guardian: Workflow is awaiting human approval. You MUST ask the user for explicit confirmation before calling ${TOOL_NAME}.\",\"additionalContext\":\"Present the workflow summary to the user and ask: 'Do you approve advancing to IMPLEMENT?' Wait for their response. Do NOT auto-approve or auto-complete.\"}}"
|
|
56
|
-
exit 0
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
# All other statuses: ALLOW
|
|
60
|
-
orch_log "APPROVAL-GUARDIAN: ALLOW ($TOOL_NAME, status=$WORKFLOW_STATUS)"
|
|
61
|
-
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"additionalContext\":\"${TOOL_NAME} allowed (status=${WORKFLOW_STATUS}).\"}}"
|
|
62
|
-
exit 0
|