Skip to content

Commit 0f25cd4

Browse files
jwaldripclaude
andcommitted
feat: add Agent Teams support with intent-level modes and dynamic hat discovery
Move operating modes from individual hats to the intent level, enabling consistent permission models across all teammates when using Claude Code's Agent Teams. Hats now carry descriptions for dynamic discovery during elaboration. Skills and commands updated to use han CLI instead of MCP tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c1e7e9b commit 0f25cd4

File tree

32 files changed

+530
-371
lines changed

32 files changed

+530
-371
lines changed

README.md

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ curl -fsSL https://han.guru/install.sh | bash
4545

4646
### Operating Modes
4747

48-
Each hat operates in one of three modes that define the level of human oversight:
48+
Each intent operates in one of three modes that define the level of human oversight:
4949

5050
| Mode | Name | Description |
5151
|------|------|-------------|
@@ -74,6 +74,7 @@ Define what you're building with clear completion criteria:
7474
---
7575
status: active
7676
workflow: default
77+
mode: OHOTL
7778
started_at: 2026-01-28T15:30:00Z
7879
---
7980

@@ -204,6 +205,7 @@ Examples:
204205
|-------|--------|------|-------------|
205206
| `status` | Y | Y | `active`, `completed`, `blocked`, `abandoned` |
206207
| `workflow` | Y | | Workflow name (default, tdd, adversarial, hypothesis) |
208+
| `mode` | Y | | Operating mode (`HITL`, `OHOTL`, `AHOTL`) |
207209
| `started_at` | Y | Y | ISO 8601 timestamp |
208210
| `completed_at` | | Y | ISO 8601 timestamp |
209211
| `branch` | | Y | Git branch name |
@@ -235,38 +237,38 @@ All hats follow the [Agent SOP format](https://github.com/strands-agents/agent-s
235237

236238
### Default Workflow
237239

238-
| Hat | Mode | Focus |
239-
|-----|------|-------|
240-
| Elaborator | HITL | Define intent and criteria with user |
241-
| Planner | HITL | Plan what to tackle this iteration |
242-
| Builder | OHOTL | Implement according to plan and criteria |
243-
| Reviewer | HITL | Verify implementation meets criteria |
240+
| Hat | Focus |
241+
|-----|-------|
242+
| Elaborator | Define intent and criteria with user |
243+
| Planner | Plan what to tackle this iteration |
244+
| Builder | Implement according to plan and criteria |
245+
| Reviewer | Verify implementation meets criteria |
244246

245247
### TDD Workflow
246248

247-
| Hat | Mode | Focus |
248-
|-----|------|-------|
249-
| Test Writer | OHOTL | Write failing tests first |
250-
| Implementer | OHOTL | Make tests pass with minimal code |
251-
| Refactorer | OHOTL | Improve code while keeping tests green |
249+
| Hat | Focus |
250+
|-----|-------|
251+
| Test Writer | Write failing tests first |
252+
| Implementer | Make tests pass with minimal code |
253+
| Refactorer | Improve code while keeping tests green |
252254

253255
### Adversarial Workflow
254256

255-
| Hat | Mode | Focus |
256-
|-----|------|-------|
257-
| Builder | OHOTL | Implement to spec |
258-
| Red Team | OHOTL | Attack - find vulnerabilities |
259-
| Blue Team | OHOTL | Defend - fix vulnerabilities |
260-
| Reviewer | HITL | Final security review |
257+
| Hat | Focus |
258+
|-----|-------|
259+
| Builder | Implement to spec |
260+
| Red Team | Attack - find vulnerabilities |
261+
| Blue Team | Defend - fix vulnerabilities |
262+
| Reviewer | Final security review |
261263

262264
### Hypothesis Workflow
263265

264-
| Hat | Mode | Focus |
265-
|-----|------|-------|
266-
| Observer | OHOTL | Gather data about the bug |
267-
| Hypothesizer | HITL | Form theories about the cause |
268-
| Experimenter | OHOTL | Test hypotheses systematically |
269-
| Analyst | HITL | Evaluate results and implement fix |
266+
| Hat | Focus |
267+
|-----|-------|
268+
| Observer | Gather data about the bug |
269+
| Hypothesizer | Form theories about the cause |
270+
| Experimenter | Test hypotheses systematically |
271+
| Analyst | Evaluate results and implement fix |
270272

271273
## State Management
272274

@@ -289,7 +291,7 @@ Session-scoped, cleared on `/reset`:
289291
| `scratchpad.md` | Learnings and progress notes |
290292
| `blockers.md` | Documented blockers |
291293

292-
> **Note:** Commands use `han_keep_save()` and `han_keep_load()` syntax which are **MCP tool calls**, not CLI commands. Claude executes these as MCP tool invocations. The hooks use `han keep` CLI commands directly.
294+
> **Note:** Both commands and hooks use `han keep` CLI commands (`han keep save`, `han keep load`, `han keep delete`, `han keep list`) for state management.
293295
294296
## Customization
295297

@@ -310,7 +312,7 @@ Create `.ai-dlc/hats/` in your project to override or add hats:
310312
<!-- .ai-dlc/hats/researcher.md -->
311313
---
312314
name: "Researcher"
313-
mode: HITL
315+
description: "Investigate before implementing. Research existing solutions and make recommendations."
314316
---
315317

316318
# Researcher

commands/advance.md

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ Advances to the next hat in the workflow sequence. For example, in the default w
3131

3232
### Step 1: Load Current State
3333

34-
```javascript
35-
// Intent-level state is stored on current branch (intent branch)
36-
const state = JSON.parse(han_keep_load({ scope: "branch", key: "iteration.json" }));
34+
```bash
35+
# Intent-level state is stored on current branch (intent branch)
36+
state=$(han keep load iteration.json --quiet)
3737
```
3838

3939
### Step 2: Determine Next Hat (or Handle Completion)
@@ -75,45 +75,37 @@ ALL_COMPLETE=$(echo "$DAG_SUMMARY" | han parse json allComplete -r)
7575
READY_COUNT=$(echo "$DAG_SUMMARY" | han parse json readyCount -r)
7676
```
7777

78-
```javascript
79-
if (dagSummary.allComplete) {
80-
// ALL UNITS COMPLETE - Mark intent as done
81-
state.status = "complete";
82-
han_keep_save({ scope: "branch", key: "iteration.json", content: JSON.stringify(state) });
83-
84-
// Output completion summary (see Step 5)
85-
return completionSummary;
86-
}
87-
88-
if (dagSummary.readyCount > 0) {
89-
// MORE UNITS READY - Loop back to builder
90-
state.hat = workflow[2] || "builder"; // Reset to builder (index 2 in default workflow)
91-
state.currentUnit = null; // Will be set by /construct when it picks next unit
92-
han_keep_save({ scope: "branch", key: "iteration.json", content: JSON.stringify(state) });
78+
```bash
79+
if [ "$ALL_COMPLETE" = "true" ]; then
80+
# ALL UNITS COMPLETE - Mark intent as done
81+
updated_state=$(echo "$state" | jq '.status = "complete"')
82+
han keep save iteration.json "$updated_state"
9383

94-
return `Unit completed. ${dagSummary.readyCount} more unit(s) ready. Continuing construction...`;
95-
}
84+
# Output completion summary (see Step 5)
85+
# return completionSummary
86+
fi
9687

