specflow-cc 1.13.0 → 1.14.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/CHANGELOG.md +49 -0
- package/README.md +16 -9
- package/agents/spec-creator.md +3 -2
- package/agents/spec-splitter.md +1 -1
- package/bin/install.js +20 -0
- package/bin/lib/config.cjs +91 -0
- package/bin/lib/core.cjs +120 -0
- package/bin/lib/spec.cjs +130 -0
- package/bin/lib/state.cjs +241 -0
- package/bin/lib/verify.cjs +117 -0
- package/bin/sf-tools.cjs +103 -0
- package/commands/sf/done.md +21 -2
- package/commands/sf/health.md +220 -0
- package/commands/sf/help.md +2 -0
- package/commands/sf/split.md +14 -3
- package/commands/sf/validate.md +154 -0
- package/hooks/context-monitor.js +121 -0
- package/hooks/statusline.js +17 -0
- package/package.json +1 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sf:health
|
|
3
|
+
description: Diagnose .specflow/ directory health and optionally repair issues
|
|
4
|
+
argument-hint: [--repair]
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Bash
|
|
8
|
+
- Glob
|
|
9
|
+
- Write
|
|
10
|
+
- AskUserQuestion
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<purpose>
|
|
14
|
+
Validate `.specflow/` directory integrity and report actionable issues. Checks for missing files, invalid state, orphaned specs, and queue inconsistencies. Optionally repairs auto-fixable issues.
|
|
15
|
+
</purpose>
|
|
16
|
+
|
|
17
|
+
<context>
|
|
18
|
+
@.specflow/STATE.md
|
|
19
|
+
</context>
|
|
20
|
+
|
|
21
|
+
<workflow>
|
|
22
|
+
|
|
23
|
+
## Step 1: Verify Initialization
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
[ -d .specflow ] && echo "OK" || echo "NOT_INITIALIZED"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**If NOT_INITIALIZED:**
|
|
30
|
+
```
|
|
31
|
+
SpecFlow not initialized. Run `/sf:init` first.
|
|
32
|
+
```
|
|
33
|
+
Exit.
|
|
34
|
+
|
|
35
|
+
## Step 2: Parse Arguments
|
|
36
|
+
|
|
37
|
+
Check if `--repair` flag is present.
|
|
38
|
+
|
|
39
|
+
## Step 3: Run Health Checks
|
|
40
|
+
|
|
41
|
+
Initialize collectors:
|
|
42
|
+
- `errors[]` — critical issues
|
|
43
|
+
- `warnings[]` — non-critical issues
|
|
44
|
+
- `info[]` — informational notes
|
|
45
|
+
- `repairs[]` — actions taken (if --repair)
|
|
46
|
+
|
|
47
|
+
### 3.1 Core Files Check
|
|
48
|
+
|
|
49
|
+
| Check | File | Severity | Repairable |
|
|
50
|
+
|-------|------|----------|------------|
|
|
51
|
+
| E001 | `.specflow/STATE.md` missing | error | Yes — regenerate minimal STATE.md |
|
|
52
|
+
| E002 | `.specflow/STATE.md` missing `## Queue` section | error | No |
|
|
53
|
+
| W001 | `.specflow/todos/TODO.md` missing | warning | Yes — create empty TODO.md |
|
|
54
|
+
| W002 | `.specflow/specs/` directory missing | warning | Yes — create directory |
|
|
55
|
+
| W003 | `.specflow/archive/` directory missing | warning | Yes — create directory |
|
|
56
|
+
| W004 | `.specflow/execution/` directory missing | warning | Yes — create directory |
|
|
57
|
+
|
|
58
|
+
For each check:
|
|
59
|
+
1. Test existence
|
|
60
|
+
2. If missing and repairable and `--repair`: fix it, add to `repairs[]`
|
|
61
|
+
3. If missing and not repairable: add to `errors[]` or `warnings[]`
|
|
62
|
+
|
|
63
|
+
### 3.2 STATE.md Integrity
|
|
64
|
+
|
|
65
|
+
Read STATE.md and validate:
|
|
66
|
+
|
|
67
|
+
**E003: Active spec references non-existent file**
|
|
68
|
+
- Extract active spec ID from `## Active Specification`
|
|
69
|
+
- If not "—" or empty, check `.specflow/specs/{ID}.md` exists
|
|
70
|
+
- If missing: error (repairable — clear active spec to "—")
|
|
71
|
+
|
|
72
|
+
**W005: Queue references non-existent spec file**
|
|
73
|
+
- Parse queue table rows
|
|
74
|
+
- For each spec ID, verify `.specflow/specs/{ID}.md` or `.specflow/archive/{ID}.md` exists
|
|
75
|
+
- If missing: warning (not repairable — spec may have been manually deleted)
|
|
76
|
+
|
|
77
|
+
**W006: Queue has duplicate spec IDs**
|
|
78
|
+
- Parse all IDs from queue table
|
|
79
|
+
- If duplicates found: warning (not repairable)
|
|
80
|
+
|
|
81
|
+
**W007: STATUS.md malformed — missing required sections**
|
|
82
|
+
- Check for: `## Active Specification`, `## Queue`
|
|
83
|
+
- If missing: warning
|
|
84
|
+
|
|
85
|
+
### 3.3 Orphaned Specs Check
|
|
86
|
+
|
|
87
|
+
**I001: Spec file exists but not in queue or archive**
|
|
88
|
+
- List all files in `.specflow/specs/`
|
|
89
|
+
- For each, check if its ID appears in STATE.md queue
|
|
90
|
+
- If not: info (may be work-in-progress)
|
|
91
|
+
|
|
92
|
+
### 3.4 Archive Consistency
|
|
93
|
+
|
|
94
|
+
**I002: Spec in queue marked as completed but not archived**
|
|
95
|
+
- Parse queue for specs with status containing "done" or "complete"
|
|
96
|
+
- Check if file exists in `.specflow/archive/`
|
|
97
|
+
- If not: info
|
|
98
|
+
|
|
99
|
+
### 3.5 Execution State
|
|
100
|
+
|
|
101
|
+
**W008: Stale execution state files**
|
|
102
|
+
- List `.specflow/execution/*.json`
|
|
103
|
+
- For each, check if the spec ID is the currently active spec
|
|
104
|
+
- If not active: warning (repairable — delete stale files)
|
|
105
|
+
|
|
106
|
+
## Step 4: Calculate Status
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
HEALTHY = 0 errors, 0 warnings
|
|
110
|
+
DEGRADED = 0 errors, 1+ warnings
|
|
111
|
+
BROKEN = 1+ errors
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Step 5: Display Results
|
|
115
|
+
|
|
116
|
+
**IMPORTANT:** Output the following directly as formatted text, NOT wrapped in a markdown code block:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
120
|
+
SpecFlow Health Check
|
|
121
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
122
|
+
|
|
123
|
+
**Status:** HEALTHY | DEGRADED | BROKEN
|
|
124
|
+
**Errors:** N | **Warnings:** N | **Info:** N
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**If repairs were performed:**
|
|
128
|
+
```
|
|
129
|
+
### Repairs Performed
|
|
130
|
+
|
|
131
|
+
- Repaired: {description}
|
|
132
|
+
- Repaired: {description}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**If errors exist:**
|
|
136
|
+
```
|
|
137
|
+
### Errors
|
|
138
|
+
|
|
139
|
+
- [{code}] {message}
|
|
140
|
+
Fix: {suggested fix}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**If warnings exist:**
|
|
144
|
+
```
|
|
145
|
+
### Warnings
|
|
146
|
+
|
|
147
|
+
- [{code}] {message}
|
|
148
|
+
Fix: {suggested fix}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**If info exists:**
|
|
152
|
+
```
|
|
153
|
+
### Info
|
|
154
|
+
|
|
155
|
+
- [{code}] {message}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Footer (if repairable issues exist and --repair was NOT used):**
|
|
159
|
+
```
|
|
160
|
+
---
|
|
161
|
+
{N} issues can be auto-repaired. Run: `/sf:health --repair`
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Step 6: Offer Repair (if applicable)
|
|
165
|
+
|
|
166
|
+
If repairable issues found and `--repair` was NOT passed:
|
|
167
|
+
- Ask user if they want to run repairs
|
|
168
|
+
- If yes, execute repairs and re-run checks to verify
|
|
169
|
+
|
|
170
|
+
## Step 7: Verify Repairs (if --repair was used)
|
|
171
|
+
|
|
172
|
+
Re-run all checks without repair to confirm resolution.
|
|
173
|
+
Display final status.
|
|
174
|
+
|
|
175
|
+
</workflow>
|
|
176
|
+
|
|
177
|
+
<error_codes>
|
|
178
|
+
|
|
179
|
+
| Code | Severity | Description | Repairable |
|
|
180
|
+
|------|----------|-------------|------------|
|
|
181
|
+
| E001 | error | STATE.md not found | Yes |
|
|
182
|
+
| E002 | error | STATE.md missing Queue section | No |
|
|
183
|
+
| E003 | error | Active spec references non-existent file | Yes |
|
|
184
|
+
| W001 | warning | TODO.md not found | Yes |
|
|
185
|
+
| W002 | warning | specs/ directory missing | Yes |
|
|
186
|
+
| W003 | warning | archive/ directory missing | Yes |
|
|
187
|
+
| W004 | warning | execution/ directory missing | Yes |
|
|
188
|
+
| W005 | warning | Queue references non-existent spec | No |
|
|
189
|
+
| W006 | warning | Queue has duplicate spec IDs | No |
|
|
190
|
+
| W007 | warning | STATE.md missing required sections | No |
|
|
191
|
+
| W008 | warning | Stale execution state files | Yes |
|
|
192
|
+
| I001 | info | Spec not in queue (may be WIP) | No |
|
|
193
|
+
| I002 | info | Completed spec not archived | No |
|
|
194
|
+
|
|
195
|
+
</error_codes>
|
|
196
|
+
|
|
197
|
+
<repair_actions>
|
|
198
|
+
|
|
199
|
+
| Action | Effect | Risk |
|
|
200
|
+
|--------|--------|------|
|
|
201
|
+
| Create STATE.md | Minimal template with empty queue | None |
|
|
202
|
+
| Create TODO.md | Empty TODO template | None |
|
|
203
|
+
| Create directories | specs/, archive/, execution/ | None |
|
|
204
|
+
| Clear active spec | Set to "—" if spec file missing | Loses active reference |
|
|
205
|
+
| Delete stale execution | Remove orphaned .json state files | None |
|
|
206
|
+
|
|
207
|
+
**Not repairable (too risky):**
|
|
208
|
+
- STATE.md content/queue entries
|
|
209
|
+
- Spec file contents
|
|
210
|
+
- Archive decisions
|
|
211
|
+
|
|
212
|
+
</repair_actions>
|
|
213
|
+
|
|
214
|
+
<success_criteria>
|
|
215
|
+
- [ ] All health checks executed
|
|
216
|
+
- [ ] Clear status reported (HEALTHY/DEGRADED/BROKEN)
|
|
217
|
+
- [ ] Each issue has error code and suggested fix
|
|
218
|
+
- [ ] --repair only fixes safe, non-destructive issues
|
|
219
|
+
- [ ] Repairs verified by re-running checks
|
|
220
|
+
</success_criteria>
|
package/commands/sf/help.md
CHANGED
|
@@ -226,6 +226,7 @@ Workflow: Spec → Audit → Revise → Run → Review → Fix → Done
|
|
|
226
226
|
| /sf:revise | Revise spec based on audit feedback |
|
|
227
227
|
| /sf:run | Execute specification |
|
|
228
228
|
| /sf:review | Review implementation (fresh context) |
|
|
229
|
+
| /sf:validate | Run validation checklist from spec |
|
|
229
230
|
| /sf:verify | Interactive human verification |
|
|
230
231
|
| /sf:fix | Fix implementation based on review |
|
|
231
232
|
| /sf:done | Finalize, commit, and archive |
|
|
@@ -288,6 +289,7 @@ Workflow: Spec → Audit → Revise → Run → Review → Fix → Done
|
|
|
288
289
|
| Command | Description |
|
|
289
290
|
|--------------|-----------------------------------------|
|
|
290
291
|
| /sf:help | This help (or detailed: /sf:help new) |
|
|
292
|
+
| /sf:health | Diagnose .specflow/ integrity (--repair)|
|
|
291
293
|
| /sf:history | View completed specifications |
|
|
292
294
|
| /sf:metrics | Project statistics and insights |
|
|
293
295
|
|
package/commands/sf/split.md
CHANGED
|
@@ -201,9 +201,20 @@ Your choice [1]:
|
|
|
201
201
|
|
|
202
202
|
**If 1 (Accept):**
|
|
203
203
|
- Agent creates child specifications
|
|
204
|
-
- Agent archives parent
|
|
205
|
-
-
|
|
206
|
-
|
|
204
|
+
- Agent archives parent and updates STATE.md
|
|
205
|
+
- **Verify archival:** After agent completes, check if parent was moved:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
if [ -f ".specflow/specs/{ID}.md" ]; then
|
|
209
|
+
mkdir -p .specflow/archive
|
|
210
|
+
mv ".specflow/specs/{ID}.md" ".specflow/archive/{ID}.md"
|
|
211
|
+
echo "ARCHIVED"
|
|
212
|
+
else
|
|
213
|
+
echo "ALREADY_ARCHIVED"
|
|
214
|
+
fi
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
- Continue to Step 8
|
|
207
218
|
|
|
208
219
|
**If 2 (Modify):**
|
|
209
220
|
- Use AskUserQuestion to get modifications
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sf:validate
|
|
3
|
+
description: Validate implementation against spec's validation checklist
|
|
4
|
+
argument-hint: [SPEC-XXX]
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Bash
|
|
8
|
+
- Glob
|
|
9
|
+
- Grep
|
|
10
|
+
- Write
|
|
11
|
+
- AskUserQuestion
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<purpose>
|
|
15
|
+
Run the validation checklist from a specification after implementation. Executes automated checks (test commands, grep verifications) and prompts for manual checks. Reports pass/fail per item and overall validation status.
|
|
16
|
+
</purpose>
|
|
17
|
+
|
|
18
|
+
<context>
|
|
19
|
+
@.specflow/STATE.md
|
|
20
|
+
</context>
|
|
21
|
+
|
|
22
|
+
<workflow>
|
|
23
|
+
|
|
24
|
+
## Step 1: Verify Initialization
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
[ -d .specflow ] && echo "OK" || echo "NOT_INITIALIZED"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**If NOT_INITIALIZED:**
|
|
31
|
+
```
|
|
32
|
+
SpecFlow not initialized. Run `/sf:init` first.
|
|
33
|
+
```
|
|
34
|
+
Exit.
|
|
35
|
+
|
|
36
|
+
## Step 2: Resolve Specification
|
|
37
|
+
|
|
38
|
+
**If SPEC-XXX argument provided:** Use that spec.
|
|
39
|
+
**If no argument:** Use active spec from STATE.md.
|
|
40
|
+
|
|
41
|
+
Read the spec file from `.specflow/specs/SPEC-XXX.md`.
|
|
42
|
+
|
|
43
|
+
**If spec not found:** Check `.specflow/archive/SPEC-XXX.md`.
|
|
44
|
+
|
|
45
|
+
**If still not found:**
|
|
46
|
+
```
|
|
47
|
+
Specification {ID} not found.
|
|
48
|
+
```
|
|
49
|
+
Exit.
|
|
50
|
+
|
|
51
|
+
## Step 3: Extract Validation Checklist
|
|
52
|
+
|
|
53
|
+
Look for `## Validation Checklist` section in the spec.
|
|
54
|
+
|
|
55
|
+
**If section not found:**
|
|
56
|
+
```
|
|
57
|
+
No validation checklist found in {ID}.
|
|
58
|
+
|
|
59
|
+
The spec was created without a validation checklist.
|
|
60
|
+
You can:
|
|
61
|
+
1. Add one manually to the spec
|
|
62
|
+
2. Use `/sf:verify` for interactive human verification
|
|
63
|
+
3. Use `/sf:review` for AI-powered code review
|
|
64
|
+
```
|
|
65
|
+
Exit.
|
|
66
|
+
|
|
67
|
+
Parse checklist items. Each item should have:
|
|
68
|
+
- **Action:** What to do (command to run, endpoint to hit, UI to check)
|
|
69
|
+
- **Expected:** What should happen
|
|
70
|
+
|
|
71
|
+
## Step 4: Display Plan
|
|
72
|
+
|
|
73
|
+
**IMPORTANT:** Output the following directly as formatted text, NOT wrapped in a markdown code block:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
77
|
+
VALIDATION: {SPEC-XXX}
|
|
78
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
79
|
+
|
|
80
|
+
**Spec:** {title}
|
|
81
|
+
**Checklist items:** {count}
|
|
82
|
+
|
|
83
|
+
Running validation...
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Step 5: Execute Checks
|
|
87
|
+
|
|
88
|
+
For each checklist item:
|
|
89
|
+
|
|
90
|
+
### 5.1 Automated Checks
|
|
91
|
+
|
|
92
|
+
If the item contains a runnable command (backtick-wrapped command, `npm test`, `curl`, etc.):
|
|
93
|
+
1. Execute the command
|
|
94
|
+
2. Check exit code and output against expected result
|
|
95
|
+
3. Record: PASS or FAIL with output
|
|
96
|
+
|
|
97
|
+
### 5.2 Code Verification Checks
|
|
98
|
+
|
|
99
|
+
If the item references code behavior (file exists, function exists, pattern present):
|
|
100
|
+
1. Use Glob/Grep/Read to verify
|
|
101
|
+
2. Record: PASS or FAIL
|
|
102
|
+
|
|
103
|
+
### 5.3 Manual Checks
|
|
104
|
+
|
|
105
|
+
If the item requires manual/visual verification (UI behavior, browser check):
|
|
106
|
+
1. Ask user via AskUserQuestion: "Did this pass? {item description}"
|
|
107
|
+
2. Record user's response
|
|
108
|
+
|
|
109
|
+
### 5.4 Track Results
|
|
110
|
+
|
|
111
|
+
For each item, record:
|
|
112
|
+
- Item description
|
|
113
|
+
- Type: automated / code / manual
|
|
114
|
+
- Result: PASS / FAIL / SKIP
|
|
115
|
+
- Details: output or user note
|
|
116
|
+
|
|
117
|
+
## Step 6: Display Results
|
|
118
|
+
|
|
119
|
+
**IMPORTANT:** Output the following directly as formatted text, NOT wrapped in a markdown code block:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
123
|
+
VALIDATION RESULTS: {SPEC-XXX}
|
|
124
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
125
|
+
|
|
126
|
+
| # | Check | Type | Result |
|
|
127
|
+
|---|-------|------|--------|
|
|
128
|
+
| 1 | {description} | automated | PASS |
|
|
129
|
+
| 2 | {description} | code | PASS |
|
|
130
|
+
| 3 | {description} | manual | FAIL |
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
**Result:** {passed}/{total} checks passed
|
|
135
|
+
|
|
136
|
+
{If all passed:}
|
|
137
|
+
Validation PASSED. Ready for `/sf:done`.
|
|
138
|
+
|
|
139
|
+
{If any failed:}
|
|
140
|
+
Validation FAILED. Fix issues and re-run `/sf:validate`.
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
</workflow>
|
|
144
|
+
|
|
145
|
+
<success_criteria>
|
|
146
|
+
- [ ] Spec resolved (argument or active)
|
|
147
|
+
- [ ] Validation checklist extracted from spec
|
|
148
|
+
- [ ] Missing checklist handled gracefully
|
|
149
|
+
- [ ] Automated commands executed and verified
|
|
150
|
+
- [ ] Code checks performed via tools
|
|
151
|
+
- [ ] Manual checks prompted to user
|
|
152
|
+
- [ ] Clear pass/fail report displayed
|
|
153
|
+
- [ ] Guidance on next step provided
|
|
154
|
+
</success_criteria>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Context Monitor - PostToolUse hook
|
|
3
|
+
// Reads context metrics from the statusline bridge file and injects
|
|
4
|
+
// warnings when context usage is high. Makes the AGENT aware of
|
|
5
|
+
// context limits (the statusline only shows the user).
|
|
6
|
+
//
|
|
7
|
+
// How it works:
|
|
8
|
+
// 1. The statusline hook writes metrics to /tmp/claude-ctx-{session_id}.json
|
|
9
|
+
// 2. This hook reads those metrics after each tool use
|
|
10
|
+
// 3. When remaining context drops below thresholds, it injects a warning
|
|
11
|
+
// as additionalContext, which the agent sees in its conversation
|
|
12
|
+
//
|
|
13
|
+
// Thresholds:
|
|
14
|
+
// WARNING (remaining <= 35%): Agent should wrap up current task
|
|
15
|
+
// CRITICAL (remaining <= 25%): Agent should stop immediately and save state
|
|
16
|
+
//
|
|
17
|
+
// Debounce: 5 tool uses between warnings to avoid spam
|
|
18
|
+
// Severity escalation bypasses debounce (WARNING -> CRITICAL fires immediately)
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const os = require('os');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
const WARNING_THRESHOLD = 35;
|
|
25
|
+
const CRITICAL_THRESHOLD = 25;
|
|
26
|
+
const STALE_SECONDS = 60;
|
|
27
|
+
const DEBOUNCE_CALLS = 5;
|
|
28
|
+
|
|
29
|
+
let input = '';
|
|
30
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
|
31
|
+
process.stdin.setEncoding('utf8');
|
|
32
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
33
|
+
process.stdin.on('end', () => {
|
|
34
|
+
clearTimeout(stdinTimeout);
|
|
35
|
+
try {
|
|
36
|
+
const data = JSON.parse(input);
|
|
37
|
+
const sessionId = data.session_id;
|
|
38
|
+
|
|
39
|
+
if (!sessionId) {
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const tmpDir = os.tmpdir();
|
|
44
|
+
const metricsPath = path.join(tmpDir, `claude-ctx-${sessionId}.json`);
|
|
45
|
+
|
|
46
|
+
if (!fs.existsSync(metricsPath)) {
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const metrics = JSON.parse(fs.readFileSync(metricsPath, 'utf8'));
|
|
51
|
+
const now = Math.floor(Date.now() / 1000);
|
|
52
|
+
|
|
53
|
+
if (metrics.timestamp && (now - metrics.timestamp) > STALE_SECONDS) {
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const remaining = metrics.remaining_percentage;
|
|
58
|
+
const usedPct = metrics.used_pct;
|
|
59
|
+
|
|
60
|
+
if (remaining > WARNING_THRESHOLD) {
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Debounce logic
|
|
65
|
+
const warnPath = path.join(tmpDir, `claude-ctx-${sessionId}-warned.json`);
|
|
66
|
+
let warnData = { callsSinceWarn: 0, lastLevel: null };
|
|
67
|
+
let firstWarn = true;
|
|
68
|
+
|
|
69
|
+
if (fs.existsSync(warnPath)) {
|
|
70
|
+
try {
|
|
71
|
+
warnData = JSON.parse(fs.readFileSync(warnPath, 'utf8'));
|
|
72
|
+
firstWarn = false;
|
|
73
|
+
} catch (e) {}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
warnData.callsSinceWarn = (warnData.callsSinceWarn || 0) + 1;
|
|
77
|
+
|
|
78
|
+
const isCritical = remaining <= CRITICAL_THRESHOLD;
|
|
79
|
+
const currentLevel = isCritical ? 'critical' : 'warning';
|
|
80
|
+
|
|
81
|
+
const severityEscalated = currentLevel === 'critical' && warnData.lastLevel === 'warning';
|
|
82
|
+
if (!firstWarn && warnData.callsSinceWarn < DEBOUNCE_CALLS && !severityEscalated) {
|
|
83
|
+
fs.writeFileSync(warnPath, JSON.stringify(warnData));
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
warnData.callsSinceWarn = 0;
|
|
88
|
+
warnData.lastLevel = currentLevel;
|
|
89
|
+
fs.writeFileSync(warnPath, JSON.stringify(warnData));
|
|
90
|
+
|
|
91
|
+
// Detect if SpecFlow is active
|
|
92
|
+
const cwd = data.cwd || process.cwd();
|
|
93
|
+
const isSfActive = fs.existsSync(path.join(cwd, '.specflow', 'STATE.md'));
|
|
94
|
+
|
|
95
|
+
let message;
|
|
96
|
+
if (isCritical) {
|
|
97
|
+
message = isSfActive
|
|
98
|
+
? `CONTEXT CRITICAL: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
|
|
99
|
+
'Context is nearly exhausted. Do NOT start new complex work. ' +
|
|
100
|
+
'Inform the user so they can run /sf:pause at the next natural stopping point.'
|
|
101
|
+
: `CONTEXT CRITICAL: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
|
|
102
|
+
'Context is nearly exhausted. Inform the user that context is low and ask how they want to proceed.';
|
|
103
|
+
} else {
|
|
104
|
+
message = isSfActive
|
|
105
|
+
? `CONTEXT WARNING: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
|
|
106
|
+
'Context is getting limited. Avoid starting new complex work. ' +
|
|
107
|
+
'Inform the user so they can prepare to pause.'
|
|
108
|
+
: `CONTEXT WARNING: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
|
|
109
|
+
'Be aware that context is getting limited. Avoid unnecessary exploration or starting new complex work.';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
process.stdout.write(JSON.stringify({
|
|
113
|
+
hookSpecificOutput: {
|
|
114
|
+
hookEventName: "PostToolUse",
|
|
115
|
+
additionalContext: message
|
|
116
|
+
}
|
|
117
|
+
}));
|
|
118
|
+
} catch (e) {
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
});
|
package/hooks/statusline.js
CHANGED
|
@@ -4,16 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
7
8
|
|
|
8
9
|
// Read JSON from stdin (Claude Code protocol)
|
|
9
10
|
let input = '';
|
|
11
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
|
10
12
|
process.stdin.setEncoding('utf8');
|
|
11
13
|
process.stdin.on('data', chunk => input += chunk);
|
|
12
14
|
process.stdin.on('end', () => {
|
|
15
|
+
clearTimeout(stdinTimeout);
|
|
13
16
|
try {
|
|
14
17
|
const data = JSON.parse(input);
|
|
15
18
|
const model = data.model?.display_name || 'Claude';
|
|
16
19
|
const dir = data.workspace?.current_dir || process.cwd();
|
|
20
|
+
const session = data.session_id || '';
|
|
17
21
|
const remaining = data.context_window?.remaining_percentage;
|
|
18
22
|
|
|
19
23
|
// Context window display (shows USED percentage scaled to 80% limit)
|
|
@@ -25,6 +29,19 @@ process.stdin.on('end', () => {
|
|
|
25
29
|
// Scale: 80% real usage = 100% displayed
|
|
26
30
|
const used = Math.min(100, Math.round((rawUsed / 80) * 100));
|
|
27
31
|
|
|
32
|
+
// Write context metrics to bridge file for the context-monitor hook
|
|
33
|
+
if (session) {
|
|
34
|
+
try {
|
|
35
|
+
const bridgePath = path.join(os.tmpdir(), `claude-ctx-${session}.json`);
|
|
36
|
+
fs.writeFileSync(bridgePath, JSON.stringify({
|
|
37
|
+
session_id: session,
|
|
38
|
+
remaining_percentage: remaining,
|
|
39
|
+
used_pct: used,
|
|
40
|
+
timestamp: Math.floor(Date.now() / 1000)
|
|
41
|
+
}));
|
|
42
|
+
} catch (e) {}
|
|
43
|
+
}
|
|
44
|
+
|
|
28
45
|
// Build progress bar (10 segments)
|
|
29
46
|
const filled = Math.floor(used / 10);
|
|
30
47
|
const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
|