@orchestrator-claude/cli 3.10.2 → 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 +57 -0
  5. package/dist/templates/base/claude/hooks/gate-guardian.sh +79 -0
  6. package/dist/templates/base/claude/hooks/orch-helpers.sh +133 -0
  7. package/dist/templates/base/claude/hooks/ping-pong-enforcer.sh +58 -0
  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 +63 -7
  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 +57 -0
  17. package/templates/base/claude/hooks/gate-guardian.sh +79 -0
  18. package/templates/base/claude/hooks/orch-helpers.sh +133 -0
  19. package/templates/base/claude/hooks/ping-pong-enforcer.sh +58 -0
  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 +63 -7
  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
@@ -2,330 +2,60 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- This project uses the **Orchestrator System** for autonomous workflow management. When handling feature requests, bug fixes, or refactoring tasks, you MUST use the orchestrator workflow instead of implementing directly.
5
+ This project uses the **Orchestrator System** for autonomous workflow management.
6
+ Orchestration is enforced by **deterministic hooks** — you do not need to memorize the workflow protocol.
6
7
 
7
- ## Critical Rule
8
+ ## Critical Rules
8
9
 
9
- **NEVER implement features directly.** Always use the orchestrator workflow:
10
-
11
- ```
12
- User Request → Detect Workflow → Research (if needed) → SPECIFY → PLAN → TASKS → Implement
13
- ```
10
+ 1. **NEVER implement features directly** always use the orchestrator workflow
11
+ 2. **NEVER edit `.orchestrator/orchestrator-index.json`** — state is in PostgreSQL
12
+ 3. **Access artifacts via MCP tools** (`artifactStore`, `artifactRetrieve`), not filesystem paths
14
13
 
15
14
  ## How to Start a Workflow
16
15
 
17
- When a user requests a new feature, bug fix, or refactoring:
18
-
19
- ### 1. Detect Workflow Type
20
-
21
- Use the MCP tool to detect the workflow type:
22
-
23
- ```
24
- mcp__orchestrator-tools__detectWorkflow({ prompt: "user request here" })
25
- ```
26
-
27
- Workflow types:
28
- - `feature_development` - New features, new functionality
29
- - `bug_fix` - Bug fixes, error corrections
30
- - `refactoring` - Code refactoring, improvements
31
- - `emergency_debug` - Urgent debugging
32
-
33
- ### 2. Start Workflow and Get Next Step
34
-
35
- **MANDATORY:** After detecting workflow type, call `startWorkflow` to create the workflow:
36
-
37
- ```
38
- mcp__orchestrator-tools__startWorkflow({ workflowType: "feature-development", prompt: "user request here" })
39
- ```
40
-
41
- This returns a deterministic `nextStep` instruction:
42
-
43
- ```json
44
- {
45
- "success": true,
46
- "workflowId": "wf-123",
47
- "nextStep": {
48
- "action": "invoke_agent",
49
- "subagent_type": "orchestrator",
50
- "prompt": "Start workflow feature-development (ID: wf-123) for user request: ..."
51
- }
52
- }
53
- ```
54
-
55
- ### 3. Execute nextStep (Invoke Orchestrator Agent)
56
-
57
- **IMMEDIATELY** follow the `nextStep` instruction by invoking the orchestrator agent via Task tool:
58
-
59
- ```
60
- Use the Task tool with subagent_type from nextStep.subagent_type
61
- Prompt: nextStep.prompt
62
- ```
63
-
64
- The orchestrator agent will:
65
- 1. Set pending actions for specialized agents
66
- 2. Return control to CLI for Ping-Pong pattern
67
- 3. CLI invokes agents via Task tool as directed by getNextAction()
68
- 4. Process continues until workflow completes
69
-
70
- ## Available MCP Tools
71
-
72
- ### orchestrator-tools
73
- - `detectWorkflow` - Detect workflow type from user prompt
74
- - `startWorkflow` - **Create workflow and get deterministic nextStep instruction** (MANDATORY after detectWorkflow)
75
- - `advancePhase` - Advance to the next workflow phase
76
- - `evaluateGate` - Evaluate if a phase gate passes
77
- - `validateArtifact` - Validate artifact against schema
78
- - `createCheckpoint` - Create a git checkpoint
79
- - `getStatus` - Get current workflow status
80
- - `getWorkflows` - List workflows with phase configs (including `requiresApproval`)
81
- - `getNextAction` - Get the next pending action (Ping-Pong pattern)
82
-
83
- ### knowledge-base
84
- - `search` - Search the knowledge base
85
- - `getDocument` - Get a specific document
86
- - `getConstitution` - Get the project constitution
87
- - `listByTier` - List documents by tier
88
- - `listByCategory` - List documents by category
89
-
90
- ## Auto-Advance Behavior (requiresApproval)
91
-
92
- **CRITICAL**: Each workflow phase has a `requiresApproval` configuration.
93
-
94
- ### How It Works
95
-
96
- 1. **Load workflow config** at start using `getWorkflows`:
97
- ```
98
- mcp__orchestrator-tools__getWorkflows()
99
- ```
100
-
101
- 2. **Check each phase's `requiresApproval`**:
102
- - `requiresApproval: false` → **AUTO-ADVANCE** after gate passes (no user interaction)
103
- - `requiresApproval: true` → **PAUSE** and ask user for approval
104
-
105
- ### Default Configuration (feature-development)
16
+ For any feature request, bug fix, or refactoring:
106
17
 