97-
// BLOCKED - No ready units, human must intervene
98-
return `All remaining units are blocked. Human intervention required.
88+
if [ "$READY_COUNT" -gt 0 ]; then
89+
# MORE UNITS READY - Loop back to builder
90+
# Reset to builder (index 2 in default workflow), clear currentUnit
91+
updated_state=$(echo "$state" | jq '.hat = "builder" | .currentUnit = null')
92+
han keep save iteration.json "$updated_state"
9993

100-
Blocked units:
101-
${dagSummary.blockedUnits.join('\n')}
94+
echo "Unit completed. ${READY_COUNT} more unit(s) ready. Continuing construction..."
95+
fi
10296

103-
Review blockers and unblock units to continue.`;
97+
# BLOCKED - No ready units, human must intervene
98+
echo "All remaining units are blocked. Human intervention required."
99+
echo "Review blockers and unblock units to continue."
104100
```
105101

106102
### Step 3: Update State
107103

108-
```javascript
109-
state.hat = nextHat;
110-
state.needsAdvance = true; // Signal SessionStart to increment iteration
111-
// Intent-level state saved to current branch (intent branch)
112-
han_keep_save({
113-
scope: "branch",
114-
key: "iteration.json",
115-
content: JSON.stringify(state)
116-
});
104+
```bash
105+
# Update hat and signal SessionStart to increment iteration
106+
# Intent-level state saved to current branch (intent branch)
107+
updated_state=$(echo "$state" | jq --arg hat "$nextHat" '.hat = $hat | .needsAdvance = true')
108+
han keep save iteration.json "$updated_state"
117109
```
118110

