@pjmendonca/devflow 1.13.2 → 1.18.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/agent.md +1 -1
- package/.claude/commands/bugfix.md +21 -0
- package/.claude/commands/checkpoint.md +0 -1
- package/.claude/commands/collab.md +0 -1
- package/.claude/commands/costs.md +88 -18
- package/.claude/commands/devflow.md +26 -0
- package/.claude/commands/handoff.md +0 -1
- package/.claude/commands/init.md +287 -0
- package/.claude/commands/memory.md +0 -1
- package/.claude/commands/pair.md +0 -1
- package/.claude/commands/review.md +27 -0
- package/.claude/commands/route.md +0 -1
- package/.claude/commands/swarm.md +0 -1
- package/.claude/commands/validate.md +55 -0
- package/.claude/hooks/session-notification.sh +44 -0
- package/.claude/hooks/session-startup.sh +427 -0
- package/.claude/hooks/session-stop.sh +38 -0
- package/.claude/hooks/session_tracker.py +272 -0
- package/.claude/settings.json +38 -0
- package/.claude/skills/costs/SKILL.md +156 -0
- package/.claude/skills/validate/SKILL.md +101 -0
- package/CHANGELOG.md +243 -0
- package/README.md +207 -10
- package/bin/devflow-install.js +2 -1
- package/bin/devflow.js +4 -0
- package/lib/constants.js +0 -1
- package/lib/exec-python.js +1 -1
- package/package.json +1 -1
- package/tooling/.automation/.checkpoint_lock +1 -0
- package/tooling/.automation/agents/architect.md +19 -0
- package/tooling/.automation/agents/ba.md +19 -0
- package/tooling/.automation/agents/maintainer.md +19 -0
- package/tooling/.automation/agents/pm.md +19 -0
- package/tooling/.automation/agents/reviewer.md +1 -1
- package/tooling/.automation/agents/writer.md +19 -0
- package/tooling/.automation/benchmarks/benchmark_20251230_100119.json +314 -0
- package/tooling/.automation/benchmarks/benchmark_20251230_100216.json +314 -0
- package/tooling/.automation/costs/config.json +31 -0
- package/tooling/.automation/costs/sessions/2025-12-29_20251229_164128.json +22 -0
- package/tooling/.automation/memory/knowledge/kg_integration-test.json +707 -1
- package/tooling/.automation/memory/knowledge/kg_test-story.json +3273 -2
- package/tooling/.automation/memory/shared/shared_integration-test.json +181 -1
- package/tooling/.automation/memory/shared/shared_test-story.json +721 -1
- package/tooling/.automation/memory/shared/shared_test.json +1254 -0
- package/tooling/.automation/memory/shared/shared_validation-check.json +227 -0
- package/tooling/.automation/overrides/templates/architect/cloud-native.yaml +5 -5
- package/tooling/.automation/overrides/templates/architect/enterprise-architect.yaml +23 -5
- package/tooling/.automation/overrides/templates/architect/pragmatic-minimalist.yaml +24 -6
- package/tooling/.automation/overrides/templates/ba/agile-storyteller.yaml +4 -4
- package/tooling/.automation/overrides/templates/ba/domain-expert.yaml +4 -4
- package/tooling/.automation/overrides/templates/ba/requirements-engineer.yaml +4 -4
- package/tooling/.automation/overrides/templates/dev/performance-engineer.yaml +18 -0
- package/tooling/.automation/overrides/templates/dev/rapid-prototyper.yaml +19 -1
- package/tooling/.automation/overrides/templates/dev/security-focused.yaml +18 -0
- package/tooling/.automation/overrides/templates/dev/user-advocate.yaml +54 -0
- package/tooling/.automation/overrides/templates/maintainer/devops-maintainer.yaml +4 -4
- package/tooling/.automation/overrides/templates/maintainer/legacy-steward.yaml +4 -4
- package/tooling/.automation/overrides/templates/maintainer/oss-maintainer.yaml +4 -4
- package/tooling/.automation/overrides/templates/maintainer/reliability-engineer.yaml +55 -0
- package/tooling/.automation/overrides/templates/pm/agile-pm.yaml +4 -4
- package/tooling/.automation/overrides/templates/pm/hybrid-delivery.yaml +3 -3
- package/tooling/.automation/overrides/templates/pm/traditional-pm.yaml +4 -4
- package/tooling/.automation/overrides/templates/reviewer/quick-sanity.yaml +18 -0
- package/tooling/.automation/overrides/templates/reviewer/thorough-critic.yaml +18 -0
- package/tooling/.automation/overrides/templates/sm/agile-coach.yaml +2 -2
- package/tooling/.automation/overrides/templates/sm/startup-pm.yaml +3 -3
- package/tooling/.automation/overrides/templates/writer/api-documentarian.yaml +5 -5
- package/tooling/.automation/overrides/templates/writer/docs-as-code.yaml +4 -4
- package/tooling/.automation/overrides/templates/writer/user-guide-author.yaml +5 -5
- package/tooling/.automation/validation/history/2025-12-29_val_002a28c1.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_01273bb1.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_03369914.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_07a449ba.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_0df1f0a2.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_10ff3d34.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_110771d7.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_13f3a7f9.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_17ba9d21.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_22247089.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_227ea6a4.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_2335d5ae.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_246824bb.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_28b4b9cd.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_2abd12cc.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_2c801b2f.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_2c8cfa8e.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_2ce76eb0.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_30351948.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_30eb7229.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_34df0e77.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_376e4d6a.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_3a4e8a1a.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_3b77a628.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_3ea4e1cf.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_44aacdb4.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_457ddfa8.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_45af6238.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_4735dba1.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_486b203c.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_49dc56cd.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_4d863d6d.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_5149a808.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_52e0bb43.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_585d6319.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_5b2d859a.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_635a7081.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_64df4905.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_70634cee.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_714553f9.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_7f7bfdbf.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_7faad91d.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_81821f8f.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_8249f3c9.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_8422b50f.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_8446c134.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_879f4e26.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_8b6d5bd7.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_8c5cd787.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_91d20bc7.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_958a12b7.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_95d91108.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_980dbb74.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_9e40c79b.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_9f499b7c.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_9f7c3b57.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_a30d5bd4.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_a6eb09c7.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_a86f7b83.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_ad5347e1.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_b0a5a993.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_bcb0192e.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_bf3c9aaa.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_c461ff88.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_c4f4e258.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_c7f0fa6d.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_c911b0e6.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_cc581964.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_cdd5a33b.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_cfd42495.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_d1c7a4ee.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_d2280d0e.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_d2a6ff69.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_d8c53ab2.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_d9c1247a.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_d9d58569.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_dabb4fd9.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_dd8fe359.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_decdffc9.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_e3a95476.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_e776dfca.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_ea70969f.json +59 -0
- package/tooling/.automation/validation/history/2025-12-29_val_ef41ea95.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_f384f9b1.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_f8adc38c.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_fa40b69e.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_fc538d54.json +41 -0
- package/tooling/.automation/validation/history/2025-12-29_val_fe814665.json +32 -0
- package/tooling/.automation/validation/history/2025-12-29_val_ffea4b12.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_02d001e5.json +59 -0
- package/tooling/.automation/validation/history/2025-12-30_val_0b8966dc.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_15455fbf.json +59 -0
- package/tooling/.automation/validation/history/2025-12-30_val_157e34b9.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_28d1d933.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_3442a52c.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_37f1ce1e.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_4f1d8a93.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_56ff1de3.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_664fd4e2.json +41 -0
- package/tooling/.automation/validation/history/2025-12-30_val_66afb0a7.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_7634663c.json +41 -0
- package/tooling/.automation/validation/history/2025-12-30_val_8ea830c3.json +41 -0
- package/tooling/.automation/validation/history/2025-12-30_val_998957c2.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_a52177db.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_a5b65a63.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_ae391d0e.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_c7895339.json +41 -0
- package/tooling/.automation/validation/history/2025-12-30_val_ca416593.json +41 -0
- package/tooling/.automation/validation/history/2025-12-30_val_cee19422.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_ddd4f4e6.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_f2e1394b.json +32 -0
- package/tooling/.automation/validation/history/2025-12-30_val_f4a7fa06.json +41 -0
- package/tooling/.automation/validation/history/2025-12-30_val_ffea3369.json +32 -0
- package/tooling/.automation/validation-config.yaml +103 -0
- package/tooling/completions/DevflowCompletion.ps1 +21 -21
- package/tooling/completions/_run-story +3 -3
- package/tooling/completions/run-story-completion.bash +8 -8
- package/tooling/docs/DOC-STANDARD.md +14 -14
- package/tooling/docs/templates/migration-spec.md +4 -4
- package/tooling/scripts/context_checkpoint.py +5 -15
- package/tooling/scripts/cost_dashboard.py +610 -13
- package/tooling/scripts/create-persona.py +1 -12
- package/tooling/scripts/create-persona.sh +44 -44
- package/tooling/scripts/lib/__init__.py +12 -1
- package/tooling/scripts/lib/agent_handoff.py +11 -2
- package/tooling/scripts/lib/agent_router.py +31 -10
- package/tooling/scripts/lib/colors.py +106 -0
- package/tooling/scripts/lib/context_monitor.py +766 -0
- package/tooling/scripts/lib/cost_config.py +229 -10
- package/tooling/scripts/lib/cost_display.py +20 -45
- package/tooling/scripts/lib/cost_tracker.py +462 -15
- package/tooling/scripts/lib/currency_converter.py +28 -5
- package/tooling/scripts/lib/pair_programming.py +102 -3
- package/tooling/scripts/lib/personality_system.py +949 -0
- package/tooling/scripts/lib/platform.py +55 -0
- package/tooling/scripts/lib/shared_memory.py +9 -3
- package/tooling/scripts/lib/swarm_orchestrator.py +514 -75
- package/tooling/scripts/lib/validation_loop.py +1014 -0
- package/tooling/scripts/memory_summarize.py +9 -2
- package/tooling/scripts/new-doc.py +2 -9
- package/tooling/scripts/personalize_agent.py +1 -12
- package/tooling/scripts/rollback-migration.sh +60 -60
- package/tooling/scripts/run-collab.ps1 +16 -16
- package/tooling/scripts/run-collab.py +88 -53
- package/tooling/scripts/run-collab.sh +4 -4
- package/tooling/scripts/run-story.py +278 -20
- package/tooling/scripts/run-story.sh +3 -3
- package/tooling/scripts/setup-checkpoint-service.py +2 -9
- package/tooling/scripts/tech-debt-tracker.py +1 -12
- package/tooling/scripts/test_adversarial_swarm.py +452 -0
- package/tooling/scripts/validate-overrides.py +1 -10
- package/tooling/scripts/validate-overrides.sh +40 -40
- package/tooling/scripts/validate_loop.py +162 -0
- package/tooling/scripts/validate_setup.py +2 -30
- package/.claude/skills/init/SKILL.md +0 -496
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Session Tracker - Tracks costs for ALL Claude Code sessions.
|
|
4
|
+
|
|
5
|
+
Called by hooks to track token usage across the entire session lifecycle.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python session_tracker.py start [--session-id ID]
|
|
9
|
+
python session_tracker.py log --input TOKENS --output TOKENS [--model MODEL]
|
|
10
|
+
python session_tracker.py end
|
|
11
|
+
python session_tracker.py status
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
# Project paths
|
|
20
|
+
PROJECT_DIR = Path(__file__).parent.parent.parent
|
|
21
|
+
COSTS_DIR = PROJECT_DIR / "tooling" / ".automation" / "costs"
|
|
22
|
+
SESSIONS_DIR = COSTS_DIR / "sessions"
|
|
23
|
+
ACTIVE_SESSION_FILE = COSTS_DIR / ".active_session.json"
|
|
24
|
+
|
|
25
|
+
# Pricing per 1M tokens (USD) - December 2025
|
|
26
|
+
PRICING = {
|
|
27
|
+
"claude-opus-4-5-20251101": {"input": 15.00, "output": 75.00},
|
|
28
|
+
"claude-sonnet-4-20250514": {"input": 3.00, "output": 15.00},
|
|
29
|
+
"claude-3-5-sonnet-20241022": {"input": 3.00, "output": 15.00},
|
|
30
|
+
"claude-3-5-haiku-20241022": {"input": 0.80, "output": 4.00},
|
|
31
|
+
"claude-3-opus-20240229": {"input": 15.00, "output": 75.00},
|
|
32
|
+
"opus": {"input": 15.00, "output": 75.00},
|
|
33
|
+
"sonnet": {"input": 3.00, "output": 15.00},
|
|
34
|
+
"haiku": {"input": 0.80, "output": 4.00},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_pricing(model: str) -> dict:
|
|
39
|
+
"""Get pricing for a model."""
|
|
40
|
+
model_lower = model.lower()
|
|
41
|
+
|
|
42
|
+
# Direct match
|
|
43
|
+
if model_lower in PRICING:
|
|
44
|
+
return PRICING[model_lower]
|
|
45
|
+
|
|
46
|
+
# Partial match
|
|
47
|
+
for key, price in PRICING.items():
|
|
48
|
+
if key in model_lower or model_lower in key:
|
|
49
|
+
return price
|
|
50
|
+
|
|
51
|
+
# Check for model family
|
|
52
|
+
if "opus" in model_lower:
|
|
53
|
+
return PRICING["opus"]
|
|
54
|
+
elif "sonnet" in model_lower:
|
|
55
|
+
return PRICING["sonnet"]
|
|
56
|
+
elif "haiku" in model_lower:
|
|
57
|
+
return PRICING["haiku"]
|
|
58
|
+
|
|
59
|
+
# Default to sonnet pricing
|
|
60
|
+
return PRICING["sonnet"]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def calculate_cost(input_tokens: int, output_tokens: int, model: str) -> float:
|
|
64
|
+
"""Calculate cost in USD."""
|
|
65
|
+
pricing = get_pricing(model)
|
|
66
|
+
input_cost = (input_tokens / 1_000_000) * pricing["input"]
|
|
67
|
+
output_cost = (output_tokens / 1_000_000) * pricing["output"]
|
|
68
|
+
return input_cost + output_cost
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def ensure_dirs():
|
|
72
|
+
"""Ensure required directories exist."""
|
|
73
|
+
COSTS_DIR.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
SESSIONS_DIR.mkdir(parents=True, exist_ok=True)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def start_session(session_id: str = None, model: str = None) -> dict:
|
|
78
|
+
"""Start a new tracking session."""
|
|
79
|
+
ensure_dirs()
|
|
80
|
+
|
|
81
|
+
now = datetime.now()
|
|
82
|
+
if not session_id:
|
|
83
|
+
session_id = now.strftime("%Y%m%d_%H%M%S")
|
|
84
|
+
|
|
85
|
+
session = {
|
|
86
|
+
"session_id": session_id,
|
|
87
|
+
"start_time": now.isoformat(),
|
|
88
|
+
"end_time": None,
|
|
89
|
+
"model": model or "unknown",
|
|
90
|
+
"story_key": None,
|
|
91
|
+
"entries": [],
|
|
92
|
+
"totals": {
|
|
93
|
+
"input_tokens": 0,
|
|
94
|
+
"output_tokens": 0,
|
|
95
|
+
"total_tokens": 0,
|
|
96
|
+
"cost_usd": 0.0,
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Save active session
|
|
101
|
+
with open(ACTIVE_SESSION_FILE, "w") as f:
|
|
102
|
+
json.dump(session, f, indent=2)
|
|
103
|
+
|
|
104
|
+
return session
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_active_session() -> dict | None:
|
|
108
|
+
"""Get the currently active session."""
|
|
109
|
+
if not ACTIVE_SESSION_FILE.exists():
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
with open(ACTIVE_SESSION_FILE) as f:
|
|
114
|
+
return json.load(f)
|
|
115
|
+
except (OSError, json.JSONDecodeError):
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def log_usage(input_tokens: int, output_tokens: int, model: str = None) -> dict | None:
|
|
120
|
+
"""Log token usage to the active session."""
|
|
121
|
+
session = get_active_session()
|
|
122
|
+
|
|
123
|
+
if not session:
|
|
124
|
+
# Auto-start session if none exists
|
|
125
|
+
session = start_session(model=model)
|
|
126
|
+
|
|
127
|
+
# Update model if provided
|
|
128
|
+
if model and session.get("model") == "unknown":
|
|
129
|
+
session["model"] = model
|
|
130
|
+
|
|
131
|
+
# Use session model for pricing
|
|
132
|
+
use_model = model or session.get("model", "sonnet")
|
|
133
|
+
cost = calculate_cost(input_tokens, output_tokens, use_model)
|
|
134
|
+
|
|
135
|
+
# Create entry
|
|
136
|
+
entry = {
|
|
137
|
+
"timestamp": datetime.now().isoformat(),
|
|
138
|
+
"model": use_model,
|
|
139
|
+
"input_tokens": input_tokens,
|
|
140
|
+
"output_tokens": output_tokens,
|
|
141
|
+
"cost_usd": cost,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
session["entries"].append(entry)
|
|
145
|
+
|
|
146
|
+
# Update totals
|
|
147
|
+
session["totals"]["input_tokens"] += input_tokens
|
|
148
|
+
session["totals"]["output_tokens"] += output_tokens
|
|
149
|
+
session["totals"]["total_tokens"] += input_tokens + output_tokens
|
|
150
|
+
session["totals"]["cost_usd"] += cost
|
|
151
|
+
|
|
152
|
+
# Save updated session
|
|
153
|
+
with open(ACTIVE_SESSION_FILE, "w") as f:
|
|
154
|
+
json.dump(session, f, indent=2)
|
|
155
|
+
|
|
156
|
+
return session
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def end_session() -> dict | None:
|
|
160
|
+
"""End the current session and save to sessions directory."""
|
|
161
|
+
session = get_active_session()
|
|
162
|
+
|
|
163
|
+
if not session:
|
|
164
|
+
return None
|
|
165
|
+
|
|
166
|
+
# Set end time
|
|
167
|
+
session["end_time"] = datetime.now().isoformat()
|
|
168
|
+
|
|
169
|
+
# Only save if there was activity
|
|
170
|
+
if session["totals"]["total_tokens"] > 0:
|
|
171
|
+
# Generate filename
|
|
172
|
+
start_time = datetime.fromisoformat(session["start_time"])
|
|
173
|
+
filename = f"{start_time.strftime('%Y-%m-%d')}_{session['session_id']}.json"
|
|
174
|
+
session_file = SESSIONS_DIR / filename
|
|
175
|
+
|
|
176
|
+
# Save to sessions directory
|
|
177
|
+
with open(session_file, "w") as f:
|
|
178
|
+
json.dump(session, f, indent=2)
|
|
179
|
+
|
|
180
|
+
# Remove active session file
|
|
181
|
+
if ACTIVE_SESSION_FILE.exists():
|
|
182
|
+
ACTIVE_SESSION_FILE.unlink()
|
|
183
|
+
|
|
184
|
+
return session
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def get_status() -> dict:
|
|
188
|
+
"""Get current session status."""
|
|
189
|
+
session = get_active_session()
|
|
190
|
+
|
|
191
|
+
if not session:
|
|
192
|
+
return {"active": False, "message": "No active session"}
|
|
193
|
+
|
|
194
|
+
totals = session.get("totals", {})
|
|
195
|
+
return {
|
|
196
|
+
"active": True,
|
|
197
|
+
"session_id": session.get("session_id"),
|
|
198
|
+
"model": session.get("model"),
|
|
199
|
+
"input_tokens": totals.get("input_tokens", 0),
|
|
200
|
+
"output_tokens": totals.get("output_tokens", 0),
|
|
201
|
+
"total_tokens": totals.get("total_tokens", 0),
|
|
202
|
+
"cost_usd": totals.get("cost_usd", 0.0),
|
|
203
|
+
"entries_count": len(session.get("entries", [])),
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def main():
|
|
208
|
+
parser = argparse.ArgumentParser(description="Session cost tracker")
|
|
209
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
210
|
+
|
|
211
|
+
# Start command
|
|
212
|
+
start_parser = subparsers.add_parser("start", help="Start a new session")
|
|
213
|
+
start_parser.add_argument("--session-id", help="Custom session ID")
|
|
214
|
+
start_parser.add_argument("--model", help="Model name")
|
|
215
|
+
|
|
216
|
+
# Log command
|
|
217
|
+
log_parser = subparsers.add_parser("log", help="Log token usage")
|
|
218
|
+
log_parser.add_argument("--input", type=int, required=True, help="Input tokens")
|
|
219
|
+
log_parser.add_argument("--output", type=int, required=True, help="Output tokens")
|
|
220
|
+
log_parser.add_argument("--model", help="Model name")
|
|
221
|
+
|
|
222
|
+
# End command
|
|
223
|
+
subparsers.add_parser("end", help="End current session")
|
|
224
|
+
|
|
225
|
+
# Status command
|
|
226
|
+
subparsers.add_parser("status", help="Get session status")
|
|
227
|
+
|
|
228
|
+
args = parser.parse_args()
|
|
229
|
+
|
|
230
|
+
if args.command == "start":
|
|
231
|
+
session = start_session(args.session_id, args.model)
|
|
232
|
+
print(json.dumps({"status": "started", "session_id": session["session_id"]}))
|
|
233
|
+
|
|
234
|
+
elif args.command == "log":
|
|
235
|
+
model = getattr(args, "model", None)
|
|
236
|
+
session = log_usage(args.input, args.output, model)
|
|
237
|
+
if session:
|
|
238
|
+
print(
|
|
239
|
+
json.dumps(
|
|
240
|
+
{
|
|
241
|
+
"status": "logged",
|
|
242
|
+
"total_tokens": session["totals"]["total_tokens"],
|
|
243
|
+
"cost_usd": round(session["totals"]["cost_usd"], 4),
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
else:
|
|
248
|
+
print(json.dumps({"status": "error", "message": "No active session"}))
|
|
249
|
+
|
|
250
|
+
elif args.command == "end":
|
|
251
|
+
session = end_session()
|
|
252
|
+
if session:
|
|
253
|
+
print(
|
|
254
|
+
json.dumps(
|
|
255
|
+
{
|
|
256
|
+
"status": "ended",
|
|
257
|
+
"total_tokens": session["totals"]["total_tokens"],
|
|
258
|
+
"cost_usd": round(session["totals"]["cost_usd"], 4),
|
|
259
|
+
"saved": session["totals"]["total_tokens"] > 0,
|
|
260
|
+
}
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
else:
|
|
264
|
+
print(json.dumps({"status": "no_session"}))
|
|
265
|
+
|
|
266
|
+
elif args.command == "status":
|
|
267
|
+
status = get_status()
|
|
268
|
+
print(json.dumps(status))
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
if __name__ == "__main__":
|
|
272
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"statusLine": {
|
|
3
|
+
"type": "command",
|
|
4
|
+
"command": "~/.claude/statusline.sh"
|
|
5
|
+
},
|
|
6
|
+
"hooks": {
|
|
7
|
+
"SessionStart": [
|
|
8
|
+
{
|
|
9
|
+
"hooks": [
|
|
10
|
+
{
|
|
11
|
+
"type": "command",
|
|
12
|
+
"command": ".claude/hooks/session-startup.sh"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"Stop": [
|
|
18
|
+
{
|
|
19
|
+
"hooks": [
|
|
20
|
+
{
|
|
21
|
+
"type": "command",
|
|
22
|
+
"command": ".claude/hooks/session-stop.sh"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"Notification": [
|
|
28
|
+
{
|
|
29
|
+
"hooks": [
|
|
30
|
+
{
|
|
31
|
+
"type": "command",
|
|
32
|
+
"command": ".claude/hooks/session-notification.sh"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: costs
|
|
3
|
+
description: View cost dashboard and spending analytics (project)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cost Dashboard Skill
|
|
7
|
+
|
|
8
|
+
Display Devflow cost tracking and spending analytics by reading session data directly.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/costs [options]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Options
|
|
17
|
+
|
|
18
|
+
| Option | Description |
|
|
19
|
+
|--------|-------------|
|
|
20
|
+
| --period day | Show today's costs only |
|
|
21
|
+
| --period week | Show this week's costs |
|
|
22
|
+
| --period month | Show this month's costs (default) |
|
|
23
|
+
| --history N | Show last N sessions |
|
|
24
|
+
| --story KEY | Filter by story key |
|
|
25
|
+
|
|
26
|
+
## Prompt
|
|
27
|
+
|
|
28
|
+
You are displaying the Devflow cost dashboard.
|
|
29
|
+
|
|
30
|
+
**Arguments:** $ARGUMENTS
|
|
31
|
+
|
|
32
|
+
### Step 1: Read Configuration
|
|
33
|
+
|
|
34
|
+
Read the configuration file:
|
|
35
|
+
- Path: `tooling/.automation/costs/config.json`
|
|
36
|
+
|
|
37
|
+
This contains:
|
|
38
|
+
- `budget_dev`: Development budget (USD)
|
|
39
|
+
- `subscription_plan`: Current plan (free/pro)
|
|
40
|
+
- `subscription_token_limit`: Monthly token limit
|
|
41
|
+
- `subscription_billing_period_days`: Days in billing period
|
|
42
|
+
- `display_currencies`: Currencies to show
|
|
43
|
+
- `currency_rates`: Exchange rates
|
|
44
|
+
|
|
45
|
+
### Step 2: Find Session Files
|
|
46
|
+
|
|
47
|
+
Find all session files:
|
|
48
|
+
- Path pattern: `tooling/.automation/costs/sessions/*.json`
|
|
49
|
+
|
|
50
|
+
### Step 3: Read and Aggregate Session Data
|
|
51
|
+
|
|
52
|
+
For each session file, extract:
|
|
53
|
+
- `session_id`: Session identifier
|
|
54
|
+
- `start_time` / `end_time`: Timestamps
|
|
55
|
+
- `story_key`: Associated story (if any)
|
|
56
|
+
- `entries[]`: Array of cost entries with `model`, `input_tokens`, `output_tokens`, `cost_usd`
|
|
57
|
+
- `totals`: Aggregated totals for the session
|
|
58
|
+
|
|
59
|
+
Identify the most recent session as the "current session".
|
|
60
|
+
|
|
61
|
+
### Step 4: Calculate Metrics
|
|
62
|
+
|
|
63
|
+
Calculate:
|
|
64
|
+
1. **Current session tokens/cost**: From the most recent session
|
|
65
|
+
2. **Cumulative tokens**: Sum of all tokens across ALL sessions this billing period
|
|
66
|
+
3. **Cumulative cost**: Sum of all `cost_usd` across ALL sessions
|
|
67
|
+
4. **Cost by model**: Group costs by model (opus, sonnet, haiku)
|
|
68
|
+
5. **Cost by story**: Group costs by story_key
|
|
69
|
+
6. **Budget usage**: (cumulative_cost / budget_dev) * 100
|
|
70
|
+
7. **Subscription usage**: (cumulative_tokens / subscription_token_limit) * 100
|
|
71
|
+
8. **Average cost per session**: cumulative_cost / session_count
|
|
72
|
+
9. **Average tokens per session**: cumulative_tokens / session_count
|
|
73
|
+
10. **Input/output ratio**: total_input_tokens / total_output_tokens
|
|
74
|
+
11. **Days remaining**: Calculate from billing period start
|
|
75
|
+
12. **Projected monthly cost**: (cumulative_cost / days_elapsed) * 30
|
|
76
|
+
13. **Projected token usage**: (cumulative_tokens / days_elapsed) * 30
|
|
77
|
+
|
|
78
|
+
### Step 5: Apply Filters
|
|
79
|
+
|
|
80
|
+
Based on $ARGUMENTS:
|
|
81
|
+
- `--period day`: Filter sessions from today only
|
|
82
|
+
- `--period week`: Filter sessions from last 7 days
|
|
83
|
+
- `--period month`: Filter sessions from last 30 days (default)
|
|
84
|
+
- `--history N`: Show only last N sessions
|
|
85
|
+
- `--story KEY`: Filter sessions matching story_key
|
|
86
|
+
|
|
87
|
+
### Step 6: Format Output
|
|
88
|
+
|
|
89
|
+
Display the dashboard using this format:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
=================================================================
|
|
93
|
+
DEVFLOW COST DASHBOARD
|
|
94
|
+
=================================================================
|
|
95
|
+
Plan: [plan] | Tokens: [cumulative]/[limit] ([%]%) | [days] days left
|
|
96
|
+
This Session: $[current_cost] | Cumulative: $[total_cost]
|
|
97
|
+
=================================================================
|
|
98
|
+
|
|
99
|
+
PERIOD: [period] SESSIONS: [count]
|
|
100
|
+
|
|
101
|
+
--- TOKEN USAGE ---------------------------------------------
|
|
102
|
+
This Session Cumulative
|
|
103
|
+
Input: [current_in] [total_in]
|
|
104
|
+
Output: [current_out] [total_out]
|
|
105
|
+
Total: [current_total] [total_total]
|
|
106
|
+
|
|
107
|
+
I/O Ratio: [ratio]:1 (higher = more input-heavy conversations)
|
|
108
|
+
|
|
109
|
+
--- COST BY MODEL -------------------------------------------
|
|
110
|
+
[model] $[cost] ([%]%) [bar]
|
|
111
|
+
|
|
112
|
+
--- COST BY STORY -------------------------------------------
|
|
113
|
+
[story-key] $[cost] ([%]%)
|
|
114
|
+
(no story) $[cost] ([%]%)
|
|
115
|
+
|
|
116
|
+
--- BUDGET STATUS -------------------------------------------
|
|
117
|
+
Spent: $[total] / $[budget] ([%]%)
|
|
118
|
+
[================================--------------------] [%]%
|
|
119
|
+
|
|
120
|
+
[WARNING] if > 75%: "Approaching budget limit!"
|
|
121
|
+
[CRITICAL] if > 90%: "Near budget limit - consider pausing"
|
|
122
|
+
|
|
123
|
+
--- PROJECTIONS (based on current usage rate) ---------------
|
|
124
|
+
Monthly token projection: [projected] / [limit] ([%]%)
|
|
125
|
+
Monthly cost projection: $[projected_cost]
|
|
126
|
+
Avg cost per session: $[avg_cost]
|
|
127
|
+
Avg tokens per session: [avg_tokens]
|
|
128
|
+
|
|
129
|
+
--- RECENT SESSIONS -----------------------------------------
|
|
130
|
+
[session_id] [date] [tokens] $[cost] [story or "-"]
|
|
131
|
+
[session_id] [date] [tokens] $[cost] [story or "-"]
|
|
132
|
+
[session_id] [date] [tokens] $[cost] [story or "-"]
|
|
133
|
+
(show last 5 sessions)
|
|
134
|
+
|
|
135
|
+
--- CURRENCIES ----------------------------------------------
|
|
136
|
+
$[USD] | E[EUR] | L[GBP] | R$[BRL]
|
|
137
|
+
=================================================================
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Budget Warnings
|
|
141
|
+
|
|
142
|
+
Display warnings based on config thresholds:
|
|
143
|
+
- If budget usage > `warning_percent` (75%): Show [WARNING]
|
|
144
|
+
- If budget usage > `critical_percent` (90%): Show [CRITICAL]
|
|
145
|
+
- If subscription usage > 80%: Show token limit warning
|
|
146
|
+
|
|
147
|
+
### Notes
|
|
148
|
+
|
|
149
|
+
- Format large numbers with K/M suffixes (e.g., 1.5K, 2.3M)
|
|
150
|
+
- Round costs to 2 decimal places
|
|
151
|
+
- Show percentages to 1 decimal place
|
|
152
|
+
- Use text-based progress bars with = and - characters
|
|
153
|
+
- Current session = most recent session file by timestamp
|
|
154
|
+
- Cumulative = sum of ALL sessions in the billing period
|
|
155
|
+
- If no sessions found, display a message indicating no cost data available
|
|
156
|
+
- Calculate days remaining: billing_period_days - days since first session of period
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Validate Skill
|
|
2
|
+
|
|
3
|
+
Run automated validation checks with feedback loops.
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
The validate skill runs a three-tier validation system:
|
|
8
|
+
|
|
9
|
+
1. **Pre-flight** (Tier 1): Checks before execution
|
|
10
|
+
- Story file exists
|
|
11
|
+
- Budget is available
|
|
12
|
+
- Dependencies are valid
|
|
13
|
+
|
|
14
|
+
2. **Inter-phase** (Tier 2): Checks between agents
|
|
15
|
+
- Code compiles/parses
|
|
16
|
+
- Linting passes
|
|
17
|
+
- Phase transitions are valid
|
|
18
|
+
|
|
19
|
+
3. **Post-completion** (Tier 3): Checks after pipeline
|
|
20
|
+
- Tests pass
|
|
21
|
+
- Types are valid
|
|
22
|
+
- Version is synced
|
|
23
|
+
- Changelog is updated
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
/validate [story-key] [--tier 1|2|3|all] [--auto-fix] [--json]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Arguments
|
|
32
|
+
|
|
33
|
+
| Argument | Description | Default |
|
|
34
|
+
|----------|-------------|---------|
|
|
35
|
+
| story-key | Story identifier to validate | Current branch |
|
|
36
|
+
| --tier | Validation tier (1, 2, 3, or all) | all |
|
|
37
|
+
| --auto-fix | Attempt automatic fixes | false |
|
|
38
|
+
| --json | Output as JSON | false |
|
|
39
|
+
| --quiet | Minimal output | false |
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
### Run all validations
|
|
44
|
+
```
|
|
45
|
+
/validate 3-5 --tier all
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Run post-completion checks with auto-fix
|
|
49
|
+
```
|
|
50
|
+
/validate --tier 3 --auto-fix
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Get JSON output for CI
|
|
54
|
+
```
|
|
55
|
+
/validate 3-5 --json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Prompt
|
|
59
|
+
|
|
60
|
+
You are running validation checks for the Devflow project.
|
|
61
|
+
|
|
62
|
+
$ARGUMENTS
|
|
63
|
+
|
|
64
|
+
Execute the validation loop:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import sys
|
|
68
|
+
sys.path.insert(0, "tooling/scripts/lib")
|
|
69
|
+
|
|
70
|
+
from validation_loop import (
|
|
71
|
+
create_validation_loop,
|
|
72
|
+
run_preflight_validation,
|
|
73
|
+
run_post_completion_validation,
|
|
74
|
+
ALL_GATES,
|
|
75
|
+
LoopContext,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Parse arguments
|
|
79
|
+
story_key = "$ARGUMENTS".split()[0] if "$ARGUMENTS" else "validation-check"
|
|
80
|
+
tier = "all" # Default to all tiers
|
|
81
|
+
|
|
82
|
+
# Create context
|
|
83
|
+
context = LoopContext(story_key=story_key)
|
|
84
|
+
|
|
85
|
+
# Run appropriate validation
|
|
86
|
+
if tier == "1" or tier == "all":
|
|
87
|
+
print("[VALIDATION] Running pre-flight checks...")
|
|
88
|
+
report = run_preflight_validation(story_key)
|
|
89
|
+
print(report.to_summary())
|
|
90
|
+
|
|
91
|
+
if tier == "3" or tier == "all":
|
|
92
|
+
print("[VALIDATION] Running post-completion checks...")
|
|
93
|
+
report = run_post_completion_validation(story_key)
|
|
94
|
+
print(report.to_summary())
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Report results clearly with:
|
|
98
|
+
- [PASS] for passed gates
|
|
99
|
+
- [WARN] for warnings
|
|
100
|
+
- [FAIL] for failures
|
|
101
|
+
- Suggested fixes when available
|