@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,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
|