the-frame-ai 0.1.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/LICENSE +21 -0
- package/README.md +335 -0
- package/README.ru.md +333 -0
- package/bin/the-frame +5 -0
- package/bin/the-frame-ai +5 -0
- package/package.json +29 -0
- package/src/cli.js +84 -0
- package/src/doctor.js +164 -0
- package/src/init.js +178 -0
- package/src/languages.js +141 -0
- package/src/manifest.js +55 -0
- package/src/update.js +87 -0
- package/src/utils.js +55 -0
- package/templates/agents/builder.md +240 -0
- package/templates/agents/devils-advocate.md +136 -0
- package/templates/agents/planner.md +277 -0
- package/templates/agents/researcher.md +195 -0
- package/templates/agents/reviewer.md +300 -0
- package/templates/commands/frame:add-task.md +57 -0
- package/templates/commands/frame:build.md +170 -0
- package/templates/commands/frame:check-deps.md +118 -0
- package/templates/commands/frame:checkpoint.md +158 -0
- package/templates/commands/frame:cleanup-memory.md +80 -0
- package/templates/commands/frame:context.md +64 -0
- package/templates/commands/frame:daily.md +77 -0
- package/templates/commands/frame:debug.md +146 -0
- package/templates/commands/frame:doctor.md +170 -0
- package/templates/commands/frame:estimate.md +105 -0
- package/templates/commands/frame:explain.md +84 -0
- package/templates/commands/frame:fast.md +89 -0
- package/templates/commands/frame:forensics.md +139 -0
- package/templates/commands/frame:headless.md +118 -0
- package/templates/commands/frame:health.md +86 -0
- package/templates/commands/frame:init.md +231 -0
- package/templates/commands/frame:migrate.md +107 -0
- package/templates/commands/frame:note.md +32 -0
- package/templates/commands/frame:pause.md +145 -0
- package/templates/commands/frame:performance.md +228 -0
- package/templates/commands/frame:plan.md +198 -0
- package/templates/commands/frame:refactor.md +161 -0
- package/templates/commands/frame:research.md +131 -0
- package/templates/commands/frame:resume.md +137 -0
- package/templates/commands/frame:retrospective.md +196 -0
- package/templates/commands/frame:review.md +174 -0
- package/templates/commands/frame:rollback.md +207 -0
- package/templates/commands/frame:ship.md +148 -0
- package/templates/commands/frame:sprint-check.md +111 -0
- package/templates/commands/frame:status.md +103 -0
- package/templates/commands/frame:unstuck.md +102 -0
- package/templates/commands/frame:wave.md +312 -0
- package/templates/commands/frame:where.md +5 -0
- package/templates/commands/frame:why.md +57 -0
- package/templates/commands/frame:worktree.md +219 -0
- package/templates/hooks/git-safety.sh +33 -0
- package/templates/hooks/quality-gate.sh +52 -0
- package/templates/hooks/safety-net.sh +13 -0
- package/templates/hooks/session-init.sh +81 -0
- package/templates/planning/pause-state.json +1 -0
- package/templates/project/CLAUDE.md +63 -0
- package/templates/project/CONTEXT.md +16 -0
- package/templates/project/MAP.md +35 -0
- package/templates/project/ROADMAP.md +12 -0
- package/templates/project/STATE.md +13 -0
- package/templates/project/config.json +74 -0
- package/templates/project/memory/anti-patterns.md +14 -0
- package/templates/project/memory/context.md +23 -0
- package/templates/project/memory/conventions.md +19 -0
- package/templates/project/memory/decisions.md +20 -0
- package/templates/project/memory/dependencies.md +23 -0
- package/templates/project/memory/metrics.md +22 -0
- package/templates/project/memory/patterns.md +30 -0
- package/templates/project/memory/wins.md +11 -0
- package/templates/project/settings.local.json +50 -0
- package/templates/project/specs/_template/PRD.md +24 -0
- package/templates/project/specs/_template/plan.md +25 -0
- package/templates/project/specs/_template/spec.md +27 -0
- package/templates/project/specs/_template/subagent-prompt.md +43 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# /frame:wave -- Parallel Task Execution
|
|
2
|
+
|
|
3
|
+
> Use for 4+ independent tasks (parallel subagents). For 1–3 tasks → `/frame:build`
|
|
4
|
+
|
|
5
|
+
Identifies dependencies, groups into waves, launches subagents in parallel.
|
|
6
|
+
|
|
7
|
+
**Role**: Orchestrator (not Builder). You coordinate and validate — subagents write the code.
|
|
8
|
+
|
|
9
|
+
## Instructions
|
|
10
|
+
|
|
11
|
+
### Step 0: Fail-fast validation + STATE.md (IN_PROGRESS)
|
|
12
|
+
|
|
13
|
+
**Before doing anything**, check:
|
|
14
|
+
- `.planning/MAP.md` exists — if missing, STOP: "Run /frame:init first — MAP.md not found."
|
|
15
|
+
- `plan.md` exists: `find docs/specs -name "plan.md" | head -1` — if missing, STOP: "No plan.md found. Run /frame:plan first."
|
|
16
|
+
|
|
17
|
+
Then immediately update `.planning/STATE.md`:
|
|
18
|
+
```markdown
|
|
19
|
+
## Current Position
|
|
20
|
+
- Phase: BUILD (wave)
|
|
21
|
+
- Feature: {feature from plan.md}
|
|
22
|
+
- Status: IN_PROGRESS
|
|
23
|
+
- Started: {timestamp}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Orchestrator heartbeat rules:**
|
|
27
|
+
- After each wave completes: "Wave {N}/{total} done — {passed}/{total_tasks} tasks committed"
|
|
28
|
+
- Before launching a wave: "Launching Wave {N}: {task_names}"
|
|
29
|
+
- No more than 3 tool calls in a row without output
|
|
30
|
+
|
|
31
|
+
### Step 1: Find plan.md
|
|
32
|
+
|
|
33
|
+
- `find docs/specs -name "plan.md" | head -5`
|
|
34
|
+
- Read plan.md
|
|
35
|
+
|
|
36
|
+
### Step 2: Determine dependencies
|
|
37
|
+
|
|
38
|
+
For each task, determine:
|
|
39
|
+
- Which files it will change (Files: `path/to/file`)
|
|
40
|
+
- Which other tasks it depends on (Dependencies: Task N)
|
|
41
|
+
- Which wave it belongs to (auto-assigned by dependencies)
|
|
42
|
+
|
|
43
|
+
**Wave algorithm**: no dependencies → Wave 1; depends on Wave N → Wave N+1; depends on multiple waves → max(waves) + 1.
|
|
44
|
+
|
|
45
|
+
If plan.md already has `Wave:` fields — use them. Otherwise compute from dependencies.
|
|
46
|
+
|
|
47
|
+
### Step 3: Group into waves
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
Wave 1: Tasks with no dependencies (parallel)
|
|
51
|
+
Wave 2: Tasks that depend on Wave 1
|
|
52
|
+
Wave 3: Tasks that depend on Wave 2
|
|
53
|
+
...
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Step 4: Check file conflicts and Risk
|
|
57
|
+
|
|
58
|
+
**File Locking Strategy:**
|
|
59
|
+
|
|
60
|
+
1. Each subagent declares files in plan.md
|
|
61
|
+
2. Orchestrator checks for file overlaps between tasks in the same wave
|
|
62
|
+
3. If overlap exists — move tasks to separate waves or merge (sequential)
|
|
63
|
+
4. If no overlap — launch in parallel
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
Subagent 1: Files: [lib/api/client.ts, types/api.ts]
|
|
67
|
+
Subagent 2: Files: [components/chat/message.tsx]
|
|
68
|
+
-> No overlap -> parallel OK
|
|
69
|
+
|
|
70
|
+
Subagent 1: Files: [components/chat/message.tsx]
|
|
71
|
+
Subagent 2: Files: [components/chat/message.tsx]
|
|
72
|
+
-> Overlap -> same wave (sequential) WARNING
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Risk strategy for waves:**
|
|
76
|
+
|
|
77
|
+
Before launching a wave, check task Risk fields:
|
|
78
|
+
|
|
79
|
+
- If the wave contains a `Risk: high` task →
|
|
80
|
+
create a git checkpoint before the wave:
|
|
81
|
+
`git tag frame/pre-wave-{N}-{timestamp}`
|
|
82
|
+
|
|
83
|
+
- If the wave contains more than one `Risk: high` task →
|
|
84
|
+
move them into a separate wave, do not run in parallel
|
|
85
|
+
|
|
86
|
+
### Step 5: Launch Wave N
|
|
87
|
+
|
|
88
|
+
Before launching, update STATE.md:
|
|
89
|
+
```markdown
|
|
90
|
+
- Phase: BUILD (wave)
|
|
91
|
+
- Status: IN_PROGRESS
|
|
92
|
+
- Wave: {N}/{total}
|
|
93
|
+
- Wave Status: Running (0/{count} subagents completed)
|
|
94
|
+
- Started: {timestamp}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
For each task in the wave, launch a subagent via the Agent tool:
|
|
98
|
+
- Fresh context (200K)
|
|
99
|
+
- Self-contained prompt from `docs/specs/_template/subagent-prompt.md`
|
|
100
|
+
- Fill in all placeholders
|
|
101
|
+
|
|
102
|
+
**Maximum 5 subagents** per wave.
|
|
103
|
+
|
|
104
|
+
If a subagent produces no output for more than 2 minutes →
|
|
105
|
+
consider it hung, log in STATE.md: `Wave Status: AGENT_HUNG ({task_name})`
|
|
106
|
+
|
|
107
|
+
### Step 6: Wave Validation (D-step)
|
|
108
|
+
|
|
109
|
+
After all subagents in the wave complete:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
{quality.commands.typecheck}
|
|
113
|
+
{quality.commands.test}
|
|
114
|
+
{quality.commands.lint}
|
|
115
|
+
git log --oneline -5
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Logic:**
|
|
119
|
+
- If **OK** -> proceed to next wave
|
|
120
|
+
- If **FAIL** -> Retry strategy (see below)
|
|
121
|
+
- **Max retries: 2** per wave
|
|
122
|
+
|
|
123
|
+
### Retry strategy
|
|
124
|
+
|
|
125
|
+
On FAIL of wave N:
|
|
126
|
+
1. Identify which tasks failed (via git log — no commit present)
|
|
127
|
+
2. Relaunch ONLY the failed subagents (not the entire wave)
|
|
128
|
+
3. Tasks with a `[DONE]` commit — do not touch
|
|
129
|
+
|
|
130
|
+
### Step 7: Repeat for each wave
|
|
131
|
+
|
|
132
|
+
Repeat Steps 5-6 for each wave until all waves are complete.
|
|
133
|
+
|
|
134
|
+
### Step 8: Final validation
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
{quality.commands.typecheck}
|
|
138
|
+
{quality.commands.test}
|
|
139
|
+
{quality.commands.lint}
|
|
140
|
+
{quality.commands.build}
|
|
141
|
+
git log --oneline -10
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Check completeness:
|
|
145
|
+
```bash
|
|
146
|
+
TOTAL=$(grep -c "^### Task" plan.md)
|
|
147
|
+
DONE=$(grep -c "\[DONE\]" plan.md)
|
|
148
|
+
|
|
149
|
+
if [ "$TOTAL" != "$DONE" ]; then
|
|
150
|
+
echo "⚠️ Unclosed tasks: $((TOTAL - DONE)) of $TOTAL"
|
|
151
|
+
grep "^### Task" plan.md | grep -v "\[DONE\]"
|
|
152
|
+
fi
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Step 9: Update STATE.md and Memory
|
|
156
|
+
|
|
157
|
+
Update `.planning/STATE.md`:
|
|
158
|
+
```markdown
|
|
159
|
+
## Current Position
|
|
160
|
+
- Phase: BUILD
|
|
161
|
+
- Feature: {feature}
|
|
162
|
+
- Task: {completed}/{total}
|
|
163
|
+
- Status: Wave execution complete -- all {N} waves passed
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Memory Updates (if patterns found):**
|
|
167
|
+
|
|
168
|
+
Analyze git log of waves. If recurring solutions are visible → suggest adding to patterns.md:
|
|
169
|
+
|
|
170
|
+
"During the wave, the following patterns were used N times: {pattern}. Add to patterns.md? (y/n)"
|
|
171
|
+
|
|
172
|
+
## Wave Failure Strategy
|
|
173
|
+
|
|
174
|
+
If wave N failed after 2 retries:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
1. git tag frame/wave-failure-{N}
|
|
178
|
+
2. Update STATE.md: Status: WAVE_FAILED, Wave: N
|
|
179
|
+
3. Show the user:
|
|
180
|
+
✅ What succeeded (committed waves 1..N-1)
|
|
181
|
+
❌ What did not complete (wave N and its dependents)
|
|
182
|
+
4. Do NOT auto-rollback previous waves
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
User decides: manual retry, fix and relaunch, or rollback via `/frame:rollback`.
|
|
186
|
+
|
|
187
|
+
## Task format in plan.md
|
|
188
|
+
|
|
189
|
+
```markdown
|
|
190
|
+
### Task 1: API Client
|
|
191
|
+
- Files: `lib/api/client.ts`
|
|
192
|
+
- Dependencies: NONE
|
|
193
|
+
- Wave: 1
|
|
194
|
+
- Risk: low
|
|
195
|
+
|
|
196
|
+
### Task 2: Types
|
|
197
|
+
- Files: `types/api.ts`
|
|
198
|
+
- Dependencies: NONE
|
|
199
|
+
- Wave: 1
|
|
200
|
+
- Risk: low
|
|
201
|
+
|
|
202
|
+
### Task 3: UI Components
|
|
203
|
+
- Files: `components/chat/message.tsx`
|
|
204
|
+
- Dependencies: Task 1 (API Client), Task 2 (Types)
|
|
205
|
+
- Wave: 2
|
|
206
|
+
- Risk: high
|
|
207
|
+
|
|
208
|
+
### Task 4: Tests for API
|
|
209
|
+
- Files: `lib/api/__tests__/client.test.ts`
|
|
210
|
+
- Dependencies: Task 1
|
|
211
|
+
- Wave: 2
|
|
212
|
+
- Risk: low
|
|
213
|
+
|
|
214
|
+
### Task 5: Tests for UI
|
|
215
|
+
- Files: `components/chat/__tests__/message.test.tsx`
|
|
216
|
+
- Dependencies: Task 3
|
|
217
|
+
- Wave: 3
|
|
218
|
+
- Risk: low
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Subagent Prompt Template
|
|
222
|
+
|
|
223
|
+
See `docs/specs/_template/subagent-prompt.md` for the full template with placeholders.
|
|
224
|
+
|
|
225
|
+
Brief version for quick use:
|
|
226
|
+
|
|
227
|
+
```markdown
|
|
228
|
+
# Task: {task_name}
|
|
229
|
+
|
|
230
|
+
## Memory (read before writing code)
|
|
231
|
+
- .planning/memory/anti-patterns.md — what to avoid
|
|
232
|
+
- .planning/memory/conventions.md — how to write code
|
|
233
|
+
- .planning/memory/dependencies.md — stack + Avoid list
|
|
234
|
+
- docs/specs/{FEATURE}/research.md → Memory Impact section
|
|
235
|
+
|
|
236
|
+
## Heartbeat rules
|
|
237
|
+
- After each D-step: "✓ Task {name}: {step} confirmed"
|
|
238
|
+
- Before a long command: "⏳ Running: {command}"
|
|
239
|
+
- No more than 3 tools in a row without output
|
|
240
|
+
|
|
241
|
+
## Context
|
|
242
|
+
- Project: {project_name}
|
|
243
|
+
- MAP: See .planning/MAP.md
|
|
244
|
+
- Spec: See docs/specs/{FEATURE}/spec.md
|
|
245
|
+
- Task: {task_description}
|
|
246
|
+
|
|
247
|
+
## Your Role: Builder
|
|
248
|
+
1. Write TEST first (RED)
|
|
249
|
+
2. Verify: `{quality.commands.test} {test_file}`
|
|
250
|
+
3. Write CODE (GREEN)
|
|
251
|
+
4. Verify: `{quality.commands.test} {test_file}`
|
|
252
|
+
5. Refactor if needed
|
|
253
|
+
6. Verify types: `{quality.commands.typecheck}`
|
|
254
|
+
7. Verify lint: `{quality.commands.lint}`
|
|
255
|
+
8. Git commit: `{type}({scope}): {description}`
|
|
256
|
+
|
|
257
|
+
## Key Files
|
|
258
|
+
- {file1} -- {purpose}
|
|
259
|
+
- {file2} -- {purpose}
|
|
260
|
+
|
|
261
|
+
## Patterns
|
|
262
|
+
- Follow project conventions from CLAUDE.md
|
|
263
|
+
- Errors: use project error reporting (no console.log for errors)
|
|
264
|
+
- Types: no `any` (use `unknown` + type guard)
|
|
265
|
+
|
|
266
|
+
## DO NOT
|
|
267
|
+
- Do not modify files outside {scope_dir}
|
|
268
|
+
- Do not skip tests
|
|
269
|
+
- Do not use `any` type
|
|
270
|
+
- Do not add dependencies without approval
|
|
271
|
+
- Do not commit without passing quality gates
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Rules
|
|
275
|
+
|
|
276
|
+
- **Maximum 5 subagents** per wave
|
|
277
|
+
- **File locking** -- check conflicts before launching a wave
|
|
278
|
+
- **Risk strategy** -- isolate high-risk tasks into a separate wave + git tag
|
|
279
|
+
- **STATE.md before launch** -- update IN_PROGRESS before each wave
|
|
280
|
+
- **Subagent heartbeat** -- >2 min without output = hung
|
|
281
|
+
- **Wave validation** -- D-step (tsc + vitest + eslint) between waves
|
|
282
|
+
- **Retry only failed** -- max 2 retries, only tasks without [DONE] commit
|
|
283
|
+
- **Wave Failure Strategy** -- git tag + STATE.md + report to user, do not auto-rollback
|
|
284
|
+
- **Completeness check** -- grep plan.md before final report
|
|
285
|
+
- **Fresh context** -- each subagent gets an isolated 200K context
|
|
286
|
+
- **Waves are sequential** -- waves do not run in parallel, only tasks within a wave do
|
|
287
|
+
|
|
288
|
+
## Algorithm (summary)
|
|
289
|
+
|
|
290
|
+
1. Read plan.md
|
|
291
|
+
2. Determine dependencies
|
|
292
|
+
3. Group into waves (independent batches)
|
|
293
|
+
4. Check file conflicts + Risk
|
|
294
|
+
5. Update STATE.md (IN_PROGRESS)
|
|
295
|
+
6. Launch subagents in parallel (max 5)
|
|
296
|
+
7. Wait for all subagents (heartbeat monitoring)
|
|
297
|
+
8. Run wave validation (tsc + vitest + eslint)
|
|
298
|
+
9. If OK -> next wave
|
|
299
|
+
10. If FAIL -> retry only failed tasks (max 2)
|
|
300
|
+
11. If FAIL after 2 retries -> Wave Failure Strategy
|
|
301
|
+
12. Repeat until all waves are complete
|
|
302
|
+
13. Final validation + completeness check
|
|
303
|
+
14. Update STATE.md + suggest Memory Updates
|
|
304
|
+
|
|
305
|
+
## Result
|
|
306
|
+
|
|
307
|
+
- Tasks executed in parallel
|
|
308
|
+
- Waves separated by dependencies
|
|
309
|
+
- High-risk tasks isolated + git checkpoint
|
|
310
|
+
- Quality gates passed between waves
|
|
311
|
+
- `.planning/STATE.md` updated
|
|
312
|
+
- Patterns suggested for memory
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# /frame:why -- Search Decision History
|
|
2
|
+
|
|
3
|
+
Find why a decision was made. Searches memory and git history.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Search for: **$ARGUMENTS**
|
|
8
|
+
|
|
9
|
+
### Step 1: Search memory files
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
grep -i "$ARGUMENTS" .planning/memory/decisions.md 2>/dev/null
|
|
13
|
+
grep -i "$ARGUMENTS" .planning/memory/anti-patterns.md 2>/dev/null
|
|
14
|
+
grep -i "$ARGUMENTS" .planning/memory/patterns.md 2>/dev/null
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Step 2: Search git history
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
git log --oneline --grep="$ARGUMENTS" | head -10
|
|
21
|
+
git log --oneline -S "$ARGUMENTS" | head -10
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Step 3: Search forensics reports
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
grep -rl "$ARGUMENTS" .planning/forensics/ 2>/dev/null | head -5
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Step 4: Output results
|
|
31
|
+
|
|
32
|
+
Format findings:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
Search: "{keyword}"
|
|
36
|
+
|
|
37
|
+
DECISIONS:
|
|
38
|
+
[DEC-XXX] {title} — {date}
|
|
39
|
+
Context: {why it was needed}
|
|
40
|
+
Decision: {what was decided}
|
|
41
|
+
|
|
42
|
+
ANTI-PATTERNS:
|
|
43
|
+
{pattern name} — {why it's bad}
|
|
44
|
+
|
|
45
|
+
GIT HISTORY:
|
|
46
|
+
{hash} {commit message}
|
|
47
|
+
|
|
48
|
+
FORENSICS:
|
|
49
|
+
{report file} — {issue title}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
If nothing found: "No records found for '{keyword}'. Check spelling or try a broader term."
|
|
53
|
+
|
|
54
|
+
## Rules
|
|
55
|
+
|
|
56
|
+
- Read-only — never modify files
|
|
57
|
+
- If $ARGUMENTS is empty: "Provide a keyword. Example: /frame:why auth"
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# /frame:worktree -- Git Worktrees
|
|
2
|
+
|
|
3
|
+
Manage isolated git worktrees for parallel work.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Command: **$ARGUMENTS**
|
|
8
|
+
|
|
9
|
+
### Step 0: Fail-fast validation
|
|
10
|
+
|
|
11
|
+
Before routing, run these checks:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git rev-parse --git-dir 2>/dev/null || echo "NOT_GIT"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
If output is `NOT_GIT` — STOP with error: "Error: not a git repository. Run this command from inside a git project."
|
|
18
|
+
|
|
19
|
+
For `create`: if `$NAME` is empty — STOP with error: "Error: name is required. Usage: /frame:worktree create <name>"
|
|
20
|
+
|
|
21
|
+
### Routing
|
|
22
|
+
|
|
23
|
+
Determine action from the first argument:
|
|
24
|
+
- `create <name>` -- create a worktree
|
|
25
|
+
- `list` -- show all worktrees
|
|
26
|
+
- `switch <name>` -- switch to a worktree
|
|
27
|
+
- `cleanup <name>` -- remove a worktree
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Action: create
|
|
32
|
+
|
|
33
|
+
Create a git worktree with an isolated workspace.
|
|
34
|
+
|
|
35
|
+
Update STATE.md at the start:
|
|
36
|
+
```markdown
|
|
37
|
+
## Current Position
|
|
38
|
+
- Phase: WORKTREE
|
|
39
|
+
- Action: create
|
|
40
|
+
- Name: feature/{name}
|
|
41
|
+
- Status: IN_PROGRESS
|
|
42
|
+
- Started: {timestamp}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Step 1: Validate Name
|
|
46
|
+
|
|
47
|
+
Name must follow git branch naming conventions:
|
|
48
|
+
```bash
|
|
49
|
+
echo "$NAME" | grep -E '^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Check if worktree already exists:
|
|
53
|
+
```bash
|
|
54
|
+
git worktree list | grep "feature/$NAME" && echo "EXISTS"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If EXISTS — STOP with error: "Error: worktree feature/{name} already exists."
|
|
58
|
+
|
|
59
|
+
### Step 2: Create Worktree
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git worktree add ../$(basename $(pwd))-$NAME -b feature/$NAME
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Say: "Worktree created, copying context..."
|
|
66
|
+
|
|
67
|
+
### Step 3: Copy Context
|
|
68
|
+
|
|
69
|
+
Copy planning context into the new worktree:
|
|
70
|
+
```bash
|
|
71
|
+
mkdir -p ../$(basename $(pwd))-$NAME/.planning
|
|
72
|
+
cp .planning/STATE.md ../$(basename $(pwd))-$NAME/.planning/
|
|
73
|
+
cp .planning/CONTEXT.md ../$(basename $(pwd))-$NAME/.planning/ 2>/dev/null || true
|
|
74
|
+
cp .planning/MAP.md ../$(basename $(pwd))-$NAME/.planning/ 2>/dev/null || true
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Step 4: Show Result
|
|
78
|
+
|
|
79
|
+
Update STATE.md:
|
|
80
|
+
```markdown
|
|
81
|
+
## Current Position
|
|
82
|
+
- Phase: WORKTREE
|
|
83
|
+
- Action: create
|
|
84
|
+
- Name: feature/{name}
|
|
85
|
+
- Status: COMPLETE
|
|
86
|
+
- Started: {timestamp}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
+======================================================================+
|
|
91
|
+
| WORKTREE CREATED |
|
|
92
|
+
+======================================================================+
|
|
93
|
+
| Name: feature/{name} |
|
|
94
|
+
| Path: ../{project}-{name} |
|
|
95
|
+
| Branch: feature/{name} |
|
|
96
|
+
| |
|
|
97
|
+
| cd ../{project}-{name} to work in isolation |
|
|
98
|
+
+======================================================================+
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Action: list
|
|
104
|
+
|
|
105
|
+
Show all active worktrees.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
git worktree list
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Output
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
+======================================================================+
|
|
115
|
+
| GIT WORKTREES |
|
|
116
|
+
+======================================================================+
|
|
117
|
+
| {path} [{branch}] |
|
|
118
|
+
| {path} [{branch}] |
|
|
119
|
+
+======================================================================+
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Action: switch
|
|
125
|
+
|
|
126
|
+
Switch to another worktree.
|
|
127
|
+
|
|
128
|
+
Show the path and instructions:
|
|
129
|
+
```bash
|
|
130
|
+
WORKTREE_PATH="../$(basename $(pwd))-$NAME"
|
|
131
|
+
if git worktree list | grep -q "feature/$NAME"; then
|
|
132
|
+
echo "WORKTREE_PATH=$WORKTREE_PATH"
|
|
133
|
+
else
|
|
134
|
+
echo "ERROR: worktree feature/$NAME not found"
|
|
135
|
+
git worktree list
|
|
136
|
+
fi
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Tell the user: "Open a new terminal and run: `cd $WORKTREE_PATH`"
|
|
140
|
+
|
|
141
|
+
Show current status of the worktree:
|
|
142
|
+
```bash
|
|
143
|
+
git -C "../$(basename $(pwd))-$NAME" status 2>/dev/null
|
|
144
|
+
git -C "../$(basename $(pwd))-$NAME" branch --show-current 2>/dev/null
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Action: cleanup
|
|
150
|
+
|
|
151
|
+
Remove a worktree and branch.
|
|
152
|
+
|
|
153
|
+
Update STATE.md at the start:
|
|
154
|
+
```markdown
|
|
155
|
+
## Current Position
|
|
156
|
+
- Phase: WORKTREE
|
|
157
|
+
- Action: cleanup
|
|
158
|
+
- Name: feature/{name}
|
|
159
|
+
- Status: IN_PROGRESS
|
|
160
|
+
- Started: {timestamp}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Step 1: Check Status
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
cd ../{project}-{name}
|
|
167
|
+
git status --short
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
If there are uncommitted changes, warn the user and request confirmation.
|
|
171
|
+
|
|
172
|
+
### Step 2: Remove Worktree
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
cd {project-root}
|
|
176
|
+
git worktree remove ../{project}-{name}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Step 3: Remove Branch (optional)
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
git branch -d feature/{name}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Step 4: Show Result
|
|
186
|
+
|
|
187
|
+
Update STATE.md:
|
|
188
|
+
```markdown
|
|
189
|
+
## Current Position
|
|
190
|
+
- Phase: WORKTREE
|
|
191
|
+
- Action: cleanup
|
|
192
|
+
- Name: feature/{name}
|
|
193
|
+
- Status: COMPLETE
|
|
194
|
+
- Started: {timestamp}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
+======================================================================+
|
|
199
|
+
| WORKTREE REMOVED |
|
|
200
|
+
+======================================================================+
|
|
201
|
+
| Name: feature/{name} |
|
|
202
|
+
| Path: removed |
|
|
203
|
+
| Branch: {deleted or kept} |
|
|
204
|
+
+======================================================================+
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Rules
|
|
208
|
+
|
|
209
|
+
- **WorktreeBase**: `../` -- sibling directories
|
|
210
|
+
- **Branch naming**: `feature/{name}`
|
|
211
|
+
- **Context copy**: STATE.md, CONTEXT.md, MAP.md
|
|
212
|
+
- **Cleanup**: check for uncommitted changes before removal
|
|
213
|
+
- **Never force** -- warn about potential work loss
|
|
214
|
+
|
|
215
|
+
## Result
|
|
216
|
+
|
|
217
|
+
- Git worktree created/removed
|
|
218
|
+
- Branch created/removed
|
|
219
|
+
- Context copied
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# git-safety.sh - Blocks dangerous git commands
|
|
3
|
+
|
|
4
|
+
read -r input
|
|
5
|
+
|
|
6
|
+
COMMAND=$(node -e "try{const i=JSON.parse(process.argv[1]);process.stdout.write(i.tool_input?.command??'')}catch{}" -- "$input" 2>/dev/null)
|
|
7
|
+
|
|
8
|
+
deny() {
|
|
9
|
+
node -e "process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:'PreToolUse',permissionDecision:'deny',permissionDecisionReason:process.argv[1]}}))" -- "$1"
|
|
10
|
+
exit 0
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
ask() {
|
|
14
|
+
node -e "process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:'PreToolUse',permissionDecision:'ask',permissionDecisionReason:process.argv[1]}}))" -- "$1"
|
|
15
|
+
exit 0
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if echo "$COMMAND" | grep -qiE 'git\s+push.*(--force|-f)' && ! echo "$COMMAND" | grep -q 'force-with-lease'; then
|
|
19
|
+
deny "FRAME Git Safety: Force push blocked. Use regular push. If you must force push, do it manually."
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if echo "$COMMAND" | grep -qiE 'git\s+reset\s+--hard'; then
|
|
23
|
+
if [ "${FRAME_INTERNAL}" = "1" ]; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
deny "FRAME Git Safety: git reset --hard blocked. Use git restore or git stash instead."
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if echo "$COMMAND" | grep -qiE 'git\s+add\s+(-A|\.)'; then
|
|
30
|
+
ask "FRAME Git Safety: FRAME recommends using specific files instead of git add -A. Are you sure you want to add all files?"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
exit 0
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# quality-gate.sh - Runs typecheck and lint after file changes
|
|
3
|
+
|
|
4
|
+
FILE_PATH=$(node -e "try{const i=JSON.parse(process.env.CLAUDE_TOOL_INPUT||'{}');process.stdout.write(i.file_path||i.path||'')}catch{}" 2>/dev/null)
|
|
5
|
+
|
|
6
|
+
if [ -z "$FILE_PATH" ]; then
|
|
7
|
+
exit 0
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
# Read quality commands from config if available
|
|
11
|
+
TYPECHECK_CMD=""
|
|
12
|
+
LINT_CMD=""
|
|
13
|
+
if [ -f ".frame/config.json" ] && command -v node &>/dev/null; then
|
|
14
|
+
CONFIG_VALID=$(node -e "try{JSON.parse(require('fs').readFileSync('.frame/config.json','utf8'));process.stdout.write('ok')}catch(e){process.stdout.write('invalid: '+e.message)}" 2>/dev/null)
|
|
15
|
+
if [ "$CONFIG_VALID" != "ok" ]; then
|
|
16
|
+
echo "FRAME Quality Gate: .frame/config.json is malformed — $CONFIG_VALID"
|
|
17
|
+
echo "Fix it or run /frame:doctor to diagnose."
|
|
18
|
+
exit 0
|
|
19
|
+
fi
|
|
20
|
+
TYPECHECK_CMD=$(node -e "try{const c=JSON.parse(require('fs').readFileSync('.frame/config.json','utf8'));process.stdout.write(c.quality?.commands?.typecheck||'')}catch{}" 2>/dev/null)
|
|
21
|
+
LINT_CMD=$(node -e "try{const c=JSON.parse(require('fs').readFileSync('.frame/config.json','utf8'));process.stdout.write(c.quality?.commands?.lint||'')}catch{}" 2>/dev/null)
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Fallback: TypeScript-only detection
|
|
25
|
+
if [ -z "$TYPECHECK_CMD" ] && echo "$FILE_PATH" | grep -qiE '\.(ts|tsx)$'; then
|
|
26
|
+
TYPECHECK_CMD="npx tsc --noEmit"
|
|
27
|
+
LINT_CMD="npx eslint --fix \"$FILE_PATH\""
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
run_cmd() {
|
|
31
|
+
local cmd="$1"
|
|
32
|
+
# Only allow commands starting with known safe prefixes
|
|
33
|
+
if ! echo "$cmd" | grep -qE '^(npx |npm run |yarn |pnpm |node |tsc |eslint |biome |deno )'; then
|
|
34
|
+
echo "FRAME Quality Gate: command not in allowlist, skipping: $cmd"
|
|
35
|
+
return 0
|
|
36
|
+
fi
|
|
37
|
+
sh -c "$cmd" 2>/dev/null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if [ -n "$TYPECHECK_CMD" ]; then
|
|
41
|
+
if ! run_cmd "$TYPECHECK_CMD"; then
|
|
42
|
+
echo "FRAME Quality Gate: typecheck failed for $FILE_PATH"
|
|
43
|
+
fi
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [ -n "$LINT_CMD" ]; then
|
|
47
|
+
if ! run_cmd "$LINT_CMD"; then
|
|
48
|
+
echo "FRAME Quality Gate: lint issues in $FILE_PATH"
|
|
49
|
+
fi
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
exit 0
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# safety-net.sh - Blocks dangerous bash commands
|
|
3
|
+
|
|
4
|
+
read -r input
|
|
5
|
+
|
|
6
|
+
COMMAND=$(node -e "try{const i=JSON.parse(process.argv[1]);process.stdout.write(i.tool_input?.command??'')}catch{}" -- "$input" 2>/dev/null)
|
|
7
|
+
|
|
8
|
+
if echo "$COMMAND" | grep -qiE 'rm\s+-[a-zA-Z]*r[a-zA-Z]*f|rm\s+--recursive.*--force|rm\s+--force.*--recursive|\bDROP\s+(TABLE|DATABASE)\b'; then
|
|
9
|
+
node -e "process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:'PreToolUse',permissionDecision:'deny',permissionDecisionReason:'FRAME Safety Net: Destructive command blocked (rm -rf or DROP TABLE/DATABASE). Use specific, safer alternatives.'}}))"
|
|
10
|
+
exit 0
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
exit 0
|