@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.
Files changed (203) hide show
  1. package/.agent-src/commands/{agents-audit.md → agents/audit.md} +4 -3
  2. package/.agent-src/commands/{agents-cleanup.md → agents/cleanup.md} +12 -6
  3. package/.agent-src/commands/{agents-prepare.md → agents/prepare.md} +4 -3
  4. package/.agent-src/commands/agents.md +46 -0
  5. package/.agent-src/commands/{chat-history-checkpoint.md → chat-history/checkpoint.md} +4 -4
  6. package/.agent-src/commands/{chat-history-clear.md → chat-history/clear.md} +4 -4
  7. package/.agent-src/commands/{chat-history-resume.md → chat-history/resume.md} +4 -4
  8. package/.agent-src/commands/chat-history/show.md +107 -0
  9. package/.agent-src/commands/chat-history.md +33 -89
  10. package/.agent-src/commands/{commit-in-chunks.md → commit/in-chunks.md} +15 -13
  11. package/.agent-src/commands/commit.md +22 -2
  12. package/.agent-src/commands/{context-create.md → context/create.md} +4 -3
  13. package/.agent-src/commands/{context-refactor.md → context/refactor.md} +4 -3
  14. package/.agent-src/commands/context.md +44 -0
  15. package/.agent-src/commands/{copilot-agents-init.md → copilot-agents/init.md} +4 -3
  16. package/.agent-src/commands/{copilot-agents-optimize.md → copilot-agents/optimize.md} +4 -3
  17. package/.agent-src/commands/copilot-agents.md +44 -0
  18. package/.agent-src/commands/council/default.md +221 -0
  19. package/.agent-src/commands/{council-design.md → council/design.md} +6 -5
  20. package/.agent-src/commands/{council-optimize.md → council/optimize.md} +7 -6
  21. package/.agent-src/commands/{council-pr.md → council/pr.md} +6 -5
  22. package/.agent-src/commands/council.md +47 -212
  23. package/.agent-src/commands/{create-pr-description.md → create-pr/description-only.md} +4 -2
  24. package/.agent-src/commands/create-pr.md +26 -5
  25. package/.agent-src/commands/{feature-dev.md → feature/dev.md} +5 -10
  26. package/.agent-src/commands/{feature-explore.md → feature/explore.md} +4 -8
  27. package/.agent-src/commands/{feature-plan.md → feature/plan.md} +4 -8
  28. package/.agent-src/commands/{feature-refactor.md → feature/refactor.md} +4 -8
  29. package/.agent-src/commands/{feature-roadmap.md → feature/roadmap.md} +6 -10
  30. package/.agent-src/commands/feature.md +6 -12
  31. package/.agent-src/commands/{fix-ci.md → fix/ci.md} +4 -8
  32. package/.agent-src/commands/{fix-portability.md → fix/portability.md} +4 -8
  33. package/.agent-src/commands/{fix-pr-bot-comments.md → fix/pr-bots.md} +4 -8
  34. package/.agent-src/commands/{fix-pr-developer-comments.md → fix/pr-developers.md} +4 -8
  35. package/.agent-src/commands/{fix-pr-comments.md → fix/pr.md} +7 -11
  36. package/.agent-src/commands/{fix-references.md → fix/refs.md} +4 -8
  37. package/.agent-src/commands/{fix-seeder.md → fix/seeder.md} +4 -8
  38. package/.agent-src/commands/fix.md +7 -13
  39. package/.agent-src/commands/{do-and-judge.md → judge/on-diff.md} +4 -3
  40. package/.agent-src/commands/judge/solo.md +90 -0
  41. package/.agent-src/commands/{do-in-steps.md → judge/steps.md} +4 -3
  42. package/.agent-src/commands/judge.md +35 -70
  43. package/.agent-src/commands/{memory-add.md → memory/add.md} +4 -3
  44. package/.agent-src/commands/{memory-full.md → memory/load.md} +4 -3
  45. package/.agent-src/commands/{memory-promote.md → memory/promote.md} +4 -3
  46. package/.agent-src/commands/{propose-memory.md → memory/propose.md} +4 -3
  47. package/.agent-src/commands/memory.md +48 -0
  48. package/.agent-src/commands/{module-create.md → module/create.md} +4 -3
  49. package/.agent-src/commands/{module-explore.md → module/explore.md} +4 -3
  50. package/.agent-src/commands/module.md +44 -0
  51. package/.agent-src/commands/{optimize-agents.md → optimize/agents.md} +4 -8
  52. package/.agent-src/commands/{optimize-augmentignore.md → optimize/augmentignore.md} +4 -9
  53. package/.agent-src/commands/{optimize-rtk-filters.md → optimize/rtk.md} +4 -8
  54. package/.agent-src/commands/{optimize-skills.md → optimize/skills.md} +4 -8
  55. package/.agent-src/commands/optimize.md +4 -10
  56. package/.agent-src/commands/{override-create.md → override/create.md} +4 -3
  57. package/.agent-src/commands/{override-manage.md → override/manage.md} +4 -3
  58. package/.agent-src/commands/override.md +44 -0
  59. package/.agent-src/commands/{roadmap-create.md → roadmap/create.md} +4 -3
  60. package/.agent-src/commands/{roadmap-execute.md → roadmap/execute.md} +4 -3
  61. package/.agent-src/commands/roadmap.md +44 -0
  62. package/.agent-src/commands/{tests-create.md → tests/create.md} +4 -3
  63. package/.agent-src/commands/{tests-execute.md → tests/execute.md} +4 -3
  64. package/.agent-src/commands/tests.md +44 -0
  65. package/.agent-src/contexts/communication/rules-auto/artifact-engagement-recording-mechanics.md +72 -0
  66. package/.agent-src/contexts/communication/rules-auto/augment-portability-mechanics.md +79 -0
  67. package/.agent-src/contexts/communication/rules-auto/augment-source-of-truth-mechanics.md +98 -0
  68. package/.agent-src/contexts/communication/rules-auto/cli-output-handling-mechanics.md +87 -0
  69. package/.agent-src/contexts/communication/rules-auto/command-suggestion-policy-mechanics.md +62 -0
  70. package/.agent-src/contexts/communication/rules-auto/docs-sync-mechanics.md +78 -0
  71. package/.agent-src/contexts/communication/rules-auto/package-ci-checks-mechanics.md +85 -0
  72. package/.agent-src/contexts/communication/rules-auto/review-routing-awareness-mechanics.md +65 -0
  73. package/.agent-src/contexts/communication/rules-auto/roadmap-progress-sync-mechanics.md +78 -0
  74. package/.agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +62 -0
  75. package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +55 -0
  76. package/.agent-src/contexts/communication/rules-auto/ui-audit-gate-mechanics.md +53 -0
  77. package/.agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +77 -0
  78. package/.agent-src/contexts/judges/no-consolidate-rationale.md +102 -0
  79. package/.agent-src/contexts/judges/persona-voice-rubric.md +140 -0
  80. package/.agent-src/rules/artifact-engagement-recording.md +13 -69
  81. package/.agent-src/rules/ask-when-uncertain.md +27 -42
  82. package/.agent-src/rules/augment-portability.md +15 -61
  83. package/.agent-src/rules/augment-source-of-truth.md +27 -93
  84. package/.agent-src/rules/cli-output-handling.md +10 -76
  85. package/.agent-src/rules/command-suggestion-policy.md +18 -59
  86. package/.agent-src/rules/commit-conventions.md +17 -14
  87. package/.agent-src/rules/direct-answers.md +34 -49
  88. package/.agent-src/rules/docker-commands.md +5 -5
  89. package/.agent-src/rules/docs-sync.md +15 -69
  90. package/.agent-src/rules/language-and-tone.md +48 -72
  91. package/.agent-src/rules/missing-tool-handling.md +28 -22
  92. package/.agent-src/rules/no-cheap-questions.md +45 -52
  93. package/.agent-src/rules/no-roadmap-references.md +73 -0
  94. package/.agent-src/rules/package-ci-checks.md +21 -61
  95. package/.agent-src/rules/preservation-guard.md +64 -29
  96. package/.agent-src/rules/review-routing-awareness.md +24 -43
  97. package/.agent-src/rules/roadmap-progress-sync.md +10 -71
  98. package/.agent-src/rules/security-sensitive-stop.md +8 -8
  99. package/.agent-src/rules/skill-quality.md +16 -48
  100. package/.agent-src/rules/slash-command-routing-policy.md +7 -4
  101. package/.agent-src/rules/think-before-action.md +52 -42
  102. package/.agent-src/rules/tool-safety.md +19 -16
  103. package/.agent-src/rules/ui-audit-gate.md +24 -38
  104. package/.agent-src/rules/user-interaction.md +13 -68
  105. package/.agent-src/skills/ai-council/SKILL.md +2 -0
  106. package/.agent-src/skills/api-testing/SKILL.md +1 -1
  107. package/.agent-src/skills/check-refs/SKILL.md +59 -40
  108. package/.agent-src/skills/conventional-commits-writing/SKILL.md +86 -28
  109. package/.agent-src/skills/copilot-agents-optimization/SKILL.md +5 -5
  110. package/.agent-src/skills/developer-like-execution/SKILL.md +4 -4
  111. package/.agent-src/skills/finishing-a-development-branch/SKILL.md +101 -65
  112. package/.agent-src/skills/flux/SKILL.md +30 -10
  113. package/.agent-src/skills/github-ci/SKILL.md +2 -2
  114. package/.agent-src/skills/judge-code-quality/SKILL.md +7 -8
  115. package/.agent-src/skills/judge-security-auditor/SKILL.md +4 -5
  116. package/.agent-src/skills/judge-test-coverage/SKILL.md +3 -4
  117. package/.agent-src/skills/lint-skills/SKILL.md +57 -39
  118. package/.agent-src/skills/md-language-check/SKILL.md +61 -39
  119. package/.agent-src/skills/override-management/SKILL.md +5 -5
  120. package/.agent-src/skills/quality-tools/SKILL.md +2 -2
  121. package/.agent-src/skills/react-shadcn-ui/SKILL.md +116 -43
  122. package/.agent-src/skills/readme-reviewer/SKILL.md +30 -29
  123. package/.agent-src/skills/readme-writing/SKILL.md +78 -53
  124. package/.agent-src/skills/readme-writing-package/SKILL.md +50 -47
  125. package/.agent-src/skills/receiving-code-review/SKILL.md +52 -47
  126. package/.agent-src/skills/refine-prompt/SKILL.md +0 -1
  127. package/.agent-src/skills/requesting-code-review/SKILL.md +35 -30
  128. package/.agent-src/skills/security/SKILL.md +7 -2
  129. package/.agent-src/skills/security-audit/SKILL.md +7 -3
  130. package/.agent-src/skills/systematic-debugging/SKILL.md +68 -60
  131. package/.agent-src/skills/test-driven-development/SKILL.md +59 -57
  132. package/.agent-src/skills/test-performance/SKILL.md +0 -1
  133. package/.agent-src/skills/traefik/SKILL.md +4 -4
  134. package/.agent-src/skills/verify-completion-evidence/SKILL.md +28 -26
  135. package/.claude-plugin/marketplace.json +22 -11
  136. package/AGENTS.md +2 -2
  137. package/CHANGELOG.md +90 -1
  138. package/README.md +18 -17
  139. package/docs/architecture.md +4 -6
  140. package/docs/catalog.md +67 -39
  141. package/docs/contracts/STABILITY.md +13 -7
  142. package/docs/contracts/adr-chat-history-split.md +1 -3
  143. package/docs/contracts/adr-command-suggestion.md +0 -2
  144. package/docs/contracts/adr-implement-ticket-runtime.md +1 -2
  145. package/docs/contracts/adr-product-ui-track.md +3 -6
  146. package/docs/contracts/adr-prompt-driven-execution.md +3 -4
  147. package/docs/contracts/agent-memory-contract.md +6 -11
  148. package/docs/contracts/artifact-engagement-flow.md +6 -9
  149. package/docs/contracts/command-clusters.md +56 -46
  150. package/docs/contracts/command-suggestion-flow.md +1 -3
  151. package/docs/contracts/context-paths.md +99 -0
  152. package/docs/contracts/file-ownership-matrix.json +6722 -0
  153. package/docs/contracts/file-ownership-matrix.md +134 -0
  154. package/docs/contracts/implement-ticket-flow.md +6 -9
  155. package/docs/contracts/linear-ai-rules-inclusion.md +0 -1
  156. package/docs/contracts/linear-ai-three-layers.md +0 -2
  157. package/docs/contracts/load-context-budget-model.md +178 -0
  158. package/docs/contracts/load-context-schema.md +1 -3
  159. package/docs/contracts/rule-interactions.md +0 -1
  160. package/docs/contracts/rule-priority-hierarchy.md +1 -1
  161. package/docs/contracts/ui-track-flow.md +7 -17
  162. package/docs/customization.md +2 -0
  163. package/docs/getting-started.md +5 -4
  164. package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +100 -0
  165. package/package.json +1 -1
  166. package/scripts/_one_off_phase4_dispatch_latency.py +108 -0
  167. package/scripts/_one_off_phase6_trigger_jaccard.py +92 -0
  168. package/scripts/_phase2_shim_helper.py +109 -0
  169. package/scripts/agent-config +10 -0
  170. package/scripts/ai_council/_one_off_2a4_acceptance.py +208 -0
  171. package/scripts/ai_council/_one_off_context_layer_v1_estimate.py +67 -0
  172. package/scripts/ai_council/_one_off_context_layer_v1_review.py +292 -0
  173. package/scripts/ai_council/_one_off_followups_review.py +259 -0
  174. package/scripts/ai_council/_one_off_nondestructive_inline_audit.py +209 -0
  175. package/scripts/ai_council/_one_off_phase_2a_budget_rebalance.py +257 -0
  176. package/scripts/ai_council/_one_off_phase_2a_post_revert.py +197 -0
  177. package/scripts/ai_council/_one_off_rule_hardening_v1.py +251 -0
  178. package/scripts/ai_council/_one_off_structural_open_questions.py +232 -0
  179. package/scripts/ai_council/_one_off_structural_optimization.py +144 -0
  180. package/scripts/ai_council/_one_off_structural_v3_gaps.py +252 -0
  181. package/scripts/ai_council/_one_off_structural_v3_review.py +240 -0
  182. package/scripts/check_always_budget.py +363 -45
  183. package/scripts/check_cluster_patterns.py +159 -0
  184. package/scripts/check_command_count_messaging.py +14 -7
  185. package/scripts/check_context_paths.py +201 -0
  186. package/scripts/check_no_roadmap_refs.py +155 -0
  187. package/scripts/check_phase_coupling.py +148 -0
  188. package/scripts/check_portability.py +2 -0
  189. package/scripts/check_references.py +29 -2
  190. package/scripts/check_safety_floor_untouched.py +125 -0
  191. package/scripts/command_suggester/loader.py +4 -1
  192. package/scripts/compress.py +59 -13
  193. package/scripts/generate_index.py +6 -2
  194. package/scripts/generate_ownership_matrix.py +323 -0
  195. package/scripts/hooks/augment-roadmap-progress.sh +57 -0
  196. package/scripts/install.py +49 -28
  197. package/scripts/lint_no_new_atomic_commands.py +12 -11
  198. package/scripts/requirements-evals.txt +1 -0
  199. package/scripts/roadmap_progress_hook.py +159 -0
  200. package/scripts/schemas/command.schema.json +4 -3
  201. package/scripts/skill_linter.py +1 -0
  202. package/scripts/sync_agent_settings.py +25 -2
  203. package/scripts/update_counts.py +7 -0
