@orchestrator-claude/cli 3.11.0 → 3.12.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.
Files changed (33) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/templates/base/CLAUDE.md.hbs +35 -332
  4. package/dist/templates/base/claude/hooks/dangling-workflow-guard.sh +33 -41
  5. package/dist/templates/base/claude/hooks/gate-guardian.sh +42 -43
  6. package/dist/templates/base/claude/hooks/orch-helpers.sh +25 -20
  7. package/dist/templates/base/claude/hooks/ping-pong-enforcer.sh +26 -40
  8. package/dist/templates/base/claude/hooks/prompt-orchestrator.sh +41 -0
  9. package/dist/templates/base/claude/hooks/session-orchestrator.sh +54 -0
  10. package/dist/templates/base/claude/hooks/workflow-guard.sh +53 -0
  11. package/dist/templates/base/claude/settings.json +37 -0
  12. package/dist/templates/base/claude/skills/workflow-status/SKILL.md +59 -291
  13. package/dist/templates/base/docker-compose.yml.hbs +2 -1
  14. package/package.json +1 -1
  15. package/templates/base/CLAUDE.md.hbs +35 -332
  16. package/templates/base/claude/hooks/dangling-workflow-guard.sh +33 -41
  17. package/templates/base/claude/hooks/gate-guardian.sh +42 -43
  18. package/templates/base/claude/hooks/orch-helpers.sh +25 -20
  19. package/templates/base/claude/hooks/ping-pong-enforcer.sh +26 -40
  20. package/templates/base/claude/hooks/prompt-orchestrator.sh +41 -0
  21. package/templates/base/claude/hooks/session-orchestrator.sh +54 -0
  22. package/templates/base/claude/hooks/workflow-guard.sh +53 -0
  23. package/templates/base/claude/settings.json +37 -0
  24. package/templates/base/claude/skills/workflow-status/SKILL.md +59 -291
  25. package/templates/base/docker-compose.yml.hbs +2 -1
  26. package/dist/templates/base/claude/hooks/post-artifact-generate.sh +0 -39
  27. package/dist/templates/base/claude/hooks/post-implement-validate.sh +0 -139
  28. package/dist/templates/base/claude/hooks/pre-agent-invoke.sh +0 -34
  29. package/dist/templates/base/claude/hooks/pre-phase-advance.sh +0 -40
  30. package/templates/base/claude/hooks/post-artifact-generate.sh +0 -39
  31. package/templates/base/claude/hooks/post-implement-validate.sh +0 -139
  32. package/templates/base/claude/hooks/pre-agent-invoke.sh +0 -34
  33. package/templates/base/claude/hooks/pre-phase-advance.sh +0 -40
@@ -60,28 +60,33 @@ orch_get_token() {
60
60
  fi
61
61
  }
62
62
 