107
18
  ```
108
- SPECIFY → requiresApproval: false auto-advance
109
- PLAN → requiresApproval: false auto-advance
110
- TASKS → requiresApproval: false auto-advance
111
- IMPLEMENT → requiresApproval: true → PAUSE HERE
19
+ 1. mcp__orchestrator-tools__detectWorkflow({ prompt: "user request" })
20
+ 2. mcp__orchestrator-tools__startWorkflow({ workflowType: "...", prompt: "..." })
21
+ 3. Follow the nextStep returned — hooks handle the rest automatically
112
22
  ```
113
23
 
114
- ### Expected Behavior
115
-
116
- - **DO NOT ask user** between SPECIFY → PLAN → TASKS phases
117
- - **Run these phases sequentially** invoking each agent automatically
118
- - **ONLY pause** before IMPLEMENT phase (or phases with `requiresApproval: true`)
119
- - This reduces unnecessary interactions and speeds up the workflow
120
-
121
- ### Example Flow
122
-
123
- ```
124
- User: "Create a calculator function"
125
-
126
- Claude:
127
- 1. detectWorkflow({ prompt: "Create a calculator function" }) → feature-development
128
- 2. startWorkflow({ workflowType: "feature-development", prompt: "..." }) → { nextStep: {...} }
129
- 3. Execute nextStep → Invoke orchestrator agent via Task tool
130
- 4. getNextAction() → { agent: "specifier", status: "awaiting_agent" }
131
- 5. Invoke specifier via Task tool → spec.md created
132
- 6. getNextAction() → { agent: "planner", status: "awaiting_agent" }
133
- 7. Invoke planner via Task tool → plan.md created
134
- 8. getNextAction() → { agent: "task-generator", status: "awaiting_agent" }
135
- 9. Invoke task-generator via Task tool → tasks.md created
136
- 10. getNextAction() → { status: "awaiting_approval" }
137
- 11. Ask user: "Artifacts ready. Approve implementation?"
138
- ```
139
-
140
- ## Workflow Phases
141
-
142
- ```
143
- RESEARCH (optional)
144
-
145
- SPECIFY
146
-
147
- PLAN
148
-
149
- TASKS
150
-
151
- [HUMAN APPROVAL REQUIRED]
152
-
153
- IMPLEMENT
154
- ```
155
-
156
- Each phase produces artifacts stored in MinIO (object storage) and tracked in PostgreSQL:
157
- - `research` - Research context documents
158
- - `specify` - Specifications (spec.md)
159
- - `plan` - Technical plans (plan.md)
160
- - `tasks` - Task backlogs (tasks.md)
161
- - `implement` - Implementation reports
162
-
163
- **CRITICAL**: Artifacts are accessed via MCP tools (`artifactStore`, `artifactRetrieve`), NOT via filesystem paths. The `.orchestrator/artifacts/` directory does NOT exist on the filesystem.
164
-
165
- ---
24
+ Workflow types: `feature_development`, `bug_fix`, `refactoring`, `emergency_debug`
166
25
 
