@event4u/agent-config 1.17.0 → 1.19.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 (158) hide show
  1. package/.agent-src/commands/council/default.md +74 -76
  2. package/.agent-src/commands/feature/roadmap.md +22 -0
  3. package/.agent-src/commands/roadmap/create.md +38 -6
  4. package/.agent-src/commands/roadmap/execute.md +36 -9
  5. package/.agent-src/rules/agent-authority.md +1 -0
  6. package/.agent-src/rules/agent-docs.md +1 -0
  7. package/.agent-src/rules/analysis-skill-routing.md +1 -0
  8. package/.agent-src/rules/architecture.md +1 -0
  9. package/.agent-src/rules/artifact-drafting-protocol.md +1 -0
  10. package/.agent-src/rules/artifact-engagement-recording.md +1 -0
  11. package/.agent-src/rules/ask-when-uncertain.md +1 -0
  12. package/.agent-src/rules/augment-portability.md +1 -0
  13. package/.agent-src/rules/augment-source-of-truth.md +1 -0
  14. package/.agent-src/rules/autonomous-execution.md +1 -0
  15. package/.agent-src/rules/capture-learnings.md +1 -0
  16. package/.agent-src/rules/chat-history-cadence.md +34 -0
  17. package/.agent-src/rules/chat-history-ownership.md +1 -0
  18. package/.agent-src/rules/chat-history-visibility.md +1 -0
  19. package/.agent-src/rules/cli-output-handling.md +2 -2
  20. package/.agent-src/rules/command-suggestion-policy.md +1 -0
  21. package/.agent-src/rules/commit-conventions.md +1 -0
  22. package/.agent-src/rules/commit-policy.md +1 -0
  23. package/.agent-src/rules/context-hygiene.md +28 -0
  24. package/.agent-src/rules/direct-answers.md +18 -26
  25. package/.agent-src/rules/docker-commands.md +1 -0
  26. package/.agent-src/rules/docs-sync.md +1 -0
  27. package/.agent-src/rules/downstream-changes.md +1 -0
  28. package/.agent-src/rules/e2e-testing.md +1 -0
  29. package/.agent-src/rules/guidelines.md +1 -0
  30. package/.agent-src/rules/improve-before-implement.md +1 -0
  31. package/.agent-src/rules/language-and-tone.md +1 -0
  32. package/.agent-src/rules/laravel-translations.md +1 -0
  33. package/.agent-src/rules/markdown-safe-codeblocks.md +1 -0
  34. package/.agent-src/rules/minimal-safe-diff.md +1 -0
  35. package/.agent-src/rules/missing-tool-handling.md +1 -0
  36. package/.agent-src/rules/model-recommendation.md +1 -0
  37. package/.agent-src/rules/no-cheap-questions.md +15 -21
  38. package/.agent-src/rules/no-roadmap-references.md +1 -0
  39. package/.agent-src/rules/non-destructive-by-default.md +1 -0
  40. package/.agent-src/rules/onboarding-gate.md +33 -0
  41. package/.agent-src/rules/package-ci-checks.md +1 -0
  42. package/.agent-src/rules/php-coding.md +1 -0
  43. package/.agent-src/rules/preservation-guard.md +1 -0
  44. package/.agent-src/rules/review-routing-awareness.md +1 -0
  45. package/.agent-src/rules/reviewer-awareness.md +1 -0
  46. package/.agent-src/rules/roadmap-progress-sync.md +49 -0
  47. package/.agent-src/rules/role-mode-adherence.md +2 -2
  48. package/.agent-src/rules/rule-type-governance.md +29 -0
  49. package/.agent-src/rules/runtime-safety.md +1 -0
  50. package/.agent-src/rules/scope-control.md +1 -0
  51. package/.agent-src/rules/security-sensitive-stop.md +1 -0
  52. package/.agent-src/rules/size-enforcement.md +1 -0
  53. package/.agent-src/rules/skill-improvement-trigger.md +1 -0
  54. package/.agent-src/rules/skill-quality.md +1 -0
  55. package/.agent-src/rules/slash-command-routing-policy.md +39 -0
  56. package/.agent-src/rules/think-before-action.md +1 -0
  57. package/.agent-src/rules/token-efficiency.md +1 -0
  58. package/.agent-src/rules/tool-safety.md +1 -0
  59. package/.agent-src/rules/ui-audit-gate.md +1 -0
  60. package/.agent-src/rules/upstream-proposal.md +1 -0
  61. package/.agent-src/rules/user-interaction.md +1 -0
  62. package/.agent-src/rules/verify-before-complete.md +1 -0
  63. package/.agent-src/skills/roadmap-management/SKILL.md +29 -4
  64. package/.agent-src/skills/verify-completion-evidence/SKILL.md +8 -1
  65. package/.agent-src/templates/agent-settings.md +16 -0
  66. package/.agent-src/templates/roadmaps.md +12 -3
  67. package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +9 -0
  68. package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +4 -0
  69. package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +4 -0
  70. package/.agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.py +163 -0
  71. package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +111 -0
  72. package/.agent-src/templates/scripts/work_engine/hooks/settings.py +36 -0
  73. package/.agent-src/templates/scripts/work_engine/scoring/decision_trace.py +141 -0
  74. package/.agent-src/templates/scripts/work_engine/scoring/memory_visibility.py +125 -0
  75. package/.claude-plugin/marketplace.json +1 -1
  76. package/CHANGELOG.md +97 -0
  77. package/README.md +20 -20
  78. package/config/agent-settings.template.yml +23 -0
  79. package/docs/architecture.md +1 -1
  80. package/docs/catalog.md +5 -2
  81. package/docs/contracts/adr-settings-sync-engine.md +127 -0
  82. package/docs/contracts/decision-trace-v1.md +146 -0
  83. package/docs/contracts/file-ownership-matrix.json +7 -0
  84. package/docs/contracts/hook-architecture-v1.md +213 -0
  85. package/docs/contracts/load-context-budget-model.md +80 -0
  86. package/docs/contracts/load-context-schema.md +20 -0
  87. package/docs/contracts/memory-visibility-v1.md +138 -0
  88. package/docs/contracts/one-off-script-lifecycle.md +109 -0
  89. package/docs/contracts/roadmap-complexity-standard.md +137 -0
  90. package/docs/contracts/rule-interactions.yml +22 -0
  91. package/docs/customization.md +1 -0
  92. package/docs/development.md +4 -1
  93. package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +134 -0
  94. package/docs/guidelines/agent-infra/direct-answers-demos.md +145 -0
  95. package/docs/guidelines/agent-infra/layered-settings.md +32 -13
  96. package/docs/guidelines/agent-infra/verify-before-complete-demos.md +128 -0
  97. package/package.json +1 -1
  98. package/scripts/agent-config +64 -0
  99. package/scripts/ai_council/bundler.py +3 -3
  100. package/scripts/ai_council/clients.py +24 -8
  101. package/scripts/ai_council/one_off_archive/2026-05/README.md +67 -0
  102. package/scripts/ai_council/one_off_archive/2026-05/_one_off_budget_v2_audit.py +206 -0
  103. package/scripts/ai_council/{_one_off_roundtrip.py → one_off_archive/2026-05/_one_off_roundtrip.py} +13 -8
  104. package/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +180 -0
  105. package/scripts/ai_council/session.py +92 -0
  106. package/scripts/build_rule_trigger_matrix.py +360 -0
  107. package/scripts/capture_showcase_session.py +361 -0
  108. package/scripts/chat_history.py +11 -1
  109. package/scripts/check_always_budget.py +46 -2
  110. package/scripts/check_one_off_location.py +81 -0
  111. package/scripts/check_references.py +6 -0
  112. package/scripts/compress.py +5 -2
  113. package/scripts/context_hygiene_hook.py +181 -0
  114. package/scripts/council_cli.py +357 -0
  115. package/scripts/hook_manifest.yaml +184 -0
  116. package/scripts/hooks/__init__.py +1 -0
  117. package/scripts/hooks/augment-context-hygiene.sh +55 -0
  118. package/scripts/hooks/augment-dispatcher.sh +72 -0
  119. package/scripts/hooks/augment-onboarding-gate.sh +55 -0
  120. package/scripts/hooks/cline-dispatcher.sh +86 -0
  121. package/scripts/hooks/cursor-dispatcher.sh +76 -0
  122. package/scripts/hooks/dispatch_hook.py +348 -0
  123. package/scripts/hooks/envelope.py +98 -0
  124. package/scripts/hooks/gemini-dispatcher.sh +117 -0
  125. package/scripts/hooks/state_io.py +122 -0
  126. package/scripts/hooks/windsurf-dispatcher.sh +123 -0
  127. package/scripts/hooks_status.py +146 -0
  128. package/scripts/install.py +728 -51
  129. package/scripts/install.sh +1 -1
  130. package/scripts/lint_examples.py +98 -0
  131. package/scripts/lint_hook_manifest.py +216 -0
  132. package/scripts/lint_one_off_age.py +184 -0
  133. package/scripts/lint_roadmap_complexity.py +127 -0
  134. package/scripts/lint_rule_tiers.py +78 -0
  135. package/scripts/lint_showcase_sessions.py +148 -0
  136. package/scripts/minimal_safe_diff_hook.py +245 -0
  137. package/scripts/onboarding_gate_hook.py +142 -0
  138. package/scripts/readme_linter.py +12 -3
  139. package/scripts/roadmap_progress_hook.py +5 -0
  140. package/scripts/schemas/rule.schema.json +5 -0
  141. package/scripts/sync_agent_settings.py +32 -129
  142. package/scripts/sync_yaml_rt.py +734 -0
  143. package/scripts/verify_before_complete_hook.py +216 -0
  144. /package/scripts/ai_council/{_one_off_2a4_acceptance.py → one_off_archive/2026-05/_one_off_2a4_acceptance.py} +0 -0
  145. /package/scripts/ai_council/{_one_off_context_layer_v1_estimate.py → one_off_archive/2026-05/_one_off_context_layer_v1_estimate.py} +0 -0
  146. /package/scripts/ai_council/{_one_off_context_layer_v1_review.py → one_off_archive/2026-05/_one_off_context_layer_v1_review.py} +0 -0
  147. /package/scripts/ai_council/{_one_off_followups_review.py → one_off_archive/2026-05/_one_off_followups_review.py} +0 -0
  148. /package/scripts/ai_council/{_one_off_nondestructive_inline_audit.py → one_off_archive/2026-05/_one_off_nondestructive_inline_audit.py} +0 -0
  149. /package/scripts/{_one_off_phase4_dispatch_latency.py → ai_council/one_off_archive/2026-05/_one_off_phase4_dispatch_latency.py} +0 -0
  150. /package/scripts/{_one_off_phase6_trigger_jaccard.py → ai_council/one_off_archive/2026-05/_one_off_phase6_trigger_jaccard.py} +0 -0
  151. /package/scripts/ai_council/{_one_off_phase_2a_budget_rebalance.py → one_off_archive/2026-05/_one_off_phase_2a_budget_rebalance.py} +0 -0
  152. /package/scripts/ai_council/{_one_off_phase_2a_post_revert.py → one_off_archive/2026-05/_one_off_phase_2a_post_revert.py} +0 -0
  153. /package/scripts/ai_council/{_one_off_rebalancing_audit.py → one_off_archive/2026-05/_one_off_rebalancing_audit.py} +0 -0
  154. /package/scripts/ai_council/{_one_off_rule_hardening_v1.py → one_off_archive/2026-05/_one_off_rule_hardening_v1.py} +0 -0
  155. /package/scripts/ai_council/{_one_off_structural_open_questions.py → one_off_archive/2026-05/_one_off_structural_open_questions.py} +0 -0
  156. /package/scripts/ai_council/{_one_off_structural_optimization.py → one_off_archive/2026-05/_one_off_structural_optimization.py} +0 -0
  157. /package/scripts/ai_council/{_one_off_structural_v3_gaps.py → one_off_archive/2026-05/_one_off_structural_v3_gaps.py} +0 -0
  158. /package/scripts/ai_council/{_one_off_structural_v3_review.py → one_off_archive/2026-05/_one_off_structural_v3_review.py} +0 -0
