forge-dev-framework 1.1.0 → 1.2.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/.claude/commands/forge/README.md +156 -189
- package/.claude/commands/forge/add-phase.md +4 -3
- package/.claude/commands/forge/complete-milestone.md +1 -1
- package/.claude/commands/forge/convert.md +31 -0
- package/.claude/commands/forge/debug.md +12 -154
- package/.claude/commands/forge/discuss.md +60 -107
- package/.claude/commands/forge/execute.md +67 -142
- package/.claude/commands/forge/generate.md +8 -107
- package/.claude/commands/forge/help.md +9 -114
- package/.claude/commands/forge/init.md +10 -74
- package/.claude/commands/forge/insert-phase.md +4 -3
- package/.claude/commands/forge/new-milestone.md +1 -1
- package/.claude/commands/forge/new-project.md +12 -91
- package/.claude/commands/forge/pause-work.md +2 -2
- package/.claude/commands/forge/plan.md +114 -129
- package/.claude/commands/forge/quick.md +17 -106
- package/.claude/commands/forge/remove-phase.md +3 -2
- package/.claude/commands/forge/resume.md +22 -0
- package/.claude/commands/forge/team-add.md +24 -0
- package/.claude/commands/forge/team-create.md +22 -0
- package/.claude/commands/forge/team-remove.md +24 -0
- package/.claude/commands/forge/team-start.md +22 -0
- package/.claude/commands/forge/team-view.md +18 -0
- package/.claude/commands/forge/verify.md +68 -147
- package/.claude/hooks/forge-context-cleanup.cjs +79 -0
- package/.claude/hooks/forge-event-guard.cjs +36 -0
- package/.claude/hooks/forge-size-guard.cjs +55 -0
- package/.claude/rules/api-patterns.md +13 -98
- package/.claude/rules/context-efficiency.md +10 -0
- package/.claude/rules/security-baseline.md +18 -204
- package/.claude/rules/testing-standards.md +16 -177
- package/.claude/rules/ui-conventions.md +17 -142
- package/bin/forge.js +5 -3
- package/dist/bin/forge.js +5 -3
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +15 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/convert.d.ts +6 -0
- package/dist/commands/convert.d.ts.map +1 -0
- package/dist/commands/convert.js +132 -0
- package/dist/commands/convert.js.map +1 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +3 -2
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/index.d.ts +4 -4
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +4 -4
- package/dist/commands/index.js.map +1 -1
- package/dist/generators/gsd-converter.d.ts +100 -0
- package/dist/generators/gsd-converter.d.ts.map +1 -0
- package/dist/generators/gsd-converter.js +335 -0
- package/dist/generators/gsd-converter.js.map +1 -0
- package/dist/templates/.claude/rules/api-patterns.md.template +212 -0
- package/dist/templates/.claude/rules/security-baseline.md.template +322 -0
- package/dist/templates/.claude/rules/testing-standards.md.template +280 -0
- package/dist/templates/.claude/rules/ui-conventions.md.template +264 -0
- package/dist/templates/.planning/forge.config.json.template +75 -0
- package/dist/templates/CLAUDE.md.template +161 -0
- package/dist/templates/PLAN.md.template +177 -0
- package/dist/templates/PROJECT.md.template +156 -0
- package/dist/templates/REQUIREMENTS.md.template +221 -0
- package/dist/templates/ROADMAP.md.template +130 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +5 -5
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -5
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/template-client.d.ts.map +1 -1
- package/dist/utils/template-client.js +3 -2
- package/dist/utils/template-client.js.map +1 -1
- package/package.json +6 -4
- package/.claude/commands/forge/resume-work.md +0 -122
- package/dist/git/__tests__/worktree.test.d.ts +0 -5
- package/dist/git/__tests__/worktree.test.d.ts.map +0 -1
- package/dist/git/__tests__/worktree.test.js +0 -121
- package/dist/git/__tests__/worktree.test.js.map +0 -1
- package/dist/git/codeowners.d.ts +0 -101
- package/dist/git/codeowners.d.ts.map +0 -1
- package/dist/git/codeowners.js +0 -216
- package/dist/git/codeowners.js.map +0 -1
- package/dist/git/commit.d.ts +0 -135
- package/dist/git/commit.d.ts.map +0 -1
- package/dist/git/commit.js +0 -223
- package/dist/git/commit.js.map +0 -1
- package/dist/git/hooks/commit-msg.d.ts +0 -8
- package/dist/git/hooks/commit-msg.d.ts.map +0 -1
- package/dist/git/hooks/commit-msg.js +0 -34
- package/dist/git/hooks/commit-msg.js.map +0 -1
- package/dist/git/hooks/pre-commit.d.ts +0 -8
- package/dist/git/hooks/pre-commit.d.ts.map +0 -1
- package/dist/git/hooks/pre-commit.js +0 -34
- package/dist/git/hooks/pre-commit.js.map +0 -1
- package/dist/git/pre-commit-hooks.d.ts +0 -117
- package/dist/git/pre-commit-hooks.d.ts.map +0 -1
- package/dist/git/pre-commit-hooks.js +0 -270
- package/dist/git/pre-commit-hooks.js.map +0 -1
- package/dist/git/wipe-protocol.d.ts +0 -281
- package/dist/git/wipe-protocol.d.ts.map +0 -1
- package/dist/git/wipe-protocol.js +0 -237
- package/dist/git/wipe-protocol.js.map +0 -1
- package/dist/git/worktree.d.ts +0 -69
- package/dist/git/worktree.d.ts.map +0 -1
- package/dist/git/worktree.js +0 -202
- package/dist/git/worktree.js.map +0 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: forge:resume
|
|
3
|
+
description: Resume work from previous session with full context restoration
|
|
4
|
+
argument-hint: ""
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Bash
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Resume work by loading `.continue-here.md` handoff file.
|
|
12
|
+
|
|
13
|
+
**Read:** .continue-here.md, state/STATE.json
|
|
14
|
+
|
|
15
|
+
**Steps:**
|
|
16
|
+
|
|
17
|
+
1. Check for `.continue-here.md`. If missing, check STATE.json for last phase.
|
|
18
|
+
2. Parse and display: phase, status, position, completed work, remaining work, decisions, blockers
|
|
19
|
+
3. Ask "Ready to continue?"
|
|
20
|
+
4. Submit WORK_RESUMED event to state/events/
|
|
21
|
+
5. Route to next action: `/forge:execute`, manual work, or `/forge:discuss`
|
|
22
|
+
6. After completing work, remove .continue-here.md and submit WORK_COMPLETED event
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: forge:team-add
|
|
3
|
+
description: Add a new member to the FORGE agent team.
|
|
4
|
+
argument-hint: <name> <role> <agent-type>
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Bash
|
|
10
|
+
- TeamCreate
|
|
11
|
+
- Task
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Add a new specialist to FORGE team by updating AgentTeam.md and recreating team.
|
|
15
|
+
|
|
16
|
+
**Read:** .planning/AgentTeam.md, ~/.claude/teams/forge/config.json
|
|
17
|
+
|
|
18
|
+
**Steps:**
|
|
19
|
+
|
|
20
|
+
1. Validate AgentTeam.md exists
|
|
21
|
+
2. Prompt for details if not in args: name (kebab-case), role, agent type, model, owned paths
|
|
22
|
+
3. Add member section to AgentTeam.md, increment version
|
|
23
|
+
4. Confirm with user, shutdown old team via SendMessage, recreate with TeamCreate, spawn all teammates
|
|
24
|
+
5. Verify with `/forge:team-view`
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: forge:team-create
|
|
3
|
+
description: Create a FORGE agent team from AgentTeam.md specification.
|
|
4
|
+
argument-hint: [--team-name <name>]
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Bash
|
|
9
|
+
- TeamCreate
|
|
10
|
+
- Task
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Create FORGE agent team from `.planning/AgentTeam.md`.
|
|
14
|
+
|
|
15
|
+
**Read:** .planning/AgentTeam.md (or .planning/AgentTeam.template.md as reference)
|
|
16
|
+
|
|
17
|
+
**Steps:**
|
|
18
|
+
|
|
19
|
+
1. If AgentTeam.md missing, spawn Explore subagent to analyze codebase and generate it
|
|
20
|
+
2. Show team config to user, confirm creation
|
|
21
|
+
3. Create team with TeamCreate, spawn all specialist teammates via Task tool
|
|
22
|
+
4. Verify at `~/.claude/teams/forge/config.json`
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: forge:team-remove
|
|
3
|
+
description: Remove a member from the FORGE agent team.
|
|
4
|
+
argument-hint: <member-name>
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Bash
|
|
10
|
+
- TeamCreate
|
|
11
|
+
- Task
|
|
12
|
+
- SendMessage
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
Remove specialist $ARGUMENTS from FORGE team by updating AgentTeam.md and recreating team.
|
|
16
|
+
|
|
17
|
+
**Read:** .planning/AgentTeam.md, ~/.claude/teams/forge/config.json
|
|
18
|
+
|
|
19
|
+
**Steps:**
|
|
20
|
+
|
|
21
|
+
1. Show current members, confirm which to remove
|
|
22
|
+
2. Remove member section from AgentTeam.md, renumber remaining, increment version
|
|
23
|
+
3. Shutdown old team, recreate with TeamCreate, spawn remaining teammates
|
|
24
|
+
4. Verify with `/forge:team-view`
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: forge:team-start
|
|
3
|
+
description: Start the FORGE agent team by spawning all teammates.
|
|
4
|
+
argument-hint: [--team-name <name>]
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Bash
|
|
8
|
+
- TeamCreate
|
|
9
|
+
- Task
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Spawn all specialist teammates defined in AgentTeam.md.
|
|
13
|
+
|
|
14
|
+
**Read:** .planning/AgentTeam.md, ~/.claude/teams/forge/config.json
|
|
15
|
+
|
|
16
|
+
**Steps:**
|
|
17
|
+
|
|
18
|
+
1. Validate AgentTeam.md exists
|
|
19
|
+
2. Parse all specialist roles (name, agent type, model, prompt)
|
|
20
|
+
3. Check existing team for inactive members
|
|
21
|
+
4. Spawn team-lead first, then all specialists via Task tool
|
|
22
|
+
5. Verify team is active, show member summary
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: forge:team-view
|
|
3
|
+
description: View FORGE agent team members and configuration.
|
|
4
|
+
argument-hint: [--team-name <name>]
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Bash
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Display current FORGE team configuration.
|
|
11
|
+
|
|
12
|
+
**Read:** ~/.claude/teams/forge/config.json, .planning/AgentTeam.md
|
|
13
|
+
|
|
14
|
+
**Steps:**
|
|
15
|
+
|
|
16
|
+
1. Read team config, extract: name, description, members
|
|
17
|
+
2. Display formatted table: name, type, model, role, owned paths, active/idle status
|
|
18
|
+
3. Compare with AgentTeam.md, flag differences
|
|
@@ -1,174 +1,95 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: forge:verify
|
|
3
|
-
description: Verify
|
|
3
|
+
description: Verify all phase plans against requirements and generate gap closure plans.
|
|
4
4
|
argument-hint: <phase-name>
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
7
|
+
- Write
|
|
7
8
|
- Glob
|
|
8
9
|
- Grep
|
|
9
10
|
- Bash
|
|
11
|
+
- Task
|
|
10
12
|
---
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
Verify that a phase plan achieves its goals and covers all requirements through goal-backward analysis.
|
|
14
|
+
Verify phase $ARGUMENTS plans cover all requirements via goal-backward analysis. If gaps are found, generate numbered gap closure plan files (same format as regular plans) that can be executed with `/forge:execute {phase} --gaps`.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
Output: VERIFICATION.md with coverage analysis and gaps identified.
|
|
17
|
-
</objective>
|
|
16
|
+
**Read:** All `.planning/phases/{phase}/*-PLAN.md` files, all `*-SUMMARY.md` files (if execution happened), REQUIREMENTS.md, ROADMAP.md, state/STATE.json
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
**Load these files NOW:**
|
|
18
|
+
**Steps:**
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
</execution_context>
|
|
20
|
+
1. **Discover all plan and summary files:**
|
|
21
|
+
- Glob `.planning/phases/{phase}/*-PLAN.md` → parse each plan's frontmatter and tasks
|
|
22
|
+
- Glob `.planning/phases/{phase}/*-SUMMARY.md` → check execution results
|
|
23
|
+
- Build complete picture: what was planned, what was executed, what succeeded
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
**
|
|
30
|
-
**
|
|
31
|
-
**
|
|
32
|
-
</context>
|
|
25
|
+
2. **Requirements coverage** — Map each requirement from REQUIREMENTS.md → plans/tasks that cover it. Mark each as:
|
|
26
|
+
- **Covered** — task exists and (if executed) summary confirms completion
|
|
27
|
+
- **Planned** — task exists but not yet executed
|
|
28
|
+
- **Gap** — no task addresses this requirement
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
3. **Goal-backward analysis** — For each milestone goal from ROADMAP.md:
|
|
31
|
+
- Trace: goal → plans → tasks → acceptance criteria
|
|
32
|
+
- Flag missing links in the chain
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
- Read PLAN.md task list
|
|
39
|
-
- Read REQUIREMENTS.md entries
|
|
40
|
-
- Load milestone goals from ROADMAP.md
|
|
34
|
+
4. **Integration completeness** — Check cross-plan contracts defined, integration points covered, no conflicting file modifications across plans in the same wave.
|
|
41
35
|
|
|
42
|
-
|
|
36
|
+
5. **Dependency validation** — Verify plan dependency graph is acyclic, all depends_on references are valid plan numbers, wave assignments are correct.
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
├─ Task acceptance criteria maps to requirement? ✅
|
|
49
|
-
└─ Verification method defined? ✅
|
|
50
|
-
|
|
51
|
-
REQ-2: API rate limiting
|
|
52
|
-
├─ Covered by: None
|
|
53
|
-
└─ ❌ GAP: No task for rate limiting
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
3. **Goal-Backward Verification**
|
|
57
|
-
|
|
58
|
-
For each milestone goal:
|
|
59
|
-
```
|
|
60
|
-
Goal: "Users can authenticate and authorize"
|
|
61
|
-
└─ Can we trace from goal → tasks → acceptance?
|
|
62
|
-
├─ auth-001: Login endpoint ✅
|
|
63
|
-
├─ auth-002: Session management ✅
|
|
64
|
-
├─ auth-003: Permission checks ✅
|
|
65
|
-
└─ Integration flow tested? ❌ Missing
|
|
66
|
-
```
|
|
38
|
+
6. **Scope validation** — Cross-reference with CONTEXT.md:
|
|
39
|
+
- Deferred ideas should NOT appear in any plan
|
|
40
|
+
- Locked decisions should be implemented as specified
|
|
41
|
+
- Claude's discretion areas should have reasonable choices
|
|
67
42
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
├─ API contract defined? ✅ contracts/auth-api.yaml
|
|
74
|
-
├─ Frontend consumes API? ✅ ui-auth-001
|
|
75
|
-
└─ Integration test exists? ❌ Gap
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
5. **Dependency Validation**
|
|
79
|
-
|
|
80
|
-
Verify dependency graph:
|
|
81
|
-
```
|
|
82
|
-
✅ No circular dependencies
|
|
83
|
-
✅ All dependencies have tasks
|
|
84
|
-
✅ Critical path identified
|
|
85
|
-
⚠ Long dependency chain: api-001 → api-002 → api-003 → api-004
|
|
86
|
-
```
|
|
43
|
+
7. **Write VERIFICATION.md** — `.planning/phases/{phase}/VERIFICATION.md` with:
|
|
44
|
+
```markdown
|
|
45
|
+
# {Phase} Verification
|
|
46
|
+
**Verified:** [timestamp]
|
|
47
|
+
**Result:** {PASS | GAPS FOUND}
|
|
87
48
|
|
|
88
|
-
|
|
49
|
+
## Requirements Coverage
|
|
50
|
+
| Requirement | Status | Covered By |
|
|
51
|
+
|-------------|--------|------------|
|
|
52
|
+
| REQ-001 | Covered | Plan 01, Task 1 |
|
|
53
|
+
| REQ-003 | GAP | — |
|
|
89
54
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
✅ auth-001: Single owner, single concept
|
|
93
|
-
❌ ui-002: Too broad (split into ui-002a, ui-002b)
|
|
94
|
-
```
|
|
55
|
+
## Goal-Backward Analysis
|
|
56
|
+
[Results per goal]
|
|
95
57
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
# <Phase> Verification
|
|
58
|
+
## Integration Check
|
|
59
|
+
[Results]
|
|
99
60
|
|
|
100
|
-
##
|
|
101
|
-
|
|
102
|
-
- REQ-2: ❌ Gap - no rate limiting task
|
|
103
|
-
...
|
|
104
|
-
|
|
105
|
-
## Goals Verification
|
|
106
|
-
- Goal 1: ✅ Achievable through tasks
|
|
107
|
-
- Goal 2: ⚠ Gap - integration test missing
|
|
108
|
-
...
|
|
109
|
-
|
|
110
|
-
## Integration Checks
|
|
111
|
-
- Phase A → Phase B: ✅ Contract defined
|
|
112
|
-
- Frontend → API: ⚠ Integration test missing
|
|
113
|
-
...
|
|
114
|
-
|
|
115
|
-
## Task Atomicity
|
|
116
|
-
- All tasks atomic? ✅
|
|
117
|
-
- Task count within limit? ✅ (5/6)
|
|
118
|
-
|
|
119
|
-
## Gaps Identified
|
|
120
|
-
1. REQ-2: No rate limiting
|
|
121
|
-
2. Integration test for auth flow
|
|
122
|
-
...
|
|
123
|
-
|
|
124
|
-
## Recommendations
|
|
125
|
-
- Add task api-005 for rate limiting
|
|
126
|
-
- Add task int-001 for integration tests
|
|
127
|
-
...
|
|
128
|
-
```
|
|
61
|
+
## Dependency Validation
|
|
62
|
+
[Results]
|
|
129
63
|
|
|
130
|
-
|
|
64
|
+
## Scope Validation
|
|
65
|
+
[Results]
|
|
131
66
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
- Ask user: "Add tasks for gaps?"
|
|
135
|
-
- If yes: Update PLAN.md
|
|
136
|
-
- If no: Document and proceed
|
|
67
|
+
## Gaps Summary
|
|
68
|
+
{count} gaps identified requiring remediation.
|
|
137
69
|
```
|
|
138
70
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
-
|
|
161
|
-
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
<next_steps>
|
|
166
|
-
If verification passes:
|
|
167
|
-
- Run `forge execute <phase>` to start execution
|
|
168
|
-
- Or manually assign tasks to agents
|
|
169
|
-
|
|
170
|
-
If gaps found:
|
|
171
|
-
- Update PLAN.md with additional tasks
|
|
172
|
-
- Re-run `forge verify <phase>`
|
|
173
|
-
- Or document risks and proceed
|
|
174
|
-
</next_steps>
|
|
71
|
+
8. **If gaps found — generate gap closure plan files:**
|
|
72
|
+
- Determine next plan number (highest existing + 1)
|
|
73
|
+
- Create `.planning/phases/{phase}/{phase}-{next}-PLAN.md` for each gap cluster
|
|
74
|
+
- Each gap closure plan uses the same format as regular plans with frontmatter:
|
|
75
|
+
```yaml
|
|
76
|
+
---
|
|
77
|
+
phase: {phase-name}
|
|
78
|
+
plan: {next-number}
|
|
79
|
+
wave: 1
|
|
80
|
+
depends_on: []
|
|
81
|
+
files_modified: [...]
|
|
82
|
+
autonomous: true
|
|
83
|
+
gap_closure: true
|
|
84
|
+
---
|
|
85
|
+
```
|
|
86
|
+
- Gap closure plans contain 2-3 remediation tasks each (same XML task format)
|
|
87
|
+
- Number sequentially after existing plans
|
|
88
|
+
- Gap plan limit: max 3 gap closure plans. If more gaps exist, recommend breaking into sub-phases.
|
|
89
|
+
|
|
90
|
+
9. **Routing:**
|
|
91
|
+
- If **no gaps:** "All requirements covered. Ready to execute." → Offer `/forge:execute {phase}`
|
|
92
|
+
- If **gaps found:** Show gap summary, list generated gap closure plans → Offer `/forge:execute {phase} --gaps` to run only gap closure plans
|
|
93
|
+
- If **already executed and gaps in results:** Generate plans targeting execution failures
|
|
94
|
+
|
|
95
|
+
**Pass criteria:** All requirements covered, goals achievable, deps valid, scope aligned with CONTEXT.md.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// FORGE Hook: Context Cleanup (SessionStart)
|
|
3
|
+
// Archives old events, cleans temp files, reports context health
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
const cwd = process.cwd();
|
|
9
|
+
const eventsDir = path.join(cwd, 'state', 'events');
|
|
10
|
+
const archiveDir = path.join(cwd, 'state', 'events', 'archive');
|
|
11
|
+
const planningDir = path.join(cwd, '.planning');
|
|
12
|
+
|
|
13
|
+
// Only run if this looks like a FORGE project
|
|
14
|
+
if (!fs.existsSync(path.join(cwd, 'state', 'STATE.json'))) {
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let archived = 0;
|
|
19
|
+
let cleaned = 0;
|
|
20
|
+
|
|
21
|
+
// 1. Archive events older than 7 days
|
|
22
|
+
if (fs.existsSync(eventsDir)) {
|
|
23
|
+
const cutoff = Date.now() - (7 * 24 * 60 * 60 * 1000);
|
|
24
|
+
const files = fs.readdirSync(eventsDir)
|
|
25
|
+
.filter(f => f.endsWith('.json') && f !== 'archive');
|
|
26
|
+
|
|
27
|
+
for (const file of files) {
|
|
28
|
+
const fp = path.join(eventsDir, file);
|
|
29
|
+
try {
|
|
30
|
+
const stat = fs.statSync(fp);
|
|
31
|
+
if (stat.mtime.getTime() < cutoff) {
|
|
32
|
+
if (!fs.existsSync(archiveDir)) {
|
|
33
|
+
fs.mkdirSync(archiveDir, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
fs.renameSync(fp, path.join(archiveDir, file));
|
|
36
|
+
archived++;
|
|
37
|
+
}
|
|
38
|
+
} catch (e) {}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 2. Clean up orphaned .continue-here.md if older than 3 days
|
|
43
|
+
const continueFile = path.join(cwd, '.continue-here.md');
|
|
44
|
+
if (fs.existsSync(continueFile)) {
|
|
45
|
+
try {
|
|
46
|
+
const stat = fs.statSync(continueFile);
|
|
47
|
+
const age = Date.now() - stat.mtime.getTime();
|
|
48
|
+
if (age > 3 * 24 * 60 * 60 * 1000) {
|
|
49
|
+
fs.unlinkSync(continueFile);
|
|
50
|
+
cleaned++;
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 3. Clean up empty quick task dirs
|
|
56
|
+
const quickDir = path.join(planningDir, 'quick');
|
|
57
|
+
if (fs.existsSync(quickDir)) {
|
|
58
|
+
const dirs = fs.readdirSync(quickDir);
|
|
59
|
+
for (const dir of dirs) {
|
|
60
|
+
const dp = path.join(quickDir, dir);
|
|
61
|
+
try {
|
|
62
|
+
if (fs.statSync(dp).isDirectory()) {
|
|
63
|
+
const contents = fs.readdirSync(dp);
|
|
64
|
+
if (contents.length === 0) {
|
|
65
|
+
fs.rmdirSync(dp);
|
|
66
|
+
cleaned++;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Report if anything happened
|
|
74
|
+
if (archived > 0 || cleaned > 0) {
|
|
75
|
+
const parts = [];
|
|
76
|
+
if (archived > 0) parts.push(`archived ${archived} old event(s)`);
|
|
77
|
+
if (cleaned > 0) parts.push(`cleaned ${cleaned} temp file(s)`);
|
|
78
|
+
process.stderr.write(`\x1b[34m→ FORGE cleanup: ${parts.join(', ')}\x1b[0m\n`);
|
|
79
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// FORGE Hook: Event Immutability Guard
|
|
3
|
+
// PreToolUse on Edit — prevents modifying existing event files in state/events/
|
|
4
|
+
// Events are append-only per FORGE architecture
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
let input = '';
|
|
9
|
+
process.stdin.setEncoding('utf8');
|
|
10
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
11
|
+
process.stdin.on('end', () => {
|
|
12
|
+
try {
|
|
13
|
+
const data = JSON.parse(input);
|
|
14
|
+
const toolName = data.tool_name || '';
|
|
15
|
+
const filePath = data.tool_input?.file_path || '';
|
|
16
|
+
|
|
17
|
+
// Only guard Edit operations on event files
|
|
18
|
+
if (toolName === 'Edit' && filePath) {
|
|
19
|
+
const resolved = path.resolve(filePath);
|
|
20
|
+
const eventsDir = path.resolve(process.cwd(), 'state', 'events');
|
|
21
|
+
|
|
22
|
+
if (resolved.startsWith(eventsDir) && resolved.endsWith('.json')) {
|
|
23
|
+
process.stdout.write(JSON.stringify({
|
|
24
|
+
decision: 'block',
|
|
25
|
+
reason: 'FORGE: Events are append-only. Cannot edit files in state/events/. Write new events instead.'
|
|
26
|
+
}));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
process.stdout.write(JSON.stringify({ decision: 'approve' }));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
// Don't block on parse errors
|
|
34
|
+
process.stdout.write(JSON.stringify({ decision: 'approve' }));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// FORGE Hook: File Size Guard
|
|
3
|
+
// PostToolUse on Write/Edit — warns when artifacts exceed token limits
|
|
4
|
+
// Limits: CLAUDE.md ~2000t, rules ~1000t, REQUIREMENTS.md ~4000t, PLAN.md ~6000t
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
// Approximate tokens as chars/4
|
|
10
|
+
const LIMITS = {
|
|
11
|
+
'CLAUDE.md': { tokens: 2000, chars: 8000 },
|
|
12
|
+
'REQUIREMENTS.md': { tokens: 4000, chars: 16000 },
|
|
13
|
+
'PLAN.md': { tokens: 6000, chars: 24000 },
|
|
14
|
+
};
|
|
15
|
+
const RULES_LIMIT = { tokens: 1000, chars: 4000 };
|
|
16
|
+
|
|
17
|
+
let input = '';
|
|
18
|
+
process.stdin.setEncoding('utf8');
|
|
19
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
20
|
+
process.stdin.on('end', () => {
|
|
21
|
+
try {
|
|
22
|
+
const data = JSON.parse(input);
|
|
23
|
+
const filePath = data.tool_input?.file_path || data.tool_result?.file_path || '';
|
|
24
|
+
|
|
25
|
+
if (!filePath) {
|
|
26
|
+
process.stdout.write(JSON.stringify({ decision: 'approve' }));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const basename = path.basename(filePath);
|
|
31
|
+
const resolved = path.resolve(filePath);
|
|
32
|
+
|
|
33
|
+
// Check specific file limits
|
|
34
|
+
let limit = LIMITS[basename];
|
|
35
|
+
|
|
36
|
+
// Check rules directory
|
|
37
|
+
if (!limit && resolved.includes('.claude/rules/') && resolved.endsWith('.md')) {
|
|
38
|
+
limit = RULES_LIMIT;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (limit && fs.existsSync(resolved)) {
|
|
42
|
+
const size = fs.statSync(resolved).size;
|
|
43
|
+
if (size > limit.chars) {
|
|
44
|
+
const approxTokens = Math.round(size / 4);
|
|
45
|
+
process.stderr.write(
|
|
46
|
+
`\x1b[33m⚠ FORGE: ${basename} is ~${approxTokens} tokens (limit: ~${limit.tokens}). Consider trimming.\x1b[0m\n`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
process.stdout.write(JSON.stringify({ decision: 'approve' }));
|
|
52
|
+
} catch (e) {
|
|
53
|
+
process.stdout.write(JSON.stringify({ decision: 'approve' }));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
@@ -1,98 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
1. Types/interfaces first
|
|
15
|
-
2. Functions/operations
|
|
16
|
-
3. Constants at the end
|
|
17
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
// src/state/event-types.ts
|
|
20
|
-
export interface StateEvent { ... }
|
|
21
|
-
export interface TaskStartedEvent extends StateEvent { ... }
|
|
22
|
-
|
|
23
|
-
export function createEvent(type: EventType, payload: unknown): StateEvent { ... }
|
|
24
|
-
|
|
25
|
-
export const EVENT_TYPES = ['TASK_STARTED', 'TASK_COMPLETED', ...] as const;
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Error Handling
|
|
29
|
-
|
|
30
|
-
- Define custom error types extending `Error`
|
|
31
|
-
- Include error codes for programmatic handling
|
|
32
|
-
- Provide context in error messages
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
export class StateValidationError extends Error {
|
|
36
|
-
constructor(
|
|
37
|
-
public schema: string,
|
|
38
|
-
public errors: z.ZodError,
|
|
39
|
-
) {
|
|
40
|
-
super(`State validation failed for ${schema}: ${errors.message}`);
|
|
41
|
-
this.name = 'StateValidationError';
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Async Operations
|
|
47
|
-
|
|
48
|
-
- Use `async/await`, not Promises directly
|
|
49
|
-
- Return typed promises: `Promise<T>`
|
|
50
|
-
- Handle rejections with try/catch
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
export async function loadState(path: string): Promise<State> {
|
|
54
|
-
try {
|
|
55
|
-
const content = await fs.readFile(path, 'utf-8');
|
|
56
|
-
return JSON.parse(content);
|
|
57
|
-
} catch (error) {
|
|
58
|
-
throw new StateLoadError(path, error);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Input Validation
|
|
64
|
-
|
|
65
|
-
- Use Zod schemas for all public APIs
|
|
66
|
-
- Validate at module boundaries
|
|
67
|
-
- Return typed results, never `any`
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import { z } from 'zod';
|
|
71
|
-
|
|
72
|
-
const EventSchema = z.object({
|
|
73
|
-
eventId: z.string(),
|
|
74
|
-
timestamp: z.string().datetime(),
|
|
75
|
-
taskId: z.string(),
|
|
76
|
-
actor: z.string(),
|
|
77
|
-
type: z.enum(EVENT_TYPES),
|
|
78
|
-
payload: z.unknown(),
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
export function parseEvent(data: unknown): StateEvent {
|
|
82
|
-
return EventSchema.parse(data);
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## File I/O
|
|
87
|
-
|
|
88
|
-
- Use absolute paths from project root
|
|
89
|
-
- Check file existence before reading
|
|
90
|
-
- Use atomic writes (write to temp, then rename)
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
export async function writeState(path: string, state: State): Promise<void> {
|
|
94
|
-
const tmpPath = `${path}.tmp`;
|
|
95
|
-
await fs.writeFile(tmpPath, JSON.stringify(state, null, 2));
|
|
96
|
-
await fs.rename(tmpPath, path);
|
|
97
|
-
}
|
|
98
|
-
```
|
|
1
|
+
---
|
|
2
|
+
globs:
|
|
3
|
+
- "src/**/*.ts"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# API Design Patterns
|
|
7
|
+
|
|
8
|
+
- **Files:** `kebab-case.ts` | **Types:** `PascalCase` | **Functions:** `camelCase`
|
|
9
|
+
- **Module exports order:** types/interfaces → functions → constants
|
|
10
|
+
- **Errors:** Custom error classes extending `Error` with error codes and context
|
|
11
|
+
- **Async:** Use `async/await` with typed `Promise<T>` returns, try/catch for rejections
|
|
12
|
+
- **Validation:** Zod schemas at all public API boundaries — never return `any`
|
|
13
|
+
- **File I/O:** Absolute paths from project root, check existence, atomic writes (write to temp → rename)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Context Efficiency
|
|
2
|
+
|
|
3
|
+
- **Read selectively:** Use `offset`/`limit` for large files — don't read entire files when only a section matters
|
|
4
|
+
- **Minimize re-reads:** Cache key info in your working memory instead of re-reading the same file multiple times
|
|
5
|
+
- **Short outputs:** Prefer concise tool output — pipe through `head`/`tail` when full output isn't needed
|
|
6
|
+
- **Atomic tasks:** One task = one focused context. Don't accumulate unrelated work in a single session
|
|
7
|
+
- **Summarize early:** After reading large files, extract what you need and move on — don't keep raw content in context
|
|
8
|
+
- **Event hygiene:** Old events auto-archive after 7 days (SessionStart hook). Don't read archived events unless replaying state
|
|
9
|
+
- **File size limits:** CLAUDE.md ~2000t, rules ~1000t each, REQUIREMENTS.md ~4000t, PLAN.md ~6000t (enforced by PostToolUse hook)
|
|
10
|
+
- **Team messages:** Keep inter-agent messages concise — include only actionable information
|