167
- ## Workflow Execution Pattern (Ping-Pong Híbrido)
26
+ ## What Hooks Enforce Automatically
168
27
 
169
- ### Critical Rule: Always Check for Pending Actions
28
+ | Hook | Trigger | What it does |
29
+ |------|---------|-------------|
30
+ | `ping-pong-enforcer` | After every Agent call | Calls `getNextAction` and injects result |
31
+ | `gate-guardian` | Before `advancePhase` | Evaluates gate, blocks if it fails |
32
+ | `dangling-workflow-guard` | On session Stop | Warns and completes dangling workflows |
33
+ | `workflow-guard` | Before Write/Edit on src/ | Blocks code writes without an active workflow |
170
34
 
171
- **MANDATORY:** After EVERY Task tool invocation that involves workflow orchestration, you MUST:
35
+ You do NOT need to manually call `getNextAction` after agents or `evaluateGate` before advancing — hooks do this deterministically.
172
36
 
173
- 1. **Call `mcp__orchestrator-tools__getNextAction()`**
174
- 2. **Analyze the response and take appropriate action**
37
+ ## Skills (On-Demand Context)
175
38
 
176
- ### Status Action Mapping
39
+ Use skills for phase-specific guidance:
177
40
 
178
- | Status | Your Action |
179
- |--------|-------------|
180
- | `awaiting_agent` | **Immediately** invoke the specified agent via Task tool |
181
- | `awaiting_approval` | **Ask user** for approval, then call `approveAction()` |
182
- | `running` | Agent is currently executing, do nothing |
183
- | No pending action | Workflow is complete or paused |
41
+ | Skill | When to use |
42
+ |-------|------------|
43
+ | `/workflow-status` | Check current workflow state |
44
+ | `/artifact-production` | Phase-specific artifact formats |
45
+ | `/tdd-discipline` | TDD protocol during IMPLEMENT |
46
+ | `/project-conventions` | Project patterns and gotchas |
47
+ | `/checkpoint-protocol` | When to create checkpoints |
48
+ | `/kb-lookup` | Search project knowledge base |
184
49
 
185
- ### Workflow Execution Loop
50
+ ## Creating Custom Definitions
186
51
 
