@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.
Files changed (55) 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 +45 -28
  4. package/dist/templates/base/claude/agents/orchestrator.md +84 -117
  5. package/dist/templates/base/claude/hooks/dangling-guard.ts +53 -0
  6. package/dist/templates/base/claude/hooks/gate-guardian.ts +102 -0
  7. package/dist/templates/base/claude/hooks/lib/api-client.ts +293 -0
  8. package/dist/templates/base/claude/hooks/lib/git-checkpoint.ts +91 -0
  9. package/dist/templates/base/claude/hooks/package.json +13 -0
  10. package/dist/templates/base/claude/hooks/post-compact.ts +44 -0
  11. package/dist/templates/base/claude/hooks/session-start.ts +97 -0
  12. package/dist/templates/base/claude/hooks/subagent-start.ts +50 -0
  13. package/dist/templates/base/claude/hooks/subagent-stop.ts +57 -0
  14. package/dist/templates/base/claude/hooks/tsconfig.json +18 -0
  15. package/dist/templates/base/claude/hooks/user-prompt.ts +95 -0
  16. package/dist/templates/base/claude/hooks/workflow-guard.ts +120 -0
  17. package/dist/templates/base/claude/settings.json +23 -22
  18. package/dist/templates/base/claude/skills/orchestrator/SKILL.md +108 -0
  19. package/package.json +1 -1
  20. package/templates/base/CLAUDE.md.hbs +45 -28
  21. package/templates/base/claude/agents/orchestrator.md +84 -117
  22. package/templates/base/claude/hooks/dangling-guard.ts +53 -0
  23. package/templates/base/claude/hooks/gate-guardian.ts +102 -0
  24. package/templates/base/claude/hooks/lib/api-client.ts +293 -0
  25. package/templates/base/claude/hooks/lib/git-checkpoint.ts +91 -0
  26. package/templates/base/claude/hooks/package.json +13 -0
  27. package/templates/base/claude/hooks/post-compact.ts +44 -0
  28. package/templates/base/claude/hooks/session-start.ts +97 -0
  29. package/templates/base/claude/hooks/subagent-start.ts +50 -0
  30. package/templates/base/claude/hooks/subagent-stop.ts +57 -0
  31. package/templates/base/claude/hooks/tsconfig.json +18 -0
  32. package/templates/base/claude/hooks/user-prompt.ts +95 -0
  33. package/templates/base/claude/hooks/workflow-guard.ts +120 -0
  34. package/templates/base/claude/settings.json +23 -22
  35. package/templates/base/claude/skills/orchestrator/SKILL.md +108 -0
  36. package/dist/templates/base/claude/hooks/approval-guardian.sh +0 -62
  37. package/dist/templates/base/claude/hooks/dangling-workflow-guard.sh +0 -57
  38. package/dist/templates/base/claude/hooks/gate-guardian.sh +0 -84
  39. package/dist/templates/base/claude/hooks/orch-helpers.sh +0 -135
  40. package/dist/templates/base/claude/hooks/ping-pong-enforcer.sh +0 -58
  41. package/dist/templates/base/claude/hooks/post-phase-checkpoint.sh +0 -203
  42. package/dist/templates/base/claude/hooks/prompt-orchestrator.sh +0 -41
  43. package/dist/templates/base/claude/hooks/session-orchestrator.sh +0 -54
  44. package/dist/templates/base/claude/hooks/track-agent-invocation.sh +0 -230
  45. package/dist/templates/base/claude/hooks/workflow-guard.sh +0 -79
  46. package/templates/base/claude/hooks/approval-guardian.sh +0 -62
  47. package/templates/base/claude/hooks/dangling-workflow-guard.sh +0 -57
  48. package/templates/base/claude/hooks/gate-guardian.sh +0 -84
  49. package/templates/base/claude/hooks/orch-helpers.sh +0 -135
  50. package/templates/base/claude/hooks/ping-pong-enforcer.sh +0 -58
  51. package/templates/base/claude/hooks/post-phase-checkpoint.sh +0 -203
  52. package/templates/base/claude/hooks/prompt-orchestrator.sh +0 -41
  53. package/templates/base/claude/hooks/session-orchestrator.sh +0 -54
  54. package/templates/base/claude/hooks/track-agent-invocation.sh +0 -230
  55. package/templates/base/claude/hooks/workflow-guard.sh +0 -79
