@event4u/agent-config 1.13.0 → 1.15.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 (291) hide show
  1. package/.agent-src/commands/agent-handoff.md +4 -1
  2. package/.agent-src/commands/agent-status.md +3 -0
  3. package/.agent-src/commands/agents-audit.md +4 -0
  4. package/.agent-src/commands/agents-cleanup.md +6 -1
  5. package/.agent-src/commands/agents-prepare.md +3 -0
  6. package/.agent-src/commands/analyze-reference-repo.md +4 -0
  7. package/.agent-src/commands/bug-fix.md +7 -3
  8. package/.agent-src/commands/bug-investigate.md +4 -0
  9. package/.agent-src/commands/chat-history-checkpoint.md +126 -0
  10. package/.agent-src/commands/chat-history-clear.md +6 -1
  11. package/.agent-src/commands/chat-history-resume.md +7 -2
  12. package/.agent-src/commands/chat-history.md +7 -2
  13. package/.agent-src/commands/check-current-md.md +137 -0
  14. package/.agent-src/commands/commit-in-chunks.md +118 -0
  15. package/.agent-src/commands/commit.md +4 -0
  16. package/.agent-src/commands/compress.md +37 -2
  17. package/.agent-src/commands/context-create.md +4 -0
  18. package/.agent-src/commands/context-refactor.md +4 -0
  19. package/.agent-src/commands/copilot-agents-init.md +3 -0
  20. package/.agent-src/commands/copilot-agents-optimize.md +3 -0
  21. package/.agent-src/commands/create-pr-description.md +4 -0
  22. package/.agent-src/commands/create-pr.md +4 -0
  23. package/.agent-src/commands/do-and-judge.md +4 -1
  24. package/.agent-src/commands/do-in-steps.md +3 -0
  25. package/.agent-src/commands/e2e-heal.md +4 -0
  26. package/.agent-src/commands/e2e-plan.md +4 -0
  27. package/.agent-src/commands/estimate-ticket.md +4 -1
  28. package/.agent-src/commands/feature-dev.md +4 -0
  29. package/.agent-src/commands/feature-explore.md +4 -0
  30. package/.agent-src/commands/feature-plan.md +4 -0
  31. package/.agent-src/commands/feature-refactor.md +4 -0
  32. package/.agent-src/commands/feature-roadmap.md +6 -0
  33. package/.agent-src/commands/fix-ci.md +4 -0
  34. package/.agent-src/commands/fix-portability.md +5 -2
  35. package/.agent-src/commands/fix-pr-bot-comments.md +4 -0
  36. package/.agent-src/commands/fix-pr-comments.md +4 -0
  37. package/.agent-src/commands/fix-pr-developer-comments.md +4 -0
  38. package/.agent-src/commands/fix-references.md +3 -0
  39. package/.agent-src/commands/fix-seeder.md +4 -0
  40. package/.agent-src/commands/implement-ticket.md +39 -13
  41. package/.agent-src/commands/jira-ticket.md +4 -0
  42. package/.agent-src/commands/judge.md +3 -0
  43. package/.agent-src/commands/memory-add.md +5 -3
  44. package/.agent-src/commands/memory-full.md +5 -2
  45. package/.agent-src/commands/memory-promote.md +7 -6
  46. package/.agent-src/commands/mode.md +3 -0
  47. package/.agent-src/commands/module-create.md +4 -0
  48. package/.agent-src/commands/module-explore.md +4 -0
  49. package/.agent-src/commands/onboard.md +33 -0
  50. package/.agent-src/commands/optimize-agents.md +4 -0
  51. package/.agent-src/commands/optimize-augmentignore.md +12 -0
  52. package/.agent-src/commands/optimize-rtk-filters.md +3 -0
  53. package/.agent-src/commands/optimize-skills.md +4 -0
  54. package/.agent-src/commands/override-create.md +4 -0
  55. package/.agent-src/commands/override-manage.md +4 -0
  56. package/.agent-src/commands/package-reset.md +3 -0
  57. package/.agent-src/commands/package-test.md +3 -0
  58. package/.agent-src/commands/prepare-for-review.md +4 -0
  59. package/.agent-src/commands/project-analyze.md +4 -0
  60. package/.agent-src/commands/project-health.md +4 -0
  61. package/.agent-src/commands/propose-memory.md +6 -8
  62. package/.agent-src/commands/quality-fix.md +4 -0
  63. package/.agent-src/commands/refine-ticket.md +12 -7
  64. package/.agent-src/commands/review-changes.md +39 -8
  65. package/.agent-src/commands/review-routing.md +4 -0
  66. package/.agent-src/commands/roadmap-create.md +18 -0
  67. package/.agent-src/commands/roadmap-execute.md +14 -1
  68. package/.agent-src/commands/rule-compliance-audit.md +4 -0
  69. package/.agent-src/commands/set-cost-profile.md +11 -0
  70. package/.agent-src/commands/sync-agent-settings.md +12 -0
  71. package/.agent-src/commands/sync-gitignore.md +3 -0
  72. package/.agent-src/commands/tests-create.md +4 -0
  73. package/.agent-src/commands/tests-execute.md +6 -3
  74. package/.agent-src/commands/threat-model.md +4 -0
  75. package/.agent-src/commands/update-form-request-messages.md +4 -0
  76. package/.agent-src/commands/upstream-contribute.md +4 -0
  77. package/.agent-src/commands/work.md +161 -0
  78. package/.agent-src/guidelines/agent-infra/engineering-memory-data-format.md +2 -6
  79. package/.agent-src/guidelines/agent-infra/layered-settings.md +0 -1
  80. package/.agent-src/guidelines/agent-infra/memory-access.md +0 -7
  81. package/.agent-src/guidelines/agent-infra/role-contracts.md +2 -4
  82. package/.agent-src/guidelines/agent-infra/self-improvement-pipeline.md +0 -1
  83. package/.agent-src/guidelines/php/patterns/strategy.md +180 -2
  84. package/.agent-src/personas/README.md +0 -1
  85. package/.agent-src/rules/artifact-drafting-protocol.md +7 -2
  86. package/.agent-src/rules/artifact-engagement-recording.md +133 -0
  87. package/.agent-src/rules/ask-when-uncertain.md +18 -13
  88. package/.agent-src/rules/augment-portability.md +64 -37
  89. package/.agent-src/rules/autonomous-execution.md +158 -0
  90. package/.agent-src/rules/chat-history-cadence.md +109 -0
  91. package/.agent-src/rules/chat-history-ownership.md +123 -0
  92. package/.agent-src/rules/chat-history-visibility.md +96 -0
  93. package/.agent-src/rules/cli-output-handling.md +27 -4
  94. package/.agent-src/rules/command-suggestion.md +134 -0
  95. package/.agent-src/rules/commit-policy.md +109 -0
  96. package/.agent-src/rules/direct-answers.md +114 -0
  97. package/.agent-src/rules/docs-sync.md +36 -0
  98. package/.agent-src/rules/downstream-changes.md +10 -9
  99. package/.agent-src/rules/improve-before-implement.md +9 -6
  100. package/.agent-src/rules/language-and-tone.md +85 -6
  101. package/.agent-src/rules/non-destructive-by-default.md +117 -0
  102. package/.agent-src/rules/package-ci-checks.md +4 -0
  103. package/.agent-src/rules/preservation-guard.md +20 -0
  104. package/.agent-src/rules/roadmap-progress-sync.md +159 -27
  105. package/.agent-src/rules/role-mode-adherence.md +1 -1
  106. package/.agent-src/rules/scope-control.md +42 -1
  107. package/.agent-src/rules/size-enforcement.md +2 -3
  108. package/.agent-src/rules/skill-quality.md +3 -8
  109. package/.agent-src/rules/ui-audit-before-build.md +106 -0
  110. package/.agent-src/rules/user-interaction.md +107 -51
  111. package/.agent-src/scripts/update_roadmap_progress.py +73 -9
  112. package/.agent-src/skills/blade-ui/SKILL.md +47 -3
  113. package/.agent-src/skills/command-routing/SKILL.md +32 -0
  114. package/.agent-src/skills/command-writing/SKILL.md +52 -2
  115. package/.agent-src/skills/description-assist/SKILL.md +21 -0
  116. package/.agent-src/skills/estimate-ticket/SKILL.md +0 -1
  117. package/.agent-src/skills/existing-ui-audit/SKILL.md +202 -0
  118. package/.agent-src/skills/fe-design/SKILL.md +78 -61
  119. package/.agent-src/skills/file-editor/SKILL.md +9 -0
  120. package/.agent-src/skills/finishing-a-development-branch/SKILL.md +4 -0
  121. package/.agent-src/skills/flux/SKILL.md +31 -4
  122. package/.agent-src/skills/guideline-writing/SKILL.md +24 -2
  123. package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +51 -9
  124. package/.agent-src/skills/livewire/SKILL.md +49 -4
  125. package/.agent-src/skills/md-language-check/SKILL.md +103 -0
  126. package/.agent-src/skills/php-coder/SKILL.md +24 -0
  127. package/.agent-src/skills/react-shadcn-ui/SKILL.md +121 -0
  128. package/.agent-src/skills/refine-prompt/SKILL.md +220 -0
  129. package/.agent-src/skills/refine-ticket/SKILL.md +32 -28
  130. package/.agent-src/skills/roadmap-management/SKILL.md +24 -11
  131. package/.agent-src/skills/rule-writing/SKILL.md +23 -1
  132. package/.agent-src/skills/skill-writing/SKILL.md +3 -5
  133. package/.agent-src/skills/upstream-contribute/SKILL.md +3 -3
  134. package/.agent-src/skills/using-git-worktrees/SKILL.md +3 -1
  135. package/.agent-src/templates/AGENTS.md +24 -6
  136. package/.agent-src/templates/agent-settings.md +149 -0
  137. package/.agent-src/templates/roadmaps.md +11 -4
  138. package/.agent-src/templates/scripts/implement_ticket/__init__.py +63 -26
  139. package/.agent-src/templates/scripts/implement_ticket/__main__.py +8 -2
  140. package/.agent-src/templates/scripts/memory_lookup.py +1 -1
  141. package/.agent-src/templates/scripts/telemetry/__init__.py +42 -0
  142. package/.agent-src/templates/scripts/telemetry/aggregator.py +154 -0
  143. package/.agent-src/templates/scripts/telemetry/boundary.py +171 -0
  144. package/.agent-src/templates/scripts/telemetry/engagement.py +238 -0
  145. package/.agent-src/templates/scripts/telemetry/report_renderer.py +170 -0
  146. package/.agent-src/templates/scripts/telemetry/settings.py +112 -0
  147. package/.agent-src/templates/scripts/telemetry_record.py +166 -0
  148. package/.agent-src/templates/scripts/telemetry_report.py +161 -0
  149. package/.agent-src/templates/scripts/telemetry_status.py +142 -0
  150. package/.agent-src/templates/scripts/work_engine/__init__.py +58 -0
  151. package/.agent-src/templates/scripts/work_engine/__main__.py +9 -0
  152. package/.agent-src/templates/scripts/work_engine/cli.py +195 -0
  153. package/.agent-src/templates/scripts/work_engine/cli_args.py +116 -0
  154. package/.agent-src/templates/scripts/{implement_ticket → work_engine}/delivery_state.py +10 -3
  155. package/.agent-src/templates/scripts/work_engine/directives/__init__.py +33 -0
  156. package/.agent-src/templates/scripts/work_engine/directives/backend/__init__.py +98 -0
  157. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/analyze.py +1 -1
  158. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/implement.py +3 -3
  159. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/memory.py +2 -2
  160. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/plan.py +2 -2
  161. package/.agent-src/templates/scripts/work_engine/directives/backend/refine.py +396 -0
  162. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/report.py +37 -5
  163. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/test.py +2 -2
  164. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/verify.py +2 -2
  165. package/.agent-src/templates/scripts/work_engine/directives/mixed/__init__.py +116 -0
  166. package/.agent-src/templates/scripts/work_engine/directives/mixed/contract.py +254 -0
  167. package/.agent-src/templates/scripts/work_engine/directives/mixed/stitch.py +229 -0
  168. package/.agent-src/templates/scripts/work_engine/directives/mixed/ui.py +231 -0
  169. package/.agent-src/templates/scripts/work_engine/directives/ui/__init__.py +113 -0
  170. package/.agent-src/templates/scripts/work_engine/directives/ui/_passthrough.py +44 -0
  171. package/.agent-src/templates/scripts/work_engine/directives/ui/apply.py +241 -0
  172. package/.agent-src/templates/scripts/work_engine/directives/ui/audit.py +414 -0
  173. package/.agent-src/templates/scripts/work_engine/directives/ui/design.py +335 -0
  174. package/.agent-src/templates/scripts/work_engine/directives/ui/polish.py +510 -0
  175. package/.agent-src/templates/scripts/work_engine/directives/ui/review.py +468 -0
  176. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/__init__.py +119 -0
  177. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.py +37 -0
  178. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.py +165 -0
  179. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.py +66 -0
  180. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/report.py +62 -0
  181. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/test.py +115 -0
  182. package/.agent-src/templates/scripts/work_engine/dispatcher.py +331 -0
  183. package/.agent-src/templates/scripts/work_engine/emitters.py +43 -0
  184. package/.agent-src/templates/scripts/work_engine/errors.py +19 -0
  185. package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +76 -0
  186. package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +54 -0
  187. package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +32 -0
  188. package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +103 -0
  189. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +44 -0
  190. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +42 -0
  191. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +50 -0
  192. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +49 -0
  193. package/.agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.py +53 -0
  194. package/.agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.py +50 -0
  195. package/.agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.py +52 -0
  196. package/.agent-src/templates/scripts/work_engine/hooks/builtin/trace.py +84 -0
  197. package/.agent-src/templates/scripts/work_engine/hooks/context.py +66 -0
  198. package/.agent-src/templates/scripts/work_engine/hooks/events.py +44 -0
  199. package/.agent-src/templates/scripts/work_engine/hooks/exceptions.py +79 -0
  200. package/.agent-src/templates/scripts/work_engine/hooks/registry.py +60 -0
  201. package/.agent-src/templates/scripts/work_engine/hooks/runner.py +73 -0
  202. package/.agent-src/templates/scripts/work_engine/hooks/settings.py +141 -0
  203. package/.agent-src/templates/scripts/work_engine/input_builders.py +163 -0
  204. package/.agent-src/templates/scripts/work_engine/intent/__init__.py +47 -0
  205. package/.agent-src/templates/scripts/work_engine/intent/classify.py +280 -0
  206. package/.agent-src/templates/scripts/work_engine/migration/__init__.py +8 -0
  207. package/.agent-src/templates/scripts/work_engine/migration/v0_to_v1.py +231 -0
  208. package/.agent-src/templates/scripts/{implement_ticket → work_engine}/persona_policy.py +1 -1
  209. package/.agent-src/templates/scripts/work_engine/resolvers/__init__.py +22 -0
  210. package/.agent-src/templates/scripts/work_engine/resolvers/diff.py +106 -0
  211. package/.agent-src/templates/scripts/work_engine/resolvers/file.py +113 -0
  212. package/.agent-src/templates/scripts/work_engine/resolvers/prompt.py +90 -0
  213. package/.agent-src/templates/scripts/work_engine/scoring/__init__.py +14 -0
  214. package/.agent-src/templates/scripts/work_engine/scoring/confidence.py +300 -0
  215. package/.agent-src/templates/scripts/work_engine/stack/__init__.py +31 -0
  216. package/.agent-src/templates/scripts/work_engine/stack/detect.py +187 -0
  217. package/.agent-src/templates/scripts/work_engine/state.py +641 -0
  218. package/.agent-src/templates/scripts/work_engine/state_io.py +202 -0
  219. package/.claude-plugin/marketplace.json +105 -2
  220. package/AGENTS.md +38 -8
  221. package/CHANGELOG.md +609 -0
  222. package/README.md +136 -14
  223. package/config/agent-settings.template.yml +45 -0
  224. package/config/gitignore-block.txt +4 -0
  225. package/docs/MIGRATION.md +122 -0
  226. package/docs/architecture.md +111 -35
  227. package/docs/contracts/STABILITY.md +95 -0
  228. package/docs/contracts/adr-chat-history-split.md +132 -0
  229. package/docs/contracts/adr-command-suggestion.md +146 -0
  230. package/docs/contracts/adr-implement-ticket-runtime.md +122 -0
  231. package/docs/contracts/adr-product-ui-track.md +384 -0
  232. package/docs/contracts/adr-prompt-driven-execution.md +187 -0
  233. package/docs/contracts/agent-memory-contract.md +149 -0
  234. package/docs/contracts/artifact-engagement-flow.md +262 -0
  235. package/docs/contracts/command-clusters.md +126 -0
  236. package/docs/contracts/command-suggestion-flow.md +148 -0
  237. package/docs/contracts/implement-ticket-flow.md +628 -0
  238. package/docs/contracts/linear-ai-rules-inclusion.md +143 -0
  239. package/docs/contracts/linear-ai-three-layers.md +131 -0
  240. package/docs/contracts/rule-interactions.md +107 -0
  241. package/docs/contracts/rule-interactions.yml +142 -0
  242. package/docs/contracts/ui-stack-extension.md +236 -0
  243. package/docs/contracts/ui-track-flow.md +338 -0
  244. package/docs/development.md +1 -1
  245. package/docs/getting-started.md +3 -3
  246. package/docs/installation.md +124 -2
  247. package/docs/migrations/commands-1.15.0.md +112 -0
  248. package/docs/showcase.md +204 -0
  249. package/docs/ui-track-mental-model.md +121 -0
  250. package/package.json +1 -1
  251. package/scripts/agent-config +199 -0
  252. package/scripts/audit_cloud_compatibility.py +288 -0
  253. package/scripts/build_cloud_bundle.py +458 -0
  254. package/scripts/build_linear_digest.py +263 -0
  255. package/scripts/chat_history.py +796 -7
  256. package/scripts/check_compression.py +139 -0
  257. package/scripts/check_iron_law_prominence.py +143 -0
  258. package/scripts/check_md_language.py +159 -0
  259. package/scripts/check_portability.py +38 -0
  260. package/scripts/check_public_links.py +185 -0
  261. package/scripts/check_references.py +1 -0
  262. package/scripts/check_reply_consistency.py +140 -0
  263. package/scripts/command_suggester/__init__.py +51 -0
  264. package/scripts/command_suggester/cooldown.py +132 -0
  265. package/scripts/command_suggester/loader.py +70 -0
  266. package/scripts/command_suggester/match.py +180 -0
  267. package/scripts/command_suggester/rank.py +120 -0
  268. package/scripts/command_suggester/render.py +86 -0
  269. package/scripts/command_suggester/sanitize.py +113 -0
  270. package/scripts/command_suggester/settings.py +125 -0
  271. package/scripts/command_suggester/types.py +78 -0
  272. package/scripts/hooks/augment-chat-history.sh +56 -0
  273. package/scripts/install-hooks.sh +67 -0
  274. package/scripts/install.py +150 -33
  275. package/scripts/lint_marketplace.py +27 -0
  276. package/scripts/lint_no_new_atomic_commands.py +179 -0
  277. package/scripts/lint_rule_interactions.py +149 -0
  278. package/scripts/memory_lookup.py +1 -1
  279. package/scripts/migrate_command_suggestions.py +151 -0
  280. package/scripts/release.py +297 -64
  281. package/scripts/schemas/command.schema.json +41 -0
  282. package/scripts/skill_linter.py +81 -0
  283. package/scripts/sync_agent_settings.py +42 -12
  284. package/scripts/update_counts.py +10 -0
  285. package/templates/consumer-settings/augment-cli-hooks.json +54 -0
  286. package/templates/consumer-settings/claude-settings.json +55 -1
  287. package/.agent-src/rules/chat-history.md +0 -171
  288. package/.agent-src/templates/scripts/implement_ticket/cli.py +0 -171
  289. package/.agent-src/templates/scripts/implement_ticket/dispatcher.py +0 -134
  290. package/.agent-src/templates/scripts/implement_ticket/steps/__init__.py +0 -49
  291. package/.agent-src/templates/scripts/implement_ticket/steps/refine.py +0 -140
