@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
@@ -0,0 +1,140 @@
1
+ """Verbosity-aware print router for scripts/*.py.
2
+
3
+ Phase 10 of road-to-token-frugality. Single source of truth for how
4
+ maintenance scripts emit progress, success, warnings, and errors.
5
+
6
+ Resolution order (first wins):
7
+ 1. AGENT_SCRIPT_VERBOSITY env var (silent | minimal | verbose)
8
+ 2. SCRIPT_OUTPUT_VERBOSE=1 alias (== verbose)
9
+ 3. .agent-settings.yml verbosity.script_output
10
+ 4. Default: minimal
11
+
12
+ Once resolved, the level is exported back into AGENT_SCRIPT_VERBOSITY
13
+ so child processes inherit the same level (Phase 10.1c). Explicit
14
+ --quiet flags on the child still win at the call site.
15
+
16
+ Levels:
17
+ silent = stderr only; success() drops; info() drops; warn() drops
18
+ minimal = success() collapsed to one end-of-run summary; info() drops
19
+ verbose = pre-Phase-10 behaviour, every call prints
20
+
21
+ error() always writes to stderr regardless of level. Iron-Law surfaces
22
+ (release confirms, install secrets prompts) bypass this module and use
23
+ plain print() so they cannot be silenced.
24
+ """
25
+ from __future__ import annotations
26
+
27
+ import os
28
+ import sys
29
+ from pathlib import Path
30
+ from typing import Final
31
+
32
+ VALID_LEVELS: Final[tuple[str, ...]] = ("silent", "minimal", "verbose")
33
+ DEFAULT_LEVEL: Final[str] = "minimal"
34
+ ENV_VAR: Final[str] = "AGENT_SCRIPT_VERBOSITY"
35
+ ENV_ALIAS: Final[str] = "SCRIPT_OUTPUT_VERBOSE"
36
+ SETTINGS_FILE: Final[str] = ".agent-settings.yml"
37
+
38
+ _resolved_level: str | None = None
39
+ _pending_summary: list[str] = []
40
+
41
+
42
+ def _read_settings_level(settings_path: Path) -> str | None:
43
+ """Read verbosity.script_output from .agent-settings.yml.
44
+
45
+ Returns None when the file is missing, PyYAML is unavailable, or
46
+ the key is absent. Errors fall through to the default level.
47
+ """
48
+ if not settings_path.is_file():
49
+ return None
50
+ try:
51
+ import yaml # type: ignore[import-untyped]
52
+ except ImportError:
53
+ return None
54
+ try:
55
+ with settings_path.open(encoding="utf-8") as fh:
56
+ data = yaml.safe_load(fh) or {}
57
+ except (OSError, yaml.YAMLError):
58
+ return None
59
+ section = data.get("verbosity") if isinstance(data, dict) else None
60
+ if not isinstance(section, dict):
61
+ return None
62
+ value = section.get("script_output")
63
+ if isinstance(value, str) and value in VALID_LEVELS:
64
+ return value
65
+ return None
66
+
67
+
68
+ def resolve_level(settings_path: Path | None = None) -> str:
69
+ """Resolve and cache the active verbosity level.
70
+
71
+ First call wins; subsequent calls return the cached value so the
72
+ process is internally consistent. Tests reset via reset_level().
73
+ """
74
+ global _resolved_level
75
+ if _resolved_level is not None:
76
+ return _resolved_level
77
+
78
+ env_value = os.environ.get(ENV_VAR, "").strip().lower()
79
+ if env_value in VALID_LEVELS:
80
+ _resolved_level = env_value
81
+ elif os.environ.get(ENV_ALIAS, "").strip() == "1":
82
+ _resolved_level = "verbose"
83
+ else:
84
+ path = settings_path or Path(SETTINGS_FILE)
85
+ _resolved_level = _read_settings_level(path) or DEFAULT_LEVEL
86
+
87
+ # Inheritance: export resolved level so child processes see it.
88
+ os.environ[ENV_VAR] = _resolved_level
89
+ return _resolved_level
90
+
91
+
92
+ def reset_level() -> None:
93
+ """Clear the cached level. Test helper."""
94
+ global _resolved_level
95
+ _resolved_level = None
96
+ _pending_summary.clear()
97
+
98
+
99
+ def info(message: str) -> None:
100
+ """Per-step progress note. Drops at silent + minimal."""
101
+ if resolve_level() == "verbose":
102
+ print(message)
103
+
104
+
105
+ def success(message: str) -> None:
106
+ """Per-step success. At minimal collected for end-of-run summary;
107
+ at verbose printed immediately; at silent dropped."""
108
+ level = resolve_level()
109
+ if level == "verbose":
110
+ print(message)
111
+ elif level == "minimal":
112
+ _pending_summary.append(message)
113
+
114
+
115
+ def warn(message: str) -> None:
116
+ """Warning. Stderr at all levels except silent."""
117
+ if resolve_level() != "silent":
118
+ print(message, file=sys.stderr)
119
+
120
+
121
+ def error(message: str) -> None:
122
+ """Error. Always stderr regardless of level."""
123
+ print(message, file=sys.stderr)
124
+
125
+
126
+ def flush_summary(headline: str | None = None) -> None:
127
+ """Emit the pending success() summary at end-of-run.
128
+
129
+ No-op at verbose (already printed) and silent (suppressed).
130
+ Use the explicit `headline` arg to override the auto-pick.
131
+ """
132
+ level = resolve_level()
133
+ if level != "minimal" or not _pending_summary:
134
+ return
135
+ if headline:
136
+ print(headline)
137
+ else:
138
+ # Default: print the last collected line as the headline.
139
+ print(_pending_summary[-1])
140
+ _pending_summary.clear()
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env python3
2
+ """Regenerate INDEX.md for an ADR directory. Parses ADR-*.md frontmatter
3
+ (adr/status/date/decision/supersedes), writes INDEX.md, splits legacy
4
+ non-numbered ADRs into an Unnumbered table, hard-fails on duplicate
5
+ numbers, filename/frontmatter mismatch, or broken supersedes links."""
6
+ import argparse, re, sys
7
+ from pathlib import Path
8
+
9
+ NAMED = re.compile(r"^ADR-(\d{3})-([a-z0-9-]+)\.md$")
10
+ FM = re.compile(r"^---\n(.*?)\n---", re.DOTALL)
11
+ FIELD = re.compile(r"^([a-z_]+):\s*(.+?)\s*$", re.MULTILINE)
12
+ HEAD = "| # | Title | Status | Date | Supersedes |\n|---|---|---|---|---|"
13
+
14
+
15
+ def fm(t):
16
+ m = FM.search(t)
17
+ return {k: v.strip(" \"'") for k, v in FIELD.findall(m.group(1))} if m else {}
18
+
19
+
20
+ def scan(d):
21
+ num, leg, errs, seen = [], [], [], {}
22
+ for p in sorted(d.glob("ADR-*.md")):
23
+ if p.name == "INDEX.md": continue
24
+ meta, m = fm(p.read_text(encoding="utf-8")), NAMED.match(p.name)
25
+ if not m:
26
+ leg.append({"path": p.name, **meta}); continue
27
+ n = m.group(1)
28
+ if meta.get("adr") and meta["adr"].lstrip("0") != n.lstrip("0"):
29
+ errs.append(f"{p.name}: adr={meta['adr']} != filename {n}")
30
+ if n in seen: errs.append(f"ADR-{n} duplicate: {p.name} and {seen[n]}")
31
+ seen[n] = p.name
32
+ num.append({"num": n, "slug": m.group(2), "path": p.name, **meta})
33
+ nums = {r["num"] for r in num}
34
+ for r in num:
35
+ s = r.get("supersedes", "—")
36
+ if s and s != "—":
37
+ t = s.replace("ADR-", "").lstrip("0").zfill(3)
38
+ if t not in nums: errs.append(f"{r['path']}: supersedes ADR-{t} not found")
39
+ return num, leg, errs
40
+
41
+
42
+ def row(r):
43
+ title = r.get("decision", r.get("slug", "—")).replace("-", " ").title()
44
+ label = f"ADR-{r['num']}" if "num" in r else r["path"][:-3]
45
+ return (f"| [{label}]({r['path']}) | {title} | {r.get('status','—')} "
46
+ f"| {r.get('date','—')} | {r.get('supersedes','—')} |")
47
+
48
+
49
+ def render(num, leg):
50
+ out = ["# ADR Index", "", "_Auto-generated by `scripts/adr/regenerate_index.py`. Do not edit._", ""]
51
+ if not num and not leg: return "\n".join(out + ["No ADRs yet.", ""])
52
+ out += [HEAD, *(row(r) for r in num)]
53
+ if leg: out += ["", "## Unnumbered (legacy)", "", HEAD, *(row(r) for r in leg)]
54
+ return "\n".join(out + [""])
55
+
56
+
57
+ def main():
58
+ ap = argparse.ArgumentParser(description="Regenerate ADR INDEX.md")
59
+ ap.add_argument("--dir", default="docs/adr/")
60
+ ap.add_argument("--check", action="store_true", help="exit 1 if INDEX.md is stale")
61
+ a = ap.parse_args()
62
+ d = Path(a.dir)
63
+ if not d.is_dir():
64
+ print(f"adr-dir not found: {d}", file=sys.stderr); return 2
65
+ num, leg, errs = scan(d)
66
+ for e in errs: print(f"error: {e}", file=sys.stderr)
67
+ if errs: return 2
68
+ rendered, idx = render(num, leg), d / "INDEX.md"
69
+ if a.check:
70
+ cur = idx.read_text(encoding="utf-8") if idx.exists() else ""
71
+ if cur != rendered: print(f"stale: {idx}", file=sys.stderr); return 1
72
+ return 0
73
+ idx.write_text(rendered, encoding="utf-8")
74
+ print(f"wrote {idx} ({len(num)} numbered, {len(leg)} legacy)")
75
+ return 0
76
+
77
+
78
+ if __name__ == "__main__":
79
+ sys.exit(main())
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env python3
2
+ """One-off: add --quiet flag to every check_*/lint_* script that lacks one.
3
+
4
+ Target pattern (the canonical example is check_one_off_location.py):
5
+ - argparse parser exists
6
+ - parser.add_argument("--quiet", action="store_true", help="Only print on failure")
7
+ - success print lines matching `print(...✅...)` are wrapped:
8
+ if not args.quiet:
9
+ print("✅ ...")
10
+
11
+ For scripts without argparse, fall back to a minimal sys.argv probe.
12
+ Reports a manual-review list for anything that doesn't match the simple pattern.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import re
17
+ from pathlib import Path
18
+
19
+ SCRIPTS = Path("scripts")
20
+ SUCCESS_RE = re.compile(r'^(\s*)(print\((?:f)?["\'].*\u2705.*\))\s*$')
21
+ # Accept any parser var name (parser, ap, p, …) — capture both lhs (args var) and rhs (parser var).
22
+ PARSE_ARGS_RE = re.compile(
23
+ r"^(\s*)([A-Za-z_][A-Za-z_0-9]*)\s*=\s*([A-Za-z_][A-Za-z_0-9]*)\.parse_args\(.*\)\s*$"
24
+ )
25
+ TOP_IMPORT_RE = re.compile(r"^(?:import |from )\S")
26
+
27
+
28
+ def has_quiet_flag(text: str) -> bool:
29
+ return '"--quiet"' in text or "'--quiet'" in text
30
+
31
+
32
+ def has_argparse(text: str) -> bool:
33
+ return "argparse" in text and ".add_argument(" in text and ".parse_args(" in text
34
+
35
+
36
+ def patch_argparse_script(text: str) -> tuple[str, int]:
37
+ """Insert --quiet arg before parse_args() call; gate ✅ print lines.
38
+
39
+ Returns (new_text, n_prints_gated). Caller checks both for sanity.
40
+ """
41
+ lines = text.splitlines(keepends=True)
42
+ out: list[str] = []
43
+ inserted = False
44
+ n_gated = 0
45
+ parse_args_var: str | None = None
46
+
47
+ for line in lines:
48
+ if not inserted:
49
+ m = PARSE_ARGS_RE.match(line.rstrip("\n"))
50
+ if m:
51
+ indent = m.group(1)
52
+ parse_args_var = m.group(2) # lhs (args var)
53
+ parser_var = m.group(3) # rhs (parser var: parser/ap/p/…)
54
+ out.append(
55
+ f'{indent}{parser_var}.add_argument("--quiet", action="store_true", '
56
+ 'help="Only print on failure")\n'
57
+ )
58
+ inserted = True
59
+ out.append(line)
60
+
61
+ if not inserted or parse_args_var is None:
62
+ return text, 0
63
+
64
+ # Now rewrite: gate success prints behind `if not <var>.quiet:`
65
+ text2 = "".join(out)
66
+ lines2 = text2.splitlines(keepends=True)
67
+ out2: list[str] = []
68
+ for line in lines2:
69
+ m = SUCCESS_RE.match(line.rstrip("\n"))
70
+ if m:
71
+ indent, stmt = m.group(1), m.group(2)
72
+ out2.append(f"{indent}if not {parse_args_var}.quiet:\n")
73
+ out2.append(f"{indent} {stmt}\n")
74
+ n_gated += 1
75
+ else:
76
+ out2.append(line)
77
+ return "".join(out2), n_gated
78
+
79
+
80
+ def patch_plain_script(text: str) -> tuple[str, int]:
81
+ """For scripts without argparse: add a sys.argv probe near the top.
82
+
83
+ Inserts after the last `import` or `from` line. Gates ✅ prints behind QUIET.
84
+ """
85
+ lines = text.splitlines(keepends=True)
86
+ last_import = -1
87
+ for i, line in enumerate(lines):
88
+ # Only TOP-LEVEL imports (column 0) — skip nested imports inside try/def.
89
+ if TOP_IMPORT_RE.match(line):
90
+ last_import = i
91
+ if last_import < 0:
92
+ return text, 0
93
+ if "import sys" not in text:
94
+ lines.insert(last_import + 1, "import sys\n")
95
+ last_import += 1
96
+ lines.insert(last_import + 1, '\nQUIET = "--quiet" in sys.argv\n')
97
+
98
+ n_gated = 0
99
+ out: list[str] = []
100
+ for line in lines:
101
+ m = SUCCESS_RE.match(line.rstrip("\n"))
102
+ if m:
103
+ indent, stmt = m.group(1), m.group(2)
104
+ out.append(f"{indent}if not QUIET:\n")
105
+ out.append(f"{indent} {stmt}\n")
106
+ n_gated += 1
107
+ else:
108
+ out.append(line)
109
+ return "".join(out), n_gated
110
+
111
+
112
+ def main() -> int:
113
+ targets = sorted(SCRIPTS.glob("check_*.py")) + sorted(SCRIPTS.glob("lint_*.py"))
114
+ skipped, patched, manual = [], [], []
115
+
116
+ for f in targets:
117
+ text = f.read_text()
118
+ if has_quiet_flag(text):
119
+ skipped.append(f.name)
120
+ continue
121
+ if has_argparse(text):
122
+ new, n = patch_argparse_script(text)
123
+ if n == 0 or new == text:
124
+ manual.append((f.name, f"argparse but no ✅-print gated (n={n})"))
125
+ continue
126
+ f.write_text(new)
127
+ patched.append((f.name, n))
128
+ else:
129
+ new, n = patch_plain_script(text)
130
+ if n == 0 or new == text:
131
+ manual.append((f.name, "plain but no ✅-print to gate"))
132
+ continue
133
+ f.write_text(new)
134
+ patched.append((f.name, n))
135
+
136
+ print(f"Skipped (already had --quiet): {len(skipped)}")
137
+ for s in skipped:
138
+ print(f" · {s}")
139
+ print(f"\nPatched: {len(patched)}")
140
+ for name, n in patched:
141
+ print(f" · {name} (gated {n} print(s))")
142
+ print(f"\nManual review needed: {len(manual)}")
143
+ for name, why in manual:
144
+ print(f" · {name}: {why}")
145
+ return 0
146
+
147
+
148
+ if __name__ == "__main__":
149
+ raise SystemExit(main())
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env python3
2
+ """One-off: inject {{.QUIET_FLAG}} into Taskfile cmds for --quiet-aware scripts."""
3
+ from __future__ import annotations
4
+ import re
5
+ from pathlib import Path
6
+
7
+ # Scripts that accept --quiet (verified: 20 total = 3 pre-existing + 17 patched).
8
+ QUIET_AWARE = {
9
+ "check_always_budget", "check_one_off_location", "check_safety_floor_untouched",
10
+ "check_augmentignore", "check_command_count_messaging", "check_compressed_paths",
11
+ "check_council_layout", "check_council_references", "check_iron_law_prominence",
12
+ "check_md_language", "check_memory_proposal", "check_public_catalog_links",
13
+ "check_reply_consistency", "check_roadmap_trackable",
14
+ "lint_examples", "lint_handoffs", "lint_load_context", "lint_roadmap_complexity",
15
+ "lint_rule_interactions", "lint_rule_tiers",
16
+ }
17
+
18
+ TASKFILES = sorted(Path("taskfiles").glob("*.yml")) + [Path("Taskfile.yml")]
19
+ patched = 0
20
+ for tf in TASKFILES:
21
+ text = tf.read_text()
22
+ new = text
23
+ for name in QUIET_AWARE:
24
+ # Match: cmd: python3 scripts/<name>.py [args]
25
+ # Insert {{.QUIET_FLAG}} after the script path, before any other args.
26
+ pat = re.compile(rf"(python3 scripts/{name}\.py)(\s|$)(?!.*QUIET_FLAG)")
27
+ new, n = pat.subn(r"\1 {{.QUIET_FLAG}}\2", new)
28
+ if n:
29
+ patched += n
30
+ if new != text:
31
+ tf.write_text(new)
32
+ print(f" patched: {tf}")
33
+ print(f"\nTotal injections: {patched}")
@@ -0,0 +1,36 @@
1
+ #!/bin/bash
2
+ # Phase 10.7 baseline — runs only verbosity-aware patched-script tasks.
3
+ # Skips broken-on-dirty-tree tasks (consistency, check-index, validate-schema).
4
+ # This subset is what actually demonstrates the --quiet effect.
5
+ TASKS="check-compressed-paths check-refs check-portability lint-roadmap-complexity check-public-catalog-links check-command-count check-cluster-patterns lint-rule-interactions lint-load-context check-context-paths check-no-roadmap-refs check-council-references lint-one-off-age check-reply-consistency check-iron-law-prominence check-always-budget check-one-off-location lint-rule-budget lint-skills lint-rule-tiers lint-handoffs lint-marketplace lint-examples"
6
+
7
+ run() {
8
+ local label=$1
9
+ local level=$2
10
+ local out=$3
11
+ AGENT_SCRIPT_VERBOSITY=$level task $TASKS > "$out" 2>&1
12
+ local lines=$(wc -l < "$out" | tr -d ' ')
13
+ local chars=$(wc -c < "$out" | tr -d ' ')
14
+ echo "$label: lines=$lines chars=$chars"
15
+ }
16
+
17
+ echo "=== MINIMAL ==="
18
+ run "MINIMAL" minimal /tmp/ci-min.log
19
+ echo ""
20
+ echo "=== VERBOSE ==="
21
+ run "VERBOSE" verbose /tmp/ci-vrb.log
22
+
23
+ ML=$(wc -l < /tmp/ci-min.log | tr -d ' ')
24
+ MC=$(wc -c < /tmp/ci-min.log | tr -d ' ')
25
+ VL=$(wc -l < /tmp/ci-vrb.log | tr -d ' ')
26
+ VC=$(wc -c < /tmp/ci-vrb.log | tr -d ' ')
27
+
28
+ echo ""
29
+ python3 -c "
30
+ ml=$ML; vl=$VL; mc=$MC; vc=$VC
31
+ dl=(vl-ml)/vl*100 if vl else 0
32
+ dc=(vc-mc)/vc*100 if vc else 0
33
+ print(f'Lines: {ml} -> {vl}, reduction={dl:.1f}% (target >=40%)')
34
+ print(f'Chars: {mc} -> {vc}, reduction={dc:.1f}%')
35
+ print('verdict:', 'MET' if dl >= 40 else f'MISSED ({dl:.1f}% < 40%)')
36
+ "
@@ -0,0 +1,26 @@
1
+ #!/bin/bash
2
+ # Phase 10.7 baseline measurement: post-consistency task ci subset.
3
+ TASKS="counts-check check-index check-router check-compression check-compressed-paths check-refs check-token-optimizer-freshness check-portability check-examples-shape lint-roadmap-complexity check-public-links check-public-catalog-links check-command-count lint-no-new-atomic-commands check-cluster-patterns lint-rule-interactions lint-load-context check-context-paths check-no-roadmap-refs check-council-references lint-one-off-age check-ownership-matrix check-reply-consistency check-iron-law-prominence check-always-budget check-one-off-location validate-schema lint-rule-budget lint-skills lint-rule-tiers lint-handoffs lint-hook-manifest lint-showcase-sessions lint-marketplace"
4
+
5
+ echo "=== MINIMAL ==="
6
+ AGENT_SCRIPT_VERBOSITY=minimal task $TASKS 2>&1 > /tmp/ci-min.log
7
+ ML=$(wc -l < /tmp/ci-min.log | tr -d ' ')
8
+ MC=$(wc -c < /tmp/ci-min.log | tr -d ' ')
9
+ echo "MINIMAL: lines=$ML chars=$MC"
10
+
11
+ echo ""
12
+ echo "=== VERBOSE ==="
13
+ AGENT_SCRIPT_VERBOSITY=verbose task $TASKS 2>&1 > /tmp/ci-vrb.log
14
+ VL=$(wc -l < /tmp/ci-vrb.log | tr -d ' ')
15
+ VC=$(wc -c < /tmp/ci-vrb.log | tr -d ' ')
16
+ echo "VERBOSE: lines=$VL chars=$VC"
17
+
18
+ echo ""
19
+ python3 -c "
20
+ ml=$ML; vl=$VL; mc=$MC; vc=$VC
21
+ dl=(vl-ml)/vl*100 if vl else 0
22
+ dc=(vc-mc)/vc*100 if vc else 0
23
+ print(f'Lines: {ml} -> {vl}, reduction={dl:.1f}% (target >=40%)')
24
+ print(f'Chars: {mc} -> {vc}, reduction={dc:.1f}%')
25
+ print('verdict:', 'MET' if dl >= 40 else f'MISSED ({dl:.1f}% < 40%)')
26
+ "
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+ TASKS=(
3
+ check-compressed-paths
4
+ check-refs
5
+ check-portability
6
+ lint-roadmap-complexity
7
+ check-public-catalog-links
8
+ check-command-count
9
+ check-cluster-patterns
10
+ lint-rule-interactions
11
+ lint-load-context
12
+ check-context-paths
13
+ check-no-roadmap-refs
14
+ check-council-references
15
+ lint-one-off-age
16
+ check-reply-consistency
17
+ check-iron-law-prominence
18
+ check-always-budget
19
+ check-one-off-location
20
+ lint-rule-budget
21
+ lint-skills
22
+ lint-rule-tiers
23
+ lint-handoffs
24
+ lint-marketplace
25
+ check-examples-shape
26
+ )
27
+ totalmin=0
28
+ totalvrb=0
29
+ totaldelta=0
30
+ for t in "${TASKS[@]}"; do
31
+ m=$(AGENT_SCRIPT_VERBOSITY=minimal task "$t" 2>&1 | wc -l | tr -d ' ')
32
+ v=$(AGENT_SCRIPT_VERBOSITY=verbose task "$t" 2>&1 | wc -l | tr -d ' ')
33
+ d=$((v - m))
34
+ totalmin=$((totalmin + m))
35
+ totalvrb=$((totalvrb + v))
36
+ totaldelta=$((totaldelta + d))
37
+ printf "%-35s min=%-5s vrb=%-5s d=%s\n" "$t" "$m" "$v" "$d"
38
+ done
39
+ echo ""
40
+ echo "TOTAL: min=$totalmin vrb=$totalvrb delta=$totaldelta"
41
+ python3 -c "print(f'reduction: {($totalvrb - $totalmin)/$totalvrb*100:.1f}%' if $totalvrb else '')"
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env python3
2
+ """One-off (Phase 10.3) — add `silent: true` to safe Taskfile tasks.
3
+
4
+ Inserts a ` silent: true` line directly after each ` <taskname>:` opener,
5
+ unless the task is in CARVE_OUTS or the line already exists.
6
+
7
+ Carve-outs stay loud:
8
+ - install / install-hooks / first-run / install-anthropic-key /
9
+ install-openai-key / setup-evals / test-triggers-live
10
+ - runtime-e2e (per roadmap explicit list)
11
+ - all release* tasks (release.yml — file-level skip)
12
+ - _ci-start / _ci-end / ci (root Taskfile orchestration)
13
+
14
+ Idempotent — running twice is a no-op.
15
+
16
+ Lifecycle: archive after Phase 10.3 lands per one-off-script-lifecycle.md.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import re
21
+ import sys
22
+ from pathlib import Path
23
+
24
+ ROOT = Path(__file__).resolve().parent.parent
25
+
26
+ CARVE_OUTS = {
27
+ "install",
28
+ "install-hooks",
29
+ "first-run",
30
+ "install-anthropic-key",
31
+ "install-openai-key",
32
+ "setup-evals",
33
+ "test-triggers-live",
34
+ "runtime-e2e",
35
+ }
36
+
37
+ # Regex matches a top-level task opener: 2 spaces + name + colon + EOL.
38
+ TASK_OPENER_RE = re.compile(r"^ ([a-z_][a-z0-9_:.-]*):$")
39
+
40
+
41
+ def patch_file(path: Path, skip_all: bool = False) -> tuple[int, int]:
42
+ """Insert ` silent: true` after each safe task opener.
43
+
44
+ Returns (added, skipped).
45
+ """
46
+ if skip_all:
47
+ return (0, 0)
48
+
49
+ text = path.read_text()
50
+ lines = text.splitlines(keepends=False)
51
+ out: list[str] = []
52
+ added = 0
53
+ skipped = 0
54
+ i = 0
55
+ while i < len(lines):
56
+ line = lines[i]
57
+ out.append(line)
58
+ m = TASK_OPENER_RE.match(line)
59
+ if m:
60
+ name = m.group(1)
61
+ # Look ahead one line — already silent?
62
+ next_line = lines[i + 1] if i + 1 < len(lines) else ""
63
+ already = next_line.strip() == "silent: true"
64
+ if name in CARVE_OUTS:
65
+ skipped += 1
66
+ elif already:
67
+ pass # idempotent
68
+ else:
69
+ out.append(" silent: true")
70
+ added += 1
71
+ i += 1
72
+ new_text = "\n".join(out) + ("\n" if text.endswith("\n") else "")
73
+ if new_text != text:
74
+ path.write_text(new_text)
75
+ return (added, skipped)
76
+
77
+
78
+ def main() -> int:
79
+ targets = [
80
+ (ROOT / "taskfiles" / "content.yml", False),
81
+ (ROOT / "taskfiles" / "ci-fast.yml", False),
82
+ (ROOT / "taskfiles" / "engine.yml", False),
83
+ (ROOT / "taskfiles" / "release.yml", True), # all release* loud
84
+ ]
85
+ total_added = 0
86
+ total_skipped = 0
87
+ for path, skip_all in targets:
88
+ added, skipped = patch_file(path, skip_all=skip_all)
89
+ rel = path.relative_to(ROOT)
90
+ print(f" {rel}: +{added} silent · {skipped} carved-out")
91
+ total_added += added
92
+ total_skipped += skipped
93
+ print(f"\n total: +{total_added} silent · {total_skipped} carved-out")
94
+ return 0
95
+
96
+
97
+ if __name__ == "__main__":
98
+ sys.exit(main())
@@ -20,6 +20,8 @@ import sys
20
20
  import time