187
- ```typescript
188
- // MANDATORY PATTERN for workflow execution
52
+ When creating agents, skills, or hooks, write to BOTH filesystem and PostgreSQL:
189
53
 
190
- // 1. Detect workflow type
191
- const detection = await detectWorkflow({ prompt: "Create authentication API" });
54
+ - **Agent**: Write `.claude/agents/{slug}.md` + `createAgentDefinition` MCP tool
55
+ - **Skill**: Write `.claude/skills/{slug}/SKILL.md` + `createSkillDefinition` MCP tool
56
+ - **Hook**: Write `.claude/hooks/{slug}.sh` + `createHook` MCP tool
192
57
 
193
- // 2. Start workflow and get nextStep (DETERMINISTIC)
194
- const startResult = await startWorkflow({
195
- workflowType: detection.workflowType,
196
- prompt: "Create authentication API"
197
- });
198
-
199
- // 3. Execute nextStep - invoke orchestrator agent
200
- await invokeTool("Task", {
201
- subagent_type: startResult.nextStep.subagent_type, // "orchestrator"
202
- prompt: startResult.nextStep.prompt
203
- });
204
-
205
- // 4. Check for pending actions (Ping-Pong loop)
206
- const action = await getNextAction();
207
-
208
- // 5. Handle the action
209
- if (action.hasAction && action.pendingAction.status === "awaiting_agent") {
210
- // Immediately invoke the specified agent
211
- await invokeTool("Task", {
212
- subagent_type: action.pendingAction.agent,
213
- prompt: action.pendingAction.prompt
214
- });
215
-
216
- // 6. REPEAT: Check for next action after agent completes
217
- const nextAction = await getNextAction();
218
- // ... continue loop
219
- }
220
-
221
- if (action.hasAction && action.pendingAction.status === "awaiting_approval") {
222
- // Inform user and wait for approval
223
- console.log("⚠️ Workflow requires approval before continuing to IMPLEMENT phase.");
224
- console.log("Review artifacts and type 'yes' to proceed.");
225
-
226
- // After user approves:
227
- await approveAction({ workflowId: action.workflowId });
228
-
229
- // Then check for next action again
230
- const nextAction = await getNextAction();
231
- // ...
232
- }
233
- ```
234
-
235
- ### What NOT to Do
236
-
237
- - ❌ **NEVER** skip `startWorkflow` after `detectWorkflow` - it creates the workflow and returns deterministic `nextStep`
238
- - ❌ **NEVER** ignore `nextStep` from `startWorkflow` - it tells you exactly which agent to invoke
239
- - ❌ **NEVER** ignore `getNextAction()` after Task tool invocation
240
- - ❌ **NEVER** decide which agent to invoke without checking `pendingAction`
241
- - ❌ **NEVER** skip to IMPLEMENT phase without user approval
242
- - ❌ **NEVER** execute multiple phases in a single agent invocation
243
- - ❌ **NEVER** use Write/Edit tools to create artifacts during orchestration (let specialized agents do it)
244
- - ❌ **NEVER** invoke agents out of order defined by `pendingAction`
245
-
246
- ### Why This Pattern?
247
-
248
- **Problem (LIM-001):** Context window bloat when orchestrator executes all phases internally in one invocation.
249
-
250
- **Solution (Ping-Pong Híbrido):** Orchestrator acts as **dispatcher**, creating `pendingActions` that guide the CLI to invoke specialized agents in separate, isolated invocations.
251
-
252
- **Benefits:**
253
- - Each agent invocation has isolated context (< 12k tokens)
254
- - Total token usage reduced by 40%+
255
- - No hallucination from massive context
256
- - Real agent metrics (not simulated)
257
- - Workflow resumable after crashes (pendingAction persists)
258
-
259
- ---
260
-
261
- ## MANDATORY: IMPLEMENT Phase Delegation
262
-
263
- > **WARNING**
264
- >
265
- > The IMPLEMENT phase REQUIRES delegation to the implementer subagent.
266
- > Direct implementation is FORBIDDEN and will break workflow governance.
267
-
268
- ### Trigger Phrases
269
-
270
- When user approves implementation with ANY of these phrases:
271
-
272
- **English:**
273
- - "yes", "yes, implement", "proceed", "go ahead", "continue"
274
- - "start implementation", "implement it", "do it"
275
-
276
- **Portuguese:**
277
- - "sim", "prossiga", "continue", "pode implementar"
278
- - "manda ver", "vai la", "implementa"
279
-
280
- ### Required Actions (IN ORDER)
281
-
282
- 1. **ANNOUNCE DELEGATION:**
283
- ```
284
- I will now delegate to the implementer agent to execute the tasks.
285
- ```
286
-
287
- 2. **ADVANCE PHASE:**
288
- ```
289
- mcp__orchestrator-tools__advancePhase({ workflowId: "current-workflow-id" })
290
- ```
291
-
292
- 3. **INVOKE IMPLEMENTER (REQUIRED):**
293
- ```
294
- Use the Task tool with subagent_type="implementer"
295
-
296
- Prompt:
297
- Execute all tasks from .orchestrator/artifacts/tasks/tasks.md
298
-
299
- Context:
300
- - Specification: .orchestrator/artifacts/specify/spec.md
301
- - Plan: .orchestrator/artifacts/plan/plan.md
302
- - Tasks: .orchestrator/artifacts/tasks/tasks.md
303
-
304
- Requirements:
305
- - Follow TDD (write tests BEFORE implementation)
306
- - Maintain {{coverageThreshold}}% minimum coverage
307
- - Create checkpoint after every 3-5 tasks
308
- - Generate implementation-report.md at the end
309
- ```
310
-
311
- ### FORBIDDEN Actions
312
-
313
- - Using Edit/Write tools directly for code implementation
314
- - Skipping Task tool invocation
315
- - Writing production code without implementer agent
316
- - Editing `.orchestrator/orchestrator-index.json` directly (state is in PostgreSQL)
317
-
318
- ### Self-Verification Checklist
319
-
320
- Before proceeding, verify:
321
- - [ ] Did I announce delegation to the user?
322
- - [ ] Did I call advancePhase via MCP?
323
- - [ ] Did I use Task tool with subagent_type="implementer"?
324
- - [ ] Is the implementer prompt complete with all context?
325
-
326
- If ANY answer is NO -> STOP and correct before continuing.
327
-
328
- ---
58
+ Built-in definitions are seeded on `orchestrator init` (idempotent).
329
59
 
