@event4u/agent-config 1.22.0 → 1.23.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/cleanup.md +31 -17
- package/.agent-src/commands/commit/in-chunks.md +30 -10
- package/.agent-src/commands/commit.md +46 -6
- package/.agent-src/commands/compress.md +19 -13
- package/.agent-src/commands/cost-report.md +120 -0
- package/.agent-src/commands/create-pr/description-only.md +8 -0
- package/.agent-src/commands/create-pr.md +95 -80
- package/.agent-src/commands/feature/plan.md +13 -7
- package/.agent-src/commands/memory/add.md +16 -8
- package/.agent-src/commands/memory/promote.md +17 -9
- package/.agent-src/commands/optimize/rtk.md +16 -11
- package/.agent-src/commands/prepare-for-review.md +12 -6
- package/.agent-src/commands/project-analyze.md +31 -20
- package/.agent-src/commands/review-changes.md +24 -15
- package/.agent-src/commands/roadmap/create.md +14 -9
- package/.agent-src/contexts/contracts/frugality-charter.md +57 -0
- package/.agent-src/rules/architecture.md +9 -0
- package/.agent-src/rules/ask-when-uncertain.md +3 -13
- package/.agent-src/rules/caveman-speak.md +78 -0
- package/.agent-src/rules/direct-answers.md +5 -14
- package/.agent-src/rules/markdown-safe-codeblocks.md +6 -7
- package/.agent-src/rules/no-cheap-questions.md +4 -14
- package/.agent-src/rules/token-efficiency.md +5 -7
- package/.agent-src/skills/adr-create/SKILL.md +197 -0
- package/.agent-src/skills/agent-docs-writing/SKILL.md +23 -1
- package/.agent-src/skills/command-writing/SKILL.md +23 -0
- package/.agent-src/skills/context-authoring/SKILL.md +23 -0
- package/.agent-src/skills/conventional-commits-writing/SKILL.md +23 -0
- package/.agent-src/skills/guideline-writing/SKILL.md +22 -0
- package/.agent-src/skills/persona-writing/SKILL.md +153 -0
- package/.agent-src/skills/readme-writing/SKILL.md +20 -0
- package/.agent-src/skills/readme-writing-package/SKILL.md +19 -0
- package/.agent-src/skills/roadmap-writing/SKILL.md +157 -0
- package/.agent-src/skills/rule-writing/SKILL.md +22 -0
- package/.agent-src/skills/script-writing/SKILL.md +226 -0
- package/.agent-src/skills/skill-writing/SKILL.md +23 -0
- package/.agent-src/skills/test-driven-development/SKILL.md +24 -0
- package/.agent-src/templates/agent-settings.md +73 -0
- package/.agent-src/templates/command.md +15 -10
- package/.agent-src/templates/rule.md +6 -0
- package/.agent-src/templates/skill.md +32 -0
- package/.claude-plugin/marketplace.json +6 -1
- package/AGENTS.md +3 -3
- package/CHANGELOG.md +35 -0
- package/README.md +5 -5
- package/docs/architecture.md +4 -4
- package/docs/customization.md +72 -0
- package/docs/decisions/INDEX.md +15 -0
- package/docs/getting-started.md +2 -2
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +27 -19
- package/docs/guidelines/agent-infra/carve-out-predicates.md +17 -0
- package/docs/guidelines/agent-infra/mcp-request-signing.md +199 -0
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +11 -4
- package/package.json +1 -1
- package/scripts/_lib/__init__.py +5 -0
- package/scripts/_lib/script_output.py +140 -0
- package/scripts/adr/regenerate_index.py +79 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_add_quiet.py +149 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_inject_quiet_flag.py +33 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_measure_v2.sh +36 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_measure_verbosity.sh +26 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_per_task.sh +41 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_silent_taskfiles.py +98 -0
- package/scripts/check_augmentignore.py +4 -1
- package/scripts/check_command_count_messaging.py +4 -1
- package/scripts/check_compressed_paths.py +4 -1
- package/scripts/check_council_layout.py +4 -1
- package/scripts/check_council_references.py +4 -1
- package/scripts/check_iron_law_prominence.py +3 -1
- package/scripts/check_md_language.py +3 -1
- package/scripts/check_memory_proposal.py +3 -1
- package/scripts/check_public_catalog_links.py +4 -1
- package/scripts/check_reply_consistency.py +8 -2
- package/scripts/check_roadmap_trackable.py +4 -1
- package/scripts/compile_router.py +27 -0
- package/scripts/compress.py +33 -19
- package/scripts/cost/budget.mjs +152 -0
- package/scripts/cost/track.mjs +144 -0
- package/scripts/first-run.sh +3 -9
- package/scripts/install-hooks.sh +19 -1
- package/scripts/install.py +17 -12
- package/scripts/install.sh +19 -8
- package/scripts/lint_examples.py +6 -2
- package/scripts/lint_handoffs.py +4 -1
- package/scripts/lint_load_context.py +4 -1
- package/scripts/lint_roadmap_complexity.py +6 -2
- package/scripts/lint_rule_interactions.py +4 -1
- package/scripts/lint_rule_tiers.py +4 -1
- package/scripts/measure_frugality_savings.py +164 -0
- package/scripts/runtime_dispatcher.py +11 -0
- package/scripts/skill_linter.py +207 -2
package/scripts/install-hooks.sh
CHANGED
|
@@ -39,7 +39,9 @@ echo "✅ Pre-push hook installed."
|
|
|
39
39
|
cat > "$HOOKS_DIR/pre-commit" << 'EOF'
|
|
40
40
|
#!/usr/bin/env bash
|
|
41
41
|
# Pre-commit hook: verify .claude-plugin/marketplace.json lists every skill
|
|
42
|
-
# that exists on disk under .claude/skills
|
|
42
|
+
# that exists on disk under .claude/skills/, AND verify
|
|
43
|
+
# agents/roadmaps-progress.md is in sync with the current state of
|
|
44
|
+
# agents/roadmaps/ (roadmap-progress-sync Iron Law).
|
|
43
45
|
|
|
44
46
|
python3 scripts/lint_marketplace.py
|
|
45
47
|
status=$?
|
|
@@ -52,6 +54,22 @@ if [ $status -ne 0 ]; then
|
|
|
52
54
|
echo " git commit --no-verify"
|
|
53
55
|
exit 1
|
|
54
56
|
fi
|
|
57
|
+
|
|
58
|
+
# Roadmap dashboard sync — only fires when staged changes touch a roadmap
|
|
59
|
+
# file or the dashboard itself, so unrelated commits stay fast.
|
|
60
|
+
if git diff --cached --name-only | grep -qE '^agents/roadmaps(-progress\.md|/)'; then
|
|
61
|
+
python3 .augment/scripts/update_roadmap_progress.py --check
|
|
62
|
+
rstatus=$?
|
|
63
|
+
if [ $rstatus -ne 0 ]; then
|
|
64
|
+
echo ""
|
|
65
|
+
echo "❌ Commit blocked — agents/roadmaps-progress.md is stale."
|
|
66
|
+
echo " Run './agent-config roadmap:progress' (or"
|
|
67
|
+
echo " 'python3 .augment/scripts/update_roadmap_progress.py'),"
|
|
68
|
+
echo " stage agents/roadmaps-progress.md, then re-commit."
|
|
69
|
+
echo " To bypass for an unrelated WIP commit: git commit --no-verify"
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
55
73
|
EOF
|
|
56
74
|
|
|
57
75
|
chmod +x "$HOOKS_DIR/pre-commit"
|
package/scripts/install.py
CHANGED
|
@@ -1280,6 +1280,7 @@ def main(argv: list[str]) -> int:
|
|
|
1280
1280
|
fail(f"Unsupported profile: {opts.profile}. Supported: {', '.join(SUPPORTED_PROFILES)}")
|
|
1281
1281
|
|
|
1282
1282
|
project_root = Path(opts.project or os.environ.get("PROJECT_ROOT") or os.getcwd()).resolve()
|
|
1283
|
+
is_first_run = not (project_root / SETTINGS_FILE).exists()
|
|
1283
1284
|
|
|
1284
1285
|
if opts.package:
|
|
1285
1286
|
package_root = Path(opts.package).resolve()
|
|
@@ -1335,18 +1336,22 @@ def main(argv: list[str]) -> int:
|
|
|
1335
1336
|
if not QUIET:
|
|
1336
1337
|
print()
|
|
1337
1338
|
success("Done.")
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1339
|
+
if is_first_run:
|
|
1340
|
+
print()
|
|
1341
|
+
print(" Try these 3 prompts with your agent:")
|
|
1342
|
+
print(' 1. "Refactor this function" → agent analyzes first')
|
|
1343
|
+
print(' 2. "Add caching to this" → agent asks instead of guessing')
|
|
1344
|
+
print(' 3. "Implement this feature" → agent respects your codebase')
|
|
1345
|
+
print()
|
|
1346
|
+
print(" Next steps:")
|
|
1347
|
+
print(" • Commit .agent-settings.yml and bridge files to your repo")
|
|
1348
|
+
print(" • New team members just run composer install / npm install — done")
|
|
1349
|
+
print(" • Inspect hook coverage: ./agent-config hooks:status")
|
|
1350
|
+
print(" • Full walkthrough: https://github.com/event4u-app/agent-config/blob/main/docs/getting-started.md")
|
|
1351
|
+
print()
|
|
1352
|
+
else:
|
|
1353
|
+
print(" Re-run complete. Walkthrough: https://github.com/event4u-app/agent-config/blob/main/docs/getting-started.md")
|
|
1354
|
+
print()
|
|
1350
1355
|
return 0
|
|
1351
1356
|
|
|
1352
1357
|
|
package/scripts/install.sh
CHANGED
|
@@ -693,11 +693,18 @@ install_cli_wrapper() {
|
|
|
693
693
|
main() {
|
|
694
694
|
parse_args "$@"
|
|
695
695
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
696
|
+
# First-run detection: gate the verbose source/target banner behind the
|
|
697
|
+
# absence of .agent-settings.yml. Re-runs print a single status line.
|
|
698
|
+
local is_first_run=false
|
|
699
|
+
[[ ! -f "$TARGET_DIR/.agent-settings.yml" ]] && is_first_run=true
|
|
700
|
+
|
|
701
|
+
if $is_first_run && ! $QUIET; then
|
|
702
|
+
echo "🔧 Syncing agent-config payload..."
|
|
703
|
+
echo " Source: $SOURCE_DIR"
|
|
704
|
+
echo " Target: $TARGET_DIR"
|
|
705
|
+
$DRY_RUN && echo " Mode: DRY RUN"
|
|
706
|
+
echo ""
|
|
707
|
+
fi
|
|
701
708
|
|
|
702
709
|
# 0. Migrate legacy infra files (root → agents/) before any content sync.
|
|
703
710
|
migrate_legacy_root_infra "$TARGET_DIR"
|
|
@@ -737,9 +744,13 @@ main() {
|
|
|
737
744
|
# 6. Manage .gitignore
|
|
738
745
|
ensure_gitignore "$TARGET_DIR"
|
|
739
746
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
747
|
+
if $is_first_run && ! $QUIET; then
|
|
748
|
+
echo ""
|
|
749
|
+
echo "✅ agent-config payload synced."
|
|
750
|
+
echo " Run scripts/install (or python3 scripts/install.py) to render .agent-settings.yml and bridges."
|
|
751
|
+
elif ! $QUIET; then
|
|
752
|
+
echo "✅ agent-config payload synced."
|
|
753
|
+
fi
|
|
743
754
|
}
|
|
744
755
|
|
|
745
756
|
main "$@"
|
package/scripts/lint_examples.py
CHANGED
|
@@ -15,6 +15,8 @@ import re
|
|
|
15
15
|
import sys
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
|
|
18
|
+
QUIET = "--quiet" in sys.argv
|
|
19
|
+
|
|
18
20
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
19
21
|
DEMO_GLOB = "docs/guidelines/agent-infra/*-demos.md"
|
|
20
22
|
REQUIRED_FM_KEYS = ("demo_for:", "layer: pattern-memory", "prose_delta:")
|
|
@@ -86,11 +88,13 @@ def main() -> int:
|
|
|
86
88
|
for p in problems:
|
|
87
89
|
print(f" - {p}", file=sys.stderr)
|
|
88
90
|
else:
|
|
89
|
-
|
|
91
|
+
if not QUIET:
|
|
92
|
+
print(f"✅ {rel}")
|
|
90
93
|
if failed:
|
|
91
94
|
print(f"\n❌ {failed} demo file(s) failed shape lint", file=sys.stderr)
|
|
92
95
|
return 1
|
|
93
|
-
|
|
96
|
+
if not QUIET:
|
|
97
|
+
print(f"\n✅ {len(demos)} demo file(s) shape-clean")
|
|
94
98
|
return 0
|
|
95
99
|
|
|
96
100
|
|
package/scripts/lint_handoffs.py
CHANGED
|
@@ -24,6 +24,8 @@ from dataclasses import dataclass
|
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
from typing import Iterable
|
|
26
26
|
|
|
27
|
+
QUIET = "--quiet" in sys.argv
|
|
28
|
+
|
|
27
29
|
REPO = Path(__file__).resolve().parents[1]
|
|
28
30
|
SKILLS_DIR = REPO / ".agent-src.uncompressed" / "skills"
|
|
29
31
|
|
|
@@ -202,7 +204,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
202
204
|
skills_dir = Path(argv[0]).resolve()
|
|
203
205
|
violations = lint(skills_dir)
|
|
204
206
|
if not violations:
|
|
205
|
-
|
|
207
|
+
if not QUIET:
|
|
208
|
+
print(f"✅ lint_handoffs: no violations under {skills_dir.relative_to(REPO)}")
|
|
206
209
|
return 0
|
|
207
210
|
for v in violations:
|
|
208
211
|
print(v.render(REPO))
|
|
@@ -19,6 +19,8 @@ from typing import Iterable
|
|
|
19
19
|
|
|
20
20
|
import yaml
|
|
21
21
|
|
|
22
|
+
QUIET = "--quiet" in sys.argv
|
|
23
|
+
|
|
22
24
|
ROOT = Path(__file__).resolve().parent.parent
|
|
23
25
|
|
|
24
26
|
SCAN_DIRS = [
|
|
@@ -185,7 +187,8 @@ def main() -> int:
|
|
|
185
187
|
print(f"❌ {e}")
|
|
186
188
|
if errors:
|
|
187
189
|
return 1
|
|
188
|
-
|
|
190
|
+
if not QUIET:
|
|
191
|
+
print(f"✅ load_context schema clean ({len(graph)} declarer(s))")
|
|
189
192
|
return 0
|
|
190
193
|
|
|
191
194
|
|
|
@@ -21,6 +21,8 @@ import re
|
|
|
21
21
|
import sys
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
|
|
24
|
+
QUIET = "--quiet" in sys.argv
|
|
25
|
+
|
|
24
26
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
25
27
|
ROADMAP_GLOB = "agents/roadmaps/*.md"
|
|
26
28
|
LIGHTWEIGHT_LINE_CAP = 600
|
|
@@ -107,7 +109,8 @@ def main() -> int:
|
|
|
107
109
|
for p in problems:
|
|
108
110
|
print(f" - {p}", file=sys.stderr)
|
|
109
111
|
else:
|
|
110
|
-
|
|
112
|
+
if not QUIET:
|
|
113
|
+
print(f"✅ {rel} [{complexity}]")
|
|
111
114
|
print()
|
|
112
115
|
light = sum(1 for _, c in summary if c == "lightweight")
|
|
113
116
|
structural = sum(1 for _, c in summary if c == "structural")
|
|
@@ -119,7 +122,8 @@ def main() -> int:
|
|
|
119
122
|
if failed:
|
|
120
123
|
print(f"\n❌ {failed} roadmap(s) failed complexity lint", file=sys.stderr)
|
|
121
124
|
return 1
|
|
122
|
-
|
|
125
|
+
if not QUIET:
|
|
126
|
+
print(f"\n✅ {len(roadmaps)} roadmap(s) complexity-clean")
|
|
123
127
|
return 0
|
|
124
128
|
|
|
125
129
|
|
|
@@ -22,6 +22,8 @@ from pathlib import Path
|
|
|
22
22
|
|
|
23
23
|
import yaml
|
|
24
24
|
|
|
25
|
+
QUIET = "--quiet" in sys.argv
|
|
26
|
+
|
|
25
27
|
ROOT = Path(__file__).resolve().parent.parent
|
|
26
28
|
MATRIX = ROOT / "docs" / "contracts" / "rule-interactions.yml"
|
|
27
29
|
RULES_DIR = ROOT / ".agent-src.uncompressed" / "rules"
|
|
@@ -141,7 +143,8 @@ def main() -> int:
|
|
|
141
143
|
if errors:
|
|
142
144
|
fail(errors)
|
|
143
145
|
|
|
144
|
-
|
|
146
|
+
if not QUIET:
|
|
147
|
+
print(f"✅ rule-interactions.yml clean — {len(declared_rules)} rules, {len(pairs)} pairs.")
|
|
145
148
|
return 0
|
|
146
149
|
|
|
147
150
|
|
|
@@ -17,6 +17,8 @@ from __future__ import annotations
|
|
|
17
17
|
import sys
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
|
+
QUIET = "--quiet" in sys.argv
|
|
21
|
+
|
|
20
22
|
REPO = Path(__file__).resolve().parents[1]
|
|
21
23
|
RULES_DIR = REPO / ".agent-src.uncompressed" / "rules"
|
|
22
24
|
|
|
@@ -70,7 +72,8 @@ def main() -> int:
|
|
|
70
72
|
)
|
|
71
73
|
return 1
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
if not QUIET:
|
|
76
|
+
print(f"✅ lint_rule_tiers: {len(rules)} rules, all tier values valid")
|
|
74
77
|
return 0
|
|
75
78
|
|
|
76
79
|
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Phase 0 baseline harness for road-to-trim-frugality-canon.
|
|
3
|
+
|
|
4
|
+
Measures the *current state* of the frugality canon along four
|
|
5
|
+
deterministic axes. Output: JSONL baseline appended to
|
|
6
|
+
agents/.frugality-baseline.jsonl (gitignored).
|
|
7
|
+
|
|
8
|
+
Metrics:
|
|
9
|
+
A. footprint — per-rule char/token count, kernel/tier breakdown
|
|
10
|
+
B. fillers — filler-phrase prevalence in chat-history corpus
|
|
11
|
+
(heuristic signal, not full transcript)
|
|
12
|
+
C. compression — uncompressed → compressed char delta per rule
|
|
13
|
+
D. redundancy — cross-ref overlap across "Interactions:" /
|
|
14
|
+
"See also" sections in the canon
|
|
15
|
+
|
|
16
|
+
Trim phases re-run this harness after each PR. Decline condition fires
|
|
17
|
+
if metric B regresses (filler prevalence increases) or metric C drops
|
|
18
|
+
below current baseline by >10% per rule.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
import re
|
|
24
|
+
from datetime import datetime, timezone
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
CANON_RULES = [
|
|
28
|
+
("direct-answers", "kernel"),
|
|
29
|
+
("no-cheap-questions", "kernel"),
|
|
30
|
+
("ask-when-uncertain", "kernel"),
|
|
31
|
+
("user-interaction", "tier_1"),
|
|
32
|
+
("caveman-speak", "tier_1"),
|
|
33
|
+
("token-efficiency", "tier_2"),
|
|
34
|
+
]
|
|
35
|
+
CHARTER = "frugality-charter"
|
|
36
|
+
|
|
37
|
+
FILLER_PATTERNS = [
|
|
38
|
+
r"\bgreat question\b", r"\bfascinating\b", r"\bexcellent point\b",
|
|
39
|
+
r"\blet me\s+(check|look|find|verify|investigate|see)\b",
|
|
40
|
+
r"\bnow\s+(i'll|i will|let's)\b",
|
|
41
|
+
r"\bgoing to\s+(check|run|use|call|invoke)\b",
|
|
42
|
+
r"\bperfect\b!?", r"\bawesome\b!?",
|
|
43
|
+
r"\bhere's what i\b", r"\bfound it\b",
|
|
44
|
+
r"^\s*(ok|okay|alright)[!,.]\s",
|
|
45
|
+
]
|
|
46
|
+
FILLER_RE = re.compile("|".join(FILLER_PATTERNS), re.IGNORECASE | re.MULTILINE)
|
|
47
|
+
|
|
48
|
+
# Cross-ref section headers to count for redundancy metric
|
|
49
|
+
XREF_HEADERS = re.compile(r"^##\s+(Interactions|See also|Related)\s*$", re.MULTILINE)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _read(path: Path) -> str:
|
|
53
|
+
return path.read_text() if path.exists() else ""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def metric_a_footprint(root: Path) -> dict:
|
|
57
|
+
"""Per-rule char count, tier classification, total kernel %."""
|
|
58
|
+
rows = []
|
|
59
|
+
kernel_total = 0
|
|
60
|
+
tier1_total = 0
|
|
61
|
+
tier2_total = 0
|
|
62
|
+
for name, tier in CANON_RULES:
|
|
63
|
+
compressed = root / ".agent-src" / "rules" / f"{name}.md"
|
|
64
|
+
chars = len(_read(compressed))
|
|
65
|
+
tokens = chars // 4 # rough 4-char/token approximation
|
|
66
|
+
rows.append({"rule": name, "tier": tier, "chars": chars, "tokens_approx": tokens})
|
|
67
|
+
if tier == "kernel":
|
|
68
|
+
kernel_total += chars
|
|
69
|
+
elif tier == "tier_1":
|
|
70
|
+
tier1_total += chars
|
|
71
|
+
elif tier == "tier_2":
|
|
72
|
+
tier2_total += chars
|
|
73
|
+
charter_chars = len(_read(root / ".agent-src" / "contexts" / "contracts" / f"{CHARTER}.md"))
|
|
74
|
+
return {
|
|
75
|
+
"rules": rows,
|
|
76
|
+
"kernel_total_chars": kernel_total,
|
|
77
|
+
"tier_1_total_chars": tier1_total,
|
|
78
|
+
"tier_2_total_chars": tier2_total,
|
|
79
|
+
"charter_chars": charter_chars,
|
|
80
|
+
"kernel_budget_chars": 26000,
|
|
81
|
+
"kernel_pct": round(100 * kernel_total / 26000, 2),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def metric_b_fillers(corpus: Path) -> dict:
|
|
86
|
+
"""Filler-phrase hits per agent turn in chat-history corpus."""
|
|
87
|
+
if not corpus.exists():
|
|
88
|
+
return {"corpus_present": False}
|
|
89
|
+
lines = corpus.read_text().splitlines()
|
|
90
|
+
agent_turns = 0
|
|
91
|
+
filler_hits = 0
|
|
92
|
+
total_chars = 0
|
|
93
|
+
for ln in lines[1:]:
|
|
94
|
+
try:
|
|
95
|
+
d = json.loads(ln)
|
|
96
|
+
except json.JSONDecodeError:
|
|
97
|
+
continue
|
|
98
|
+
if d.get("t") != "agent":
|
|
99
|
+
continue
|
|
100
|
+
text = d.get("text", "")
|
|
101
|
+
agent_turns += 1
|
|
102
|
+
total_chars += len(text)
|
|
103
|
+
filler_hits += len(FILLER_RE.findall(text))
|
|
104
|
+
return {
|
|
105
|
+
"corpus_present": True,
|
|
106
|
+
"agent_turns": agent_turns,
|
|
107
|
+
"filler_hits_total": filler_hits,
|
|
108
|
+
"filler_hits_per_turn": round(filler_hits / max(agent_turns, 1), 3),
|
|
109
|
+
"agent_chars_total": total_chars,
|
|
110
|
+
"patterns_count": len(FILLER_PATTERNS),
|
|
111
|
+
"note": "chat-history texts are digests, not full transcripts; signal not output volume",
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def metric_c_compression(root: Path) -> dict:
|
|
116
|
+
"""Uncompressed → compressed char delta per rule."""
|
|
117
|
+
rows = []
|
|
118
|
+
for name, _ in CANON_RULES:
|
|
119
|
+
un = len(_read(root / ".agent-src.uncompressed" / "rules" / f"{name}.md"))
|
|
120
|
+
co = len(_read(root / ".agent-src" / "rules" / f"{name}.md"))
|
|
121
|
+
delta = un - co
|
|
122
|
+
ratio = round(co / un, 3) if un else 0
|
|
123
|
+
rows.append({"rule": name, "uncompressed_chars": un, "compressed_chars": co, "delta": delta, "ratio": ratio})
|
|
124
|
+
return {"rules": rows}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def metric_d_redundancy(root: Path) -> dict:
|
|
128
|
+
"""Cross-ref section count + total xref-block size."""
|
|
129
|
+
rows = []
|
|
130
|
+
for name, _ in CANON_RULES:
|
|
131
|
+
path = root / ".agent-src.uncompressed" / "rules" / f"{name}.md"
|
|
132
|
+
text = _read(path)
|
|
133
|
+
xref_count = len(XREF_HEADERS.findall(text))
|
|
134
|
+
# naive: chars after last xref header to EOF
|
|
135
|
+
m = list(XREF_HEADERS.finditer(text))
|
|
136
|
+
xref_block_chars = (len(text) - m[-1].start()) if m else 0
|
|
137
|
+
rows.append({"rule": name, "xref_sections": xref_count, "xref_block_chars": xref_block_chars})
|
|
138
|
+
return {"rules": rows, "total_xref_chars": sum(r["xref_block_chars"] for r in rows)}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def main() -> int:
|
|
142
|
+
root = Path(__file__).resolve().parent.parent
|
|
143
|
+
corpus = root / "agents" / ".agent-chat-history"
|
|
144
|
+
|
|
145
|
+
record = {
|
|
146
|
+
"schema_version": 1,
|
|
147
|
+
"ts": datetime.now(tz=timezone.utc).isoformat(timespec="seconds"),
|
|
148
|
+
"phase": "phase_0_baseline",
|
|
149
|
+
"metric_a_footprint": metric_a_footprint(root),
|
|
150
|
+
"metric_b_fillers": metric_b_fillers(corpus),
|
|
151
|
+
"metric_c_compression": metric_c_compression(root),
|
|
152
|
+
"metric_d_redundancy": metric_d_redundancy(root),
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
out = root / "agents" / ".frugality-baseline.jsonl"
|
|
156
|
+
with out.open("a") as fh:
|
|
157
|
+
fh.write(json.dumps(record, ensure_ascii=False) + "\n")
|
|
158
|
+
print(json.dumps(record, indent=2, ensure_ascii=False))
|
|
159
|
+
print(f"\nappended → {out.relative_to(root)}", flush=True)
|
|
160
|
+
return 0
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
raise SystemExit(main())
|
|
@@ -30,6 +30,7 @@ from typing import List, Optional
|
|
|
30
30
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
31
31
|
from runtime_registry import SkillRuntime, build_registry
|
|
32
32
|
from runtime_handler import ExecutionResult, HandlerError, execute_shell
|
|
33
|
+
from _lib.script_output import resolve_level # type: ignore[import-not-found]
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
@dataclass
|
|
@@ -187,6 +188,16 @@ def _print_execution(result: ExecutionResult, fmt: str) -> None:
|
|
|
187
188
|
if fmt == "json":
|
|
188
189
|
print(json.dumps(asdict(result), indent=2))
|
|
189
190
|
return
|
|
191
|
+
level = resolve_level()
|
|
192
|
+
if level == "silent" and result.is_success:
|
|
193
|
+
return
|
|
194
|
+
if level == "minimal" and result.is_success:
|
|
195
|
+
marker = "✅" if result.is_success else "❌"
|
|
196
|
+
print(
|
|
197
|
+
f"{marker} {result.skill_name} · {result.handler} · "
|
|
198
|
+
f"exit={result.exit_code} ({result.duration_ms}ms)"
|
|
199
|
+
)
|
|
200
|
+
return
|
|
190
201
|
print(f"Skill: {result.skill_name}")
|
|
191
202
|
print(f"Handler: {result.handler}")
|
|
192
203
|
print(f"Command: {' '.join(result.command)}")
|