21
21
  from pathlib import Path
22
22
 
23
+ QUIET = "--quiet" in sys.argv
24
+
23
25
  STALE_DAYS = 90
24
26
  MIN_USEFUL_LINES = 5
25
27
  REPO_ROOT = Path(__file__).resolve().parent.parent
@@ -57,7 +59,8 @@ def check() -> int:
57
59
 
58
60
  def _emit(notes: list[str]) -> None:
59
61
  if not notes:
60
- print("✅ .augmentignore advisory: nothing to suggest.")
62
+ if not QUIET:
63
+ print("✅ .augmentignore advisory: nothing to suggest.")
61
64
  return
62
65
  print("📒 .augmentignore advisory (non-blocking):")
63
66
  for n in notes:
@@ -38,6 +38,8 @@ import re
38
38
  import sys
39
39
  from pathlib import Path
40
40
 
41
+ QUIET = "--quiet" in sys.argv
42
+
41
43
  ROOT = Path(__file__).resolve().parent.parent
42
44
  COMMANDS_DIR = ROOT / ".agent-src.uncompressed" / "commands"
43
45
  README = ROOT / "README.md"
@@ -107,7 +109,8 @@ def main() -> int:
107
109
  errors.append(err)
108
110
 
109
111
  if not errors:
110
- print("✅ All command-count messaging in sync with registry.")
112
+ if not QUIET:
113
+ print("✅ All command-count messaging in sync with registry.")
111
114
  return 0