package/dist/index.d.ts CHANGED
@@ -12,5 +12,5 @@
12
12
  /**
13
13
  * CLI version
14
14
  */
15
- export declare const CLI_VERSION = "3.17.1";
15
+ export declare const CLI_VERSION = "3.19.0";
16
16
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import { OutputFormatter } from './formatters/OutputFormatter.js';
24
24
  /**
25
25
  * CLI version
26
26
  */
27
- export const CLI_VERSION = '3.17.1';
27
+ export const CLI_VERSION = '3.19.0';
28
28
  /**
29
29
  * Main CLI function
30
30
  */
@@ -3,52 +3,64 @@
3
3
  ## Overview
4
4
 
5
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
+ The main conversation IS the orchestratorit coordinates all phases directly, dispatching sub-agents as needed.
7
7
 
8
8
  ## Critical Rules
9
9
 
10
- 1. **NEVER implement features directly** — always use the orchestrator workflow
11
- 2. **NEVER bump versions manually** — always use `/release patch|minor|major`
12
- 3. **NEVER edit `.orchestrator/orchestrator-index.json`** — state is in PostgreSQL
13
- 4. **Access artifacts via MCP tools** (`artifactStore`, `artifactRetrieve`), not filesystem paths
14
-
15
- ## How to Start a Workflow
16
-
17
- For any feature request, bug fix, or refactoring:
10
+ 1. **On session start, greet via AskUserQuestion** — use `[ORCHESTRATOR-DATA]` from the SessionStart hook context (already injected). Do NOT load any skill for the greeting. Format:
11
+ ```
12
+ AskUserQuestion({
13
+ question: "━━ Orchestrator {version} ━━\n{status_summary}\nNext: {next_item}\n\nWhat would you like to do?",
14
+ options: ["Start workflow for next item", "Start workflow for something else", "Free conversation"]
15
+ })
16
+ ```
17
+ If `[ORCHESTRATOR-DATA]` is missing (API down), fallback: invoke `/orchestrator` skill.
18
+ If there is an active workflow, adapt greeting to show workflow state and offer "Continue workflow".
19
+ 2. **To start or manage workflows, invoke `/orchestrator`** — it coordinates phases, dispatches sub-agents, and enforces gates via AskUserQuestion.
20
+ 3. **NEVER implement features directly** — start a workflow first
21
+ 4. **NEVER bump versions manually** — always use `/release patch|minor|major`
22
+ 5. **NEVER edit `.orchestrator/orchestrator-index.json`** — state is in PostgreSQL
23
+ 6. **Access artifacts via MCP tools** (`artifactStore`, `artifactRetrieve`), not filesystem paths
24
+ 7. **NEVER invoke `Agent(subagent_type: "orchestrator")`** — YOU are the orchestrator (RFC-022)
25
+
26
+ ## How Workflows Work
27
+
28
+ The main conversation is the orchestrator. Sub-agents (specifier, planner, implementer, etc.) are dispatched via the Agent tool directly.
18
29
 
19
30
  ```
20
- 1. mcp__orchestrator-tools__detectWorkflow({ prompt: "user request" })
21
- 2. mcp__orchestrator-tools__startWorkflow({ type: "...", prompt: "..." })
22
- 3. MUST invoke orchestrator agent: Agent(subagent_type: "orchestrator")
23
- 4. After EVERY phase agent completes, MUST invoke orchestrator agent again
31
+ 1. AskUserQuestion (greeting) ← immediate, uses hook data
32
+ 2. User chooses to start workflow
33
+ 3. /orchestrator skill + detectWorkflow ← loads coordination context
34
+ 4. Agent(subagent_type: "specifier") ← dispatched from main conversation
35
+ 5. AskUserQuestion → gate check ← human approval
36
+ 6. advancePhase ← MCP tool
37
+ 7. Agent(subagent_type: "planner") ← next phase
38
+ 8. ...repeat until complete...
39
+ 9. completeWorkflow ← MCP tool
24
40
  ```
25
41
 
26
- **MANDATORY:** The orchestrator agent coordinates ALL phase transitions. It calls `evaluateGate`, `advancePhase`, and `setPendingAction`. Do NOT call these tools directly from the main conversation. The ping-pong-enforcer hook will remind you after each sub-agent completes.
27
-
28
- **Exceptions** (may be called directly): `detectWorkflow`, `startWorkflow`, `approveAction`, `completeWorkflow`, `getStatus`.
42
+ **Key principle:** Every phase gate uses `AskUserQuestion` never auto-approve transitions.
29
43
 
