@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.
Files changed (91) hide show
  1. package/.agent-src/commands/agents/cleanup.md +31 -17
  2. package/.agent-src/commands/commit/in-chunks.md +30 -10
  3. package/.agent-src/commands/commit.md +46 -6
  4. package/.agent-src/commands/compress.md +19 -13
  5. package/.agent-src/commands/cost-report.md +120 -0
  6. package/.agent-src/commands/create-pr/description-only.md +8 -0
  7. package/.agent-src/commands/create-pr.md +95 -80
  8. package/.agent-src/commands/feature/plan.md +13 -7
  9. package/.agent-src/commands/memory/add.md +16 -8
  10. package/.agent-src/commands/memory/promote.md +17 -9
  11. package/.agent-src/commands/optimize/rtk.md +16 -11
  12. package/.agent-src/commands/prepare-for-review.md +12 -6
  13. package/.agent-src/commands/project-analyze.md +31 -20
  14. package/.agent-src/commands/review-changes.md +24 -15
  15. package/.agent-src/commands/roadmap/create.md +14 -9
  16. package/.agent-src/contexts/contracts/frugality-charter.md +57 -0
  17. package/.agent-src/rules/architecture.md +9 -0
  18. package/.agent-src/rules/ask-when-uncertain.md +3 -13
  19. package/.agent-src/rules/caveman-speak.md +78 -0
  20. package/.agent-src/rules/direct-answers.md +5 -14
  21. package/.agent-src/rules/markdown-safe-codeblocks.md +6 -7
  22. package/.agent-src/rules/no-cheap-questions.md +4 -14
  23. package/.agent-src/rules/token-efficiency.md +5 -7
  24. package/.agent-src/skills/adr-create/SKILL.md +197 -0
  25. package/.agent-src/skills/agent-docs-writing/SKILL.md +23 -1
  26. package/.agent-src/skills/command-writing/SKILL.md +23 -0
  27. package/.agent-src/skills/context-authoring/SKILL.md +23 -0
  28. package/.agent-src/skills/conventional-commits-writing/SKILL.md +23 -0
  29. package/.agent-src/skills/guideline-writing/SKILL.md +22 -0
  30. package/.agent-src/skills/persona-writing/SKILL.md +153 -0
  31. package/.agent-src/skills/readme-writing/SKILL.md +20 -0
  32. package/.agent-src/skills/readme-writing-package/SKILL.md +19 -0
  33. package/.agent-src/skills/roadmap-writing/SKILL.md +157 -0
  34. package/.agent-src/skills/rule-writing/SKILL.md +22 -0
  35. package/.agent-src/skills/script-writing/SKILL.md +226 -0
  36. package/.agent-src/skills/skill-writing/SKILL.md +23 -0
  37. package/.agent-src/skills/test-driven-development/SKILL.md +24 -0
  38. package/.agent-src/templates/agent-settings.md +73 -0
  39. package/.agent-src/templates/command.md +15 -10
  40. package/.agent-src/templates/rule.md +6 -0
  41. package/.agent-src/templates/skill.md +32 -0
  42. package/.claude-plugin/marketplace.json +6 -1
  43. package/AGENTS.md +3 -3
  44. package/CHANGELOG.md +35 -0
  45. package/README.md +5 -5
  46. package/docs/architecture.md +4 -4
  47. package/docs/customization.md +72 -0
  48. package/docs/decisions/INDEX.md +15 -0
  49. package/docs/getting-started.md +2 -2
  50. package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +27 -19
  51. package/docs/guidelines/agent-infra/carve-out-predicates.md +17 -0
  52. package/docs/guidelines/agent-infra/mcp-request-signing.md +199 -0
  53. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +11 -4
  54. package/package.json +1 -1
  55. package/scripts/_lib/__init__.py +5 -0
  56. package/scripts/_lib/script_output.py +140 -0
  57. package/scripts/adr/regenerate_index.py +79 -0
  58. package/scripts/ai_council/one_off_archive/2026-05/_one_off_add_quiet.py +149 -0
  59. package/scripts/ai_council/one_off_archive/2026-05/_one_off_inject_quiet_flag.py +33 -0
  60. package/scripts/ai_council/one_off_archive/2026-05/_one_off_measure_v2.sh +36 -0
  61. package/scripts/ai_council/one_off_archive/2026-05/_one_off_measure_verbosity.sh +26 -0
  62. package/scripts/ai_council/one_off_archive/2026-05/_one_off_per_task.sh +41 -0
  63. package/scripts/ai_council/one_off_archive/2026-05/_one_off_silent_taskfiles.py +98 -0
  64. package/scripts/check_augmentignore.py +4 -1
  65. package/scripts/check_command_count_messaging.py +4 -1
  66. package/scripts/check_compressed_paths.py +4 -1
  67. package/scripts/check_council_layout.py +4 -1
  68. package/scripts/check_council_references.py +4 -1
  69. package/scripts/check_iron_law_prominence.py +3 -1
  70. package/scripts/check_md_language.py +3 -1
  71. package/scripts/check_memory_proposal.py +3 -1
  72. package/scripts/check_public_catalog_links.py +4 -1
  73. package/scripts/check_reply_consistency.py +8 -2
  74. package/scripts/check_roadmap_trackable.py +4 -1
  75. package/scripts/compile_router.py +27 -0
  76. package/scripts/compress.py +33 -19
  77. package/scripts/cost/budget.mjs +152 -0
  78. package/scripts/cost/track.mjs +144 -0
  79. package/scripts/first-run.sh +3 -9
  80. package/scripts/install-hooks.sh +19 -1
  81. package/scripts/install.py +17 -12
  82. package/scripts/install.sh +19 -8
  83. package/scripts/lint_examples.py +6 -2
  84. package/scripts/lint_handoffs.py +4 -1
  85. package/scripts/lint_load_context.py +4 -1
  86. package/scripts/lint_roadmap_complexity.py +6 -2
  87. package/scripts/lint_rule_interactions.py +4 -1
  88. package/scripts/lint_rule_tiers.py +4 -1
  89. package/scripts/measure_frugality_savings.py +164 -0
  90. package/scripts/runtime_dispatcher.py +11 -0
  91. package/scripts/skill_linter.py +207 -2