330
60
  ## Quality Rules
331
61
 
@@ -335,33 +65,6 @@ If ANY answer is NO -> STOP and correct before continuing.
335
65
  - **Clean Architecture**: Dependencies point inward
336
66
  - **TypeScript Strict**: `strict: true` required
337
67
 
338
- ## State Management
339
-
340
- ```
341
- PostgreSQL (Single Source of Truth):
342
- ├── workflows # Workflow state, pending actions, phases
343
- ├── artifacts # Artifact metadata
344
- ├── checkpoints # Checkpoint metadata
345
- └── agent_invocations # Agent invocation history
346
-
347
- MinIO (Object Storage):
348
- └── artifacts/ # Generated artifact content by phase
349
-
350
- .orchestrator/ (DEPRECATED - do NOT read or write directly):
351
- └── orchestrator-index.json # DEPRECATED: state is in PostgreSQL
352
- ```
353
-
354
- **CRITICAL**: All workflow state is in PostgreSQL. All artifact content is in MinIO. Access everything through MCP tools. NEVER edit `.orchestrator/orchestrator-index.json` directly.
355
-
356
- ## Important Notes
357
-
358
- 1. **Always start with workflow detection** - Don't skip this step
359
- 2. **Let agents do their work** - Don't bypass the workflow
360
- 3. **Validate artifacts** - Use the artifact-validator skill
361
- 4. **Check gates** - Use phase-gate-evaluator before advancing
362
- 5. **Create checkpoints** - After each validated artifact
363
- 6. **Consult CONSTITUTION** - For project rules and constraints
364
-
365
68
  ---
366
69
 
367
70
  *This project was initialized with the Orchestrator CLI.*