30
44
  Workflow types: `feature_development`, `bug_fix`, `refactoring`, `emergency_debug`
31
45
 
32
- ## What Hooks Enforce Automatically
46
+ ## What Hooks Enforce Automatically (TypeScript, RFC-022)
33
47
 
34
48
  | Hook | Trigger | What it does |
35
49
  |------|---------|-------------|
36
- | `ping-pong-enforcer` | After every Agent call | Calls `getNextAction` and injects result |
37
- | `gate-guardian` | Before `advancePhase` | Evaluates gate, blocks if it fails |
38
- | `approval-guardian` | Before `approveAction`/`completeWorkflow` | Blocks auto-approve when workflow awaiting_approval |
39
- | `workflow-guard` | Before Write/Edit on src/ | Blocks code writes without an active workflow |
40
- | `dangling-workflow-guard` | On session Stop | Warns and completes dangling workflows |
41
- | `session-orchestrator` | On session Start | Injects workflow status context |
42
- | `prompt-orchestrator` | On user prompt Submit | Injects workflow status context |
43
-
44
- You do NOT need to manually call `getNextAction` after agents or `evaluateGate` before advancing — hooks do this deterministically.
50
+ | `session-start.ts` | SessionStart | Injects `[ORCHESTRATOR-DATA]` (version, workflow, next item) for greeting |
51
+ | `user-prompt.ts` | UserPromptSubmit | Detects workflow type, injects hints |
52
+ | `gate-guardian.ts` | Before `advancePhase` | Blocks unless IMPLEMENT phase has approval |
53
+ | `workflow-guard.ts` | Before Write/Edit on src/ | Blocks code writes without active workflow |
54
+ | `subagent-start.ts` | SubagentStart | Registers agent invocation via API |
55
+ | `subagent-stop.ts` | SubagentStop | Completes invocation + git checkpoint + next step guidance |
56
+ | `dangling-guard.ts` | Stop | Warns about dangling workflows |
57
+ | `post-compact.ts` | PostCompact | Re-injects workflow state after context compaction |
45
58
 
46
59
  ## Skills (On-Demand Context)
47
60
 
48
- Use skills for phase-specific guidance:
49
-
50
61
  | Skill | When to use |
51
62
  |-------|------------|
63
+ | `/orchestrator` | **Workflow coordination** — phase dispatch, sub-agent management, gate checks |
52
64
  | `/workflow-status` | Check current workflow state |
53
65
  | `/artifact-production` | Phase-specific artifact formats |
54
66
  | `/tdd-discipline` | TDD protocol during IMPLEMENT |
@@ -67,6 +79,11 @@ Use skills for phase-specific guidance:
67
79
  - **Clean Architecture**: Dependencies point inward
68
80
  - **TypeScript Strict**: `strict: true` required
69
81
 
82
+ ## MCP Tool Notes
83
+
84
+ Extended MCP tools (`artifactStore`, `completeWorkflow`, KB graph tools) are deferred-loaded.
85
+ On the first call in a session you may get `No such tool available` — retry once and it will succeed.
86
+
70
87
  ---
71
88
 
72
89
  *This project was initialized with the Orchestrator CLI.*
@@ -1,155 +1,122 @@
1
1
  ---
2
2
  name: orchestrator
3
- description: "[REFERENCE ONLY] Orchestration guide. DO NOT invoke as subagent - MCP tools not available to subagents. Main conversation should use MCP tools directly."
4
- tools: Read
5
- model: sonnet
3
+ description: Main conversation orchestrator coordinates workflows, dispatches phase agents, enforces gates via AskUserQuestion. Use as default agent (settings.json "agent":"orchestrator") for deterministic session initialization.
6
4
  color: orange
7
- permissionMode: default
5
+ memory: project
6
+ skills:
7
+ - orchestrator
8
+ - workflow-coordination
9
+ - backlog-synthesis
10
+ - project-conventions
11
+ hooks:
12
+ SessionStart:
13
+ - hooks:
14
+ - type: command
15
+ command: "npx tsx $CLAUDE_PROJECT_DIR/.claude/hooks/session-start.ts"
8
16
  ---
9
17
 
10
- # Orchestrator - Reference Guide (NOT a Subagent)
18
+ # Orchestrator Main Conversation Agent (v3.0)
11
19
 