@@ -1,15 +1,20 @@
1
1
  #!/usr/bin/env python3
2
- """Sync `.agent-settings.yml` against the template + profile.
2
+ """Sync `.agent-settings.yml` against the template + profile (additive merge).
3
3
 
4
4
  Applies the section-aware merge rules documented in
5
5
  `docs/guidelines/agent-infra/layered-settings.md`:
6
6
 
7
- - Template section order always winsreorder keys to match.
8
- - Existing user scalar values are preserved verbatim (as parsed).
9
- - Missing keys land with their template / profile default.
10
- - Template comments replace user comments in the same position.
11
- - Unknown user keys (not in the template) are preserved in a trailing
12
- `_user:` block so custom additions never get silently dropped.
7
+ - **User lines are preserved verbatim**comments, quoting, and key order
8
+ survive every sync. Existing values, custom inline comments, and
9
+ user-chosen ordering are never modified.
10
+ - Missing template keys are inserted (leaf into existing parent section,
11
+ full subtree at EOF for entirely missing top-level sections).
12
+ - Top-level user-only sections (no home in the template) are moved to a
13
+ single-level `_user:` block at the end of the file.
14
+ - The `_user:` block is single-level only — legacy multi-prefix
15
+ corruption (`_user._user.foo`) heals to `foo` on the next sync.
16
+ - Template comment changes on already-existing user keys do **not**
17
+ propagate (existing line untouched is the deal).
13
18
 