@@ -0,0 +1,57 @@
1
+ #!/bin/bash
2
+ # dangling-workflow-guard.sh — ADR-013 Phase 5 Hook (JSON Structured Output)
3
+ # Trigger: Stop
4
+ # Purpose: Before conversation ends, check for workflows in in_progress state.
5
+ # Blocks stop and instructs LLM to call completeWorkflow.
6
+ #
7
+ # Output: JSON with decision: "block" + reason (forces LLM to continue)
8
+ # Exit 0 with block JSON = prevent stop
9
+ # Check stop_hook_active to prevent infinite loops
10
+
11
+ set -euo pipefail
12
+
13
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
+ source "$SCRIPT_DIR/orch-helpers.sh"
15
+
16
+ STDIN_DATA=$(orch_read_stdin)
17
+ orch_log "DANGLING-GUARD: Stop hook triggered"
18
+
19
+ # Prevent infinite loops
20
+ STOP_ACTIVE=$(orch_json_field "$STDIN_DATA" "stop_hook_active")
21
+ if [ "$STOP_ACTIVE" = "true" ]; then
22
+ orch_log "DANGLING-GUARD: stop_hook_active=true, allowing stop to prevent loop"
23
+ exit 0
24
+ fi
25
+
26
+ # Find active workflow
27
+ WORKFLOW_ID=$(orch_get_active_workflow 2>/dev/null) || WORKFLOW_ID=""
28
+
29
+ if [ -z "$WORKFLOW_ID" ]; then
30
+ orch_log "DANGLING-GUARD: No active workflow, clean exit"
31
+ exit 0
32
+ fi
33
+
34
+ # Get workflow status
35
+ TOKEN=$(orch_get_token 2>/dev/null) || TOKEN=""
36
+ if [ -z "$TOKEN" ]; then
37
+ orch_log "DANGLING-GUARD: Auth failed, allowing stop"
38
+ exit 0
39
+ fi
40
+
41
+ STATUS_RESP=$(curl -sf --max-time 5 "${API_URL}/api/v1/workflows/${WORKFLOW_ID}/status" \
42
+ -H "Authorization: Bearer $TOKEN" \
43
+ -H "X-Project-ID: $PROJECT_ID" 2>/dev/null) || STATUS_RESP=""
44
+
45
+ PHASE=$(orch_json_field "$STATUS_RESP" "currentPhase")
46
+ STATUS=$(orch_json_field "$STATUS_RESP" "status")
47
+
48
+ orch_log "DANGLING-GUARD: workflow=$WORKFLOW_ID status=$STATUS phase=$PHASE"
49
+
50
+ if [ "$STATUS" = "in_progress" ] || [ "$STATUS" = "awaiting_agent" ] || [ "$STATUS" = "awaiting_approval" ]; then
51
+ orch_log "DANGLING-GUARD: BLOCK stop (workflow still active)"
52
+ echo "{\"decision\":\"block\",\"reason\":\"[DANGLING-GUARD] Workflow ${WORKFLOW_ID} is still ${STATUS} (phase: ${PHASE}). You must call mcp__orchestrator-extended__completeWorkflow({ workflowId: '${WORKFLOW_ID}' }) before ending the session.\"}"
53
+ exit 0
54
+ fi
55
+
56
+ orch_log "DANGLING-GUARD: Workflow completed or not active, allowing stop"
57
+ exit 0
@@ -0,0 +1,79 @@
1
+ #!/bin/bash
2
+ # gate-guardian.sh — ADR-013 Phase 5 Hook (JSON Structured Output)
3
+ # Trigger: PreToolUse on mcp__orchestrator-tools__advancePhase
4
+ # Purpose: Evaluate gate before phase advance. FAIL-CLOSED: blocks when uncertain.
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
+ orch_log "GATE-GUARDIAN: PreToolUse advancePhase triggered"
16
+
17
+ # Extract parameters
18
+ WORKFLOW_ID=$(orch_json_field "$STDIN_DATA" "tool_input.workflowId")
19
+ TARGET_PHASE=$(orch_json_field "$STDIN_DATA" "tool_input.targetPhase")
20
+ [ -z "$WORKFLOW_ID" ] && WORKFLOW_ID=$(orch_json_field "$STDIN_DATA" "workflowId")
21
+ [ -z "$TARGET_PHASE" ] && TARGET_PHASE=$(orch_json_field "$STDIN_DATA" "targetPhase")
22
+
23
+ orch_log "GATE-GUARDIAN: workflow=$WORKFLOW_ID targetPhase=$TARGET_PHASE"
24
+
25
+ # FAIL-CLOSED: if we can't parse input, DENY
26
+ if [ -z "$WORKFLOW_ID" ] || [ -z "$TARGET_PHASE" ]; then
27
+ orch_log "GATE-GUARDIAN: DENY (could not parse input)"
28
+ echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Gate Guardian: Could not parse workflowId or targetPhase.","additionalContext":"Ensure you pass both workflowId and targetPhase to advancePhase."}}'
29
+ exit 0
30
+ fi
31
+
32
+ # Get auth token — FAIL-CLOSED on auth failure
33
+ TOKEN=$(orch_get_token 2>/dev/null) || TOKEN=""
34
+ if [ -z "$TOKEN" ]; then
35
+ orch_log "GATE-GUARDIAN: DENY (auth failed)"
36
+ echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Gate Guardian: Authentication failed.","additionalContext":"Check ORCHESTRATOR_ADMIN_EMAIL, ORCHESTRATOR_ADMIN_PASSWORD, ORCHESTRATOR_PROJECT_ID env vars."}}'
37
+ exit 0
38
+ fi
39
+
40
+ # Special handling for IMPLEMENT phase: require approval
41
+ TARGET_LOWER=$(echo "$TARGET_PHASE" | tr '[:upper:]' '[:lower:]')
42
+ if [ "$TARGET_LOWER" = "implement" ]; then
43
+ orch_log "GATE-GUARDIAN: Checking approval for IMPLEMENT phase"
44
+
45
+ PENDING=$(curl -sf --max-time 5 "${API_URL}/api/v1/workflows/${WORKFLOW_ID}/pending-action" \
46
+ -H "Authorization: Bearer $TOKEN" \
47
+ -H "X-Project-ID: $PROJECT_ID" 2>/dev/null) || PENDING=""
48
+
49
+ if [ -n "$PENDING" ]; then
50
+ PENDING_STATUS=$(orch_json_field "$PENDING" "status")
51
+ if [ "$PENDING_STATUS" != "approved" ] && [ "$PENDING_STATUS" != "awaiting_agent" ]; then
52
+ orch_log "GATE-GUARDIAN: DENY (IMPLEMENT requires approval, status=$PENDING_STATUS)"
53
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Gate Guardian: IMPLEMENT requires human approval. Status: ${PENDING_STATUS}.\",\"additionalContext\":\"Ask the user for approval first. Then call mcp__orchestrator-tools__approveAction.\"}}"
54
+ exit 0
55
+ fi
56
+ fi
57
+ fi
58
+
59
+ # Evaluate gate via API — FAIL-CLOSED on failure
60
+ GATE_RESULT=$(orch_evaluate_gate "$WORKFLOW_ID" "$TARGET_PHASE" 2>/dev/null) || GATE_RESULT=""
61
+ if [ -z "$GATE_RESULT" ]; then
62
+ orch_log "GATE-GUARDIAN: DENY (gate evaluation failed)"
63
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Gate Guardian: Gate evaluation failed for phase '${TARGET_PHASE}'.\",\"additionalContext\":\"Required artifacts may be missing. Complete the current phase before advancing.\"}}"
64
+ exit 0
65
+ fi
66
+
67
+ PASSED=$(orch_json_field "$GATE_RESULT" "passed")
68
+
69
+ if [ "$PASSED" = "true" ]; then
70
+ orch_log "GATE-GUARDIAN: ALLOW (gate passed for $TARGET_PHASE)"
71
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"additionalContext\":\"Gate to '${TARGET_PHASE}' passed.\"}}"
72
+ exit 0
73
+ fi
74
+
75
+ # Gate failed
76
+ REASONS=$(orch_json_field "$GATE_RESULT" "reasons")
77
+ orch_log "GATE-GUARDIAN: DENY (gate failed: $REASONS)"
78
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Gate Guardian: Gate to '${TARGET_PHASE}' did not pass.\",\"additionalContext\":\"Reasons: ${REASONS}. Complete current phase requirements before advancing.\"}}"
79
+ exit 0
@@ -0,0 +1,133 @@
1
+ #!/bin/bash
2
+ # orch-helpers.sh — Shared helper functions for Orchestrator hooks
3
+ # ADR-013: Deterministic Orchestration
4
+ # Sourced by all hooks. NOT called directly.
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
8
+ STATE_DIR="$PROJECT_ROOT/.orchestrator/.state"
9
+ DEBUG_LOG="$STATE_DIR/hook-debug.log"
10
+
11
+ # API Configuration (from .env or defaults)
12
+ API_URL="${ORCHESTRATOR_API_URL:-http://localhost:3001}"
13
+ AUTH_EMAIL="${ORCHESTRATOR_ADMIN_EMAIL:-${ORCHESTRATOR_AUTH_EMAIL:-admin@orchestrator.local}}"
14
+ AUTH_PASSWORD="${ORCHESTRATOR_ADMIN_PASSWORD:-${ORCHESTRATOR_AUTH_PASSWORD:-admin123}}"
15
+ PROJECT_ID="${ORCHESTRATOR_PROJECT_ID}"
16
+
17
+ mkdir -p "$STATE_DIR"
18
+
19
+ orch_log() {
20
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*" >> "$DEBUG_LOG" 2>/dev/null || true
21
+ }
22
+
23
+ # Read stdin (Claude Code passes JSON via stdin)
24
+ orch_read_stdin() {
25
+ if [ ! -t 0 ]; then
26
+ cat
27
+ else
28
+ echo ""
29
+ fi
30
+ }
31
+
32
+ # Get cached JWT token (login if expired)
33
+ orch_get_token() {
34
+ local cached="$STATE_DIR/jwt-token"
35
+ if [ -f "$cached" ]; then
36
+ local age
37
+ age=$(( $(date +%s) - $(stat -c%Y "$cached" 2>/dev/null || stat -f%m "$cached" 2>/dev/null || echo 0) ))
38
+ if [ "$age" -lt 3500 ]; then
39
+ cat "$cached"
40
+ return
41
+ fi
42
+ fi
43
+
44
+ [ -z "$PROJECT_ID" ] && return 1
45
+
46
+ local resp
47
+ resp=$(curl -sf --max-time 5 -X POST "${API_URL}/api/v1/auth/login" \
48
+ -H "Content-Type: application/json" \
49
+ -H "X-Project-ID: $PROJECT_ID" \
50
+ -d "{\"email\":\"${AUTH_EMAIL}\",\"password\":\"${AUTH_PASSWORD}\"}" 2>/dev/null) || return 1
51
+
52
+ local token
53
+ token=$(echo "$resp" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const j=JSON.parse(d);console.log(j.accessToken||j.data?.accessToken||'')}catch{console.log('')}})" 2>/dev/null)
54
+
55
+ if [ -n "$token" ] && [ "$token" != "" ]; then
56
+ echo "$token" > "$cached"
57
+ echo "$token"
58
+ else
59
+ return 1
60
+ fi
61
+ }
62
+
63
+ # Get the active (non-terminal) workflow ID
64
+ # Checks in_progress, awaiting_agent, and awaiting_approval statuses
65
+ orch_get_active_workflow() {
66
+ local token
67
+ token=$(orch_get_token) || return 1
68
+
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
90
+ }
91
+
92
+ # Call getNextAction for a workflow
93
+ orch_get_next_action() {
94
+ local workflow_id="$1"
95
+ local token
96
+ token=$(orch_get_token) || return 1
97
+
98
+ curl -sf --max-time 5 "${API_URL}/api/v1/workflows/${workflow_id}/pending-action" \
99
+ -H "Authorization: Bearer $token" \
100
+ -H "X-Project-ID: $PROJECT_ID" 2>/dev/null
101
+ }
102
+
103
+ # Call evaluateGate for a workflow
104
+ orch_evaluate_gate() {
105
+ local workflow_id="$1"
106
+ local target_phase="$2"
107
+ local token
108
+ token=$(orch_get_token) || return 1
109
+
110
+ curl -sf --max-time 10 -X POST "${API_URL}/api/v1/workflows/${workflow_id}/gate/evaluate" \
111
+ -H "Content-Type: application/json" \
112
+ -H "Authorization: Bearer $token" \
113
+ -H "X-Project-ID: $PROJECT_ID" \
114
+ -d "{\"targetPhase\":\"${target_phase}\"}" 2>/dev/null
115
+ }
116
+
117
+ # Extract a field from JSON string using node
118
+ orch_json_field() {
119
+ local json="$1"
120
+ local field="$2"
121
+ echo "$json" | node -e "
122
+ let d='';
123
+ process.stdin.on('data',c=>d+=c);
124
+ process.stdin.on('end',()=>{
125
+ try {
126
+ const j=JSON.parse(d);
127
+ const parts='${field}'.split('.');
128
+ let v=j;
129
+ for(const p of parts) v=v?.[p];
130
+ console.log(v||'');
131
+ } catch { console.log(''); }
132
+ });" 2>/dev/null
133
+ }