@@ -8,6 +8,8 @@ Checks that compression preserved structural integrity:
8
8
  - All code blocks preserved exactly
9
9
  - YAML frontmatter identical
10
10
  - Word count reduction within healthy range (10-60%)
11
+ - Iron Law sections (## Iron Law / ### Iron Law / ## The Iron Law / Iron Laws / numbered)
12
+ preserved per `preservation-guard`: heading verbatim at original level, ≤ 15% reduction
11
13
 
12
14
  Exit codes: 0 = clean, 1 = issues found, 3 = internal error
13
15
  """
@@ -60,6 +62,103 @@ def extract_frontmatter(text: str) -> str:
60
62
  return m.group(1).strip() if m else ""
61
63
 
62
64
 
65
+ # Matches `## Iron Law`, `## The Iron Law`, `## Iron Laws`, `### Iron Law — …`,
66
+ # `## Iron Law 1 — …`, etc. Any heading level 2-6.
67
+ IRON_LAW_HEADING = re.compile(r"^(#{2,6})\s+(The\s+)?Iron Laws?\b")
68
+
69
+ LIST_ITEM_RE = re.compile(r"^(?:[-*+]|\d+\.)\s")
70
+ INNER_HEADING_RE = re.compile(r"^#{1,6}\s")
71
+
72
+
73
+ def count_iron_law_structure(body: str) -> dict:
74
+ """Count structural units in an Iron Law body.
75
+
76
+ Returns counts of paragraphs (blank-line-separated prose blocks),
77
+ list items (bullet + numbered), and fenced code blocks. Caveman
78
+ compression may shorten word count freely; what must NOT change is
79
+ the count of these structural units. Each represents a passage of
80
+ the law that the source decided to keep.
81
+
82
+ Multi-line list items (bullet text wrapped to indented continuation
83
+ lines, no blank line between) count as ONE list item, not as a
84
+ list item plus a paragraph.
85
+ """
86
+ paragraphs = 0
87
+ list_items = 0
88
+ code_blocks = 0
89
+ in_code = False
90
+ state = "blank" # "blank" | "paragraph" | "list"
91
+ for line in body.splitlines():
92
+ stripped = line.strip()
93
+ if stripped.startswith("```"):
94
+ if not in_code:
95
+ code_blocks += 1
96
+ in_code = not in_code
97
+ state = "blank"
98
+ continue
99
+ if in_code:
100
+ continue
101
+ if not stripped:
102
+ state = "blank"
103
+ continue
104
+ if LIST_ITEM_RE.match(stripped):
105
+ list_items += 1
106
+ state = "list"
107
+ continue
108
+ if INNER_HEADING_RE.match(stripped):
109
+ state = "blank"
110
+ continue
111
+ # Indented non-empty line right after a list item is a wrap
112
+ # continuation of that item, not a new paragraph.
113
+ if state == "list" and line.startswith((" ", "\t")):
114
+ continue
115
+ if state != "paragraph":
116
+ paragraphs += 1
117
+ state = "paragraph"
118
+ return {"paragraphs": paragraphs, "list_items": list_items, "code_blocks": code_blocks}
119
+
120
+
121
+ def extract_iron_law_sections(text: str) -> list[tuple[str, int, str]]:
122
+ """Return [(heading, level, body)] for each Iron Law section.
123
+
124
+ Body is everything after the heading until the next heading at the same
125
+ or higher (numerically lower) level — fenced code blocks included verbatim.
126
+ """
127
+ lines = text.splitlines()
128
+ sections: list[tuple[str, int, str]] = []
129
+ i = 0
130
+ in_code = False
131
+ while i < len(lines):
132
+ line = lines[i]
133
+ if line.strip().startswith("```"):
134
+ in_code = not in_code
135
+ i += 1
136
+ continue
137
+ if not in_code:
138
+ m = IRON_LAW_HEADING.match(line)
139
+ if m:
140
+ heading = line.rstrip()
141
+ level = len(m.group(1))
142
+ body_lines: list[str] = []
143
+ j = i + 1
144
+ inner_code = False
145
+ while j < len(lines):
146
+ jline = lines[j]
147
+ if jline.strip().startswith("```"):
148
+ inner_code = not inner_code
149
+ if not inner_code:
150
+ hm = re.match(r"^(#{1,6})\s", jline)
151
+ if hm and len(hm.group(1)) <= level:
152
+ break
153
+ body_lines.append(jline)
154
+ j += 1
155
+ sections.append((heading, level, "\n".join(body_lines)))
156
+ i = j
157
+ continue
158
+ i += 1
159
+ return sections
160
+
161
+
63
162
  def check_pair(rel_path: str, source: str, compressed: str) -> List[Issue]:
64
163
  """Compare source and compressed versions of a file."""
65
164
  issues: List[Issue] = []
@@ -96,6 +195,46 @@ def check_pair(rel_path: str, source: str, compressed: str) -> List[Issue]:
96
195
  issues.append(Issue(rel_path, "modified_code_block", "error",
97
196
  f"Code block {i+1} content changed during compression"))
98
197
 
198
+ # Iron Law preservation — non-negotiable behavioral rules, see preservation-guard
199
+ src_laws = extract_iron_law_sections(source)
200
+ cmp_laws = extract_iron_law_sections(compressed)
201
+ cmp_law_map = {h: (lvl, body) for h, lvl, body in cmp_laws}
202
+ # Build a level-agnostic lookup so we can detect heading-level downgrades
203
+ # (`## Iron Law` → `### Iron Law`).
204
+ cmp_law_by_text = {h.lstrip("# ").strip(): (lvl, h, body)
205
+ for h, lvl, body in cmp_laws}
206
+ for src_heading, src_level, src_body in src_laws:
207
+ src_text = src_heading.lstrip("# ").strip()
208
+ if src_heading not in cmp_law_map:
209
+ # Heading text may exist at a different level → downgrade
210
+ if src_text in cmp_law_by_text:
211
+ cmp_level, cmp_heading, _ = cmp_law_by_text[src_text]
212
+ if cmp_level != src_level:
213
+ issues.append(Issue(rel_path, "iron_law_heading_downgrade", "error",
214
+ f"Iron Law heading level changed: "
215
+ f"{'#' * src_level} → {'#' * cmp_level} "
216
+ f"({src_heading.strip()})"))
217
+ continue
218
+ issues.append(Issue(rel_path, "iron_law_missing", "error",
219
+ f"Iron Law section removed during compression: "
220
+ f"{src_heading.strip()}"))
221
+ continue
222
+ # Section exists at correct level — check structural-unit survival.
223
+ # Caveman compression is fine (drop articles, terse phrasing); what
224
+ # must NOT change is the count of paragraphs, list items, and code
225
+ # blocks. Each is a passage the source kept on purpose.
226
+ _, cmp_body = cmp_law_map[src_heading]
227
+ src_struct = count_iron_law_structure(src_body)
228
+ cmp_struct = count_iron_law_structure(cmp_body)
229
+ for kind, src_n in src_struct.items():
230
+ cmp_n = cmp_struct[kind]
231
+ if cmp_n < src_n:
232
+ issues.append(Issue(rel_path, "iron_law_passage_dropped", "error",
233
+ f"Iron Law section dropped "
234
+ f"{src_n - cmp_n} {kind} "
235
+ f"({src_n} → {cmp_n}): "
236
+ f"{src_heading.strip()}"))
237
+
99
238
  # Word count ratio
100
239
  src_words = len(source.split())
101
240
  cmp_words = len(compressed.split())
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Iron Law prominence checker — enforces that any rule file declaring an
4
+ "Iron Law" places it at the top of the file at H2 level.
5
+
6
+ Rules:
7
+ 1. No heading at H3 or deeper may match "Iron Law(s)" — Iron Laws must
8
+ be H2 sections, never sub-sections.
9
+ 2. If a file declares one or more Iron-Law H2 sections, at least one
10
+ of them must be among the first two H2 headings of the file.
11
+
12
+ Files with no Iron-Law heading at all are exempt — they may legitimately
13
+ reference Iron Laws from other rules in prose only.
14
+
15
+ Code blocks are skipped to avoid false positives on quoted text.
16
+
17
+ Exit codes: 0 = clean, 1 = violations found, 3 = internal error.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import argparse
23
+ import json
24
+ import re
25
+ import sys
26
+ from dataclasses import dataclass, asdict
27
+ from pathlib import Path
28
+
29
+ HEADING_RE = re.compile(r"^(#{1,6})\s+(.+?)\s*$")
30
+ IRON_LAW_RE = re.compile(r"\biron\s+laws?\b", re.IGNORECASE)
31
+ FENCE_RE = re.compile(r"^\s*```")
32
+
33
+
34
+ @dataclass
35
+ class Violation:
36
+ file: str
37
+ line: int
38
+ kind: str # "deep_iron_law" | "buried_iron_law"
39
+ detail: str
40
+
41
+
42
+ def _parse_headings(text: str) -> list[tuple[int, int, str]]:
43
+ """Return (line_no, depth, title) for each heading outside code fences."""
44
+ headings: list[tuple[int, int, str]] = []
45
+ in_fence = False
46
+ for lineno, raw in enumerate(text.splitlines(), start=1):
47
+ if FENCE_RE.match(raw):
48
+ in_fence = not in_fence
49
+ continue
50
+ if in_fence:
51
+ continue
52
+ m = HEADING_RE.match(raw)
53
+ if not m:
54
+ continue
55
+ depth = len(m.group(1))
56
+ title = m.group(2).strip()
57
+ headings.append((lineno, depth, title))
58
+ return headings
59
+
60
+
61
+ def scan_file(path: Path) -> list[Violation]:
62
+ text = path.read_text(encoding="utf-8")
63
+ headings = _parse_headings(text)
64
+
65
+ violations: list[Violation] = []
66
+
67
+ # Rule 1: no Iron Law at H3 or deeper
68
+ for lineno, depth, title in headings:
69
+ if depth >= 3 and IRON_LAW_RE.search(title):
70
+ violations.append(Violation(
71
+ file=str(path), line=lineno, kind="deep_iron_law",
72
+ detail=f"H{depth} heading `{title}` — promote to H2",
73
+ ))
74
+
75
+ # Rule 2: if any H2 Iron Law exists, it must be in first 2 H2 positions
76
+ h2 = [(ln, t) for ln, d, t in headings if d == 2]
77
+ iron_h2 = [(ln, t) for ln, t in h2 if IRON_LAW_RE.search(t)]
78
+ if iron_h2:
79
+ first_two_lines = {ln for ln, _ in h2[:2]}
80
+ if not any(ln in first_two_lines for ln, _ in iron_h2):
81
+ first_iron_ln, first_iron_title = iron_h2[0]
82
+ preceding = [t for ln, t in h2 if ln < first_iron_ln]
83
+ violations.append(Violation(
84
+ file=str(path), line=first_iron_ln, kind="buried_iron_law",
85
+ detail=(
86
+ f"Iron Law H2 `{first_iron_title}` at line {first_iron_ln} "
87
+ f"is preceded by {len(preceding)} non-Iron-Law H2 section(s): "
88
+ f"{preceding}. Move Iron Law into the first 2 H2 positions."
89
+ ),
90
+ ))
91
+
92
+ return violations
93
+
94
+
95
+ def _resolve_targets(paths: list[str]) -> list[Path]:
96
+ out: list[Path] = []
97
+ for raw in paths:
98
+ p = Path(raw)
99
+ if p.is_dir():
100
+ out.extend(sorted(p.rglob("*.md")))
101
+ elif p.suffix == ".md":
102
+ out.append(p)
103
+ return out
104
+
105
+
106
+ def main() -> int:
107
+ parser = argparse.ArgumentParser(description=__doc__)
108
+ parser.add_argument(
109
+ "paths", nargs="*",
110
+ default=[".agent-src.uncompressed/rules"],
111
+ help="Files or directories to scan (default: .agent-src.uncompressed/rules)",
112
+ )
113
+ parser.add_argument("--format", choices=["text", "json"], default="text")
114
+ args = parser.parse_args()
115
+
116
+ targets = _resolve_targets(args.paths)
117
+ all_violations: list[Violation] = []
118
+ for path in targets:
119
+ if not path.exists():
120
+ print(f"⚠️ Not found: {path}", file=sys.stderr)
121
+ continue
122
+ all_violations.extend(scan_file(path))
123
+
124
+ if args.format == "json":
125
+ print(json.dumps([asdict(v) for v in all_violations], indent=2, ensure_ascii=False))
126
+ else:
127
+ if not all_violations:
128
+ print(f"✅ Iron Law prominence clean ({len(targets)} file(s) scanned).")
129
+ else:
130
+ print(f"❌ {len(all_violations)} Iron-Law prominence violation(s):\n")
131
+ for v in all_violations:
132
+ print(f" {v.file}:{v.line} — {v.kind}")
133
+ print(f" │ {v.detail}")
134
+
135
+ return 1 if all_violations else 0
136
+
137
+
138
+ if __name__ == "__main__":
139
+ try:
140
+ sys.exit(main())
141
+ except Exception as exc: # noqa: BLE001
142
+ print(f"❌ Internal error: {exc}", file=sys.stderr)
143
+ sys.exit(3)
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Markdown language checker — enforces language-and-tone § ".md files are ALWAYS English".
4
+
5
+ Scans .md files for German content (umlauts, function words, quoted DE phrases)
6
+ in body prose, skipping:
7
+ - Fenced code blocks (``` ... ```)
8
+ - Inline code (`...`)
9
+ - Labeled DE: ... · EN: ... anchor blocks
10
+ - URLs and file paths inside backticks
11
+
12
+ Exit codes: 0 = clean, 1 = violations found, 3 = internal error.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import json
19
+ import re
20
+ import sys
21
+ from dataclasses import dataclass, asdict
22
+ from pathlib import Path
23
+
24
+ # Umlauts and German-only characters
25
+ UMLAUT_RE = re.compile(r"[äöüÄÖÜß]")
26
+
27
+ # German function words that almost never appear in English technical prose
28
+ DE_WORDS = [
29
+ "für", "nicht", "dass", "wenn", "sollte", "werden", "arbeite",
30
+ "selbstständig", "jetzt", "einfach", "weiter", "lösche", "frag",
31
+ "schreib", "mach", "auch", "hier", "diese", "dieser", "dieses",
32
+ "vermutlich", "bitte", "kannst", "sollen", "müssen", "wäre",
33
+ ]
34
+ DE_WORD_RE = re.compile(
35
+ r"\b(" + "|".join(re.escape(w) for w in DE_WORDS) + r")\b",
36
+ re.IGNORECASE,
37
+ )
38
+
39
+ # Labeled bilingual anchor: lines starting with "DE:" or "- DE:" (and same for EN)
40
+ DE_ANCHOR_RE = re.compile(r"^\s*[-*]?\s*(DE|EN):\s", re.IGNORECASE)
41
+
42
+ # Inline code spans
43
+ INLINE_CODE_RE = re.compile(r"`[^`]*`")
44
+
45
+ # Per-line escape: append `<!-- md-language-check: ignore -->` to a line
46
+ # to suppress findings on that line. For meta-documentation that quotes
47
+ # German tokens as trigger examples (e.g. inside language-and-tone.md).
48
+ IGNORE_RE = re.compile(r"<!--\s*md-language-check:\s*ignore\s*-->", re.IGNORECASE)
49
+
50
+
51
+ @dataclass
52
+ class Violation:
53
+ file: str
54
+ line: int
55
+ kind: str # "umlaut" | "de_word"
56
+ match: str
57
+ context: str
58
+
59
+
60
+ def _strip_inline_code(text: str) -> str:
61
+ return INLINE_CODE_RE.sub("", text)
62
+
63
+
64
+ def scan_file(path: Path) -> list[Violation]:
65
+ violations: list[Violation] = []
66
+ try:
67
+ lines = path.read_text(encoding="utf-8").splitlines()
68
+ except (OSError, UnicodeDecodeError) as exc:
69
+ print(f"⚠️ Cannot read {path}: {exc}", file=sys.stderr)
70
+ return violations
71
+
72
+ in_fence = False
73
+ in_frontmatter = False
74
+ for lineno, raw in enumerate(lines, start=1):
75
+ stripped = raw.lstrip()
76
+
77
+ # YAML frontmatter at top of file
78
+ if lineno == 1 and stripped == "---":
79
+ in_frontmatter = True
80
+ continue
81
+ if in_frontmatter:
82
+ if stripped == "---":
83
+ in_frontmatter = False
84
+ continue
85
+
86
+ # Fenced code blocks
87
+ if stripped.startswith("```") or stripped.startswith("~~~"):
88
+ in_fence = not in_fence
89
+ continue
90
+ if in_fence:
91
+ continue
92
+
93
+ # Indented code blocks (4+ leading spaces, non-list)
94
+ if raw.startswith(" ") and not stripped.startswith(("-", "*", "+", "1", "2", "3", "4", "5", "6", "7", "8", "9")):
95
+ continue
96
+
97
+ # Labeled bilingual anchor — allowed location for DE prose
98
+ if DE_ANCHOR_RE.match(raw):
99
+ continue
100
+
101
+ # Per-line opt-out marker
102
+ if IGNORE_RE.search(raw):
103
+ continue
104
+
105
+ # Strip inline code spans before scanning
106
+ scan_text = _strip_inline_code(raw)
107
+
108
+ for m in UMLAUT_RE.finditer(scan_text):
109
+ violations.append(Violation(
110
+ file=str(path), line=lineno, kind="umlaut",
111
+ match=m.group(0), context=raw.rstrip(),
112
+ ))
113
+
114
+ for m in DE_WORD_RE.finditer(scan_text):
115
+ violations.append(Violation(
116
+ file=str(path), line=lineno, kind="de_word",
117
+ match=m.group(0), context=raw.rstrip(),
118
+ ))
119
+
120
+ return violations
121
+
122
+
123
+ def main() -> int:
124
+ parser = argparse.ArgumentParser(description=__doc__)
125
+ parser.add_argument("paths", nargs="+", help="One or more .md files to scan")
126
+ parser.add_argument("--format", choices=["text", "json"], default="text")
127
+ args = parser.parse_args()
128
+
129
+ all_violations: list[Violation] = []
130
+ for raw_path in args.paths:
131
+ path = Path(raw_path)
132
+ if not path.exists():
133
+ print(f"⚠️ Not found: {path}", file=sys.stderr)
134
+ continue
135
+ if path.suffix != ".md":
136
+ print(f"⚠️ Skipping non-.md: {path}", file=sys.stderr)
137
+ continue
138
+ all_violations.extend(scan_file(path))
139
+
140
+ if args.format == "json":
141
+ print(json.dumps([asdict(v) for v in all_violations], indent=2, ensure_ascii=False))
142
+ else:
143
+ if not all_violations:
144
+ print("✅ No German content detected.")
145
+ else:
146
+ print(f"❌ {len(all_violations)} violation(s) found:\n")
147
+ for v in all_violations:
148
+ print(f" {v.file}:{v.line} — {v.kind} `{v.match}`")
149
+ print(f" │ {v.context}")
150
+
151
+ return 1 if all_violations else 0
152
+
153
+
154
+ if __name__ == "__main__":
155
+ try:
156
+ sys.exit(main())
157
+ except Exception as exc: # noqa: BLE001
158
+ print(f"❌ Internal error: {exc}", file=sys.stderr)
159
+ sys.exit(3)
@@ -157,6 +157,8 @@ ALLOWLIST = [
157
157
  r"agent-config", # refers to the package concept, not a specific project
158
158
  r"shared.*package", # "shared package" concept
159
159
  r"package repository", # "package repository" concept
160
+ r"scripts/mcp_server/", # MCP server module path (road-to-mcp-server.md Phase 1)
161
+ r"scripts\.mcp_server", # MCP server Python module entrypoint
160
162
  ]
161
163
 
162
164
  # Directories to scan (only package files, not project-specific agents/)
@@ -329,6 +331,42 @@ _CLI_INVOCATION_MAP: list[tuple[re.Pattern, str]] = [
329
331
  re.compile(r"bash\s+scripts/first-run\.sh\b"),
330
332
  "./agent-config first-run",
331
333
  ),
334
+ (
335
+ re.compile(r"(?:PYTHONPATH=\S+\s+)?python3\s+-m\s+work_engine\b"),
336
+ "./agent-config implement-ticket",
337
+ ),
338
+ (
339
+ re.compile(r"(?:PYTHONPATH=\S+\s+)?python3\s+-m\s+implement_ticket\b"),
340
+ "./agent-config implement-ticket",
341
+ ),
342
+ (
343
+ re.compile(r"python3\s+scripts/memory_lookup\.py\b"),
344
+ "./agent-config memory:lookup",
345
+ ),
346
+ (
347
+ re.compile(r"python3\s+scripts/memory_signal\.py\b"),
348
+ "./agent-config memory:signal",
349
+ ),
350
+ (
351
+ re.compile(r"python3\s+scripts/memory_hash\.py\b"),
352
+ "./agent-config memory:hash",
353
+ ),
354
+ (
355
+ re.compile(r"python3\s+scripts/check_memory_proposal\.py\b"),
356
+ "./agent-config memory:check-proposal",
357
+ ),
358
+ (
359
+ re.compile(r"python3\s+scripts/check_memory\.py\b"),
360
+ "./agent-config memory:check",
361
+ ),
362
+ (
363
+ re.compile(r"python3\s+scripts/check_proposal\.py\b"),
364
+ "./agent-config proposal:check",
365
+ ),
366
+ (
367
+ re.compile(r"python3\s+scripts/refine_ticket_detect\.py\b"),
368
+ "./agent-config refine-ticket:detect",
369
+ ),
332
370
  ]
333
371
 
334
372
  # Paths that legitimately document the raw invocations (e.g. the CLI's