119111
### Step 4: Confirm (Normal Advancement)

commands/construct.md

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,10 @@ fi
9797

9898
### Step 1: Load State
9999

100-
```javascript
101-
// Intent-level state is stored on the current branch (intent branch)
102-
const state = JSON.parse(han_keep_load({ scope: "branch", key: "iteration.json" }));
103-
const intentSlug = han_keep_load({ scope: "branch", key: "intent-slug" }) || null;
100+
```bash
101+
# Intent-level state is stored on the current branch (intent branch)
102+
state=$(han keep load iteration.json --quiet)
103+
intentSlug=$(han keep load intent-slug --quiet)
104104
```
105105

106106
If no state exists:
@@ -153,14 +153,11 @@ update_unit_status "$UNIT_FILE" "in_progress"
153153

154154
**Track current unit in iteration state** so `/advance` knows which unit to mark completed:
155155

156-
```javascript
157-
state.currentUnit = UNIT_NAME; // e.g., "unit-01-core-backend"
158-
// Intent-level state saved to current branch (intent branch)
159-
han_keep_save({
160-
scope: "branch",
161-
key: "iteration.json",
162-
content: JSON.stringify(state)
163-
});
156+
```bash
157+
# Update currentUnit in state, e.g., "unit-01-core-backend"
158+
# Intent-level state saved to current branch (intent branch)
159+
updated_state=$(echo "$state" | jq --arg unit "$UNIT_NAME" '.currentUnit = $unit')
160+
han keep save iteration.json "$updated_state"
164161
```
165162