112
115
 
113
116
  print(f"❌ Command-count messaging drift — {len(errors)} mismatch(es):")
@@ -40,6 +40,8 @@ from pathlib import Path
40
40
 
41
41
  import yaml
42
42
 
43
+ QUIET = "--quiet" in sys.argv
44
+
43
45
  ROOT = Path(__file__).resolve().parent.parent
44
46
  RULES_DIR = ROOT / ".agent-src" / "rules"
45
47
 
@@ -205,7 +207,8 @@ def main() -> int:
205
207
  print(f"\n{len(viols)} violation(s) in .agent-src/rules/")
206
208
  return 1
207
209
  rule_count = len(list(RULES_DIR.glob('*.md')))
208
- print(f"✅ compressed-path check clean ({rule_count} rules, {len(audited)} ignore(s) audited)")
210
+ if not QUIET:
211
+ print(f"✅ compressed-path check clean ({rule_count} rules, {len(audited)} ignore(s) audited)")
209
212
  return 0
210
213
 
211
214
 
@@ -32,6 +32,8 @@ import re
32
32
  import sys
33
33
  from pathlib import Path
34
34
 
35
+ QUIET = "--quiet" in sys.argv
36
+
35
37
  AGENTS_ROOT = Path("agents")
36
38
  CANONICAL_DIRS = {
37
39
  "council-questions": ".md",
@@ -97,7 +99,8 @@ def main() -> int:
97
99
  '§ "Output path convention"'
98
100
  )
99
101
  return 1
100
- print("✅ Council layout clean.")
102
+ if not QUIET:
103
+ print("✅ Council layout clean.")
101
104
  return 0
102
105
 
103
106