12
- ## CRITICAL WARNING
20
+ ## Identity
13
21
 
14
- > **DO NOT INVOKE THIS AS A SUBAGENT VIA TASK TOOL**
15
- >
16
- > MCP tools (getContext, executeAction, canAdvance) are NOT available to subagents.
17
- > The main conversation MUST act as the orchestrator and use MCP tools directly.
18
- >
19
- > This file exists as a REFERENCE GUIDE for orchestration patterns only.
22
+ You ARE the workflow orchestrator. You coordinate all phases directly from this conversation — no orchestrator sub-agent exists. You dispatch specialized sub-agents (specifier, planner, implementer, etc.) via the Agent tool.
20
23
 
21
- ## Correct Pattern
24
+ ## Session Start Protocol
22
25
 
23
- The **main conversation** should:
24
- 1. Use `mcp__orchestrator-tools__getContext()` to get state
25
- 2. Use `mcp__orchestrator-tools__executeAction()` to advance phases
26
- 3. Invoke specialized agents (specifier, planner, etc.) via Task tool
27
- 4. Repeat until workflow complete
26
+ On EVERY session start, BEFORE any text output to the user:
28
27
 
29
- ## Identity (for reference)
28
+ 1. Invoke the `/orchestrator` skill via `Skill("orchestrator")`
29
+ 2. The skill reads project state, checks workflow status, and presents an `AskUserQuestion` greeting
30
+ 3. Wait for the user's response before proceeding
30
31
 
31
- The orchestrator role is performed by the **main conversation**, not a subagent.
32
+ This is non-negotiable. The `/orchestrator` skill is your entry point.
32
33
 
33
- The role: **Choose actions using MCP tools, delegate artifact creation to specialized agents.**
34
+ ## Workflow Coordination
34
35
 
35
- ## Available Tools
36
+ ### Starting a Workflow
36
37
 
37
- ### MCP Tools (Deterministic Layer)
38
-
39
- 1. **getContext()** - Get current workflow state and available actions
40
- - Input: `{ workflowId?: string }`
41
- - Output: `{ workflow, availableActions, nextAgent, pendingApproval, artifacts }`
42
-
43
- 2. **executeAction(action, prompt?)** - Execute a chosen action
44
- - Input: `{ action: string, prompt?: string, workflowId?: string }`
45
- - Output: `{ newState, pendingAction?, error? }`
46
-
47
- 3. **canAdvance(targetPhase)** - Check if can advance to target phase
48
- - Input: `{ workflowId: string, targetPhase: string }`
49
- - Output: `{ canAdvance: boolean, blockers: string[], gateStatus? }`
50
-
51
- ## Decision Loop
52
-
53
- 1. **Call getContext()** to understand current state
54
- 2. **Analyze availableActions** array
55
- 3. **Choose the most appropriate action** based on:
56
- - User intent
57
- - Workflow progress
58
- - Available actions
59
- - Blockers
60
- 4. **Call executeAction(action)** with:
61
- - Selected action name
62
- - Composed prompt for subagent (if needed)
63
- 5. **Communicate result** to user
64
-
65
- ## What You DO
66
-
67
- - Interpret user intent
68
- - Choose actions from availableActions
69
- - Compose prompts for subagents
70
- - Handle user approval requests
71
- - Report workflow progress
38
+ ```
39
+ detectWorkflow({ prompt: "user's request" })
40
+ startWorkflow({ type: "feature_development|bug_fix|refactoring", prompt: "..." })
41
+ ```
72
42
 
73
- ## What You DON'T Do
43
+ ### Phase Dispatch Loop
74
44
 
75
- - Evaluate gates (code does this)
76
- - Validate transitions (code does this)
77
- - Check artifacts (code does this)
78
- - Generate artifacts directly
79
- - Execute multiple phases
45
+ For each phase, follow this cycle:
80
46
 
81
- ## MANDATORY PROHIBITIONS
47
+ 1. **Dispatch** the phase sub-agent via `Agent(subagent_type: "{agent}")`:
82
48
 
83
- **CRITICAL: Violating these rules breaks checkpoints, metrics, and recovery.**
49
+ | Phase | Agent | Artifact |
50
+ |-------|-------|----------|
51
+ | research | researcher | research findings |
52
+ | specify | specifier | spec.md → artifactStore |
53
+ | plan | planner | plan.md → artifactStore |
54
+ | tasks | task-generator | tasks.md → artifactStore |
55
+ | implement | implementer | code + tests |
56
+ | review | reviewer | review feedback |
84
57
 
