Skip to content

Commit 9c7c9d4

Browse files
feat: add context pressure and cost velocity MCP tools, friction patterns
Wire get_context_pressure and get_cost_velocity tool registrations, deduplicate mkAssistantWithUsage test helper from parallel agent merge, add friction pattern classification to get_live_friction response.
1 parent a4859fe commit 9c7c9d4

6 files changed

Lines changed: 21 additions & 31 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to claudewatch are documented here.
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- **`get_context_pressure`** — context window usage tracker for the current live session. Sums input/output tokens, counts compaction events, estimates usage ratio against 200k window. Status levels: "comfortable" (<50%), "filling" (50-75%), "pressure" (75-90%), "critical" (>=90%).
10+
11+
- **`get_cost_velocity`** — cost burn rate for the current live session over a 10-minute sliding window. Computes per-minute USD spend from token counts and Sonnet pricing. Status levels: "efficient" (<$0.05/min), "normal" ($0.05-0.20/min), "burning" (>=$0.20/min).
12+
13+
- **Friction pattern classification**`get_live_friction` now includes a `patterns` field that collapses raw friction events into typed groups with counts, consecutive run detection, and first/last turn references. Sorted by frequency for quick triage.
14+
715
## [0.7.0] - 2026-03-02
816

917
### Added

docs/IMPL-push-observability.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ After Wave 1 completes (all 4 agents):
629629
- [x] Wave 1 Agent B -- `get_context_pressure` MCP tool
630630
- [x] Wave 1 Agent C -- `get_cost_velocity` MCP tool
631631
- [x] Wave 1 Agent D -- Friction pattern classification
632-
- [ ] Post-merge -- Register tools in `tools.go`, fix test counts
632+
- [x] Post-merge -- Register tools in `tools.go`, deduplicate `mkAssistantWithUsage` test helper (B/C collision), all tests pass
633633

634634
---
635635

internal/claude/active_live.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ type LiveFrictionEvent struct {
2929

3030
// FrictionPattern represents a grouped friction pattern collapsed from individual events.
3131
type FrictionPattern struct {
32-
Type string `json:"type"` // e.g. "tool_error:Edit", "retry:Bash"
33-
Count int `json:"count"` // total occurrences
34-
Consecutive bool `json:"consecutive"` // true if all occurrences were consecutive
35-
FirstTurn int `json:"first_turn"` // position of first occurrence in events slice
36-
LastTurn int `json:"last_turn"` // position of last occurrence
32+
Type string `json:"type"` // e.g. "tool_error:Edit", "retry:Bash"
33+
Count int `json:"count"` // total occurrences
34+
Consecutive bool `json:"consecutive"` // true if all occurrences were consecutive
35+
FirstTurn int `json:"first_turn"` // position of first occurrence in events slice
36+
LastTurn int `json:"last_turn"` // position of last occurrence
3737
}
3838

3939
// LiveFrictionStats holds friction statistics parsed from a live JSONL session.

internal/claude/active_live_cost_test.go

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,12 @@
11
package claude
22

33
import (
4-
"encoding/json"
5-
"fmt"
64
"os"
75
"path/filepath"
86
"testing"
97
"time"
108
)
119

12-
// mkAssistantWithUsage builds a JSONL entry for an assistant message with token usage.
13-
func mkAssistantWithUsage(ts string, inputTokens, outputTokens int) map[string]any {
14-
msg, _ := json.Marshal(map[string]any{
15-
"role": "assistant",
16-
"content": []map[string]any{},
17-
"usage": map[string]any{
18-
"input_tokens": inputTokens,
19-
"output_tokens": outputTokens,
20-
},
21-
})
22-
return map[string]any{
23-
"type": "assistant",
24-
"timestamp": ts,
25-
"message": json.RawMessage(msg),
26-
}
27-
}
28-
2910
func TestParseLiveCostVelocity_EmptyFile(t *testing.T) {
3011
dir := t.TempDir()
3112
path := filepath.Join(dir, "empty.jsonl")
@@ -172,8 +153,8 @@ func TestParseLiveCostVelocity_MixedWindowEntries(t *testing.T) {
172153
recentTS := time.Now().Add(-2 * time.Minute).UTC().Format(time.RFC3339)
173154

174155
path := writeLiveJSONL(t, []map[string]any{
175-
mkAssistantWithUsage(oldTS, 5_000_000, 1_000_000), // outside window
176-
mkAssistantWithUsage(recentTS, 100_000, 10_000), // inside window
156+
mkAssistantWithUsage(oldTS, 5_000_000, 1_000_000), // outside window
157+
mkAssistantWithUsage(recentTS, 100_000, 10_000), // inside window
177158
})
178159

179160
pricing := CostPricing{InputPerMillion: 3.0, OutputPerMillion: 15.0}
@@ -188,5 +169,4 @@ func TestParseLiveCostVelocity_MixedWindowEntries(t *testing.T) {
188169
t.Fatalf("expected WindowCostUSD~%f, got %f", expectedCost, stats.WindowCostUSD)
189170
}
190171

191-
_ = fmt.Sprintf("") // suppress unused import
192172
}

internal/claude/active_live_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -457,9 +457,9 @@ func TestCollapseFrictionPatterns_MixedTypes(t *testing.T) {
457457

458458
func TestCollapseFrictionPatterns_ConsecutiveDetection(t *testing.T) {
459459
events := []LiveFrictionEvent{
460-
{Type: "tool_error", Tool: "Edit", Count: 1}, // idx 0
461-
{Type: "retry", Tool: "Bash", Count: 2}, // idx 1
462-
{Type: "tool_error", Tool: "Edit", Count: 1}, // idx 2 — gap (retry:Bash at idx 1)
460+
{Type: "tool_error", Tool: "Edit", Count: 1}, // idx 0
461+
{Type: "retry", Tool: "Bash", Count: 2}, // idx 1
462+
{Type: "tool_error", Tool: "Edit", Count: 1}, // idx 2 — gap (retry:Bash at idx 1)
463463
}
464464
patterns := collapseFrictionPatterns(events)
465465

internal/mcp/tools.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ func addTools(s *Server) {
149149
addCostTools(s)
150150
addVelocityTools(s)
151151
addLiveTools(s)
152+
addContextTools(s)
153+
addCostVelocityTools(s)
152154
s.registerTool(toolDef{
153155
Name: "get_project_comparison",
154156
Description: "All projects compared side by side in a single call. Returns a ranked list of all projects with health score, friction rate, has_claude_md, agent success rate, and session count.",

0 commit comments

Comments
 (0)