@@ -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"
@@ -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
- print()
1339
- print(" Try these 3 prompts with your agent:")
1340
- print(' 1. "Refactor this function" → agent analyzes first')
1341
- print(' 2. "Add caching to this" → agent asks instead of guessing')
1342
- print(' 3. "Implement this feature" → agent respects your codebase')
1343
- print()
1344
- print(" Next steps:")
1345
- print(" Commit .agent-settings.yml and bridge files to your repo")
1346
- print(" • New team members just run composer install / npm install — done")
1347
- print(" • Inspect hook coverage: ./agent-config hooks:status")
1348
- print(" • Full walkthrough: https://github.com/event4u-app/agent-config/blob/main/docs/getting-started.md")
1349
- print()
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
 
@@ -693,11 +693,18 @@ install_cli_wrapper() {
693
693
  main() {
694
694
  parse_args "$@"
695
695
 
696
- $QUIET || echo "🔧 Syncing agent-config payload..."
697
- $QUIET || echo " Source: $SOURCE_DIR"
698
- $QUIET || echo " Target: $TARGET_DIR"
699
- $DRY_RUN && ! $QUIET && echo " Mode: DRY RUN"
700
- echo ""
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
- echo ""
741
- $QUIET || echo "✅ agent-config payload synced."
742
- $QUIET || echo " Run scripts/install (or python3 scripts/install.py) to render .agent-settings.yml and bridges."
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 "$@"
@@ -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
- print(f"✅ {rel}")
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
- print(f"\n✅ {len(demos)} demo file(s) shape-clean")
96
+ if not QUIET:
97
+ print(f"\n✅ {len(demos)} demo file(s) shape-clean")
94
98
  return 0
95
99
 
96
100
 
@@ -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
- print(f"✅ lint_handoffs: no violations under {skills_dir.relative_to(REPO)}")
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
- print(f"✅ load_context schema clean ({len(graph)} declarer(s))")
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
- print(f"✅ {rel} [{complexity}]")
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
- print(f"\n✅ {len(roadmaps)} roadmap(s) complexity-clean")
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
- print(f"✅ rule-interactions.yml clean — {len(declared_rules)} rules, {len(pairs)} pairs.")
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
- print(f"✅ lint_rule_tiers: {len(rules)} rules, all tier values valid")
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)}")