85
- You MUST NOT:
86
- - Use Edit/Write tools on `.orchestrator/orchestrator-index.json` (workflow state managed via PostgreSQL)
87
- - Use Edit/Write tools to create artifacts (subagents create them)
88
- - Spawn subagents internally via Task tool (use executeAction instead)
89
- - Execute multiple phases in a single invocation
90
- - Skip MCP tools for "efficiency"
91
- - Attempt to manually update workflow state (MCP tools → REST API → PostgreSQL)
58
+ 2. **Gate check** via `AskUserQuestion` — present artifact summary, ask for approval
59
+ 3. **Advance** via `advancePhase({ workflowId })` on approval
60
+ 4. **Repeat** until all phases complete, then `completeWorkflow`
92
61
 
93
- **WHY:** The deterministic layer (MCP tools) triggers:
94
- - Auto-checkpoints after artifact approval
95
- - Agent invocation metrics
96
- - Gate evaluation hooks
97
- - Recovery points
62
+ ### Gate Rules
98
63
 
99
- Bypassing MCP tools = bypassing ALL these features.
64
+ - EVERY phase transition requires `AskUserQuestion` NEVER auto-approve
65
+ - IMPLEMENT phase requires explicit user approval before dispatching implementer
66
+ - Sub-agents MUST store artifacts via `artifactStore` (include this in every dispatch prompt)
100
67
 
101
- ## RETURN TO CLI Pattern
68
+ ## Sub-Agent Dispatch
102
69
 
103
- After calling executeAction(), you MUST return control to CLI:
70
+ When dispatching a phase agent, include:
104
71
 
105
72
  ```
106
- // CORRECT - Return with pendingAction
107
- getContext() executeAction("advance_to_specify") RETURN
108
- // CLI will invoke specifier based on pendingAction
73
+ Agent(subagent_type: "{agent}", prompt: "
74
+ [Workflow] Type: {type}, Phase: {phase}, Project: {projectId}
75
+ [Previous artifact] {summary of previous phase output}
76
+ [Task] {phase-specific instructions}
109
77
 
110
- // WRONG - Do not spawn agent yourself
111
- getContext() → executeAction("advance_to_specify") → Task(specifier) ← WRONG!
78
+ Store your output via mcp__orchestrator-extended__artifactStore.
79
+ ")
112
80
  ```
113
81
 
114
- The CLI reads `pendingAction` via MCP tools (PostgreSQL) and invokes the appropriate agent. Your job is to SET the pendingAction via executeAction(), not to execute it yourself.
115
-
116
- ## Example Flow
82
+ ## Available Agents
117
83
 
118
- User: "Create OAuth2 API"
84
+ - `specifier`: Feature specifications from requirements
85
+ - `planner`: Technical implementation plans
86
+ - `task-generator`: Atomic task backlogs
87
+ - `implementer`: TDD code implementation
88
+ - `researcher`: Technical research (Perplexity)
89
+ - `reviewer`: Artifact and code review
90
+ - `legacy-discoverer`, `api-extractor`, `schema-extractor`: Legacy analysis
91
+ - `code-archaeologist`, `business-rule-miner`, `legacy-synthesizer`: Deep analysis
92
+ - `docs-guardian`: Documentation compliance
119
93
 
120
- 1. Call getContext() → availableActions: ["advance_to_specify"]
121
- 2. Choose action: "advance_to_specify"
122
- 3. Call executeAction("advance_to_specify", "Generate specification for OAuth2 API...")
123
- 4. Report: "Specification phase started. Returning control to CLI."
124
- 5. **RETURN** ← You stop here! CLI handles the rest.
94
+ ## Rules (RFC 2119)
125
95
 
126
- **What happens next (NOT your responsibility):**
127
- - CLI reads pendingAction via MCP tools (PostgreSQL)
128
- - CLI invokes specifier agent
129
- - Specifier creates spec.md
130
- - CLI invokes orchestrator again for next phase
131
- - Repeat until workflow completes
96
+ ### MUST
97
+ 1. MUST invoke `/orchestrator` skill on session start before any output
98
+ 2. MUST use `AskUserQuestion` at every phase gate — never auto-approve
99
+ 3. MUST dispatch phase agents via `Agent(subagent_type:)` — never inline work
100
+ 4. MUST store artifacts in MinIO via `artifactStore`
132
101
 