@@ -43,24 +43,24 @@ class Violation:
43
43
 
44
44
 
45
45
  def load_locked_clusters() -> set[str]:
46
- """Parse the Phase 1 cluster table from the locked contract."""
46
+ """Parse the locked cluster table from the contract."""
47
47
  text = (ROOT / CLUSTER_CONTRACT).read_text(encoding="utf-8")
48
- # Locate the Phase 1 table; cluster names sit in backticks in column 1.
49
- in_phase_1 = False
48
+ # Locate the locked-clusters table; cluster names sit in backticks in column 1.
49
+ in_table = False
50
50
  clusters: set[str] = set()
51
51
  for line in text.splitlines():
52
- if line.startswith("## Phase 1 clusters"):
53
- in_phase_1 = True
52
+ if line.startswith("## Locked clusters"):
53
+ in_table = True
54
54
  continue
55
- if in_phase_1 and line.startswith("## "):
55
+ if in_table and line.startswith("## "):
56
56
  break
57
- if in_phase_1:
57
+ if in_table:
58
58
  m = re.match(r"\|\s*`([a-z][a-z0-9-]*)`\s*\|", line)
59
59
  if m:
60
60
  clusters.add(m.group(1))
61
61
  if not clusters:
62
62
  print(
63
- f"❌ Could not parse Phase 1 cluster table from {CLUSTER_CONTRACT}",
63
+ f"❌ Could not parse locked-clusters table from {CLUSTER_CONTRACT}",
64
64
  file=sys.stderr,
65
65
  )
