@colin4k1024/tsp 2.5.1 → 2.5.3
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/bin/lib/install-surface.js +5 -0
- package/commands/dashboard.md +105 -0
- package/commands/goal.md +142 -0
- package/commands/heartbeat.md +129 -0
- package/commands/triage.md +108 -0
- package/hooks/harness-context-monitor.js +4 -0
- package/hooks/harness-statusline.js +34 -11
- package/hooks/hooks.json +23 -23
- package/manifests/install-modules.json +98 -31
- package/package.json +2 -1
- package/schemas/goal.schema.json +172 -0
- package/scripts/hooks/session-start-goal-resume.js +95 -0
- package/scripts/hooks/suggest-compact.js +122 -19
- package/scripts/lib/blame-attribution.js +210 -0
- package/scripts/lib/completion-oracle.js +351 -0
- package/scripts/lib/heartbeat-scheduler.js +265 -0
- package/scripts/lib/install/request.js +1 -1
- package/scripts/lib/install-manifests.js +9 -1
- package/scripts/lib/install-targets/cangming-home.js +143 -0
- package/scripts/lib/install-targets/codewhale-home.js +187 -0
- package/scripts/lib/install-targets/registry.js +5 -1
- package/scripts/lib/transcript-usage.js +183 -0
- package/scripts/lib/wave-cost-advisor.js +155 -0
- package/scripts/test-cangming-install.js +105 -0
- package/skills/goal-convergence/SKILL.md +150 -0
- package/skills/loop-heartbeat/SKILL.md +120 -0
- package/skills/mcp-connector-bridge/SKILL.md +132 -0
- package/skills/rework-loop/SKILL.md +131 -0
|
@@ -37,6 +37,11 @@ const TARGET_METADATA = Object.freeze({
|
|
|
37
37
|
installPath: '~/.config/opencode/',
|
|
38
38
|
scope: 'home-level',
|
|
39
39
|
},
|
|
40
|
+
cangming: {
|
|
41
|
+
label: 'Cangming',
|
|
42
|
+
installPath: '~/.config/cangming/',
|
|
43
|
+
scope: 'home-level',
|
|
44
|
+
},
|
|
40
45
|
codebuddy: {
|
|
41
46
|
label: 'CodeBuddy',
|
|
42
47
|
installPath: './.codebuddy/',
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Dashboard Command
|
|
2
|
+
|
|
3
|
+
Real-time progress visibility for goals, waves, and triage. Shows what the loop
|
|
4
|
+
is doing, how much budget remains, and what needs human attention.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
`/dashboard`
|
|
9
|
+
|
|
10
|
+
## Output
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
14
|
+
║ LOOP ENGINEERING DASHBOARD ║
|
|
15
|
+
╠══════════════════════════════════════════════════════════════╣
|
|
16
|
+
║ ║
|
|
17
|
+
║ ACTIVE GOALS ║
|
|
18
|
+
║ ───────────── ║
|
|
19
|
+
║ ▶ [goal-a1b2c3d4] "Fix all failing tests" ║
|
|
20
|
+
║ Iteration: 3/15 | Budget: $1.20/$10 | Time: 25m/2h ║
|
|
21
|
+
║ Last oracle: FAIL — 2 tests still failing in auth module ║
|
|
22
|
+
║ Next hint: Focus on token refresh mock ║
|
|
23
|
+
║ ║
|
|
24
|
+
║ ⏸ [goal-e5f6g7h8] "Achieve 80% coverage" ║
|
|
25
|
+
║ Iteration: 7/15 | Budget: $3.50/$10 | Paused ║
|
|
26
|
+
║ ║
|
|
27
|
+
║ HEARTBEAT ║
|
|
28
|
+
║ ───────────── ║
|
|
29
|
+
║ Status: Running (interval: 30m) ║
|
|
30
|
+
║ Last run: 12m ago — 2/3 passed, 1 goal created ║
|
|
31
|
+
║ Next run: 18m ║
|
|
32
|
+
║ Budget: $0.80/$2.00 per hour ║
|
|
33
|
+
║ ║
|
|
34
|
+
║ TRIAGE INBOX ║
|
|
35
|
+
║ ───────────── ║
|
|
36
|
+
║ [3 pending] [1 high] [2 medium] ║
|
|
37
|
+
║ • HIGH: sentry — 5 unresolved errors (2h ago) ║
|
|
38
|
+
║ • MED: lint — 4 new lint errors (30m ago) ║
|
|
39
|
+
║ • MED: deps — 2 moderate vulnerabilities (1d ago) ║
|
|
40
|
+
║ ║
|
|
41
|
+
║ WAVE EXECUTION ║
|
|
42
|
+
║ ───────────── ║
|
|
43
|
+
║ (No active waves) ║
|
|
44
|
+
║ ║
|
|
45
|
+
║ REWORK TRACKING ║
|
|
46
|
+
║ ───────────── ║
|
|
47
|
+
║ Persistent trouble spots: 1 ║
|
|
48
|
+
║ • src/auth/refresh.ts — 3 attempts, last: fail ║
|
|
49
|
+
║ ║
|
|
50
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Data Sources
|
|
54
|
+
|
|
55
|
+
The dashboard aggregates from:
|
|
56
|
+
|
|
57
|
+
| Source | Location |
|
|
58
|
+
|--------|----------|
|
|
59
|
+
| Active goals | `~/.claude/goals/*.json` |
|
|
60
|
+
| Heartbeat state | `~/.claude/heartbeat-last-run.json` |
|
|
61
|
+
| Triage inbox | `~/.claude/triage/inbox.jsonl` |
|
|
62
|
+
| Wave progress | Worker `status.md` files in coordination dirs |
|
|
63
|
+
| Rework tracking | `~/.claude/rework-tracking.json` |
|
|
64
|
+
| Cost tracking | Goal history `costDollars` fields |
|
|
65
|
+
|
|
66
|
+
## Sections
|
|
67
|
+
|
|
68
|
+
### Active Goals
|
|
69
|
+
- State icon: ▶ (active), ⏸ (paused), ✓ (converged), ⚠ (escalated)
|
|
70
|
+
- Progress: iteration/max, cost/budget, time/duration
|
|
71
|
+
- Last oracle verdict and hint
|
|
72
|
+
|
|
73
|
+
### Heartbeat
|
|
74
|
+
- Running/stopped status
|
|
75
|
+
- Last run results (passed/failed count)
|
|
76
|
+
- Time to next run
|
|
77
|
+
- Hourly budget usage
|
|
78
|
+
|
|
79
|
+
### Triage Inbox
|
|
80
|
+
- Pending count by severity
|
|
81
|
+
- Top 3 most recent items with source and age
|
|
82
|
+
- Action hint: "Use /triage to act on items"
|
|
83
|
+
|
|
84
|
+
### Wave Execution
|
|
85
|
+
- Active wave index / total waves
|
|
86
|
+
- Per-worker status and progress estimate
|
|
87
|
+
- Critical path and ETA (when workers report progress)
|
|
88
|
+
|
|
89
|
+
### Rework Tracking
|
|
90
|
+
- Persistent trouble spots (files with 3+ rework attempts)
|
|
91
|
+
- Escalation recommendations
|
|
92
|
+
|
|
93
|
+
## Integration
|
|
94
|
+
|
|
95
|
+
- **`/goal status`**: Dashboard includes goal details
|
|
96
|
+
- **`/heartbeat status`**: Dashboard includes heartbeat state
|
|
97
|
+
- **`/triage stats`**: Dashboard includes inbox summary
|
|
98
|
+
- **Wave execution**: Dashboard tracks parallel worker progress
|
|
99
|
+
|
|
100
|
+
## Arguments
|
|
101
|
+
|
|
102
|
+
$ARGUMENTS:
|
|
103
|
+
- (none) — Show full dashboard
|
|
104
|
+
- `--compact` — One-line summary per section
|
|
105
|
+
- `--json` — Machine-readable output
|
package/commands/goal.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Goal Command
|
|
2
|
+
|
|
3
|
+
Define and run a goal-oriented autonomous loop with an external completion oracle.
|
|
4
|
+
|
|
5
|
+
The goal primitive keeps iterating until verifiable stopping conditions are met,
|
|
6
|
+
checked by a SEPARATE model (the oracle) to eliminate author-bias.
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
`/goal <subcommand> [args]`
|
|
11
|
+
|
|
12
|
+
## Subcommands
|
|
13
|
+
|
|
14
|
+
### Create & Start
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
/goal "make all tests pass"
|
|
18
|
+
/goal "achieve 80% coverage" --budget-iterations 10 --budget-dollars 5
|
|
19
|
+
/goal "fix lint errors in src/" --checker sonnet
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Creates a new goal, infers stopping conditions from the objective, and starts
|
|
23
|
+
the maker-oracle loop immediately.
|
|
24
|
+
|
|
25
|
+
**Auto-inferred stopping conditions:**
|
|
26
|
+
- "tests pass" → `npm test` exit code 0
|
|
27
|
+
- "coverage N%" → coverage report >= N
|
|
28
|
+
- "lint clean" → `npm run lint` exit code 0
|
|
29
|
+
- "build passes" → `npm run build` exit code 0
|
|
30
|
+
- Custom: specify with `--condition "command"`
|
|
31
|
+
|
|
32
|
+
### Status
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
/goal status
|
|
36
|
+
/goal status <goalId>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Show active goals with current iteration, oracle verdict history, budget usage,
|
|
40
|
+
and estimated remaining iterations.
|
|
41
|
+
|
|
42
|
+
### Resume
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
/goal resume
|
|
46
|
+
/goal resume <goalId>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Resume an active goal from a previous session. Reads state from
|
|
50
|
+
`~/.claude/goals/{goalId}.json` and re-enters the maker loop with the oracle's
|
|
51
|
+
last `nextHint` as guidance.
|
|
52
|
+
|
|
53
|
+
### Pause
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
/goal pause
|
|
57
|
+
/goal pause <goalId>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Pause without abandoning. State is persisted for later `/goal resume`.
|
|
61
|
+
|
|
62
|
+
### List
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
/goal list
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Show all goals (active, paused, converged, escalated) with summary stats.
|
|
69
|
+
|
|
70
|
+
## The Maker-Oracle Loop
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
┌─────────────────────────────────────────────────────┐
|
|
74
|
+
│ /goal "fix all tests" │
|
|
75
|
+
│ │
|
|
76
|
+
│ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
|
|
77
|
+
│ │ MAKER │───▶│ ORACLE │───▶│ CONVERGE │ │
|
|
78
|
+
│ │(primary)│ │(checker)│ │ or LOOP │ │
|
|
79
|
+
│ └─────────┘ └─────────┘ └──────────┘ │
|
|
80
|
+
│ ▲ │ │ │
|
|
81
|
+
│ │ verdict: fail │ │
|
|
82
|
+
│ │ + nextHint │ │
|
|
83
|
+
│ └──────────────┘ │ │
|
|
84
|
+
│ │ │
|
|
85
|
+
│ Budget exhausted? ──▶ ESCALATE to triage│ │
|
|
86
|
+
└─────────────────────────────────────────────────────┘
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
1. **Maker** (your primary model) executes one iteration toward the objective
|
|
90
|
+
2. **Oracle** (different model, read-only) evaluates ALL stopping conditions
|
|
91
|
+
3. If all pass → goal state becomes `converged`
|
|
92
|
+
4. If any fail → oracle provides `nextHint` → maker iterates again
|
|
93
|
+
5. If budget exhausted → goal becomes `escalated` → lands in triage inbox
|
|
94
|
+
|
|
95
|
+
## Oracle Behavior
|
|
96
|
+
|
|
97
|
+
The oracle:
|
|
98
|
+
- Uses a DIFFERENT model than the maker (default: haiku for speed/cost)
|
|
99
|
+
- Has READ-ONLY tool access (Read, Bash without writes)
|
|
100
|
+
- Cannot modify code — only evaluate conditions
|
|
101
|
+
- Returns structured verdict: `{converged, reasons[], nextHint}`
|
|
102
|
+
- Is the reason you can walk away from a running loop
|
|
103
|
+
|
|
104
|
+
## Budget Controls
|
|
105
|
+
|
|
106
|
+
| Control | Default | Flag |
|
|
107
|
+
|---------|---------|------|
|
|
108
|
+
| Max iterations | 15 | `--budget-iterations N` |
|
|
109
|
+
| Max duration | 2h | `--budget-duration Nh` |
|
|
110
|
+
| Max cost | $10 | `--budget-dollars N` |
|
|
111
|
+
| Checker model | haiku | `--checker model` |
|
|
112
|
+
|
|
113
|
+
When ANY budget limit is hit, the goal escalates rather than continuing blindly.
|
|
114
|
+
|
|
115
|
+
## State Persistence
|
|
116
|
+
|
|
117
|
+
Goals persist to `~/.claude/goals/{goalId}.json` (follows goal.schema.json).
|
|
118
|
+
|
|
119
|
+
- Active goals survive session restarts
|
|
120
|
+
- SessionStart hook displays active goal reminder
|
|
121
|
+
- Full iteration history is preserved for debugging
|
|
122
|
+
|
|
123
|
+
## Integration
|
|
124
|
+
|
|
125
|
+
- **Triage inbox:** Escalated goals create triage items (`/triage`)
|
|
126
|
+
- **Heartbeat:** Heartbeat scans can auto-create goals (`/heartbeat`)
|
|
127
|
+
- **Checkpoint:** Each iteration creates an implicit checkpoint
|
|
128
|
+
- **Verification loop:** Oracle uses verification-loop patterns internally
|
|
129
|
+
|
|
130
|
+
## Arguments
|
|
131
|
+
|
|
132
|
+
$ARGUMENTS:
|
|
133
|
+
- `<objective>` — Natural language goal description (quoted string)
|
|
134
|
+
- `--budget-iterations N` — Max iterations (default 15)
|
|
135
|
+
- `--budget-duration Nm|Nh` — Max wall time (default 2h)
|
|
136
|
+
- `--budget-dollars N` — Max cost USD (default 10)
|
|
137
|
+
- `--checker model` — Oracle model (default haiku)
|
|
138
|
+
- `--condition "command"` — Additional stopping condition command
|
|
139
|
+
- `status [goalId]` — Show goal status
|
|
140
|
+
- `resume [goalId]` — Resume paused/interrupted goal
|
|
141
|
+
- `pause [goalId]` — Pause active goal
|
|
142
|
+
- `list` — List all goals
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Heartbeat Command
|
|
2
|
+
|
|
3
|
+
Run discovery scans on a schedule to find issues, auto-create goals, or surface
|
|
4
|
+
findings to the triage inbox. The heartbeat is what makes a loop an actual loop —
|
|
5
|
+
not a one-shot run.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
`/heartbeat <subcommand> [args]`
|
|
10
|
+
|
|
11
|
+
## Subcommands
|
|
12
|
+
|
|
13
|
+
### Start
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
/heartbeat start
|
|
17
|
+
/heartbeat start --interval 30m
|
|
18
|
+
/heartbeat start --scan test-health,lint-drift
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Registers a recurring CronCreate job that runs configured scans. Empty runs
|
|
22
|
+
(all scans pass) archive themselves silently.
|
|
23
|
+
|
|
24
|
+
### Stop
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
/heartbeat stop
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Cancels the recurring heartbeat job via CronDelete.
|
|
31
|
+
|
|
32
|
+
### Status
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
/heartbeat status
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Shows:
|
|
39
|
+
- Last scan time and results
|
|
40
|
+
- Next scheduled run
|
|
41
|
+
- Budget usage (cost per hour)
|
|
42
|
+
- Active scan configurations
|
|
43
|
+
|
|
44
|
+
### Run Once
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
/heartbeat run
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Execute all configured scans immediately (one-shot, no scheduling).
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
Heartbeat reads from `.claude/heartbeat.yaml` in the project root:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
heartbeat:
|
|
58
|
+
interval: "30m" # How often to tick (default: 30m)
|
|
59
|
+
scans:
|
|
60
|
+
- name: "test-health"
|
|
61
|
+
command: "npm test 2>&1 | tail -10"
|
|
62
|
+
onFailure: "auto-goal" # auto-create a goal to fix it
|
|
63
|
+
description: "Run test suite"
|
|
64
|
+
- name: "lint-drift"
|
|
65
|
+
command: "npm run lint -- --quiet 2>&1 | wc -l"
|
|
66
|
+
threshold: 0 # numeric threshold: pass if output <= threshold
|
|
67
|
+
onFailure: "triage" # surface to triage inbox for human
|
|
68
|
+
description: "Check lint errors"
|
|
69
|
+
- name: "dependency-audit"
|
|
70
|
+
command: "npm audit --production 2>&1 | grep -c 'vulnerabilities'"
|
|
71
|
+
threshold: 0
|
|
72
|
+
onFailure: "triage"
|
|
73
|
+
description: "Audit dependencies"
|
|
74
|
+
- name: "type-check"
|
|
75
|
+
command: "npx tsc --noEmit 2>&1; echo EXIT:$?"
|
|
76
|
+
onFailure: "auto-goal"
|
|
77
|
+
description: "TypeScript type checking"
|
|
78
|
+
budget:
|
|
79
|
+
maxDollarsPerHour: 2.0 # Pause heartbeat if cost exceeds this
|
|
80
|
+
pauseOnExhaust: true # Pause vs stop on budget exhaust
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Scan Classification
|
|
84
|
+
|
|
85
|
+
Each scan result is classified into an action:
|
|
86
|
+
|
|
87
|
+
| Action | Behavior |
|
|
88
|
+
|--------|----------|
|
|
89
|
+
| `auto-goal` | Create a goal automatically from the failure (e.g., "fix 3 failing tests") |
|
|
90
|
+
| `triage` | Append to triage inbox for human review |
|
|
91
|
+
| `notify` | Desktop notification only (informational) |
|
|
92
|
+
| `ignore` | Log silently, take no action |
|
|
93
|
+
|
|
94
|
+
## Flow
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
┌────────────────────────────────────────────┐
|
|
98
|
+
│ /heartbeat start │
|
|
99
|
+
│ │
|
|
100
|
+
│ CronCreate (every 30m) │
|
|
101
|
+
│ │ │
|
|
102
|
+
│ ▼ │
|
|
103
|
+
│ Run all scans │
|
|
104
|
+
│ │ │
|
|
105
|
+
│ ┌────┼────────┬──────────┐ │
|
|
106
|
+
│ ▼ ▼ ▼ ▼ │
|
|
107
|
+
│ PASS FAIL FAIL FAIL │
|
|
108
|
+
│ │ (goal) (triage) (notify) │
|
|
109
|
+
│ │ │ │ │ │
|
|
110
|
+
│ ▼ ▼ ▼ ▼ │
|
|
111
|
+
│ Skip /goal /triage Desktop │
|
|
112
|
+
│ create inbox notification │
|
|
113
|
+
└────────────────────────────────────────────┘
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Integration
|
|
117
|
+
|
|
118
|
+
- **`/goal`**: Heartbeat auto-creates goals from `auto-goal` scan failures
|
|
119
|
+
- **`/triage`**: Heartbeat populates triage inbox from `triage` scan failures
|
|
120
|
+
- **Budget**: Integrates with cost tracking; pauses if hourly budget exceeded
|
|
121
|
+
- **Hooks**: Uses CronCreate/CronDelete for scheduling (7-day auto-expiry applies)
|
|
122
|
+
|
|
123
|
+
## Arguments
|
|
124
|
+
|
|
125
|
+
$ARGUMENTS:
|
|
126
|
+
- `start [--interval Nm|Nh] [--scan name1,name2]` — Start recurring heartbeat
|
|
127
|
+
- `stop` — Cancel heartbeat
|
|
128
|
+
- `status` — Show heartbeat state
|
|
129
|
+
- `run` — Execute scans once (no scheduling)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Triage Command
|
|
2
|
+
|
|
3
|
+
View and act on findings that the loop could not auto-resolve. The triage inbox
|
|
4
|
+
is where issues land when they need human judgment.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
`/triage [subcommand] [args]`
|
|
9
|
+
|
|
10
|
+
## Subcommands
|
|
11
|
+
|
|
12
|
+
### List (default)
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
/triage
|
|
16
|
+
/triage list
|
|
17
|
+
/triage list --source heartbeat
|
|
18
|
+
/triage list --severity high
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Show pending triage items with source, severity, and suggested actions.
|
|
22
|
+
|
|
23
|
+
### Act
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
/triage act <id> goal # Promote to a goal (auto-fix)
|
|
27
|
+
/triage act <id> dismiss # Dismiss (not actionable)
|
|
28
|
+
/triage act <id> defer # Defer to next session
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Take action on a triage item. `goal` creates a goal from the finding.
|
|
32
|
+
|
|
33
|
+
### Stats
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
/triage stats
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Show inbox health metrics: pending count, sources breakdown, age distribution.
|
|
40
|
+
|
|
41
|
+
### Clear
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
/triage clear --resolved
|
|
45
|
+
/triage clear --older-than 7d
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Remove resolved or stale items.
|
|
49
|
+
|
|
50
|
+
## Triage Item Structure
|
|
51
|
+
|
|
52
|
+
Each item in the inbox has:
|
|
53
|
+
|
|
54
|
+
| Field | Description |
|
|
55
|
+
|-------|-------------|
|
|
56
|
+
| `id` | Unique identifier (e.g., `triage-lq4x8`) |
|
|
57
|
+
| `source` | Where it came from (e.g., `heartbeat:lint-drift`, `goal:escalated`) |
|
|
58
|
+
| `severity` | `high`, `medium`, `low` |
|
|
59
|
+
| `summary` | One-line description |
|
|
60
|
+
| `detail` | Full scan output or error context |
|
|
61
|
+
| `suggestedActions` | What the system recommends |
|
|
62
|
+
| `createdAt` | When the item was created |
|
|
63
|
+
| `status` | `pending`, `acted`, `dismissed`, `deferred` |
|
|
64
|
+
|
|
65
|
+
## Sources
|
|
66
|
+
|
|
67
|
+
Items enter triage from:
|
|
68
|
+
|
|
69
|
+
1. **Heartbeat scans** with `onFailure: "triage"` classification
|
|
70
|
+
2. **Escalated goals** that exhausted their budget without converging
|
|
71
|
+
3. **Manual** items added by the user
|
|
72
|
+
4. **Connectors** (MCP) that discover issues from external tools
|
|
73
|
+
|
|
74
|
+
## Flow
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
┌──────────────┐ ┌─────────┐ ┌──────────────┐
|
|
78
|
+
│ Heartbeat │────▶│ TRIAGE │────▶│ /triage act │
|
|
79
|
+
│ Goal escape │ │ INBOX │ │ goal/dismiss│
|
|
80
|
+
│ Connectors │ │ (.jsonl)│ │ /defer │
|
|
81
|
+
└──────────────┘ └─────────┘ └──────────────┘
|
|
82
|
+
│
|
|
83
|
+
▼
|
|
84
|
+
Statusline: [Triage: N]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Storage
|
|
88
|
+
|
|
89
|
+
Inbox is stored as newline-delimited JSON at `~/.claude/triage/inbox.jsonl`.
|
|
90
|
+
This format is:
|
|
91
|
+
- Append-only (safe for concurrent writes)
|
|
92
|
+
- Human-readable (one JSON object per line)
|
|
93
|
+
- Easy to grep and filter
|
|
94
|
+
|
|
95
|
+
## Integration
|
|
96
|
+
|
|
97
|
+
- **`/heartbeat`**: Primary source of triage items
|
|
98
|
+
- **`/goal`**: Escalated goals create triage items; triage items can become goals
|
|
99
|
+
- **Statusline**: Hook shows `[Triage: N]` count when items are pending
|
|
100
|
+
- **Session start**: Active triage items mentioned in session context
|
|
101
|
+
|
|
102
|
+
## Arguments
|
|
103
|
+
|
|
104
|
+
$ARGUMENTS:
|
|
105
|
+
- `list [--source name] [--severity level]` — List pending items (default)
|
|
106
|
+
- `act <id> goal|dismiss|defer` — Take action on an item
|
|
107
|
+
- `stats` — Show inbox metrics
|
|
108
|
+
- `clear --resolved|--older-than Nd` — Clean up resolved/stale items
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// DEPRECATED: This hook's functionality has been merged into suggest-compact.js.
|
|
3
|
+
// The strategic-compact hook now covers all thresholds (65/70/85/95%).
|
|
4
|
+
// This file is retained for backward compatibility but is no longer registered in hooks.json.
|
|
5
|
+
//
|
|
2
6
|
// harness-context-monitor.js — PostToolUse hook (all tools)
|
|
3
7
|
// Reads context metrics from the bridge file written by harness-statusline.js
|
|
4
8
|
// and injects advisory warnings into agent context when usage is high.
|
|
@@ -52,22 +52,45 @@ process.stdin.on('end', () => {
|
|
|
52
52
|
if (remaining != null) {
|
|
53
53
|
const usableRemaining = Math.max(0, ((remaining - AUTO_COMPACT_BUFFER_PCT) / (100 - AUTO_COMPACT_BUFFER_PCT)) * 100);
|
|
54
54
|
usedPct = Math.max(0, Math.min(100, Math.round(100 - usableRemaining)));
|
|
55
|
+
} else if (data.transcript_path) {
|
|
56
|
+
// Parse actual token usage from transcript JSONL (CCometixLine approach)
|
|
57
|
+
try {
|
|
58
|
+
const { resolveTranscriptMetrics } = require('../scripts/lib/transcript-usage');
|
|
59
|
+
const modelId = (data.model && data.model.id) || process.env.CLAUDE_MODEL || null;
|
|
60
|
+
const metrics = resolveTranscriptMetrics(data.transcript_path, modelId);
|
|
61
|
+
if (metrics) {
|
|
62
|
+
usedPct = Math.max(0, Math.min(100, Math.round(metrics.usagePct)));
|
|
63
|
+
}
|
|
64
|
+
} catch (_) { /* ignore */ }
|
|
55
65
|
|
|
56
|
-
//
|
|
57
|
-
if (
|
|
66
|
+
// Final fallback: file size estimate
|
|
67
|
+
if (usedPct == null) {
|
|
58
68
|
try {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
active_role: activeRole,
|
|
65
|
-
timestamp: Math.floor(Date.now() / 1000),
|
|
66
|
-
}));
|
|
69
|
+
const stat = fs.statSync(data.transcript_path);
|
|
70
|
+
const estimatedTokens = Math.round(stat.size * 0.25);
|
|
71
|
+
if (estimatedTokens > 0) {
|
|
72
|
+
usedPct = Math.max(0, Math.min(100, Math.round((estimatedTokens / 200000) * 100)));
|
|
73
|
+
}
|
|
67
74
|
} catch (_) { /* ignore */ }
|
|
68
75
|
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Write bridge file for downstream hooks (suggest-compact, context-monitor)
|
|
79
|
+
if (usedPct != null && session) {
|
|
80
|
+
try {
|
|
81
|
+
const bridgePath = path.join(os.tmpdir(), `harness-ctx-${session}.json`);
|
|
82
|
+
fs.writeFileSync(bridgePath, JSON.stringify({
|
|
83
|
+
session_id: session,
|
|
84
|
+
remaining_percentage: remaining != null ? remaining : null,
|
|
85
|
+
used_pct: usedPct,
|
|
86
|
+
active_role: activeRole,
|
|
87
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
88
|
+
}));
|
|
89
|
+
} catch (_) { /* ignore */ }
|
|
90
|
+
}
|
|
69
91
|
|
|
70
|
-
|
|
92
|
+
// Build progress bar (10 segments)
|
|
93
|
+
if (usedPct != null) {
|
|
71
94
|
const filled = Math.floor(usedPct / 10);
|
|
72
95
|
const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
|
|
73
96
|
if (usedPct < 50) {
|
package/hooks/hooks.json
CHANGED
|
@@ -68,6 +68,17 @@
|
|
|
68
68
|
"description": "Doc file warning: warn about non-standard documentation files (exit code 0; warns only)",
|
|
69
69
|
"id": "pre:write:doc-file-warning"
|
|
70
70
|
},
|
|
71
|
+
{
|
|
72
|
+
"matcher": "*",
|
|
73
|
+
"hooks": [
|
|
74
|
+
{
|
|
75
|
+
"type": "command",
|
|
76
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/harness-statusline.js\""
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"description": "Display current role and context usage statusline (must run before strategic-compact to write bridge file)",
|
|
80
|
+
"id": "pre:all:harness-statusline"
|
|
81
|
+
},
|
|
71
82
|
{
|
|
72
83
|
"matcher": "*",
|
|
73
84
|
"hooks": [
|
|
@@ -77,7 +88,7 @@
|
|
|
77
88
|
"timeout": 10
|
|
78
89
|
}
|
|
79
90
|
],
|
|
80
|
-
"description": "Suggest strategic context compaction when context usage crosses 70/85/95% thresholds",
|
|
91
|
+
"description": "Suggest strategic context compaction when context usage crosses 65/70/85/95% thresholds",
|
|
81
92
|
"id": "pre:all:strategic-compact"
|
|
82
93
|
},
|
|
83
94
|
{
|
|
@@ -152,28 +163,6 @@
|
|
|
152
163
|
"description": "Check MCP server health before MCP tool execution and block unhealthy MCP calls",
|
|
153
164
|
"id": "pre:mcp-health-check"
|
|
154
165
|
},
|
|
155
|
-
{
|
|
156
|
-
"matcher": "*",
|
|
157
|
-
"hooks": [
|
|
158
|
-
{
|
|
159
|
-
"type": "command",
|
|
160
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/harness-statusline.js\""
|
|
161
|
-
}
|
|
162
|
-
],
|
|
163
|
-
"description": "Display current role and context usage statusline",
|
|
164
|
-
"id": "pre:all:harness-statusline"
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
"matcher": "*",
|
|
168
|
-
"hooks": [
|
|
169
|
-
{
|
|
170
|
-
"type": "command",
|
|
171
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/harness-context-monitor.js\""
|
|
172
|
-
}
|
|
173
|
-
],
|
|
174
|
-
"description": "Monitor context budget, warn at 35%/25% thresholds",
|
|
175
|
-
"id": "pre:all:harness-context-monitor"
|
|
176
|
-
},
|
|
177
166
|
{
|
|
178
167
|
"matcher": "Bash",
|
|
179
168
|
"hooks": [
|
|
@@ -234,6 +223,17 @@
|
|
|
234
223
|
"description": "Load previous context and detect package manager on new session",
|
|
235
224
|
"id": "session:start"
|
|
236
225
|
},
|
|
226
|
+
{
|
|
227
|
+
"matcher": "*",
|
|
228
|
+
"hooks": [
|
|
229
|
+
{
|
|
230
|
+
"type": "command",
|
|
231
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/session-start-goal-resume.js\""
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
"description": "Check for active goals from previous sessions and inject resume reminder",
|
|
235
|
+
"id": "session:goal-resume"
|
|
236
|
+
},
|
|
237
237
|
{
|
|
238
238
|
"matcher": "*",
|
|
239
239
|
"hooks": [
|