agent-notes 2.0.4__py3-none-any.whl
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.
- agent_notes/VERSION +1 -0
- agent_notes/__init__.py +1 -0
- agent_notes/__main__.py +4 -0
- agent_notes/cli.py +348 -0
- agent_notes/commands/__init__.py +27 -0
- agent_notes/commands/_install_helpers.py +262 -0
- agent_notes/commands/build.py +170 -0
- agent_notes/commands/doctor.py +112 -0
- agent_notes/commands/info.py +95 -0
- agent_notes/commands/install.py +99 -0
- agent_notes/commands/list.py +169 -0
- agent_notes/commands/memory.py +430 -0
- agent_notes/commands/regenerate.py +152 -0
- agent_notes/commands/set_role.py +143 -0
- agent_notes/commands/uninstall.py +26 -0
- agent_notes/commands/update.py +169 -0
- agent_notes/commands/validate.py +199 -0
- agent_notes/commands/wizard.py +720 -0
- agent_notes/config.py +154 -0
- agent_notes/data/agents/agents.yaml +352 -0
- agent_notes/data/agents/analyst.md +45 -0
- agent_notes/data/agents/api-reviewer.md +47 -0
- agent_notes/data/agents/architect.md +46 -0
- agent_notes/data/agents/coder.md +28 -0
- agent_notes/data/agents/database-specialist.md +45 -0
- agent_notes/data/agents/debugger.md +47 -0
- agent_notes/data/agents/devil.md +47 -0
- agent_notes/data/agents/devops.md +38 -0
- agent_notes/data/agents/explorer.md +23 -0
- agent_notes/data/agents/integrations.md +44 -0
- agent_notes/data/agents/lead.md +216 -0
- agent_notes/data/agents/performance-profiler.md +44 -0
- agent_notes/data/agents/refactorer.md +48 -0
- agent_notes/data/agents/reviewer.md +44 -0
- agent_notes/data/agents/security-auditor.md +44 -0
- agent_notes/data/agents/system-auditor.md +38 -0
- agent_notes/data/agents/tech-writer.md +32 -0
- agent_notes/data/agents/test-runner.md +36 -0
- agent_notes/data/agents/test-writer.md +39 -0
- agent_notes/data/cli/claude.yaml +25 -0
- agent_notes/data/cli/copilot.yaml +18 -0
- agent_notes/data/cli/opencode.yaml +22 -0
- agent_notes/data/commands/brainstorm.md +8 -0
- agent_notes/data/commands/debug.md +9 -0
- agent_notes/data/commands/review.md +10 -0
- agent_notes/data/global-claude.md +290 -0
- agent_notes/data/global-copilot.md +27 -0
- agent_notes/data/global-opencode.md +40 -0
- agent_notes/data/hooks/session-context.md.tpl +19 -0
- agent_notes/data/models/claude-haiku-4-5.yaml +15 -0
- agent_notes/data/models/claude-opus-4-1.yaml +16 -0
- agent_notes/data/models/claude-opus-4-5.yaml +16 -0
- agent_notes/data/models/claude-opus-4-6.yaml +16 -0
- agent_notes/data/models/claude-opus-4-7.yaml +15 -0
- agent_notes/data/models/claude-sonnet-4-5.yaml +16 -0
- agent_notes/data/models/claude-sonnet-4-6.yaml +15 -0
- agent_notes/data/models/claude-sonnet-4.yaml +16 -0
- agent_notes/data/pricing.yaml +33 -0
- agent_notes/data/roles/orchestrator.yaml +5 -0
- agent_notes/data/roles/reasoner.yaml +5 -0
- agent_notes/data/roles/scout.yaml +5 -0
- agent_notes/data/roles/worker.yaml +5 -0
- agent_notes/data/rules/code-quality.md +9 -0
- agent_notes/data/rules/safety.md +10 -0
- agent_notes/data/scripts/cost-report +211 -0
- agent_notes/data/skills/brainstorming/SKILL.md +57 -0
- agent_notes/data/skills/code-review/SKILL.md +64 -0
- agent_notes/data/skills/debugging-protocol/SKILL.md +51 -0
- agent_notes/data/skills/docker-compose/SKILL.md +318 -0
- agent_notes/data/skills/docker-compose-advanced/SKILL.md +575 -0
- agent_notes/data/skills/docker-dockerfile/SKILL.md +385 -0
- agent_notes/data/skills/docker-dockerfile-languages/SKILL.md +293 -0
- agent_notes/data/skills/git/SKILL.md +87 -0
- agent_notes/data/skills/rails-active-storage/SKILL.md +321 -0
- agent_notes/data/skills/rails-broadcasting/SKILL.md +374 -0
- agent_notes/data/skills/rails-concerns/SKILL.md +806 -0
- agent_notes/data/skills/rails-controllers/SKILL.md +510 -0
- agent_notes/data/skills/rails-controllers-advanced/SKILL.md +441 -0
- agent_notes/data/skills/rails-helpers/SKILL.md +677 -0
- agent_notes/data/skills/rails-initializers/SKILL.md +79 -0
- agent_notes/data/skills/rails-javascript/SKILL.md +567 -0
- agent_notes/data/skills/rails-jobs/SKILL.md +700 -0
- agent_notes/data/skills/rails-kamal/SKILL.md +483 -0
- agent_notes/data/skills/rails-lib/SKILL.md +101 -0
- agent_notes/data/skills/rails-mailers/SKILL.md +321 -0
- agent_notes/data/skills/rails-migrations/SKILL.md +268 -0
- agent_notes/data/skills/rails-models/SKILL.md +459 -0
- agent_notes/data/skills/rails-models-advanced/SKILL.md +398 -0
- agent_notes/data/skills/rails-routes/SKILL.md +804 -0
- agent_notes/data/skills/rails-style/SKILL.md +538 -0
- agent_notes/data/skills/rails-testing-controllers/SKILL.md +343 -0
- agent_notes/data/skills/rails-testing-models/SKILL.md +296 -0
- agent_notes/data/skills/rails-testing-system/SKILL.md +375 -0
- agent_notes/data/skills/rails-validations/SKILL.md +108 -0
- agent_notes/data/skills/rails-view-components/SKILL.md +511 -0
- agent_notes/data/skills/rails-view-components-advanced/SKILL.md +376 -0
- agent_notes/data/skills/rails-views/SKILL.md +413 -0
- agent_notes/data/skills/rails-views-advanced/SKILL.md +450 -0
- agent_notes/data/skills/refactoring-protocol/SKILL.md +64 -0
- agent_notes/data/skills/tdd/SKILL.md +57 -0
- agent_notes/data/templates/__init__.py +1 -0
- agent_notes/data/templates/__pycache__/__init__.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__init__.py +1 -0
- agent_notes/data/templates/frontmatter/__pycache__/__init__.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__pycache__/claude.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__pycache__/cursor.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__pycache__/opencode.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/claude.py +44 -0
- agent_notes/data/templates/frontmatter/opencode.py +104 -0
- agent_notes/doctor_checks.py +189 -0
- agent_notes/domain/__init__.py +17 -0
- agent_notes/domain/agent.py +34 -0
- agent_notes/domain/cli_backend.py +40 -0
- agent_notes/domain/diagnostics.py +29 -0
- agent_notes/domain/diff.py +44 -0
- agent_notes/domain/model.py +27 -0
- agent_notes/domain/role.py +13 -0
- agent_notes/domain/rule.py +13 -0
- agent_notes/domain/skill.py +15 -0
- agent_notes/domain/state.py +46 -0
- agent_notes/install_state.py +11 -0
- agent_notes/registries/__init__.py +16 -0
- agent_notes/registries/_base.py +46 -0
- agent_notes/registries/agent_registry.py +107 -0
- agent_notes/registries/cli_registry.py +89 -0
- agent_notes/registries/model_registry.py +85 -0
- agent_notes/registries/role_registry.py +64 -0
- agent_notes/registries/rule_registry.py +80 -0
- agent_notes/registries/skill_registry.py +141 -0
- agent_notes/services/__init__.py +8 -0
- agent_notes/services/diagnostics/__init__.py +47 -0
- agent_notes/services/diagnostics/_checks.py +272 -0
- agent_notes/services/diagnostics/_display.py +346 -0
- agent_notes/services/diagnostics/_fix.py +169 -0
- agent_notes/services/diff.py +349 -0
- agent_notes/services/fs.py +195 -0
- agent_notes/services/install_state_builder.py +210 -0
- agent_notes/services/installer.py +293 -0
- agent_notes/services/memory_backend.py +155 -0
- agent_notes/services/rendering.py +329 -0
- agent_notes/services/session_context.py +23 -0
- agent_notes/services/settings_writer.py +79 -0
- agent_notes/services/state_store.py +249 -0
- agent_notes/services/ui.py +419 -0
- agent_notes/services/user_config.py +62 -0
- agent_notes/services/validation.py +67 -0
- agent_notes/state.py +21 -0
- agent_notes-2.0.4.dist-info/METADATA +14 -0
- agent_notes-2.0.4.dist-info/RECORD +162 -0
- agent_notes-2.0.4.dist-info/WHEEL +5 -0
- agent_notes-2.0.4.dist-info/entry_points.txt +2 -0
- agent_notes-2.0.4.dist-info/licenses/LICENSE +21 -0
- agent_notes-2.0.4.dist-info/top_level.txt +2 -0
- tests/conftest.py +20 -0
- tests/functional/__init__.py +0 -0
- tests/functional/test_build_commands.py +88 -0
- tests/functional/test_registries.py +128 -0
- tests/integration/__init__.py +0 -0
- tests/integration/test_build_output.py +129 -0
- tests/plugins/__init__.py +0 -0
- tests/plugins/test_agents.py +93 -0
- tests/plugins/test_skills.py +77 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
id: claude-opus-4-5
|
|
2
|
+
label: Claude Opus 4.5
|
|
3
|
+
family: claude
|
|
4
|
+
class: opus
|
|
5
|
+
deprecated: true
|
|
6
|
+
aliases:
|
|
7
|
+
anthropic: claude-opus-4-5
|
|
8
|
+
github-copilot: github-copilot/claude-opus-4.5
|
|
9
|
+
pricing:
|
|
10
|
+
input: 5.00
|
|
11
|
+
output: 25.00
|
|
12
|
+
cache: 0.50
|
|
13
|
+
capabilities:
|
|
14
|
+
vision: true
|
|
15
|
+
long_context: false
|
|
16
|
+
tool_use: true
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
id: claude-opus-4-6
|
|
2
|
+
label: Claude Opus 4.6
|
|
3
|
+
family: claude
|
|
4
|
+
class: opus
|
|
5
|
+
deprecated: true
|
|
6
|
+
aliases:
|
|
7
|
+
anthropic: claude-opus-4-6
|
|
8
|
+
github-copilot: github-copilot/claude-opus-4.6
|
|
9
|
+
pricing:
|
|
10
|
+
input: 5.00
|
|
11
|
+
output: 25.00
|
|
12
|
+
cache: 0.50
|
|
13
|
+
capabilities:
|
|
14
|
+
vision: true
|
|
15
|
+
long_context: true
|
|
16
|
+
tool_use: true
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
id: claude-opus-4-7
|
|
2
|
+
label: Claude Opus 4.7
|
|
3
|
+
family: claude
|
|
4
|
+
class: opus
|
|
5
|
+
aliases:
|
|
6
|
+
anthropic: opus
|
|
7
|
+
github-copilot: github-copilot/claude-opus-4.7
|
|
8
|
+
pricing:
|
|
9
|
+
input: 5.00
|
|
10
|
+
output: 25.00
|
|
11
|
+
cache: 0.50
|
|
12
|
+
capabilities:
|
|
13
|
+
vision: true
|
|
14
|
+
long_context: true
|
|
15
|
+
tool_use: true
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
id: claude-sonnet-4-5
|
|
2
|
+
label: Claude Sonnet 4.5
|
|
3
|
+
family: claude
|
|
4
|
+
class: sonnet
|
|
5
|
+
deprecated: true
|
|
6
|
+
aliases:
|
|
7
|
+
anthropic: claude-sonnet-4-5
|
|
8
|
+
github-copilot: github-copilot/claude-sonnet-4.5
|
|
9
|
+
pricing:
|
|
10
|
+
input: 3.00
|
|
11
|
+
output: 15.00
|
|
12
|
+
cache: 0.30
|
|
13
|
+
capabilities:
|
|
14
|
+
vision: true
|
|
15
|
+
long_context: false
|
|
16
|
+
tool_use: true
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
id: claude-sonnet-4-6
|
|
2
|
+
label: Claude Sonnet 4.6
|
|
3
|
+
family: claude
|
|
4
|
+
class: sonnet
|
|
5
|
+
aliases:
|
|
6
|
+
anthropic: sonnet
|
|
7
|
+
github-copilot: github-copilot/claude-sonnet-4.6
|
|
8
|
+
pricing:
|
|
9
|
+
input: 3.00
|
|
10
|
+
output: 15.00
|
|
11
|
+
cache: 0.30
|
|
12
|
+
capabilities:
|
|
13
|
+
vision: true
|
|
14
|
+
long_context: true
|
|
15
|
+
tool_use: true
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
id: claude-sonnet-4
|
|
2
|
+
label: Claude Sonnet 4
|
|
3
|
+
family: claude
|
|
4
|
+
class: sonnet
|
|
5
|
+
deprecated: true
|
|
6
|
+
aliases:
|
|
7
|
+
anthropic: claude-sonnet-4-20250514
|
|
8
|
+
github-copilot: github-copilot/claude-sonnet-4
|
|
9
|
+
pricing:
|
|
10
|
+
input: 3.00
|
|
11
|
+
output: 15.00
|
|
12
|
+
cache: 0.30
|
|
13
|
+
capabilities:
|
|
14
|
+
vision: true
|
|
15
|
+
long_context: true
|
|
16
|
+
tool_use: true
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
baseline:
|
|
2
|
+
label: Claude Opus 4.7
|
|
3
|
+
price:
|
|
4
|
+
in: 5.00
|
|
5
|
+
out: 25.00
|
|
6
|
+
cache: 0.50
|
|
7
|
+
|
|
8
|
+
providers:
|
|
9
|
+
- name: Anthropic
|
|
10
|
+
models:
|
|
11
|
+
- name: Claude Haiku 4.5
|
|
12
|
+
match: "*haiku*"
|
|
13
|
+
price: {in: 1.00, out: 5.00, cache: 0.10}
|
|
14
|
+
updated_at: "2026-04"
|
|
15
|
+
- name: Claude Sonnet
|
|
16
|
+
match: "*sonnet*"
|
|
17
|
+
price: {in: 3.00, out: 15.00, cache: 0.30}
|
|
18
|
+
updated_at: "2026-04"
|
|
19
|
+
- name: Claude Opus 4.7 / 4.6 / 4.5
|
|
20
|
+
match: ["*opus-4.7*", "*opus-4.6*", "*opus-4.5*"]
|
|
21
|
+
price: {in: 5.00, out: 25.00, cache: 0.50}
|
|
22
|
+
updated_at: "2026-04"
|
|
23
|
+
- name: Claude Opus (legacy)
|
|
24
|
+
match: ["*opus-4.1*", "*opus*"]
|
|
25
|
+
price: {in: 15.00, out: 75.00, cache: 1.50}
|
|
26
|
+
updated_at: "2026-04"
|
|
27
|
+
|
|
28
|
+
- name: OpenAI
|
|
29
|
+
models:
|
|
30
|
+
- name: GPT-4 / o-series
|
|
31
|
+
match: ["gpt-*", "o1*", "o3*", "o4*"]
|
|
32
|
+
price: {in: 2.50, out: 10.00, cache: 0.50}
|
|
33
|
+
updated_at: "2026-04"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Code Quality
|
|
2
|
+
|
|
3
|
+
- Read existing code first. Match project indentation, naming, and file organization.
|
|
4
|
+
- Small focused methods. One responsibility per method.
|
|
5
|
+
- Guard clauses and early returns over deep nesting.
|
|
6
|
+
- Meaningful names that describe purpose, not implementation.
|
|
7
|
+
- Only build what was asked for. No speculative abstractions.
|
|
8
|
+
- Validate at system boundaries (user input, external APIs). Trust internal code.
|
|
9
|
+
- No comments for obvious code. Comment the "why", not the "what".
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Safety
|
|
2
|
+
|
|
3
|
+
- Confirm before destructive or irreversible operations:
|
|
4
|
+
- `git push --force`, `git reset --hard`, amending published commits
|
|
5
|
+
- `rm -rf`, deleting files or branches, dropping database tables
|
|
6
|
+
- Operations visible to others: pushing code, commenting on PRs/issues
|
|
7
|
+
- Never commit secrets: `.env`, `*.pem`, credentials, API keys, tokens.
|
|
8
|
+
- Never bypass safety checks (`--no-verify`, `--force`) without explicit user request.
|
|
9
|
+
- Never force-push to main or master branches.
|
|
10
|
+
- When encountering obstacles, do not use destructive actions as shortcuts.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Session cost report — prices sourced from agent_notes/data/pricing.yaml."""
|
|
3
|
+
import sqlite3
|
|
4
|
+
from fnmatch import fnmatch
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
BOLD = "\033[1m"
|
|
8
|
+
DIM = "\033[2m"
|
|
9
|
+
YELLOW = "\033[0;33m"
|
|
10
|
+
GREEN = "\033[0;32m"
|
|
11
|
+
CYAN = "\033[0;36m"
|
|
12
|
+
NC = "\033[0m"
|
|
13
|
+
|
|
14
|
+
PRICING = {{PRICING}}
|
|
15
|
+
|
|
16
|
+
DB = Path.home() / ".local/share/opencode/opencode.db"
|
|
17
|
+
|
|
18
|
+
SQL = """
|
|
19
|
+
WITH cs AS (SELECT id FROM session WHERE parent_id IS NULL ORDER BY time_created DESC LIMIT 1),
|
|
20
|
+
conv_start AS (
|
|
21
|
+
SELECT COALESCE(
|
|
22
|
+
(SELECT json_extract(m2.data,'$.time.created')
|
|
23
|
+
FROM message m1 JOIN message m2 ON m1.session_id=m2.session_id
|
|
24
|
+
WHERE m1.session_id=(SELECT id FROM cs)
|
|
25
|
+
AND json_extract(m2.data,'$.time.created') > json_extract(m1.data,'$.time.created')
|
|
26
|
+
AND json_extract(m2.data,'$.time.created') - json_extract(m1.data,'$.time.created') > 1800000
|
|
27
|
+
AND NOT EXISTS (
|
|
28
|
+
SELECT 1 FROM message mx WHERE mx.session_id=m1.session_id
|
|
29
|
+
AND json_extract(mx.data,'$.time.created') > json_extract(m1.data,'$.time.created')
|
|
30
|
+
AND json_extract(mx.data,'$.time.created') < json_extract(m2.data,'$.time.created'))
|
|
31
|
+
ORDER BY json_extract(m1.data,'$.time.created') DESC LIMIT 1),
|
|
32
|
+
0) AS start_ts
|
|
33
|
+
)
|
|
34
|
+
SELECT
|
|
35
|
+
COALESCE(json_extract(m.data,'$.agent'), 'lead') AS agent,
|
|
36
|
+
(SELECT json_extract(m2.data,'$.modelID') FROM message m2
|
|
37
|
+
WHERE m2.session_id = s.id AND json_extract(m2.data,'$.role') = 'assistant'
|
|
38
|
+
ORDER BY json_extract(m2.data,'$.time.completed') DESC LIMIT 1) AS model,
|
|
39
|
+
SUM(json_extract(m.data,'$.tokens.input')) AS inp,
|
|
40
|
+
SUM(json_extract(m.data,'$.tokens.output')) AS outp,
|
|
41
|
+
SUM(json_extract(m.data,'$.tokens.cache.read')) AS cache,
|
|
42
|
+
ROUND(SUM(
|
|
43
|
+
CASE WHEN json_extract(m.data,'$.time.completed') IS NOT NULL
|
|
44
|
+
AND json_extract(m.data,'$.time.created') IS NOT NULL
|
|
45
|
+
THEN (json_extract(m.data,'$.time.completed') - json_extract(m.data,'$.time.created')) / 1000.0
|
|
46
|
+
ELSE 0 END
|
|
47
|
+
), 1) AS sec
|
|
48
|
+
FROM session s
|
|
49
|
+
JOIN message m ON m.session_id = s.id
|
|
50
|
+
CROSS JOIN cs
|
|
51
|
+
CROSS JOIN conv_start
|
|
52
|
+
WHERE (s.parent_id = cs.id OR s.id = cs.id)
|
|
53
|
+
AND json_extract(m.data,'$.role') = 'assistant'
|
|
54
|
+
AND json_extract(m.data,'$.time.created') >= conv_start.start_ts
|
|
55
|
+
AND (s.time_created >= conv_start.start_ts OR s.id = (SELECT id FROM cs))
|
|
56
|
+
GROUP BY s.id
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _build_price_table() -> list[tuple[list[str], dict]]:
|
|
61
|
+
rows = []
|
|
62
|
+
for provider in PRICING.get("providers", []):
|
|
63
|
+
for model in provider.get("models", []):
|
|
64
|
+
patterns = model["match"] if isinstance(model["match"], list) else [model["match"]]
|
|
65
|
+
rows.append((patterns, model["price"]))
|
|
66
|
+
return rows
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
_PRICE_TABLE = _build_price_table()
|
|
70
|
+
_BASELINE = PRICING["baseline"]["price"]
|
|
71
|
+
_BASELINE_LABEL = PRICING["baseline"]["label"]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_price(model_id: str) -> dict:
|
|
75
|
+
for patterns, price in _PRICE_TABLE:
|
|
76
|
+
if any(fnmatch(model_id, p) for p in patterns):
|
|
77
|
+
return price
|
|
78
|
+
return {"in": 3.00, "out": 15.00, "cache": 0.30}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def calculate_cost(model_id: str, inp: int, outp: int, cache: int) -> float:
|
|
82
|
+
p = get_price(model_id)
|
|
83
|
+
return (inp * p["in"] + outp * p["out"] + cache * p["cache"]) / 1_000_000
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def baseline_cost(inp: int, outp: int, cache: int) -> float:
|
|
87
|
+
p = _BASELINE
|
|
88
|
+
return (inp * p["in"] + outp * p["out"] + cache * p["cache"]) / 1_000_000
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def tier_color(model_id: str) -> str:
|
|
92
|
+
if "opus" in model_id:
|
|
93
|
+
return YELLOW
|
|
94
|
+
if "sonnet" in model_id:
|
|
95
|
+
return CYAN
|
|
96
|
+
return DIM
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def fmt_num(n: int) -> str:
|
|
100
|
+
if n >= 1_000_000:
|
|
101
|
+
return f"{n / 1_000_000:.2f}m"
|
|
102
|
+
if n >= 1_000:
|
|
103
|
+
return f"{n / 1_000:.2f}k"
|
|
104
|
+
return str(n)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def fmt_tokens(inp, outp, cache) -> str:
|
|
108
|
+
return f"{fmt_num(inp)}/{fmt_num(outp)}/{fmt_num(cache)}"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def fmt_time(sec: float) -> str:
|
|
112
|
+
s = int(round(sec))
|
|
113
|
+
if s < 60:
|
|
114
|
+
return f"{s}s"
|
|
115
|
+
m, s = divmod(s, 60)
|
|
116
|
+
if m < 60:
|
|
117
|
+
return f"{m}m {s}s" if s else f"{m}m"
|
|
118
|
+
h, m = divmod(m, 60)
|
|
119
|
+
return f"{h}h {m}m" if m else f"{h}h"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def fmt_cost(c: float) -> str:
|
|
123
|
+
return f"${c:.4f}"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def main() -> None:
|
|
127
|
+
if not DB.exists():
|
|
128
|
+
print(f"Database not found: {DB}")
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
rows = sqlite3.connect(DB).execute(SQL).fetchall()
|
|
132
|
+
if not rows:
|
|
133
|
+
print("No sessions found.")
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
records = [
|
|
137
|
+
(agent, model or "unknown", inp or 0, outp or 0, cache or 0, sec or 0)
|
|
138
|
+
for agent, model, inp, outp, cache, sec in rows
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
costs = [
|
|
142
|
+
(agent, model, inp, outp, cache, sec,
|
|
143
|
+
calculate_cost(model, inp, outp, cache),
|
|
144
|
+
baseline_cost(inp, outp, cache))
|
|
145
|
+
for agent, model, inp, outp, cache, sec in records
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
_total_inp = sum(i for _, _, i, *_ in costs)
|
|
149
|
+
_total_outp = sum(o for _, _, _, o, *_ in costs)
|
|
150
|
+
_total_cache= sum(c for _, _, _, _, c, *_ in costs)
|
|
151
|
+
_max_sec = max(s for _, _, _, _, _, s, *_ in costs)
|
|
152
|
+
_total_sec = sum(s for _, _, _, _, _, s, *_ in costs)
|
|
153
|
+
_total_time = f"{fmt_time(_max_sec)} / {fmt_time(_total_sec)} seq"
|
|
154
|
+
|
|
155
|
+
agent_col_w = max(len(f"{a}({m})") for a, m, *_ in costs) + 2
|
|
156
|
+
tok_col_w = max(
|
|
157
|
+
max(len(fmt_tokens(i, o, c)) for _, _, i, o, c, *_ in costs),
|
|
158
|
+
len(fmt_tokens(_total_inp, _total_outp, _total_cache))
|
|
159
|
+
) + 2
|
|
160
|
+
time_col_w = max(
|
|
161
|
+
max(len(fmt_time(s)) for _, _, _, _, _, s, *_ in costs),
|
|
162
|
+
len(_total_time)
|
|
163
|
+
) + 2
|
|
164
|
+
W = (agent_col_w, tok_col_w, time_col_w, 12, 12)
|
|
165
|
+
|
|
166
|
+
header = (
|
|
167
|
+
f"{'agent(model)':<{W[0]}}"
|
|
168
|
+
f" {'in/out/cache':<{W[1]}}"
|
|
169
|
+
f" {'time':<{W[2]}}"
|
|
170
|
+
f" {'actual':<{W[3]}}"
|
|
171
|
+
f" {f'vs {_BASELINE_LABEL}':<{W[4]}}"
|
|
172
|
+
)
|
|
173
|
+
print(BOLD + header + NC)
|
|
174
|
+
print(DIM + "-" * len(header) + NC)
|
|
175
|
+
|
|
176
|
+
total_inp = total_outp = total_cache = 0
|
|
177
|
+
total_actual = total_vs = max_sec = total_sec = 0.0
|
|
178
|
+
|
|
179
|
+
for agent, model, inp, outp, cache, sec, actual, vs in costs:
|
|
180
|
+
label = f"{agent}({model})"
|
|
181
|
+
time_str = fmt_time(sec)
|
|
182
|
+
col = tier_color(model)
|
|
183
|
+
print(
|
|
184
|
+
col + f"{label:<{W[0]}}" + NC
|
|
185
|
+
+ f" {fmt_tokens(inp, outp, cache):<{W[1]}}"
|
|
186
|
+
+ f" {time_str:<{W[2]}}"
|
|
187
|
+
+ f" {fmt_cost(actual):<{W[3]}}"
|
|
188
|
+
+ f" {fmt_cost(vs):<{W[4]}}"
|
|
189
|
+
)
|
|
190
|
+
total_inp += inp; total_outp += outp; total_cache += cache
|
|
191
|
+
total_actual += actual; total_vs += vs
|
|
192
|
+
max_sec = max(max_sec, sec)
|
|
193
|
+
total_sec += sec
|
|
194
|
+
|
|
195
|
+
saved_pct = round((1 - total_actual / total_vs) * 100) if total_vs else 0
|
|
196
|
+
total_label = f"TOTAL (saved {saved_pct}%)"
|
|
197
|
+
total_time = _total_time
|
|
198
|
+
col = GREEN if total_actual <= 5 else YELLOW
|
|
199
|
+
print(
|
|
200
|
+
col + BOLD
|
|
201
|
+
+ f"{total_label:<{W[0]}}"
|
|
202
|
+
+ f" {fmt_tokens(total_inp, total_outp, total_cache):<{W[1]}}"
|
|
203
|
+
+ f" {total_time:<{W[2]}}"
|
|
204
|
+
+ f" {fmt_cost(total_actual):<{W[3]}}"
|
|
205
|
+
+ f" {fmt_cost(total_vs):<{W[4]}}"
|
|
206
|
+
+ NC
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
main()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: brainstorming
|
|
3
|
+
description: "Explore multiple approaches before committing — read context, surface tradeoffs, then decide"
|
|
4
|
+
group: process
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Brainstorming
|
|
8
|
+
|
|
9
|
+
Use when the problem has multiple valid solutions and the choice has long-term consequences: API design, data model, architecture, technology selection.
|
|
10
|
+
|
|
11
|
+
## Process
|
|
12
|
+
|
|
13
|
+
### 1. Read the context first
|
|
14
|
+
|
|
15
|
+
Before generating options, read:
|
|
16
|
+
- Existing patterns in the codebase — there may already be an established approach to follow.
|
|
17
|
+
- Similar features already implemented — don't invent something the codebase already knows how to do.
|
|
18
|
+
- Any stated constraints in tickets, comments, or configuration.
|
|
19
|
+
|
|
20
|
+
### 2. Generate options (diverge)
|
|
21
|
+
|
|
22
|
+
Produce at least three meaningfully distinct approaches. For each:
|
|
23
|
+
- **Name** — one noun phrase.
|
|
24
|
+
- **Summary** — two sentences max.
|
|
25
|
+
- **Main advantage** — what it does best.
|
|
26
|
+
- **Main cost or risk** — what you're giving up.
|
|
27
|
+
|
|
28
|
+
Do not evaluate yet. Generate first. If one option is clearly dominant, still generate alternatives — the exercise surfaces the tradeoffs you'd otherwise miss.
|
|
29
|
+
|
|
30
|
+
If a key technical assumption is unproven, include a **spike option**: a throwaway implementation to test the risky assumption before committing to a direction.
|
|
31
|
+
|
|
32
|
+
### 3. Apply constraints (filter)
|
|
33
|
+
|
|
34
|
+
Filter against the project's real constraints:
|
|
35
|
+
- Performance requirements
|
|
36
|
+
- Team familiarity and maintainability
|
|
37
|
+
- Existing patterns and dependencies in the codebase
|
|
38
|
+
- Timeline and scope
|
|
39
|
+
|
|
40
|
+
Remove options that violate hard constraints. Do not remove options just because they're unfamiliar or non-obvious.
|
|
41
|
+
|
|
42
|
+
### 4. Recommend (converge)
|
|
43
|
+
|
|
44
|
+
Pick one option. State:
|
|
45
|
+
- Which option you recommend.
|
|
46
|
+
- Why it wins against the specific alternatives — not in the abstract.
|
|
47
|
+
- What you are explicitly trading away — be honest, not diplomatic.
|
|
48
|
+
|
|
49
|
+
Present the recommendation. Do not begin implementation until the user agrees.
|
|
50
|
+
|
|
51
|
+
## Anti-patterns
|
|
52
|
+
|
|
53
|
+
- Generating one real option and two obvious strawmen to make it look like a comparison.
|
|
54
|
+
- Recommending the first option you thought of.
|
|
55
|
+
- Listing tradeoffs without actually comparing them against each other.
|
|
56
|
+
- Skipping the codebase read and inventing patterns from scratch.
|
|
57
|
+
- Framing a spike as a commitment — spikes are exploratory, not production code.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review
|
|
3
|
+
description: "Systematic code review: correctness, safety, performance, clarity, consistency"
|
|
4
|
+
group: process
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Code Review
|
|
8
|
+
|
|
9
|
+
Work through these five lenses in order. Report findings grouped by lens, ranked by severity within each group.
|
|
10
|
+
|
|
11
|
+
## Lens 1 — Correctness
|
|
12
|
+
|
|
13
|
+
- Does the logic match the stated intent?
|
|
14
|
+
- Are edge cases handled: empty input, nil/None, off-by-one, concurrent access, zero/negative values?
|
|
15
|
+
- Are error paths handled and surfaced to callers correctly?
|
|
16
|
+
- Do the tests cover behavior, not just the happy path?
|
|
17
|
+
- Is there new code with no tests at all? That is a blocking issue.
|
|
18
|
+
|
|
19
|
+
## Lens 2 — Safety
|
|
20
|
+
|
|
21
|
+
- Is user input validated at the system boundary?
|
|
22
|
+
- Are secrets, credentials, or PII handled safely — not logged, not exposed in responses?
|
|
23
|
+
- Are SQL queries parameterized, not interpolated?
|
|
24
|
+
- Are file paths sanitized before use?
|
|
25
|
+
- Does this change affect authentication or authorization logic? If yes, flag for deeper scrutiny.
|
|
26
|
+
|
|
27
|
+
## Lens 3 — Performance
|
|
28
|
+
|
|
29
|
+
- Are there N+1 queries — loading a record, then querying for each related record in a loop?
|
|
30
|
+
- Are expensive operations (network calls, disk I/O, serialization) inside hot loops?
|
|
31
|
+
- Are large collections being loaded into memory when streaming or pagination would suffice?
|
|
32
|
+
- Is the change reversible if it causes a production performance regression?
|
|
33
|
+
|
|
34
|
+
## Lens 4 — Clarity
|
|
35
|
+
|
|
36
|
+
- Are names accurate — do they describe what, not how?
|
|
37
|
+
- Is control flow easy to follow? Guard clauses beat deep nesting.
|
|
38
|
+
- Are comments present only where the why is non-obvious? No comments that restate what the code already says.
|
|
39
|
+
- Would a new team member understand this without asking?
|
|
40
|
+
|
|
41
|
+
## Lens 5 — Consistency
|
|
42
|
+
|
|
43
|
+
- Does this match the existing patterns in the codebase?
|
|
44
|
+
- Does it follow project naming conventions?
|
|
45
|
+
- Does it introduce a new abstraction or dependency that already exists elsewhere?
|
|
46
|
+
- Are the tests written in the same style as existing tests?
|
|
47
|
+
|
|
48
|
+
## Output format
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
BLOCKING
|
|
52
|
+
- [file:line] [finding] — [why it matters]
|
|
53
|
+
|
|
54
|
+
SUGGESTIONS
|
|
55
|
+
- [file:line] [finding] — [alternative if applicable]
|
|
56
|
+
|
|
57
|
+
APPROVED (if no blocking issues)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A BLOCKING finding must be resolved before merge. A SUGGESTION is optional.
|
|
61
|
+
|
|
62
|
+
## Scope discipline
|
|
63
|
+
|
|
64
|
+
Do not flag cosmetic changes unless they create real ambiguity. A review that lists 20 nits trains authors to ignore reviews entirely.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debugging-protocol
|
|
3
|
+
description: "4-phase systematic debugging: instrument → evidence → hypothesis → fix"
|
|
4
|
+
group: process
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Debugging Protocol
|
|
8
|
+
|
|
9
|
+
Never guess. Never change code randomly. Follow the four phases.
|
|
10
|
+
|
|
11
|
+
## Phase 1 — Instrument
|
|
12
|
+
|
|
13
|
+
Before forming any hypothesis, add observability:
|
|
14
|
+
- Add logging at the entry and exit of the failing code path.
|
|
15
|
+
- Log inputs, intermediate values, and outputs — not just that something happened.
|
|
16
|
+
- If a test is failing: read the **full stack trace** before doing anything else.
|
|
17
|
+
- If a runtime error: reproduce in the smallest possible isolated case.
|
|
18
|
+
|
|
19
|
+
Do not modify production logic in this phase.
|
|
20
|
+
|
|
21
|
+
**For regressions:** run `git log --oneline -20` and use `git bisect` to find the commit that introduced the failure before instrumenting. Knowing when it broke tells you where to look.
|
|
22
|
+
|
|
23
|
+
## Phase 2 — Gather evidence
|
|
24
|
+
|
|
25
|
+
Run the instrumented version. Collect:
|
|
26
|
+
- Exact error message and file:line location.
|
|
27
|
+
- What values are actually present vs. what was expected.
|
|
28
|
+
- The full call stack at the point of failure.
|
|
29
|
+
- Whether this is a **code bug** or a **test bug** — read the test's intent before assuming the production code is wrong.
|
|
30
|
+
|
|
31
|
+
Check dependency changelogs if a third-party library was recently updated and the failure is at an integration boundary.
|
|
32
|
+
|
|
33
|
+
## Phase 3 — Form a hypothesis
|
|
34
|
+
|
|
35
|
+
State it explicitly before touching anything:
|
|
36
|
+
|
|
37
|
+
> "I believe the bug is caused by X, because the evidence shows Y."
|
|
38
|
+
|
|
39
|
+
Test with the smallest possible change — one that confirms or disproves the hypothesis without being the permanent fix. If the hypothesis is wrong: return to Phase 2 with the new evidence. Do not stack changes.
|
|
40
|
+
|
|
41
|
+
## Phase 4 — Fix
|
|
42
|
+
|
|
43
|
+
Apply the minimal fix for the root cause:
|
|
44
|
+
- Fix the root cause, not the symptom.
|
|
45
|
+
- Remove all instrumentation from Phase 1.
|
|
46
|
+
- Run the full test suite.
|
|
47
|
+
- Confirm the original failure is gone and nothing else regressed.
|
|
48
|
+
|
|
49
|
+
## Escalation rule
|
|
50
|
+
|
|
51
|
+
Three failed hypothesis cycles means stop and do an architectural review. The bug likely signals that a design assumption is wrong, not just a logic error in one function.
|