66
66
  sys.exit(3)
@@ -83,7 +83,7 @@ def added_command_files(baseline: str) -> list[Path]:
83
83
  file=sys.stderr)
84
84
  sys.exit(3)
85
85
  files = [Path(p) for p in result.stdout.splitlines()
86
- if p.endswith(".md") and p != ""]
86
+ if p.endswith(".md") and p != "" and Path(p).name != "AGENTS.md"]
87
87
  # Also include untracked (newly added, uncommitted) files.
88
88
  try:
89
89
  wt = subprocess.run(
@@ -97,7 +97,7 @@ def added_command_files(baseline: str) -> list[Path]:
97
97
  if status.strip() not in ("A", "??", "AM"):
98
98
  continue
99
99
  path = line[3:].strip().split(" -> ")[-1]
100
- if path.endswith(".md"):
100
+ if path.endswith(".md") and Path(path).name != "AGENTS.md":
101
101
  p = Path(path)
102
102
  if p not in files:
103
103
  files.append(p)
@@ -107,7 +107,8 @@ def added_command_files(baseline: str) -> list[Path]:
107
107
 
108
108
 
109
109
  def all_command_files() -> list[Path]:
110
- return sorted((ROOT / COMMANDS_DIR).glob("*.md"))
110
+ return sorted(p for p in (ROOT / COMMANDS_DIR).rglob("*.md")
111
+ if p.name != "AGENTS.md")
111
112
 
112
113
 
113
114
  def parse_frontmatter(path: Path) -> dict[str, str]:
@@ -5,3 +5,4 @@
5
5
  # be upgraded, bump the version here and rerun the setup script.
6
6
 
7
7
  anthropic==0.96.0
8
+ openai==1.109.1
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env python3
2
+ """Platform-agnostic PostToolUse hook for the `roadmap-progress-sync` rule.
3
+
4
+ Reads a JSON event from stdin (Augment / Claude / Cursor / Cline /
5
+ Windsurf / Gemini PostToolUse-shaped envelopes), decides whether the
6
+ tool call wrote to a roadmap file under `agents/roadmaps/`, and — when
7
+ it did — re-runs `update_roadmap_progress.py` so the dashboard stays
8
+ in sync without depending on agent self-discipline.
9
+
10
+ Exit code is **always 0**. Hooks must never block the agent loop; the
11
+ worst-case is a no-op when stdin is malformed or the regenerator is
12
+ missing.
13
+
14
+ Output discipline:
15
+ - stdout: nothing (Augment would surface stdout to the user)
16
+ - stderr: one short line in --verbose mode, otherwise silent
17
+
18
+ CLI:
19
+ python3 scripts/roadmap_progress_hook.py [--platform NAME] [--verbose]
20
+
21
+ The `--platform` flag is informational only — the filter logic reads
22
+ the same field names across platforms (tool_name, tool_input.path,
23
+ file_changes[].path).
24
+ """
25
+ from __future__ import annotations
26
+
27
+ import argparse
28
+ import json
29
+ import subprocess
30
+ import sys
31
+ from pathlib import Path
32
+
33
+ # Tools whose successful execution can write to a roadmap file. We keep
34
+ # the list explicit so an unknown tool name (e.g. a new MCP tool that
35
+ # happens to mention a roadmap path in its input) does not trigger a
36
+ # spurious regeneration.
37
+ WRITE_TOOLS = frozenset({
38
+ "str-replace-editor",
39
+ "save-file",
40
+ "remove-files",
41
+ # Claude Code / Cursor naming variants — kept for cross-platform
42
+ # parity if this hook is ever wired beyond Augment.
43
+ "Edit",
44
+ "Write",
45
+ "MultiEdit",
46
+ })
47
+
48
+ ROADMAP_PREFIX = "agents/roadmaps/"
49
+ # Paths under these subtrees are tracked but not part of the open list
50
+ # the dashboard summarises — regenerating on every archived edit would
51
+ # be wasteful. The check still fires on the parent dir itself.
52
+ ROADMAP_EXCLUDED_PARTS = frozenset({"archive", "skipped"})
53
+ DASHBOARD_PATH = "agents/roadmaps-progress.md"
54
+
55
+
56
+ def _candidate_paths(payload: dict) -> list[str]:
57
+ """Pull every plausible file path out of a PostToolUse payload."""
58
+ out: list[str] = []
59
+ fc = payload.get("file_changes")
60
+ if isinstance(fc, list):
61
+ for entry in fc:
62
+ if isinstance(entry, dict):
63
+ p = entry.get("path")
64
+ if isinstance(p, str) and p:
65
+ out.append(p)
66
+ ti = payload.get("tool_input")
67
+ if isinstance(ti, dict):
68
+ for key in ("path", "file_path", "target_file"):
69
+ v = ti.get(key)
70
+ if isinstance(v, str) and v:
71
+ out.append(v)
72
+ return out
73
+
74
+
75
+ def _is_roadmap_touch(path: str) -> bool:
76
+ """Return True if `path` is a roadmap file we should react to."""
77
+ norm = path.lstrip("./").replace("\\", "/")
78
+ if not norm.startswith(ROADMAP_PREFIX):
79
+ return False
80
+ if norm == DASHBOARD_PATH:
81
+ # Defensive — the dashboard sits at agents/roadmaps-progress.md,
82
+ # NOT inside agents/roadmaps/. The prefix check above already
83
+ # excludes it, but keep this explicit so a future relocation
84
+ # cannot turn the hook into an infinite loop.
85
+ return False
86
+ rest = norm[len(ROADMAP_PREFIX):]
87
+ parts = rest.split("/")
88
+ if len(parts) >= 2 and parts[0] in ROADMAP_EXCLUDED_PARTS:
89
+ return False
90
+ if not norm.endswith(".md"):
91
+ return False
92
+ return True
93
+
94
+
95
+ def _resolve_regenerator(consumer_root: Path) -> Path | None:
96
+ """Find the regenerator script — package-shipped or installed copy."""
97
+ for candidate in (
98
+ consumer_root / ".augment" / "scripts" / "update_roadmap_progress.py",
99
+ consumer_root / ".agent-src" / "scripts" / "update_roadmap_progress.py",
100
+ consumer_root / ".agent-src.uncompressed" / "scripts" / "update_roadmap_progress.py",
101
+ ):
102
+ if candidate.is_file():
103
+ return candidate
104
+ return None
105
+
106
+
107
+ def run(stdin_text: str, *, consumer_root: Path, verbose: bool = False) -> int:
108
+ payload: dict = {}
109
+ if stdin_text.strip():
110
+ try:
111
+ decoded = json.loads(stdin_text)
112
+ if isinstance(decoded, dict):
113
+ payload = decoded
114
+ except json.JSONDecodeError:
115
+ return 0 # malformed stdin → silent no-op, never block
116
+
117
+ tool = payload.get("tool_name") or payload.get("toolName") or payload.get("tool")
118
+ if not isinstance(tool, str) or tool not in WRITE_TOOLS:
119
+ return 0
120
+
121
+ paths = _candidate_paths(payload)
122
+ if not any(_is_roadmap_touch(p) for p in paths):
123
+ return 0
124
+
125
+ script = _resolve_regenerator(consumer_root)
126
+ if script is None:
127
+ if verbose:
128
+ print("roadmap-progress-hook: regenerator not found, skipping",
129
+ file=sys.stderr)
130
+ return 0
131
+
132
+ try:
133
+ subprocess.run(
134
+ [sys.executable, str(script)],
135
+ cwd=consumer_root, check=False,
136
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
137
+ timeout=30,
138
+ )
139
+ except (OSError, subprocess.SubprocessError):
140
+ pass # never propagate regenerator failures into the agent loop
141
+
142
+ if verbose:
143
+ print(f"roadmap-progress-hook: regenerated for tool={tool}",
144
+ file=sys.stderr)
145
+ return 0
146
+
147
+
148
+ def main(argv: list[str] | None = None) -> int:
149
+ parser = argparse.ArgumentParser(description=__doc__)
150
+ parser.add_argument("--platform", default="generic",
151
+ help="informational platform tag (augment/claude/...)")
152
+ parser.add_argument("--verbose", action="store_true",
153
+ help="emit one stderr line per invocation")
154
+ args = parser.parse_args(argv)
155
+ return run(sys.stdin.read(), consumer_root=Path.cwd(), verbose=args.verbose)
156
+
157
+
158
+ if __name__ == "__main__": # pragma: no cover
159
+ sys.exit(main())
@@ -9,7 +9,8 @@
9
9
  "properties": {
10
10
  "name": {
11
11
  "type": "string",
12
- "pattern": "^[a-z][a-z0-9-]*$"
12
+ "pattern": "^[a-z][a-z0-9-]*(:[a-z][a-z0-9-]*)?$",
13
+ "$comment": "Top-level commands use the bare slug (`commit`). Nested cluster commands under `commands/<cluster>/<sub>.md` use the colon form (`council:default`) to mirror Claude Code's `/cluster:sub` rendering. Directory slug for `.claude/skills/` is the hyphenated form (`council-default`), generated by compress.py."
13
14
  },
14
15
  "description": {
15
16
  "type": "string",
@@ -40,8 +41,8 @@
40
41
  },
41
42
  "superseded_by": {
42
43
  "type": "string",
43
- "pattern": "^[a-z][a-z0-9-]*( [a-z][a-z0-9-]*)?$",
44
- "description": "Set on deprecation shims. Format: '<cluster> <sub>' (e.g. 'fix ci'). See docs/contracts/command-clusters.md § Deprecation shim contract."
44
+ "pattern": "^[a-z][a-z0-9-]*( (--[a-z][a-z0-9-]*|[a-z][a-z0-9-]*))?$",
45
+ "description": "Set on deprecation shims. Format: '<cluster> <sub>' (e.g. 'fix ci') or '<cluster> --<flag>' for flag-clusters (e.g. 'commit --in-chunks'). See docs/contracts/command-clusters.md § Deprecation shim contract."
45
46
  },
46
47
  "deprecated_in": {
47
48
  "type": "string",
@@ -886,6 +886,7 @@ def lint_command(path: Path, text: str) -> LintResult:
886
886
  "error", "shim_missing_warning",
887
887
  "Deprecation shim must contain a one-line warning matching "
888
888
  "'⚠️ /<old-name> is deprecated; use /<cluster> <sub> instead.'"
889
+ " (or '/<cluster> --<flag>' for flag-clusters)"
889
890
  " (see docs/contracts/command-clusters.md § Deprecation shim contract)"
890
891
  ))
891
892
 
@@ -136,8 +136,31 @@ def _append_unknown(body: str, user_flat: dict[str, object], known: set[str]) ->
136
136
 
137
137
 
138
138
  def render_target(template_body: str, user_data: dict) -> str:
139
- """Return the desired `.agent-settings.yml` body for the given user data."""
140
- user_flat = _flatten(user_data) if user_data else {}
139
+ """Return the desired `.agent-settings.yml` body for the given user data.
140
+
141
+ The trailing ``_user:`` block (emitted by :func:`_append_unknown`) is
142
+ already in dotted-key form on every read after the first sync. Re-
143
+ flattening it would prepend another ``_user.`` segment on every run
144
+ and accumulate forever, so we strip the wrapper and merge its
145
+ contents straight into the flat dict.
146
+ """
147
+ if user_data:
148
+ user_only = user_data.pop("_user", None) if isinstance(user_data, dict) else None
149
+ user_flat = _flatten(user_data)
150
+ if isinstance(user_only, dict):
151
+ for key, value in user_only.items():
152
+ # Dotted keys round-trip verbatim — never re-flatten them.
153
+ if isinstance(key, str):
154
+ # Heal legacy corruption: pre-fix syncs prepended a
155
+ # `_user.` segment per run, so a key may carry an
156
+ # arbitrary number of them. Strip them all back to
157
+ # the original leaf path.
158
+ healed = key
159
+ while healed.startswith("_user."):
160
+ healed = healed[len("_user."):]
161
+ user_flat[healed] = value
162
+ else:
163
+ user_flat = {}
141
164
  known = _template_keys(template_body)
142
165
  body = _apply_user_values(template_body, user_flat)
143
166
  return _append_unknown(body, user_flat, known)
@@ -37,6 +37,13 @@ def count(kind: str) -> int:
37
37
  if not pdir.exists():
38
38
  return 0
39
39
  return sum(1 for f in pdir.glob("*.md") if f.name != "README.md")
40
+ if kind == "commands":
41
+ # Commands may be flat (`commands/<name>.md`) or nested under a
42
+ # cluster directory (`commands/<cluster>/<sub>.md`). Walk the tree
43
+ # and skip the AGENTS.md reference orchestrator.
44
+ return sum(
45
+ 1 for f in (SRC / kind).rglob("*.md") if f.name != "AGENTS.md"
46
+ )
40
47
  return sum(1 for _ in (SRC / kind).glob("*.md"))
41
48
 
42
49