133
- ## Rules
102
+ ### MUST NOT
103
+ 1. MUST NOT invoke `Agent(subagent_type: "orchestrator")` — YOU are the orchestrator
104
+ 2. MUST NOT bypass approval flow at IMPLEMENT gate
105
+ 3. MUST NOT generate artifacts directly — delegate to specialized agents
134
106
 
135
- - ALWAYS start with getContext()
136
- - NEVER hardcode transitions
137
- - ALWAYS use availableActions to decide
138
- - If pendingApproval, ask user for approval first
139
- - Trust the deterministic layer
107
+ ### SHOULD
108
+ 1. SHOULD keep sub-agent prompts focused with workflow context
109
+ 2. SHOULD summarize artifacts at gates, not dump full content
110
+ 3. SHOULD use `/workflow-status` to diagnose stuck workflows
140
111
 
141
- ## Specialized Agents (Invoked via executeAction)
112
+ ## Token Efficiency
142
113
 
143
- - `specifier`: Generates feature specifications
144
- - `planner`: Creates technical plans
145
- - `task-generator`: Generates task backlog
146
- - `implementer`: Executes implementation
147
- - `researcher`: Conducts research with Perplexity
148
- - `reviewer`: Reviews and validates artifacts
114
+ - Delegate heavy work to sub-agents — main conversation stays lean with summaries
115
+ - Use the 3-File Rule: if >3 files needed, dispatch an Explore agent
116
+ - MCP tools are deferred — call `ToolSearch` before first use of any `mcp__*` tool
149
117
 
150
118
  ---
151
119
 
