@event4u/agent-config 1.16.0 → 1.17.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/.agent-src/commands/{agents-audit.md → agents/audit.md} +4 -3
- package/.agent-src/commands/{agents-cleanup.md → agents/cleanup.md} +12 -6
- package/.agent-src/commands/{agents-prepare.md → agents/prepare.md} +4 -3
- package/.agent-src/commands/agents.md +46 -0
- package/.agent-src/commands/{chat-history-checkpoint.md → chat-history/checkpoint.md} +4 -4
- package/.agent-src/commands/{chat-history-clear.md → chat-history/clear.md} +4 -4
- package/.agent-src/commands/{chat-history-resume.md → chat-history/resume.md} +4 -4
- package/.agent-src/commands/chat-history/show.md +107 -0
- package/.agent-src/commands/chat-history.md +33 -89
- package/.agent-src/commands/{commit-in-chunks.md → commit/in-chunks.md} +15 -13
- package/.agent-src/commands/commit.md +22 -2
- package/.agent-src/commands/{context-create.md → context/create.md} +4 -3
- package/.agent-src/commands/{context-refactor.md → context/refactor.md} +4 -3
- package/.agent-src/commands/context.md +44 -0
- package/.agent-src/commands/{copilot-agents-init.md → copilot-agents/init.md} +4 -3
- package/.agent-src/commands/{copilot-agents-optimize.md → copilot-agents/optimize.md} +4 -3
- package/.agent-src/commands/copilot-agents.md +44 -0
- package/.agent-src/commands/council/default.md +221 -0
- package/.agent-src/commands/{council-design.md → council/design.md} +6 -5
- package/.agent-src/commands/{council-optimize.md → council/optimize.md} +7 -6
- package/.agent-src/commands/{council-pr.md → council/pr.md} +6 -5
- package/.agent-src/commands/council.md +47 -212
- package/.agent-src/commands/{create-pr-description.md → create-pr/description-only.md} +4 -2
- package/.agent-src/commands/create-pr.md +26 -5
- package/.agent-src/commands/{feature-dev.md → feature/dev.md} +5 -10
- package/.agent-src/commands/{feature-explore.md → feature/explore.md} +4 -8
- package/.agent-src/commands/{feature-plan.md → feature/plan.md} +4 -8
- package/.agent-src/commands/{feature-refactor.md → feature/refactor.md} +4 -8
- package/.agent-src/commands/{feature-roadmap.md → feature/roadmap.md} +6 -10
- package/.agent-src/commands/feature.md +6 -12
- package/.agent-src/commands/{fix-ci.md → fix/ci.md} +4 -8
- package/.agent-src/commands/{fix-portability.md → fix/portability.md} +4 -8
- package/.agent-src/commands/{fix-pr-bot-comments.md → fix/pr-bots.md} +4 -8
- package/.agent-src/commands/{fix-pr-developer-comments.md → fix/pr-developers.md} +4 -8
- package/.agent-src/commands/{fix-pr-comments.md → fix/pr.md} +7 -11
- package/.agent-src/commands/{fix-references.md → fix/refs.md} +4 -8
- package/.agent-src/commands/{fix-seeder.md → fix/seeder.md} +4 -8
- package/.agent-src/commands/fix.md +7 -13
- package/.agent-src/commands/{do-and-judge.md → judge/on-diff.md} +4 -3
- package/.agent-src/commands/judge/solo.md +90 -0
- package/.agent-src/commands/{do-in-steps.md → judge/steps.md} +4 -3
- package/.agent-src/commands/judge.md +35 -70
- package/.agent-src/commands/{memory-add.md → memory/add.md} +4 -3
- package/.agent-src/commands/{memory-full.md → memory/load.md} +4 -3
- package/.agent-src/commands/{memory-promote.md → memory/promote.md} +4 -3
- package/.agent-src/commands/{propose-memory.md → memory/propose.md} +4 -3
- package/.agent-src/commands/memory.md +48 -0
- package/.agent-src/commands/{module-create.md → module/create.md} +4 -3
- package/.agent-src/commands/{module-explore.md → module/explore.md} +4 -3
- package/.agent-src/commands/module.md +44 -0
- package/.agent-src/commands/{optimize-agents.md → optimize/agents.md} +4 -8
- package/.agent-src/commands/{optimize-augmentignore.md → optimize/augmentignore.md} +4 -9
- package/.agent-src/commands/{optimize-rtk-filters.md → optimize/rtk.md} +4 -8
- package/.agent-src/commands/{optimize-skills.md → optimize/skills.md} +4 -8
- package/.agent-src/commands/optimize.md +4 -10
- package/.agent-src/commands/{override-create.md → override/create.md} +4 -3
- package/.agent-src/commands/{override-manage.md → override/manage.md} +4 -3
- package/.agent-src/commands/override.md +44 -0
- package/.agent-src/commands/{roadmap-create.md → roadmap/create.md} +4 -3
- package/.agent-src/commands/{roadmap-execute.md → roadmap/execute.md} +4 -3
- package/.agent-src/commands/roadmap.md +44 -0
- package/.agent-src/commands/{tests-create.md → tests/create.md} +4 -3
- package/.agent-src/commands/{tests-execute.md → tests/execute.md} +4 -3
- package/.agent-src/commands/tests.md +44 -0
- package/.agent-src/contexts/communication/rules-auto/artifact-engagement-recording-mechanics.md +72 -0
- package/.agent-src/contexts/communication/rules-auto/augment-portability-mechanics.md +79 -0
- package/.agent-src/contexts/communication/rules-auto/augment-source-of-truth-mechanics.md +98 -0
- package/.agent-src/contexts/communication/rules-auto/cli-output-handling-mechanics.md +87 -0
- package/.agent-src/contexts/communication/rules-auto/command-suggestion-policy-mechanics.md +62 -0
- package/.agent-src/contexts/communication/rules-auto/docs-sync-mechanics.md +78 -0
- package/.agent-src/contexts/communication/rules-auto/package-ci-checks-mechanics.md +85 -0
- package/.agent-src/contexts/communication/rules-auto/review-routing-awareness-mechanics.md +65 -0
- package/.agent-src/contexts/communication/rules-auto/roadmap-progress-sync-mechanics.md +78 -0
- package/.agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +62 -0
- package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +55 -0
- package/.agent-src/contexts/communication/rules-auto/ui-audit-gate-mechanics.md +53 -0
- package/.agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +77 -0
- package/.agent-src/contexts/judges/no-consolidate-rationale.md +102 -0
- package/.agent-src/contexts/judges/persona-voice-rubric.md +140 -0
- package/.agent-src/rules/artifact-engagement-recording.md +13 -69
- package/.agent-src/rules/ask-when-uncertain.md +27 -42
- package/.agent-src/rules/augment-portability.md +15 -61
- package/.agent-src/rules/augment-source-of-truth.md +27 -93
- package/.agent-src/rules/cli-output-handling.md +10 -76
- package/.agent-src/rules/command-suggestion-policy.md +18 -59
- package/.agent-src/rules/commit-conventions.md +17 -14
- package/.agent-src/rules/direct-answers.md +34 -49
- package/.agent-src/rules/docker-commands.md +5 -5
- package/.agent-src/rules/docs-sync.md +15 -69
- package/.agent-src/rules/language-and-tone.md +48 -72
- package/.agent-src/rules/missing-tool-handling.md +28 -22
- package/.agent-src/rules/no-cheap-questions.md +45 -52
- package/.agent-src/rules/no-roadmap-references.md +73 -0
- package/.agent-src/rules/package-ci-checks.md +21 -61
- package/.agent-src/rules/preservation-guard.md +64 -29
- package/.agent-src/rules/review-routing-awareness.md +24 -43
- package/.agent-src/rules/roadmap-progress-sync.md +10 -71
- package/.agent-src/rules/security-sensitive-stop.md +8 -8
- package/.agent-src/rules/skill-quality.md +16 -48
- package/.agent-src/rules/slash-command-routing-policy.md +7 -4
- package/.agent-src/rules/think-before-action.md +52 -42
- package/.agent-src/rules/tool-safety.md +19 -16
- package/.agent-src/rules/ui-audit-gate.md +24 -38
- package/.agent-src/rules/user-interaction.md +13 -68
- package/.agent-src/skills/ai-council/SKILL.md +2 -0
- package/.agent-src/skills/api-testing/SKILL.md +1 -1
- package/.agent-src/skills/check-refs/SKILL.md +59 -40
- package/.agent-src/skills/conventional-commits-writing/SKILL.md +86 -28
- package/.agent-src/skills/copilot-agents-optimization/SKILL.md +5 -5
- package/.agent-src/skills/developer-like-execution/SKILL.md +4 -4
- package/.agent-src/skills/finishing-a-development-branch/SKILL.md +101 -65
- package/.agent-src/skills/flux/SKILL.md +30 -10
- package/.agent-src/skills/github-ci/SKILL.md +2 -2
- package/.agent-src/skills/judge-code-quality/SKILL.md +7 -8
- package/.agent-src/skills/judge-security-auditor/SKILL.md +4 -5
- package/.agent-src/skills/judge-test-coverage/SKILL.md +3 -4
- package/.agent-src/skills/lint-skills/SKILL.md +57 -39
- package/.agent-src/skills/md-language-check/SKILL.md +61 -39
- package/.agent-src/skills/override-management/SKILL.md +5 -5
- package/.agent-src/skills/quality-tools/SKILL.md +2 -2
- package/.agent-src/skills/react-shadcn-ui/SKILL.md +116 -43
- package/.agent-src/skills/readme-reviewer/SKILL.md +30 -29
- package/.agent-src/skills/readme-writing/SKILL.md +78 -53
- package/.agent-src/skills/readme-writing-package/SKILL.md +50 -47
- package/.agent-src/skills/receiving-code-review/SKILL.md +52 -47
- package/.agent-src/skills/refine-prompt/SKILL.md +0 -1
- package/.agent-src/skills/requesting-code-review/SKILL.md +35 -30
- package/.agent-src/skills/security/SKILL.md +7 -2
- package/.agent-src/skills/security-audit/SKILL.md +7 -3
- package/.agent-src/skills/systematic-debugging/SKILL.md +68 -60
- package/.agent-src/skills/test-driven-development/SKILL.md +59 -57
- package/.agent-src/skills/test-performance/SKILL.md +0 -1
- package/.agent-src/skills/traefik/SKILL.md +4 -4
- package/.agent-src/skills/verify-completion-evidence/SKILL.md +28 -26
- package/.claude-plugin/marketplace.json +22 -11
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +90 -1
- package/README.md +18 -17
- package/docs/architecture.md +4 -6
- package/docs/catalog.md +67 -39
- package/docs/contracts/STABILITY.md +13 -7
- package/docs/contracts/adr-chat-history-split.md +1 -3
- package/docs/contracts/adr-command-suggestion.md +0 -2
- package/docs/contracts/adr-implement-ticket-runtime.md +1 -2
- package/docs/contracts/adr-product-ui-track.md +3 -6
- package/docs/contracts/adr-prompt-driven-execution.md +3 -4
- package/docs/contracts/agent-memory-contract.md +6 -11
- package/docs/contracts/artifact-engagement-flow.md +6 -9
- package/docs/contracts/command-clusters.md +56 -46
- package/docs/contracts/command-suggestion-flow.md +1 -3
- package/docs/contracts/context-paths.md +99 -0
- package/docs/contracts/file-ownership-matrix.json +6722 -0
- package/docs/contracts/file-ownership-matrix.md +134 -0
- package/docs/contracts/implement-ticket-flow.md +6 -9
- package/docs/contracts/linear-ai-rules-inclusion.md +0 -1
- package/docs/contracts/linear-ai-three-layers.md +0 -2
- package/docs/contracts/load-context-budget-model.md +178 -0
- package/docs/contracts/load-context-schema.md +1 -3
- package/docs/contracts/rule-interactions.md +0 -1
- package/docs/contracts/rule-priority-hierarchy.md +1 -1
- package/docs/contracts/ui-track-flow.md +7 -17
- package/docs/customization.md +2 -0
- package/docs/getting-started.md +5 -4
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +100 -0
- package/package.json +1 -1
- package/scripts/_one_off_phase4_dispatch_latency.py +108 -0
- package/scripts/_one_off_phase6_trigger_jaccard.py +92 -0
- package/scripts/_phase2_shim_helper.py +109 -0
- package/scripts/agent-config +10 -0
- package/scripts/ai_council/_one_off_2a4_acceptance.py +208 -0
- package/scripts/ai_council/_one_off_context_layer_v1_estimate.py +67 -0
- package/scripts/ai_council/_one_off_context_layer_v1_review.py +292 -0
- package/scripts/ai_council/_one_off_followups_review.py +259 -0
- package/scripts/ai_council/_one_off_nondestructive_inline_audit.py +209 -0
- package/scripts/ai_council/_one_off_phase_2a_budget_rebalance.py +257 -0
- package/scripts/ai_council/_one_off_phase_2a_post_revert.py +197 -0
- package/scripts/ai_council/_one_off_rule_hardening_v1.py +251 -0
- package/scripts/ai_council/_one_off_structural_open_questions.py +232 -0
- package/scripts/ai_council/_one_off_structural_optimization.py +144 -0
- package/scripts/ai_council/_one_off_structural_v3_gaps.py +252 -0
- package/scripts/ai_council/_one_off_structural_v3_review.py +240 -0
- package/scripts/check_always_budget.py +363 -45
- package/scripts/check_cluster_patterns.py +159 -0
- package/scripts/check_command_count_messaging.py +14 -7
- package/scripts/check_context_paths.py +201 -0
- package/scripts/check_no_roadmap_refs.py +155 -0
- package/scripts/check_phase_coupling.py +148 -0
- package/scripts/check_portability.py +2 -0
- package/scripts/check_references.py +29 -2
- package/scripts/check_safety_floor_untouched.py +125 -0
- package/scripts/command_suggester/loader.py +4 -1
- package/scripts/compress.py +59 -13
- package/scripts/generate_index.py +6 -2
- package/scripts/generate_ownership_matrix.py +323 -0
- package/scripts/hooks/augment-roadmap-progress.sh +57 -0
- package/scripts/install.py +49 -28
- package/scripts/lint_no_new_atomic_commands.py +12 -11
- package/scripts/requirements-evals.txt +1 -0
- package/scripts/roadmap_progress_hook.py +159 -0
- package/scripts/schemas/command.schema.json +4 -3
- package/scripts/skill_linter.py +1 -0
- package/scripts/sync_agent_settings.py +25 -2
- package/scripts/update_counts.py +7 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Phase 4.3.1 — council cluster dispatch-latency benchmark.
|
|
3
|
+
|
|
4
|
+
Measures the wall-clock overhead of the cluster dispatch layer for the
|
|
5
|
+
`/council` family. Compares:
|
|
6
|
+
|
|
7
|
+
baseline: directly read council-pr.md / council-design.md (atomic shape)
|
|
8
|
+
cluster : read council.md (dispatcher) + parse table + read council-pr.md
|
|
9
|
+
/ council-design.md (cluster shape)
|
|
10
|
+
|
|
11
|
+
The dispatch layer in agent-config is a markdown parse, not a runtime
|
|
12
|
+
function, so this benchmarks the file-system + frontmatter + table-row
|
|
13
|
+
extraction cost. Threshold per roadmap § 4.3.1: ≤ +100ms wall-clock.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
import statistics
|
|
19
|
+
import time
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
23
|
+
COMMANDS = ROOT / ".agent-src/commands"
|
|
24
|
+
N_ITER = 1000 # cold + warm; markdown is tiny so we run a lot of iterations
|
|
25
|
+
|
|
26
|
+
FRONTMATTER_RE = re.compile(r"^---\n(.*?)\n---\n", re.DOTALL)
|
|
27
|
+
TABLE_ROW_RE = re.compile(r"\|\s*`/council\s+([a-z-]+)`\s*\|\s*`([^`]+)`")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _read_atomic(target: str) -> str:
|
|
31
|
+
"""Baseline: directly read the routed file (atomic shape)."""
|
|
32
|
+
path = COMMANDS / f"council-{target}.md"
|
|
33
|
+
text = path.read_text(encoding="utf-8")
|
|
34
|
+
m = FRONTMATTER_RE.match(text)
|
|
35
|
+
if not m:
|
|
36
|
+
raise RuntimeError(f"no frontmatter in {path}")
|
|
37
|
+
return text
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _read_cluster(target: str) -> str:
|
|
41
|
+
"""Cluster: read dispatcher, parse routing table, then read routed file."""
|
|
42
|
+
dispatcher = (COMMANDS / "council.md").read_text(encoding="utf-8")
|
|
43
|
+
routes = dict(TABLE_ROW_RE.findall(dispatcher))
|
|
44
|
+
routed = routes.get(target)
|
|
45
|
+
if routed is None:
|
|
46
|
+
raise RuntimeError(f"no route for {target!r} in dispatcher")
|
|
47
|
+
text = (COMMANDS / routed).read_text(encoding="utf-8")
|
|
48
|
+
m = FRONTMATTER_RE.match(text)
|
|
49
|
+
if not m:
|
|
50
|
+
raise RuntimeError(f"no frontmatter in {routed}")
|
|
51
|
+
return text
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _bench(fn, target: str, n: int) -> list[float]:
|
|
55
|
+
samples: list[float] = []
|
|
56
|
+
for _ in range(n):
|
|
57
|
+
t0 = time.perf_counter()
|
|
58
|
+
fn(target)
|
|
59
|
+
samples.append((time.perf_counter() - t0) * 1000.0)
|
|
60
|
+
return samples
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _summary(name: str, samples: list[float]) -> None:
|
|
64
|
+
samples = sorted(samples)
|
|
65
|
+
p50 = statistics.median(samples)
|
|
66
|
+
p95 = samples[int(len(samples) * 0.95)]
|
|
67
|
+
p99 = samples[int(len(samples) * 0.99)]
|
|
68
|
+
mean = statistics.mean(samples)
|
|
69
|
+
print(f" {name:18s} mean={mean:6.3f}ms p50={p50:6.3f}ms p95={p95:6.3f}ms p99={p99:6.3f}ms")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def main() -> int:
|
|
73
|
+
print(f"Phase 4.3.1 — council cluster dispatch latency (n={N_ITER} per probe)")
|
|
74
|
+
print()
|
|
75
|
+
|
|
76
|
+
overruns = 0
|
|
77
|
+
for target in ("pr", "design"):
|
|
78
|
+
print(f"target = /council {target}")
|
|
79
|
+
|
|
80
|
+
# warm cache
|
|
81
|
+
_read_atomic(target)
|
|
82
|
+
_read_cluster(target)
|
|
83
|
+
|
|
84
|
+
baseline = _bench(_read_atomic, target, N_ITER)
|
|
85
|
+
cluster = _bench(_read_cluster, target, N_ITER)
|
|
86
|
+
|
|
87
|
+
_summary("atomic (baseline)", baseline)
|
|
88
|
+
_summary("cluster (dispatcher)", cluster)
|
|
89
|
+
|
|
90
|
+
delta_mean = statistics.mean(cluster) - statistics.mean(baseline)
|
|
91
|
+
delta_p95 = sorted(cluster)[int(N_ITER * 0.95)] - sorted(baseline)[int(N_ITER * 0.95)]
|
|
92
|
+
verdict = "PASS" if delta_p95 <= 100.0 else "FAIL"
|
|
93
|
+
marker = "✅" if verdict == "PASS" else "❌"
|
|
94
|
+
print(f" delta-mean = {delta_mean:+.3f}ms delta-p95 = {delta_p95:+.3f}ms threshold = +100ms {marker} {verdict}")
|
|
95
|
+
print()
|
|
96
|
+
|
|
97
|
+
if delta_p95 > 100.0:
|
|
98
|
+
overruns += 1
|
|
99
|
+
|
|
100
|
+
if overruns:
|
|
101
|
+
print(f"❌ {overruns} probe(s) exceeded +100ms p95 threshold.")
|
|
102
|
+
return 1
|
|
103
|
+
print("✅ All probes within +100ms p95 threshold.")
|
|
104
|
+
return 0
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Phase 6.1 — chat-history-* trigger overlap (Jaccard).
|
|
3
|
+
|
|
4
|
+
Source of truth per rule = frontmatter `description:` field
|
|
5
|
+
(the trigger surface that decides when an `auto` rule activates).
|
|
6
|
+
Tokens = lowercased alphanum words length ≥ 3, minus a small
|
|
7
|
+
stop-list of file-name fragments and connective words that carry
|
|
8
|
+
no trigger signal.
|
|
9
|
+
|
|
10
|
+
Output: pairwise Jaccard + branch verdict per roadmap § 6.1.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import re
|
|
15
|
+
from itertools import combinations
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
19
|
+
RULES_DIR = ROOT / ".agent-src.uncompressed/rules"
|
|
20
|
+
|
|
21
|
+
RULES = [
|
|
22
|
+
"chat-history-cadence",
|
|
23
|
+
"chat-history-ownership",
|
|
24
|
+
"chat-history-visibility",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
STOP = {
|
|
28
|
+
"the", "and", "for", "with", "from", "via", "per", "not",
|
|
29
|
+
"into", "onto", "out", "off", "any", "all", "this", "that",
|
|
30
|
+
"agent", "chat", "history",
|
|
31
|
+
"agentchathistory", "chathistory",
|
|
32
|
+
"rule", "rules", "file", "files",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
DESC_RE = re.compile(r'^description:\s*"([^"]+)"', re.MULTILINE)
|
|
36
|
+
TOKEN_RE = re.compile(r"[a-z][a-z0-9_]{2,}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def tokens(rule_id: str) -> set[str]:
|
|
40
|
+
text = (RULES_DIR / f"{rule_id}.md").read_text(encoding="utf-8")
|
|
41
|
+
m = DESC_RE.search(text)
|
|
42
|
+
if not m:
|
|
43
|
+
raise RuntimeError(f"no description in {rule_id}")
|
|
44
|
+
desc = m.group(1).lower()
|
|
45
|
+
raw = TOKEN_RE.findall(desc)
|
|
46
|
+
return {t for t in raw if t not in STOP}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def jaccard(a: set[str], b: set[str]) -> float:
|
|
50
|
+
union = a | b
|
|
51
|
+
if not union:
|
|
52
|
+
return 0.0
|
|
53
|
+
return len(a & b) / len(union)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def main() -> int:
|
|
57
|
+
sets = {r: tokens(r) for r in RULES}
|
|
58
|
+
|
|
59
|
+
print(f"Phase 6.1 — trigger Jaccard (source: frontmatter `description:`)")
|
|
60
|
+
print()
|
|
61
|
+
for r, ts in sets.items():
|
|
62
|
+
print(f" {r} ({len(ts)} tokens)")
|
|
63
|
+
print(f" {sorted(ts)}")
|
|
64
|
+
print()
|
|
65
|
+
|
|
66
|
+
print("Pairwise Jaccard:")
|
|
67
|
+
print()
|
|
68
|
+
print(f" {'pair':55s} intersect union Jaccard")
|
|
69
|
+
pairs_above = 0
|
|
70
|
+
for a, b in combinations(RULES, 2):
|
|
71
|
+
inter = sets[a] & sets[b]
|
|
72
|
+
union = sets[a] | sets[b]
|
|
73
|
+
j = jaccard(sets[a], sets[b])
|
|
74
|
+
marker = " **" if j >= 0.30 else ""
|
|
75
|
+
print(f" {a + ' × ' + b:55s} {len(inter):>8d} {len(union):>5d} {j:>6.3f}{marker}")
|
|
76
|
+
print(f" intersection: {sorted(inter)}")
|
|
77
|
+
if j >= 0.30:
|
|
78
|
+
pairs_above += 1
|
|
79
|
+
print()
|
|
80
|
+
|
|
81
|
+
if pairs_above >= 2:
|
|
82
|
+
print(f"VERDICT: ≥ 30% on {pairs_above}/3 pairs → PROCEED to 6.2 (unified shape).")
|
|
83
|
+
return 0
|
|
84
|
+
if pairs_above == 1:
|
|
85
|
+
print(f"VERDICT: mixed ({pairs_above}/3 pairs ≥ 30%) → ESCALATE to council.")
|
|
86
|
+
return 0
|
|
87
|
+
print(f"VERDICT: < 30% on all 3 pairs → STOP at 6.1 (orthogonal — current shape optimal).")
|
|
88
|
+
return 0
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""One-shot helper: add `superseded_by:` + `deprecated_in:` + warning
|
|
3
|
+
banner to Phase 2 atomic-command shims.
|
|
4
|
+
|
|
5
|
+
Idempotent — if a file already has `superseded_by:` set, it is skipped.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
# (file-stem, "<cluster> <sub>") — "<cluster> --flag" for flag-based
|
|
12
|
+
PHASE2_SHIMS: list[tuple[str, str]] = [
|
|
13
|
+
# chat-history cluster
|
|
14
|
+
("chat-history-resume", "chat-history resume"),
|
|
15
|
+
("chat-history-clear", "chat-history clear"),
|
|
16
|
+
("chat-history-checkpoint", "chat-history checkpoint"),
|
|
17
|
+
# agents cluster
|
|
18
|
+
("agents-audit", "agents audit"),
|
|
19
|
+
("agents-cleanup", "agents cleanup"),
|
|
20
|
+
("agents-prepare", "agents prepare"),
|
|
21
|
+
# memory cluster
|
|
22
|
+
("memory-add", "memory add"),
|
|
23
|
+
("memory-full", "memory load"),
|
|
24
|
+
("memory-promote", "memory promote"),
|
|
25
|
+
("propose-memory", "memory propose"),
|
|
26
|
+
# roadmap cluster
|
|
27
|
+
("roadmap-create", "roadmap create"),
|
|
28
|
+
("roadmap-execute", "roadmap execute"),
|
|
29
|
+
# module cluster
|
|
30
|
+
("module-create", "module create"),
|
|
31
|
+
("module-explore", "module explore"),
|
|
32
|
+
# tests cluster
|
|
33
|
+
("tests-create", "tests create"),
|
|
34
|
+
("tests-execute", "tests execute"),
|
|
35
|
+
# context cluster
|
|
36
|
+
("context-create", "context create"),
|
|
37
|
+
("context-refactor", "context refactor"),
|
|
38
|
+
# override cluster
|
|
39
|
+
("override-create", "override create"),
|
|
40
|
+
("override-manage", "override manage"),
|
|
41
|
+
# copilot-agents cluster
|
|
42
|
+
("copilot-agents-init", "copilot-agents init"),
|
|
43
|
+
("copilot-agents-optimize", "copilot-agents optimize"),
|
|
44
|
+
# judge cluster (do-and-judge / do-in-steps now sub-commands)
|
|
45
|
+
("do-and-judge", "judge on-diff"),
|
|
46
|
+
("do-in-steps", "judge steps"),
|
|
47
|
+
# commit / create-pr — flag-based clusters
|
|
48
|
+
("commit-in-chunks", "commit --in-chunks"),
|
|
49
|
+
("create-pr-description", "create-pr --description-only"),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
DEPRECATED_IN = "1.17.0"
|
|
53
|
+
COMMANDS_DIR = Path(".agent-src.uncompressed/commands")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def patch_file(stem: str, target: str) -> str:
|
|
57
|
+
path = COMMANDS_DIR / f"{stem}.md"
|
|
58
|
+
if not path.exists():
|
|
59
|
+
return f"SKIP {stem}: not found"
|
|
60
|
+
text = path.read_text(encoding="utf-8")
|
|
61
|
+
if "superseded_by:" in text.split("---", 2)[1] if text.startswith("---") else False:
|
|
62
|
+
return f"SKIP {stem}: already shimmed"
|
|
63
|
+
|
|
64
|
+
if not text.startswith("---\n"):
|
|
65
|
+
return f"SKIP {stem}: no frontmatter"
|
|
66
|
+
end = text.find("\n---\n", 4)
|
|
67
|
+
if end == -1:
|
|
68
|
+
return f"SKIP {stem}: malformed frontmatter"
|
|
69
|
+
fm_block = text[4:end]
|
|
70
|
+
body = text[end + len("\n---\n"):]
|
|
71
|
+
|
|
72
|
+
if "superseded_by:" in fm_block:
|
|
73
|
+
return f"SKIP {stem}: already shimmed"
|
|
74
|
+
|
|
75
|
+
new_fm_lines = fm_block.rstrip("\n").splitlines()
|
|
76
|
+
new_fm_lines.append(f"superseded_by: {target}")
|
|
77
|
+
new_fm_lines.append(f'deprecated_in: "{DEPRECATED_IN}"')
|
|
78
|
+
new_fm = "\n".join(new_fm_lines)
|
|
79
|
+
|
|
80
|
+
is_flag = target.startswith(("commit ", "create-pr "))
|
|
81
|
+
if is_flag:
|
|
82
|
+
cluster_invocation = f"/{target}"
|
|
83
|
+
else:
|
|
84
|
+
cluster_invocation = f"/{target}"
|
|
85
|
+
banner = (
|
|
86
|
+
f"> ⚠️ /{stem} is deprecated; use {cluster_invocation} instead.\n"
|
|
87
|
+
f"> This shim is retained for one release cycle "
|
|
88
|
+
f"({DEPRECATED_IN} → next minor) and forwards to the same "
|
|
89
|
+
f"instructions below. See "
|
|
90
|
+
f"[`docs/contracts/command-clusters.md`]"
|
|
91
|
+
f"(../../docs/contracts/command-clusters.md).\n\n"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
new_text = f"---\n{new_fm}\n---\n\n{banner}{body.lstrip(chr(10))}"
|
|
95
|
+
path.write_text(new_text, encoding="utf-8")
|
|
96
|
+
return f"OK {stem} → {target}"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def main() -> int:
|
|
100
|
+
results = []
|
|
101
|
+
for stem, target in PHASE2_SHIMS:
|
|
102
|
+
results.append(patch_file(stem, target))
|
|
103
|
+
for r in results:
|
|
104
|
+
print(r)
|
|
105
|
+
return 0
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
sys.exit(main())
|
package/scripts/agent-config
CHANGED
|
@@ -70,6 +70,8 @@ Commands:
|
|
|
70
70
|
Usage: chat-history:hook --platform <claude|augment|cursor|cline|windsurf|gemini>
|
|
71
71
|
chat-history:checkpoint Append a phase-boundary entry to .agent-chat-history
|
|
72
72
|
(CHECKPOINT fallback for platforms without native hooks)
|
|
73
|
+
roadmap-progress:hook PostToolUse hook entry point (read JSON from stdin)
|
|
74
|
+
Regenerates roadmaps-progress.md when a tool wrote under agents/roadmaps/
|
|
73
75
|
telemetry:record Append one artefact-engagement event (default-off)
|
|
74
76
|
telemetry:status Print artefact-engagement telemetry status (read-only)
|
|
75
77
|
telemetry:report Aggregate the engagement log into a quartile report
|
|
@@ -316,6 +318,13 @@ cmd_chat_history_hook() {
|
|
|
316
318
|
exec python3 "$script" hook-dispatch "$@"
|
|
317
319
|
}
|
|
318
320
|
|
|
321
|
+
cmd_roadmap_progress_hook() {
|
|
322
|
+
require_python3
|
|
323
|
+
local script
|
|
324
|
+
script="$(resolve_script "scripts/roadmap_progress_hook.py")" || return 1
|
|
325
|
+
exec python3 "$script" "$@"
|
|
326
|
+
}
|
|
327
|
+
|
|
319
328
|
cmd_chat_history_checkpoint() {
|
|
320
329
|
require_python3
|
|
321
330
|
local script
|
|
@@ -436,6 +445,7 @@ main() {
|
|
|
436
445
|
refine-ticket:detect) cmd_refine_ticket_detect "$@" ;;
|
|
437
446
|
chat-history:hook) cmd_chat_history_hook "$@" ;;
|
|
438
447
|
chat-history:checkpoint) cmd_chat_history_checkpoint "$@" ;;
|
|
448
|
+
roadmap-progress:hook) cmd_roadmap_progress_hook "$@" ;;
|
|
439
449
|
telemetry:record) cmd_telemetry_record "$@" ;;
|
|
440
450
|
telemetry:status) cmd_telemetry_status "$@" ;;
|
|
441
451
|
telemetry:report) cmd_telemetry_report "$@" ;;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Council acceptance review of Phase 0.4 2A.4 worked example.
|
|
2
|
+
|
|
3
|
+
Purpose: Phase 0.4.3 of road-to-structural-optimization.md requires a
|
|
4
|
+
council acceptance pass on the 2A.4 obligation-keyword-diff contract
|
|
5
|
+
before Phase 2A may begin. The artefact lives at
|
|
6
|
+
`agents/roadmaps/structural-optimization-2A4-example.md` plus two
|
|
7
|
+
sandbox files. Status will move from `draft` to `locked` only on
|
|
8
|
+
ACCEPT or ACCEPT_WITH_REVISIONS where revisions are minor.
|
|
9
|
+
|
|
10
|
+
Invocation:
|
|
11
|
+
.venv/bin/python -m scripts.ai_council._one_off_2a4_acceptance
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from scripts.ai_council.bundler import bundle_files
|
|
19
|
+
from scripts.ai_council.clients import (
|
|
20
|
+
AnthropicClient,
|
|
21
|
+
OpenAIClient,
|
|
22
|
+
load_anthropic_key,
|
|
23
|
+
load_openai_key,
|
|
24
|
+
)
|
|
25
|
+
from scripts.ai_council.orchestrator import (
|
|
26
|
+
CostBudget,
|
|
27
|
+
CouncilQuestion,
|
|
28
|
+
consult,
|
|
29
|
+
estimate,
|
|
30
|
+
)
|
|
31
|
+
from scripts.ai_council.pricing import estimate_cost, load_prices
|
|
32
|
+
from scripts.ai_council.project_context import detect_project_context
|
|
33
|
+
from scripts.ai_council.session import SessionManifest, save as save_session
|
|
34
|
+
|
|
35
|
+
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
36
|
+
ARTEFACTS = [
|
|
37
|
+
REPO_ROOT / "agents/roadmaps/structural-optimization-2A4-example.md",
|
|
38
|
+
REPO_ROOT / "agents/roadmaps/examples/2A4-direct-answers/direct-answers.slim.md",
|
|
39
|
+
REPO_ROOT / "agents/roadmaps/examples/2A4-direct-answers/direct-answers-mechanics.md",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
ORIGINAL_ASK = (
|
|
43
|
+
"Phase 0.4 of road-to-structural-optimization v3.1 dry-runs the "
|
|
44
|
+
"2A.4 obligation-keyword-diff contract on `direct-answers.md` to "
|
|
45
|
+
"lock the contract before Phase 2A begins. The artefact and two "
|
|
46
|
+
"sandbox files (slim rule + extracted mechanics) are presented. "
|
|
47
|
+
"Council task: ACCEPT / ACCEPT_WITH_REVISIONS / REJECT the "
|
|
48
|
+
"contract for use across the remaining 8 always-rules in Phase 2A."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
REVIEW_PROMPT = """\
|
|
52
|
+
# Council Acceptance Review — 2A.4 Worked Example
|
|
53
|
+
|
|
54
|
+
## Context
|
|
55
|
+
|
|
56
|
+
The host agent ran Phase 0.4 of `road-to-structural-optimization` v3.1: \
|
|
57
|
+
took one always-rule (`direct-answers.md`, smallest of the top-3), split \
|
|
58
|
+
it into a slim RULE+LOGIC half and a MECHANICS context, then applied \
|
|
59
|
+
the 2A.4 obligation-keyword diff contract. The artefact is the report \
|
|
60
|
+
of that dry-run; the two sandbox files are the actual produced split.
|
|
61
|
+
|
|
62
|
+
You are not asked to re-litigate the v3.1 roadmap or the choice of \
|
|
63
|
+
`direct-answers.md` — both were settled in earlier rounds. Verdict \
|
|
64
|
+
solely concerns whether the **contract** (keyword × counts × \
|
|
65
|
+
accept-rationale table, plus its tie-break rules) is now ready to be \
|
|
66
|
+
applied to the remaining 8 always-rules in Phase 2A.
|
|
67
|
+
|
|
68
|
+
## Output Contract (STRICT)
|
|
69
|
+
|
|
70
|
+
Produce exactly these blocks in order. Be decisive — total response \
|
|
71
|
+
budget <= 1200 words.
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
### Contract correctness
|
|
75
|
+
|
|
76
|
+
**Verdict:** <ACCEPT | ACCEPT_WITH_REVISIONS | REJECT>
|
|
77
|
+
**Keyword extraction completeness:** <COMPLETE | PARTIAL — list missing>
|
|
78
|
+
**Tie-break rules sufficient:** <YES | NO — name the gap>
|
|
79
|
+
**Required revisions (numbered, 1-3 max, only on ACCEPT_WITH_REVISIONS):**
|
|
80
|
+
1. <one sentence — smallest change>
|
|
81
|
+
2. <...>
|
|
82
|
+
3. <...>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
### Sandbox split quality
|
|
87
|
+
|
|
88
|
+
**Slim file preserves all RULE+LOGIC obligations:** <YES | NO — list lost>
|
|
89
|
+
**Mechanics file holds only mechanics+examples:** <YES | NO — list misplaced>
|
|
90
|
+
**Round-trip: rule_slim + load_context(mechanics) == original behaviour:**
|
|
91
|
+
<YES | NO — name the divergence>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
### Generalisability to remaining 8 rules
|
|
96
|
+
|
|
97
|
+
**Contract scales without per-rule tuning:** <YES | NO — name failure mode>
|
|
98
|
+
**Single biggest risk on the next rule (likely `non-destructive-by-default`):**
|
|
99
|
+
<one sentence>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
### Final verdict
|
|
104
|
+
|
|
105
|
+
**Lockable as-is for Phase 2A?** <YES | NO>
|
|
106
|
+
**If NO, single blocking change required:** <one sentence>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Verdict definitions:
|
|
110
|
+
- **ACCEPT** — contract ships unchanged; status moves to locked.
|
|
111
|
+
- **ACCEPT_WITH_REVISIONS** — locks after the 1-3 listed revisions land.
|
|
112
|
+
- **REJECT** — contract is structurally wrong; describe the fault.
|
|
113
|
+
|
|
114
|
+
The three artefacts follow this prompt verbatim.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def main() -> int:
|
|
119
|
+
anthropic = AnthropicClient(api_key=load_anthropic_key(), model="claude-sonnet-4-5")
|
|
120
|
+
openai = OpenAIClient(api_key=load_openai_key(), model="gpt-4o")
|
|
121
|
+
members = [anthropic, openai]
|
|
122
|
+
|
|
123
|
+
context = bundle_files(ARTEFACTS)
|
|
124
|
+
project = detect_project_context(REPO_ROOT)
|
|
125
|
+
table = load_prices()
|
|
126
|
+
|
|
127
|
+
user_prompt = REVIEW_PROMPT + "\n\n---\n\n" + context.text
|
|
128
|
+
|
|
129
|
+
question = CouncilQuestion(
|
|
130
|
+
mode="files",
|
|
131
|
+
user_prompt=user_prompt,
|
|
132
|
+
max_tokens=3072,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
estimates = estimate(
|
|
136
|
+
question, members, table, project=project, original_ask=ORIGINAL_ASK,
|
|
137
|
+
)
|
|
138
|
+
print("=== ESTIMATE (single round, max tokens) ===")
|
|
139
|
+
total_est = 0.0
|
|
140
|
+
for c, e in zip(members, estimates):
|
|
141
|
+
print(f" {c.name}/{c.model}: ~{e.input_tokens} in + {e.output_tokens} out = ${e.total_usd:.4f}")
|
|
142
|
+
total_est += e.total_usd
|
|
143
|
+
print(f" TOTAL per round (max): ${total_est:.4f}")
|
|
144
|
+
print()
|
|
145
|
+
|
|
146
|
+
budget = CostBudget(
|
|
147
|
+
max_input_tokens=200_000,
|
|
148
|
+
max_output_tokens=80_000,
|
|
149
|
+
max_calls=20,
|
|
150
|
+
max_total_usd=2.50,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
rounds_collected: list[list] = []
|
|
154
|
+
|
|
155
|
+
def _on_round_complete(round_idx: int, round_responses) -> None:
|
|
156
|
+
rounds_collected.append(list(round_responses))
|
|
157
|
+
print(f"=== ROUND {round_idx + 1} COMPLETE ===")
|
|
158
|
+
for r in round_responses:
|
|
159
|
+
if r.error:
|
|
160
|
+
print(f" [error] {r.provider}/{r.model}: {r.error}")
|
|
161
|
+
continue
|
|
162
|
+
actual = estimate_cost(r.provider, r.model, r.input_tokens, r.output_tokens, table)
|
|
163
|
+
print(f" [done] {r.provider}/{r.model}: {r.input_tokens} in / "
|
|
164
|
+
f"{r.output_tokens} out · {r.latency_ms} ms · ${actual.total_usd:.4f}")
|
|
165
|
+
print()
|
|
166
|
+
|
|
167
|
+
print("=== CONSULT (1 round, 2A.4 acceptance review) ===")
|
|
168
|
+
consult(
|
|
169
|
+
members, question, budget,
|
|
170
|
+
rounds=1,
|
|
171
|
+
on_round_complete=_on_round_complete,
|
|
172
|
+
table=table, project=project, original_ask=ORIGINAL_ASK,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if not rounds_collected:
|
|
176
|
+
print("[error] no rounds completed", file=sys.stderr)
|
|
177
|
+
return 1
|
|
178
|
+
|
|
179
|
+
actual_total = 0.0
|
|
180
|
+
for round_responses in rounds_collected:
|
|
181
|
+
for r in round_responses:
|
|
182
|
+
if r.error:
|
|
183
|
+
continue
|
|
184
|
+
actual = estimate_cost(r.provider, r.model, r.input_tokens, r.output_tokens, table)
|
|
185
|
+
actual_total += actual.total_usd
|
|
186
|
+
print(f"=== TOTAL ACTUAL: ${actual_total:.4f} ===")
|
|
187
|
+
|
|
188
|
+
final_round = rounds_collected[-1]
|
|
189
|
+
if not [r for r in final_round if not r.error]:
|
|
190
|
+
return 1
|
|
191
|
+
|
|
192
|
+
manifest = SessionManifest(
|
|
193
|
+
mode="files",
|
|
194
|
+
artefact="agents/roadmaps/structural-optimization-2A4-example.md",
|
|
195
|
+
original_ask=ORIGINAL_ASK,
|
|
196
|
+
members=[f"{r.provider}/{r.model}" for r in final_round],
|
|
197
|
+
rounds=len(rounds_collected),
|
|
198
|
+
cost_usd_estimated=total_est,
|
|
199
|
+
cost_usd_actual=actual_total,
|
|
200
|
+
extra={"purpose": "Council acceptance review of Phase 0.4 2A.4 worked example"},
|
|
201
|
+
)
|
|
202
|
+
session_dir = save_session(manifest=manifest, responses=rounds_collected)
|
|
203
|
+
print(f"[saved] {session_dir.relative_to(REPO_ROOT)}/")
|
|
204
|
+
return 1 if any(r.error for round_r in rounds_collected for r in round_r) else 0
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
if __name__ == "__main__":
|
|
208
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""One-shot estimator for the v1 council review (no consult call).
|
|
2
|
+
|
|
3
|
+
Sibling of `_one_off_context_layer_v1_review.py`. Prints bundle size and
|
|
4
|
+
per-model token / cost projection so the user can confirm spend before
|
|
5
|
+
the actual consult fires.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from scripts.ai_council._one_off_context_layer_v1_review import (
|
|
12
|
+
ORIGINAL_ASK,
|
|
13
|
+
REVIEW_PROMPT_HEADER,
|
|
14
|
+
ROADMAP_PATH,
|
|
15
|
+
_diff_stat,
|
|
16
|
+
_pr_body,
|
|
17
|
+
)
|
|
18
|
+
from scripts.ai_council.bundler import bundle_prompt
|
|
19
|
+
from scripts.ai_council.clients import (
|
|
20
|
+
AnthropicClient,
|
|
21
|
+
OpenAIClient,
|
|
22
|
+
load_anthropic_key,
|
|
23
|
+
load_openai_key,
|
|
24
|
+
)
|
|
25
|
+
from scripts.ai_council.orchestrator import CouncilQuestion, estimate
|
|
26
|
+
from scripts.ai_council.pricing import load_prices
|
|
27
|
+
from scripts.ai_council.project_context import detect_project_context
|
|
28
|
+
|
|
29
|
+
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main() -> int:
|
|
33
|
+
roadmap_text = ROADMAP_PATH.read_text(encoding="utf-8")
|
|
34
|
+
parts = [
|
|
35
|
+
REVIEW_PROMPT_HEADER,
|
|
36
|
+
"## PR #36 — diff --stat\n\n```\n" + _diff_stat() + "\n```",
|
|
37
|
+
"## PR #36 — body\n\n" + _pr_body(),
|
|
38
|
+
"## Roadmap v1\n\n" + roadmap_text,
|
|
39
|
+
]
|
|
40
|
+
bundle_text = "\n\n---\n\n".join(parts)
|
|
41
|
+
print(f"Bundle bytes: {len(bundle_text.encode('utf-8'))}")
|
|
42
|
+
|
|
43
|
+
ctx = bundle_prompt(bundle_text)
|
|
44
|
+
project = detect_project_context(REPO_ROOT)
|
|
45
|
+
table = load_prices()
|
|
46
|
+
|
|
47
|
+
anthropic = AnthropicClient(api_key=load_anthropic_key(), model="claude-sonnet-4-5")
|
|
48
|
+
openai = OpenAIClient(api_key=load_openai_key(), model="gpt-4o")
|
|
49
|
+
members = [anthropic, openai]
|
|
50
|
+
|
|
51
|
+
question = CouncilQuestion(mode="prompt", user_prompt=ctx.text, max_tokens=4096)
|
|
52
|
+
estimates = estimate(
|
|
53
|
+
question, members, table, project=project, original_ask=ORIGINAL_ASK,
|
|
54
|
+
)
|
|
55
|
+
total = 0.0
|
|
56
|
+
for c, e in zip(members, estimates):
|
|
57
|
+
print(
|
|
58
|
+
f" {c.name}/{c.model}: ~{e.input_tokens} in + "
|
|
59
|
+
f"{e.output_tokens} out = ${e.total_usd:.4f}"
|
|
60
|
+
)
|
|
61
|
+
total += e.total_usd
|
|
62
|
+
print(f" TOTAL (max, single round): ${total:.4f}")
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
raise SystemExit(main())
|