14
19
  Idempotent — writing a file that is already in sync is a no-op.
15
20
 
@@ -33,7 +38,8 @@ import sys
33
38
  from pathlib import Path
34
39
 
35
40
  sys.path.insert(0, str(Path(__file__).resolve().parent))
36
- import install as _install # noqa: E402 — shares _yaml_scalar + _replace_template_value
41
+ import install as _install # noqa: E402 — profile parsing + template rendering
42
+ import sync_yaml_rt as _rt # noqa: E402 — additive round-trip merge
37
43
 
38
44
  try:
39
45
  import yaml # type: ignore
@@ -46,126 +52,6 @@ DEFAULT_TEMPLATE = Path(__file__).resolve().parent.parent / "config" / "agent-se
46
52
  DEFAULT_PROFILE_DIR = Path(__file__).resolve().parent.parent / "config" / "profiles"
47
53
 
48
54
 
49
- def _flatten(data: dict, prefix: str = "") -> dict[str, object]:
50
- """Flatten nested dicts to dotted keys — recurses to all leaves.
51
-
52
- Lists, scalars, and ``None`` are leaves. Dicts are walked and their
53
- keys folded into the dotted path.
54
- """
55
- out: dict[str, object] = {}
56
- for key, value in data.items():
57
- path = f"{prefix}{key}"
58
- if isinstance(value, dict):
59
- out.update(_flatten(value, prefix=f"{path}."))
60
- else:
61
- out[path] = value
62
- return out
63
-
64
-
65
- def _as_yaml_value(value: object) -> str | None:
66
- """Format *value* as an inline-YAML literal.
67
-
68
- Returns ``None`` when the value cannot be safely represented as a
69
- scalar / flow-style sequence (e.g. unsupported types). Callers
70
- must skip those keys so the template default sticks instead of
71
- producing malformed YAML.
72
- """
73
- if isinstance(value, bool):
74
- return "true" if value else "false"
75
- if isinstance(value, int):
76
- return str(value)
77
- if isinstance(value, float):
78
- return repr(value)
79
- if value is None:
80
- return "~"
81
- if isinstance(value, list):
82
- items: list[str] = []
83
- for item in value:
84
- rendered = _as_yaml_value(item)
85
- if rendered is None:
86
- return None
87
- items.append(rendered)
88
- return "[" + ", ".join(items) + "]"
89
- if isinstance(value, str):
90
- return _install._yaml_scalar(value)
91
- return None
92
-
93
-
94
- def _template_keys(template_body: str) -> set[str]:
95
- """Return the set of dotted keys declared by the rendered template."""
96
- data = yaml.safe_load(template_body) or {}
97
- if not isinstance(data, dict):
98
- return set()
99
- return set(_flatten(data).keys())
100
-
101
-
102
- def _apply_user_values(template_body: str, user_flat: dict[str, object]) -> str:
103
- """Overlay every known user value on the rendered template body.
104
-
105
- Keys whose value cannot be rendered inline (see :func:`_as_yaml_value`)
106
- are skipped so the template default survives instead of corrupting
107
- the file.
108
- """
109
- body = template_body
110
- for dotted, value in user_flat.items():
111
- rendered = _as_yaml_value(value)
112
- if rendered is None:
113
- continue
114
- body = _install._replace_template_value_raw(body, dotted, rendered)
115
- return body
116
-
117
-
118
- def _append_unknown(body: str, user_flat: dict[str, object], known: set[str]) -> str:
119
- """Emit user keys that have no home in the template under `_user:`."""
120
- unknown = sorted(k for k in user_flat if k not in known)
121
- if not unknown:
122
- return body
123
- lines = [
124
- "",
125
- "# Unknown keys preserved by sync_agent_settings.py — review and move",
126
- "# them into the template or drop them.",
127
- "_user:",
128
- ]
129
- for key in unknown:
130
- rendered = _as_yaml_value(user_flat[key])
131
- if rendered is None:
132
- continue
133
- lines.append(f" {key}: {rendered}")
134
- suffix = "\n".join(lines) + "\n"
135
- return body + (suffix if body.endswith("\n") else "\n" + suffix)
136
-
137
-
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
-
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 = {}
164
- known = _template_keys(template_body)
165
- body = _apply_user_values(template_body, user_flat)
166
- return _append_unknown(body, user_flat, known)
167
-
168
-
169
55
  def load_profile(profile_dir: Path, profile: str) -> dict[str, str]:
170
56
  profile_source = profile_dir / f"{profile}.ini"
171
57
  if not profile_source.is_file():
@@ -231,10 +117,27 @@ def main(argv: list[str] | None = None) -> int:
231
117
  except FileNotFoundError as exc:
232
118
  print(f"error: {exc}", file=sys.stderr)
233
119
  return 2
120
+ except yaml.YAMLError as exc:
121
+ print(f"error: cannot parse {target}: {exc}", file=sys.stderr)
122
+ return 2
234
123
 
235
- new_text = render_target(template_body, user_data)
236
124
  existing_text = target.read_text(encoding="utf-8") if target.is_file() else ""
237
125
 
126
+ if existing_text:
127
+ # Additive merge — preserves user lines verbatim, inserts only
128
+ # the template keys the user is missing.
129
+ try:
130
+ new_text = _rt.sync(existing_text, template_body)
131
+ except ValueError as exc:
132
+ print(
133
+ f"error: cannot parse {target}: {exc}",
134
+ file=sys.stderr,
135
+ )
136
+ return 2
137
+ else:
138
+ # First-run / file absent — write the rendered template as-is.
139
+ new_text = template_body
140
+
238
141
  if new_text == existing_text:
239
142
  if not args.quiet:
240
143
  print(f"✅ {target}: already in sync (profile={profile})")