152
- **Version:** 2.2
153
- **Type:** Reference Guide (NOT a subagent)
154
- **Architecture:** Main conversation uses MCP tools directly
155
- **Fix:** BUG-002 - Subagents don't have MCP access; main conversation is orchestrator
120
+ **Version:** 3.0
121
+ **Architecture:** Main-as-Orchestrator (RFC-022)
122
+ **Predecessor:** v2.2 (sub-agent, deprecated)
@@ -0,0 +1,53 @@
1
+ /**
2
+ * dangling-guard.ts — Stop hook (RFC-022 Phase 2)
3
+ * Replaces dangling-workflow-guard.sh
4
+ * Blocks stop if workflow still active.
5
+ */
6
+
7
+ import {
8
+ log,
9
+ readStdin,
10
+ getField,
11
+ getActiveWorkflow,
12
+ getWorkflowStatus,
13
+ outputBlock,
14
+ } from "./lib/api-client.js";
15
+
16
+ const HOOK = "DANGLING-GUARD";
17
+
18
+ async function main(): Promise<void> {
19
+ const stdin = await readStdin();
20
+ log(HOOK, "Stop hook triggered");
21
+
22
+ // Prevent infinite loops
23
+ const stopActive = getField(stdin, "stop_hook_active");
24
+ if (stopActive === "true") {
25
+ log(HOOK, "stop_hook_active=true, allowing stop to prevent loop");
26
+ return;
27
+ }
28
+
29
+ const workflow = await getActiveWorkflow();
30
+ if (!workflow) {
31
+ log(HOOK, "No active workflow, clean exit");
32
+ return;
33
+ }
34
+
35
+ const status = await getWorkflowStatus(workflow.id);
36
+ const wfStatus = status?.status || workflow.status;
37
+ const phase = status?.currentPhase || workflow.currentPhase;
38
+
39
+ log(HOOK, `workflow=${workflow.id} status=${wfStatus} phase=${phase}`);
40
+
41
+ const activeStatuses = ["in_progress", "awaiting_agent", "awaiting_approval"];
42
+ if (activeStatuses.includes(wfStatus)) {
43
+ log(HOOK, "BLOCK stop (workflow still active)");
44
+ outputBlock(
45
+ `[DANGLING-GUARD] Workflow ${workflow.id} is still ${wfStatus} (phase: ${phase}). You must call mcp__orchestrator-extended__completeWorkflow({ workflowId: '${workflow.id}' }) before ending the session.`
46
+ );
47
+ return;
48
+ }
49
+
50
+ log(HOOK, "Workflow completed or not active, allowing stop");
51
+ }
52
+
53
+ main().catch(() => process.exit(0));
@@ -0,0 +1,102 @@
1
+ /**
2
+ * gate-guardian.ts — PreToolUse[advancePhase] hook (RFC-022 Phase 2)
3
+ * Replaces gate-guardian.sh
4
+ * Guards IMPLEMENT phase advance (requires human approval).
5
+ * ALLOWs quick/interactive modes and non-IMPLEMENT phases.
6
+ * FAIL-CLOSED on parse errors.
7
+ */
8
+
9
+ import {
10
+ log,
11
+ readStdin,
12
+ getField,
13
+ getToken,
14
+ getWorkflowMode,
15
+ getNextAction,
16
+ outputPreToolUse,
17
+ } from "./lib/api-client.js";
18
+
19
+ const HOOK = "GATE-GUARDIAN";
20
+
21
+ async function main(): Promise<void> {
22
+ const stdin = await readStdin();
23
+ log(HOOK, "PreToolUse advancePhase triggered");
24
+
25
+ const workflowId = getField(stdin, "tool_input.workflowId") || getField(stdin, "workflowId");
26
+ const targetPhase = getField(stdin, "tool_input.targetPhase") || getField(stdin, "targetPhase");
27
+
28
+ log(HOOK, `workflow=${workflowId} targetPhase=${targetPhase}`);
29
+
30
+ // Check workflow mode — quick/interactive skip gate
31
+ if (workflowId) {
32
+ const mode = await getWorkflowMode(workflowId);
33
+ log(HOOK, `workflow=${workflowId} mode=${mode || "legacy"}`);
34
+
35
+ if (mode === "quick") {
36
+ outputPreToolUse({
37
+ decision: "allow",
38
+ context: `Gate to '${targetPhase}' allowed: quick mode skips gate evaluation.`,
39
+ });
40
+ return;
41
+ }
42
+ if (mode === "interactive") {
43
+ outputPreToolUse({
44
+ decision: "allow",
45
+ context: `Gate to '${targetPhase}' allowed: interactive mode skips artifact gate evaluation.`,
46
+ });
47
+ return;
48
+ }
49
+ }
50
+
51
+ // FAIL-CLOSED: can't parse input → DENY
52
+ if (!workflowId || !targetPhase) {
53
+ log(HOOK, "DENY (could not parse input)");
54
+ outputPreToolUse({
55
+ decision: "deny",
56
+ reason: "Gate Guardian: Could not parse workflowId or targetPhase.",
57
+ context: "Ensure you pass both workflowId and targetPhase to advancePhase.",
58
+ });
59
+ return;
60
+ }
61
+
62
+ // Auth check — FAIL-CLOSED
63
+ const token = await getToken();
64
+ if (!token) {
65
+ log(HOOK, "DENY (auth failed)");
66
+ outputPreToolUse({
67
+ decision: "deny",
68
+ reason: "Gate Guardian: Authentication failed.",
69
+ context:
70
+ "Check ORCHESTRATOR_ADMIN_EMAIL, ORCHESTRATOR_ADMIN_PASSWORD, ORCHESTRATOR_PROJECT_ID env vars.",
71
+ });
72
+ return;
73
+ }
74
+
75
+ // IMPLEMENT phase requires approval
76
+ if (targetPhase.toLowerCase() === "implement") {
77
+ log(HOOK, "Checking approval for IMPLEMENT phase");
78
+ const action = await getNextAction(workflowId);
79
+ if (action) {
80
+ const status = action.pendingAction?.status || "";
81
+ if (status !== "approved" && status !== "awaiting_agent") {
82
+ log(HOOK, `DENY (IMPLEMENT requires approval, status=${status})`);
83
+ outputPreToolUse({
84
+ decision: "deny",
85
+ reason: `Gate Guardian: IMPLEMENT requires human approval. Status: ${status}.`,
86
+ context:
87
+ "Ask the user for approval first. Then call mcp__orchestrator-tools__approveAction.",
88
+ });
89
+ return;
90
+ }
91
+ }
92
+ }
93
+
94
+ // Non-IMPLEMENT or approved: ALLOW
95
+ log(HOOK, `ALLOW (phase=${targetPhase})`);
96
+ outputPreToolUse({
97
+ decision: "allow",
98
+ context: `Gate to '${targetPhase}' allowed.`,
99
+ });
100
+ }
101
+
102
+ main().catch(() => process.exit(0));