@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
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -3,52 +3,64 @@
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
5
|
This project uses the **Orchestrator System** for autonomous workflow management.
|
|
6
|
-
|
|
6
|
+
The main conversation IS the orchestrator — it coordinates all phases directly, dispatching sub-agents as needed.
|
|
7
7
|
|
|
8
8
|
## Critical Rules
|
|
9
9
|
|
|
10
|
-
1. **
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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.
|
|
21
|
-
2.
|
|
22
|
-
3.
|
|
23
|
-
4.
|
|
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
|
-
**
|
|
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
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `workflow-guard` | Before Write/Edit on src/ | Blocks code writes without
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
| `
|
|
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:
|
|
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
|
-
|
|
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
|
|
18
|
+
# Orchestrator — Main Conversation Agent (v3.0)
|
|
11
19
|
|
|
12
|
-
##
|
|
20
|
+
## Identity
|
|
13
21
|
|
|
14
|
-
|
|
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
|
-
##
|
|
24
|
+
## Session Start Protocol
|
|
22
25
|
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
+
This is non-negotiable. The `/orchestrator` skill is your entry point.
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
## Workflow Coordination
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
### Starting a Workflow
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
43
|
+
### Phase Dispatch Loop
|
|
74
44
|
|
|
75
|
-
|
|
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
|
-
|
|
47
|
+
1. **Dispatch** the phase sub-agent via `Agent(subagent_type: "{agent}")`:
|
|
82
48
|
|
|
83
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
94
|
-
- Auto-checkpoints after artifact approval
|
|
95
|
-
- Agent invocation metrics
|
|
96
|
-
- Gate evaluation hooks
|
|
97
|
-
- Recovery points
|
|
62
|
+
### Gate Rules
|
|
98
63
|
|
|
99
|
-
|
|
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
|
-
##
|
|
68
|
+
## Sub-Agent Dispatch
|
|
102
69
|
|
|
103
|
-
|
|
70
|
+
When dispatching a phase agent, include:
|
|
104
71
|
|
|
105
72
|
```
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
111
|
-
|
|
78
|
+
Store your output via mcp__orchestrator-extended__artifactStore.
|
|
79
|
+
")
|
|
112
80
|
```
|
|
113
81
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
## Example Flow
|
|
82
|
+
## Available Agents
|
|
117
83
|
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
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
|
-
|
|
136
|
-
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
##
|
|
112
|
+
## Token Efficiency
|
|
142
113
|
|
|
143
|
-
-
|
|
144
|
-
-
|
|
145
|
-
- `
|
|
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:**
|
|
153
|
-
**
|
|
154
|
-
**
|
|
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));
|