166163
**Note:** The `update_unit_status` function validates that:
@@ -392,10 +389,10 @@ These are managed by the orchestrator and stored on the intent branch (`ai-dlc/{
392389
| `current-plan.md` | Planner's output |
393390
| `intent-slug` | Slug identifier |
394391

395-
```javascript
396-
// Orchestrator reads/writes to current branch (intent branch)
397-
han_keep_load({ scope: "branch", key: "iteration.json" })
398-
han_keep_save({ scope: "branch", key: "iteration.json", content: "..." })
392+
```bash
393+
# Orchestrator reads/writes to current branch (intent branch)
394+
han keep load iteration.json --quiet
395+
han keep save iteration.json "..."
399396
```
400397

401398
### Unit-Level State (use current branch - omit `branch_name`)
@@ -408,10 +405,10 @@ These are specific to each unit's worktree branch:
408405
| `next-prompt.md` | Continuation prompt for this unit |
409406
| `blockers.md` | Blockers specific to this unit |
410407

411-
```javascript
412-
// Subagent reads/writes to its own branch (current branch)
413-
han_keep_load({ scope: "branch", key: "scratchpad.md" })
414-
han_keep_save({ scope: "branch", key: "scratchpad.md", content: "..." })
408+
```bash
409+
# Subagent reads/writes to its own branch (current branch)
410+
han keep load scratchpad.md --quiet
411+
han keep save scratchpad.md "..."
415412
```
416413

417414
**Why this matters:** When a subagent runs in a worktree on branch `ai-dlc/{intent}/{unit}`, it saves its own working notes to its unit branch. The orchestrator runs on the intent branch and manages intent-level state there.

commands/elaborate.md

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -372,26 +372,12 @@ This ensures:
372372

373373
Intent-level state is saved to the current branch (which is now the intent branch):
374374

375-
```javascript
376-
// Intent-level state → current branch (intent branch)
377-
han_keep_save({
378-
scope: "branch",
379-
key: "intent-slug",
380-
content: "{intent-slug}"
381-
})
382-
383-
// Intent-level state → current branch (intent branch)
384-
han_keep_save({
385-
scope: "branch",
386-
key: "iteration.json",
387-
content: JSON.stringify({
388-
iteration: 1,
389-
hat: "{first-hat-after-elaborator}",
390-
workflowName: "{workflow}",
391-
workflow: ["{hat1}", "{hat2}", ...],
392-
status: "active"
393-
})
394-
})
375+
```bash
376+
# Intent-level state → current branch (intent branch)
377+
han keep save intent-slug "{intent-slug}"
378+
379+
# Intent-level state → current branch (intent branch)
380+
han keep save iteration.json '{"iteration":1,"hat":"{first-hat-after-elaborator}","workflowName":"{workflow}","workflow":["{hat1}","{hat2}"],"status":"active"}'
395381
```
396382

397383
---

commands/fail.md

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ If already at the first hat (elaborator by default), this command is blocked.
2828

2929
### Step 1: Load Current State
3030

31-
```javascript
32-
// Intent-level state is stored on current branch (intent branch)
33-
const state = JSON.parse(han_keep_load({ scope: "branch", key: "iteration.json" }));
31+
```bash
32+
# Intent-level state is stored on current branch (intent branch)
33+
state=$(han keep load iteration.json --quiet)
3434
```
3535

3636
### Step 2: Determine Previous Hat
@@ -52,26 +52,19 @@ const prevHat = workflow[prevIndex];
5252

5353
Before updating state, save the reason for failing:
5454

55-
```javascript
56-
// Append to blockers (unit-level state - saved to current branch)
57-
const reason = "Reviewer found issues: [describe issues]";
58-
han_keep_save({
59-
scope: "branch",
60-
key: "blockers.md",
61-
content: reason
62-
});
55+
```bash
56+
# Append to blockers (unit-level state - saved to current branch)
57+
reason="Reviewer found issues: [describe issues]"
58+
han keep save blockers.md "$reason"
6359
```
6460

6561
### Step 4: Update State
6662

67-
```javascript
68-
state.hat = prevHat;
69-
// Intent-level state saved to current branch (intent branch)
70-
han_keep_save({
71-
scope: "branch",
72-
key: "iteration.json",
73-
content: JSON.stringify(state)
74-
});
63+
```bash
64+
# Update hat to previous hat
65+
# Intent-level state saved to current branch (intent branch)
66+
updated_state=$(echo "$state" | jq --arg hat "$prevHat" '.hat = $hat')
67+
han keep save iteration.json "$updated_state"
7568
```
7669

7770
### Step 5: Confirm

commands/reset.md

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,33 +34,20 @@ The work you did is preserved in git. Only the AI-DLC workflow state is cleared.
3434

3535
If the task is not complete, warn:
3636

37-
```javascript
38-
// Intent-level state is on current branch (intent branch)
39-
const state = JSON.parse(han_keep_load({ scope: "branch", key: "iteration.json" }) || "{}");
40-
41-
if (state.status !== "complete") {
42-
console.log("Warning: Task is not complete. Current hat:", state.hat);
43-
console.log("Are you sure you want to clear all state?");
44-
}
37+
```bash
38+
# Intent-level state is on current branch (intent branch)
39+
state=$(han keep load iteration.json --quiet)
40+
41+
status=$(echo "$state" | jq -r '.status // empty')
42+
if [ "$status" != "complete" ]; then
43+
hat=$(echo "$state" | jq -r '.hat // empty')
44+
echo "Warning: Task is not complete. Current hat: $hat"
45+
echo "Are you sure you want to clear all state?"
46+
fi
4547
```
4648

4749
### Step 2: Delete All AI-DLC Keys
4850

49-
```javascript
50-
// Clear intent-level state (from current branch / intent branch)
51-
han_keep_delete({ scope: "branch", key: "iteration.json" });
52-
han_keep_delete({ scope: "branch", key: "intent.md" });
53-
han_keep_delete({ scope: "branch", key: "completion-criteria.md" });
54-
han_keep_delete({ scope: "branch", key: "current-plan.md" });
55-
han_keep_delete({ scope: "branch", key: "intent-slug" });
56-
57-
// Clear unit-level state (from current branch, if on a unit branch)
58-
han_keep_delete({ scope: "branch", key: "scratchpad.md" });
59-
han_keep_delete({ scope: "branch", key: "blockers.md" });
60-
han_keep_delete({ scope: "branch", key: "next-prompt.md" });
61-
```
62-
63-
Or use the CLI:
6451
```bash
6552
# Clear intent-level state from current branch (intent branch)
6653
han keep delete iteration.json

0 commit comments

Comments
 (0)