63
- # Get the active (in_progress) workflow ID
63
+ # Get the active (non-terminal) workflow ID
64
+ # Checks in_progress, awaiting_agent, and awaiting_approval statuses
64
65
  orch_get_active_workflow() {
65
66
  local token
66
67
  token=$(orch_get_token) || return 1
67
68
 
68
- local resp
69
- resp=$(curl -sf --max-time 5 "${API_URL}/api/v1/workflows?status=in_progress&limit=1" \
70
- -H "Authorization: Bearer $token" \
71
- -H "X-Project-ID: $PROJECT_ID" 2>/dev/null) || return 1
72
-
73
- echo "$resp" | node -e "
74
- let d='';
75
- process.stdin.on('data',c=>d+=c);
76
- process.stdin.on('end',()=>{
77
- try {
78
- const j=JSON.parse(d);
79
- const wfs=j.data||j;
80
- const wf=Array.isArray(wfs)?wfs[0]:wfs;
81
- const id=wf?.id||'';
82
- if(id) console.log(id); else process.exit(1);
83
- } catch { process.exit(1); }
84
- });" 2>/dev/null
69
+ local status id
70
+ for status in in_progress awaiting_agent awaiting_approval; do
71
+ local resp
72
+ resp=$(curl -sf --max-time 3 "${API_URL}/api/v1/workflows?status=${status}&limit=1" \
73
+ -H "Authorization: Bearer $token" \
74
+ -H "X-Project-ID: $PROJECT_ID" 2>/dev/null) || continue
75
+
76
+ id=$(echo "$resp" | node -e "
77
+ let d='';
78
+ process.stdin.on('data',c=>d+=c);
79
+ process.stdin.on('end',()=>{
80
+ try {
81
+ const j=JSON.parse(d);
82
+ const wfs=j.data||j;
83
+ const wf=Array.isArray(wfs)?wfs[0]:wfs;
84
+ const id=wf?.id||'';
85
+ if(id) console.log(id); else process.exit(1);
86
+ } catch { process.exit(1); }
87
+ });" 2>/dev/null) && [ -n "$id" ] && echo "$id" && return 0
88
+ done
89
+ return 1
85
90
  }
86
91
 
87
92
  # Call getNextAction for a workflow
@@ -90,7 +95,7 @@ orch_get_next_action() {
90
95
  local token
91
96
  token=$(orch_get_token) || return 1
92
97
 
93
- curl -sf --max-time 5 "${API_URL}/api/v1/workflows/${workflow_id}/next-action" \
98
+ curl -sf --max-time 5 "${API_URL}/api/v1/workflows/${workflow_id}/pending-action" \
94
99
  -H "Authorization: Bearer $token" \
95
100
  -H "X-Project-ID: $PROJECT_ID" 2>/dev/null
96
101
  }
@@ -102,7 +107,7 @@ orch_evaluate_gate() {
102
107
  local token
103
108
  token=$(orch_get_token) || return 1
104
109
 
105
- curl -sf --max-time 10 -X POST "${API_URL}/api/v1/workflows/${workflow_id}/evaluate-gate" \
110
+ curl -sf --max-time 10 -X POST "${API_URL}/api/v1/workflows/${workflow_id}/gate/evaluate" \
106
111
  -H "Content-Type: application/json" \
107
112
  -H "Authorization: Bearer $token" \
108
113
  -H "X-Project-ID: $PROJECT_ID" \
@@ -1,72 +1,58 @@
1
1
  #!/bin/bash
2
- # ping-pong-enforcer.sh — ADR-013 Core Hook
3
- # Trigger: PostToolUse on Agent
4
- # Purpose: After ANY sub-agent returns, automatically call getNextAction
5
- # and inject the result into the conversation.
2
+ # ping-pong-enforcer.sh — ADR-013 Phase 5 Hook (JSON Structured Output)
3
+ # Trigger: PostToolUse on Task (Agent tool)
4
+ # Purpose: After ANY sub-agent returns, call getNextAction and inject
5
+ # the result as additionalContext into the main conversation.
6
6
  #
7
- # This makes Ping-Pong DETERMINISTIC: the LLM always receives the next
8
- # action regardless of whether it "remembers" to call getNextAction.
9
- #
10
- # Output goes to stdout → injected into Claude conversation context.
11
- # Exit 0 always (non-blocking — informational injection).
7
+ # Output: JSON with additionalContext containing next action instructions
12
8
 
13
9
  set -euo pipefail
14
10
 
15
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
12
  source "$SCRIPT_DIR/orch-helpers.sh"
17
13
 
18
- # Read stdin (Claude Code passes tool result JSON)
19
- STDIN_DATA=$(orch_read_stdin)
20
14
  orch_log "PING-PONG: PostToolUse Agent triggered"
21
15
 
22
16
  # Find active workflow
23
- WORKFLOW_ID=$(orch_get_active_workflow 2>/dev/null) || {
17
+ WORKFLOW_ID=$(orch_get_active_workflow 2>/dev/null) || WORKFLOW_ID=""
18
+
19
+ if [ -z "$WORKFLOW_ID" ]; then
24
20
  orch_log "PING-PONG: No active workflow, skipping"
25
21
  exit 0
26
- }
22
+ fi
27
23
 
28
24
  orch_log "PING-PONG: Active workflow=$WORKFLOW_ID"
29
25
 
30
26
  # Call getNextAction
31
- NEXT_ACTION=$(orch_get_next_action "$WORKFLOW_ID" 2>/dev/null) || {
32
- orch_log "PING-PONG: getNextAction failed, skipping"
27
+ NEXT_ACTION=$(orch_get_next_action "$WORKFLOW_ID" 2>/dev/null) || NEXT_ACTION=""
28
+
29
+ if [ -z "$NEXT_ACTION" ]; then
30
+ orch_log "PING-PONG: getNextAction failed or empty"
31
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[PING-PONG] Workflow ${WORKFLOW_ID} is active but getNextAction returned empty. Check workflow status with mcp__orchestrator-tools__getStatus.\"}}"
33
32
  exit 0
34
- }
33
+ fi
35
34
 
36
- # Parse response
35
+ # Extract key fields for the context message
37
36
  HAS_ACTION=$(orch_json_field "$NEXT_ACTION" "hasAction")
38
- STATUS=$(orch_json_field "$NEXT_ACTION" "pendingAction.status")
39
37
  AGENT=$(orch_json_field "$NEXT_ACTION" "pendingAction.agent")
40
- CURRENT_PHASE=$(orch_json_field "$NEXT_ACTION" "currentPhase")
38
+ STATUS=$(orch_json_field "$NEXT_ACTION" "pendingAction.status")
39
+ PROMPT=$(orch_json_field "$NEXT_ACTION" "pendingAction.prompt")
41
40
 
42
- orch_log "PING-PONG: hasAction=$HAS_ACTION status=$STATUS agent=$AGENT phase=$CURRENT_PHASE"
41
+ orch_log "PING-PONG: hasAction=$HAS_ACTION agent=$AGENT status=$STATUS"
43
42
 
44
- # Inject result into conversation
45
43
  if [ "$HAS_ACTION" = "true" ]; then
46
44
  if [ "$STATUS" = "awaiting_agent" ]; then
47
- echo ""
48
- echo "[ORCHESTRATOR] Workflow $WORKFLOW_ID — next action: invoke agent '$AGENT'"
49
- echo "You MUST invoke this agent via Task tool with subagent_type='$AGENT'."
50
- echo "Workflow ID: $WORKFLOW_ID | Phase: $CURRENT_PHASE"
45
+ CONTEXT="[PING-PONG] Next action for workflow ${WORKFLOW_ID}: Invoke agent '${AGENT}' via Agent tool. Status: awaiting_agent."
46
+ [ -n "$PROMPT" ] && CONTEXT="${CONTEXT} Prompt: ${PROMPT}"
51
47
  elif [ "$STATUS" = "awaiting_approval" ]; then
52
- echo ""
53
- echo "[ORCHESTRATOR] Workflow $WORKFLOW_ID — AWAITING HUMAN APPROVAL"
54
- echo "Phase: $CURRENT_PHASE. Ask the user to review artifacts and approve before continuing."
55
- echo "After approval, call: mcp__orchestrator-tools__approveAction({ workflowId: '$WORKFLOW_ID' })"
48
+ CONTEXT="[PING-PONG] Workflow ${WORKFLOW_ID} requires human approval before continuing. Ask the user to approve, then call mcp__orchestrator-tools__approveAction."
56
49
  else
57
- echo ""
58
- echo "[ORCHESTRATOR] Workflow $WORKFLOW_ID — pending action status: $STATUS"
59
- echo "Full response: $NEXT_ACTION"
50
+ CONTEXT="[PING-PONG] Workflow ${WORKFLOW_ID} pending action: agent=${AGENT}, status=${STATUS}."
60
51
  fi
61
52
  else
62
- if [ "$CURRENT_PHASE" = "implement" ]; then
63
- echo ""
64
- echo "[ORCHESTRATOR] Workflow $WORKFLOW_ID — no more actions. Phase: implement."
65
- echo "You MUST call: mcp__orchestrator-tools__completeWorkflow({ workflowId: '$WORKFLOW_ID' })"
66
- else
67
- echo ""
68
- echo "[ORCHESTRATOR] Workflow $WORKFLOW_ID — no pending actions. Phase: $CURRENT_PHASE."
69
- fi
53
+ CONTEXT="[PING-PONG] No pending actions for workflow ${WORKFLOW_ID}. If current phase is implement, call mcp__orchestrator-extended__completeWorkflow to finalize."
70
54
  fi
71
55
 
56
+ # Output structured JSON
57
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"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)}}"
72
58
  exit 0
@@ -0,0 +1,41 @@
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
@@ -0,0 +1,54 @@
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
@@ -0,0 +1,53 @@
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
+ # Only guard src/ and tests/ paths (production code)
24
+ case "$FILE_PATH" in
25
+ */src/*|*/tests/*)
26
+ ;;
27
+ *)
28
+ orch_log "workflow-guard: ALLOW (non-guarded path)"
29
+ exit 0
30
+ ;;
31
+ esac
32
+
33
+ # Allow config and non-code files
34
+ case "$FILE_PATH" in
35
+ *.json|*.yml|*.yaml|*.md|*.env|*.env.*)
36
+ orch_log "workflow-guard: ALLOW (config/doc file)"
37
+ exit 0
38
+ ;;
39
+ esac
40
+
41
+ # Check for active workflow
42
+ WORKFLOW_ID=$(orch_get_active_workflow 2>/dev/null) || WORKFLOW_ID=""
43
+
44
+ if [ -n "$WORKFLOW_ID" ]; then
45
+ orch_log "workflow-guard: ALLOW (active workflow: $WORKFLOW_ID)"
46
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"additionalContext\":\"Active workflow: ${WORKFLOW_ID}. Write allowed.\"}}"
47
+ exit 0
48
+ fi
49
+
50
+ # No active workflow — DENY with structured instructions
51
+ orch_log "workflow-guard: DENY (no active workflow)"
52
+ 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."}}'
53
+ exit 0
@@ -37,6 +37,32 @@
37
37
  "NODE_ENV": "development"
38
38
  },
39
39
  "hooks": {
40
+ "SessionStart": [
41
+ {
42
+ "matcher": "startup",
43
+ "hooks": [
44
+ {
45
+ "type": "command",
46
+ "command": ".claude/hooks/session-orchestrator.sh",
47
+ "timeout": 10000,
48
+ "on_failure": "ignore"
49
+ }
50
+ ]
51
+ }
52
+ ],
53
+ "UserPromptSubmit": [
54
+ {
55
+ "matcher": "",
56
+ "hooks": [
57
+ {
58
+ "type": "command",
59
+ "command": ".claude/hooks/prompt-orchestrator.sh",
60
+ "timeout": 10000,
61
+ "on_failure": "ignore"
62
+ }
63
+ ]
64
+ }
65
+ ],
40
66
  "PreToolUse": [
41
67
  {
42
68
  "matcher": "Task",
@@ -59,6 +85,17 @@
59
85
  "on_failure": "warn"
60
86
  }
61
87
  ]
88
+ },
89
+ {
90
+ "matcher": "Write|Edit",
91
+ "hooks": [
92
+ {
93
+ "type": "command",
94
+ "command": ".claude/hooks/workflow-guard.sh",
95
+ "timeout": 10000,
96
+ "on_failure": "warn"
97
+ }
98
+ ]
62
99
  }
63
100
  ],
64
101
  "PostToolUse": [