@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.
- package/.agent-src/commands/council/default.md +74 -76
- package/.agent-src/commands/feature/roadmap.md +22 -0
- package/.agent-src/commands/roadmap/create.md +38 -6
- package/.agent-src/commands/roadmap/execute.md +36 -9
- package/.agent-src/rules/agent-authority.md +1 -0
- package/.agent-src/rules/agent-docs.md +1 -0
- package/.agent-src/rules/analysis-skill-routing.md +1 -0
- package/.agent-src/rules/architecture.md +1 -0
- package/.agent-src/rules/artifact-drafting-protocol.md +1 -0
- package/.agent-src/rules/artifact-engagement-recording.md +1 -0
- package/.agent-src/rules/ask-when-uncertain.md +1 -0
- package/.agent-src/rules/augment-portability.md +1 -0
- package/.agent-src/rules/augment-source-of-truth.md +1 -0
- package/.agent-src/rules/autonomous-execution.md +1 -0
- package/.agent-src/rules/capture-learnings.md +1 -0
- package/.agent-src/rules/chat-history-cadence.md +34 -0
- package/.agent-src/rules/chat-history-ownership.md +1 -0
- package/.agent-src/rules/chat-history-visibility.md +1 -0
- package/.agent-src/rules/cli-output-handling.md +2 -2
- package/.agent-src/rules/command-suggestion-policy.md +1 -0
- package/.agent-src/rules/commit-conventions.md +1 -0
- package/.agent-src/rules/commit-policy.md +1 -0
- package/.agent-src/rules/context-hygiene.md +28 -0
- package/.agent-src/rules/direct-answers.md +18 -26
- package/.agent-src/rules/docker-commands.md +1 -0
- package/.agent-src/rules/docs-sync.md +1 -0
- package/.agent-src/rules/downstream-changes.md +1 -0
- package/.agent-src/rules/e2e-testing.md +1 -0
- package/.agent-src/rules/guidelines.md +1 -0
- package/.agent-src/rules/improve-before-implement.md +1 -0
- package/.agent-src/rules/language-and-tone.md +1 -0
- package/.agent-src/rules/laravel-translations.md +1 -0
- package/.agent-src/rules/markdown-safe-codeblocks.md +1 -0
- package/.agent-src/rules/minimal-safe-diff.md +1 -0
- package/.agent-src/rules/missing-tool-handling.md +1 -0
- package/.agent-src/rules/model-recommendation.md +1 -0
- package/.agent-src/rules/no-cheap-questions.md +15 -21
- package/.agent-src/rules/no-roadmap-references.md +1 -0
- package/.agent-src/rules/non-destructive-by-default.md +1 -0
- package/.agent-src/rules/onboarding-gate.md +33 -0
- package/.agent-src/rules/package-ci-checks.md +1 -0
- package/.agent-src/rules/php-coding.md +1 -0
- package/.agent-src/rules/preservation-guard.md +1 -0
- package/.agent-src/rules/review-routing-awareness.md +1 -0
- package/.agent-src/rules/reviewer-awareness.md +1 -0
- package/.agent-src/rules/roadmap-progress-sync.md +49 -0
- package/.agent-src/rules/role-mode-adherence.md +2 -2
- package/.agent-src/rules/rule-type-governance.md +29 -0
- package/.agent-src/rules/runtime-safety.md +1 -0
- package/.agent-src/rules/scope-control.md +1 -0
- package/.agent-src/rules/security-sensitive-stop.md +1 -0
- package/.agent-src/rules/size-enforcement.md +1 -0
- package/.agent-src/rules/skill-improvement-trigger.md +1 -0
- package/.agent-src/rules/skill-quality.md +1 -0
- package/.agent-src/rules/slash-command-routing-policy.md +39 -0
- package/.agent-src/rules/think-before-action.md +1 -0
- package/.agent-src/rules/token-efficiency.md +1 -0
- package/.agent-src/rules/tool-safety.md +1 -0
- package/.agent-src/rules/ui-audit-gate.md +1 -0
- package/.agent-src/rules/upstream-proposal.md +1 -0
- package/.agent-src/rules/user-interaction.md +1 -0
- package/.agent-src/rules/verify-before-complete.md +1 -0
- package/.agent-src/skills/roadmap-management/SKILL.md +29 -4
- package/.agent-src/skills/verify-completion-evidence/SKILL.md +8 -1
- package/.agent-src/templates/agent-settings.md +16 -0
- package/.agent-src/templates/roadmaps.md +12 -3
- package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +9 -0
- package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +4 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +4 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.py +163 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +111 -0
- package/.agent-src/templates/scripts/work_engine/hooks/settings.py +36 -0
- package/.agent-src/templates/scripts/work_engine/scoring/decision_trace.py +141 -0
- package/.agent-src/templates/scripts/work_engine/scoring/memory_visibility.py +125 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +97 -0
- package/README.md +20 -20
- package/config/agent-settings.template.yml +23 -0
- package/docs/architecture.md +1 -1
- package/docs/catalog.md +5 -2
- package/docs/contracts/adr-settings-sync-engine.md +127 -0
- package/docs/contracts/decision-trace-v1.md +146 -0
- package/docs/contracts/file-ownership-matrix.json +7 -0
- package/docs/contracts/hook-architecture-v1.md +213 -0
- package/docs/contracts/load-context-budget-model.md +80 -0
- package/docs/contracts/load-context-schema.md +20 -0
- package/docs/contracts/memory-visibility-v1.md +138 -0
- package/docs/contracts/one-off-script-lifecycle.md +109 -0
- package/docs/contracts/roadmap-complexity-standard.md +137 -0
- package/docs/contracts/rule-interactions.yml +22 -0
- package/docs/customization.md +1 -0
- package/docs/development.md +4 -1
- package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +134 -0
- package/docs/guidelines/agent-infra/direct-answers-demos.md +145 -0
- package/docs/guidelines/agent-infra/layered-settings.md +32 -13
- package/docs/guidelines/agent-infra/verify-before-complete-demos.md +128 -0
- package/package.json +1 -1
- package/scripts/agent-config +64 -0
- package/scripts/ai_council/bundler.py +3 -3
- package/scripts/ai_council/clients.py +24 -8
- package/scripts/ai_council/one_off_archive/2026-05/README.md +67 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_budget_v2_audit.py +206 -0
- package/scripts/ai_council/{_one_off_roundtrip.py → one_off_archive/2026-05/_one_off_roundtrip.py} +13 -8
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +180 -0
- package/scripts/ai_council/session.py +92 -0
- package/scripts/build_rule_trigger_matrix.py +360 -0
- package/scripts/capture_showcase_session.py +361 -0
- package/scripts/chat_history.py +11 -1
- package/scripts/check_always_budget.py +46 -2
- package/scripts/check_one_off_location.py +81 -0
- package/scripts/check_references.py +6 -0
- package/scripts/compress.py +5 -2
- package/scripts/context_hygiene_hook.py +181 -0
- package/scripts/council_cli.py +357 -0
- package/scripts/hook_manifest.yaml +184 -0
- package/scripts/hooks/__init__.py +1 -0
- package/scripts/hooks/augment-context-hygiene.sh +55 -0
- package/scripts/hooks/augment-dispatcher.sh +72 -0
- package/scripts/hooks/augment-onboarding-gate.sh +55 -0
- package/scripts/hooks/cline-dispatcher.sh +86 -0
- package/scripts/hooks/cursor-dispatcher.sh +76 -0
- package/scripts/hooks/dispatch_hook.py +348 -0
- package/scripts/hooks/envelope.py +98 -0
- package/scripts/hooks/gemini-dispatcher.sh +117 -0
- package/scripts/hooks/state_io.py +122 -0
- package/scripts/hooks/windsurf-dispatcher.sh +123 -0
- package/scripts/hooks_status.py +146 -0
- package/scripts/install.py +728 -51
- package/scripts/install.sh +1 -1
- package/scripts/lint_examples.py +98 -0
- package/scripts/lint_hook_manifest.py +216 -0
- package/scripts/lint_one_off_age.py +184 -0
- package/scripts/lint_roadmap_complexity.py +127 -0
- package/scripts/lint_rule_tiers.py +78 -0
- package/scripts/lint_showcase_sessions.py +148 -0
- package/scripts/minimal_safe_diff_hook.py +245 -0
- package/scripts/onboarding_gate_hook.py +142 -0
- package/scripts/readme_linter.py +12 -3
- package/scripts/roadmap_progress_hook.py +5 -0
- package/scripts/schemas/rule.schema.json +5 -0
- package/scripts/sync_agent_settings.py +32 -129
- package/scripts/sync_yaml_rt.py +734 -0
- package/scripts/verify_before_complete_hook.py +216 -0
- /package/scripts/ai_council/{_one_off_2a4_acceptance.py → one_off_archive/2026-05/_one_off_2a4_acceptance.py} +0 -0
- /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
- /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
- /package/scripts/ai_council/{_one_off_followups_review.py → one_off_archive/2026-05/_one_off_followups_review.py} +0 -0
- /package/scripts/ai_council/{_one_off_nondestructive_inline_audit.py → one_off_archive/2026-05/_one_off_nondestructive_inline_audit.py} +0 -0
- /package/scripts/{_one_off_phase4_dispatch_latency.py → ai_council/one_off_archive/2026-05/_one_off_phase4_dispatch_latency.py} +0 -0
- /package/scripts/{_one_off_phase6_trigger_jaccard.py → ai_council/one_off_archive/2026-05/_one_off_phase6_trigger_jaccard.py} +0 -0
- /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
- /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
- /package/scripts/ai_council/{_one_off_rebalancing_audit.py → one_off_archive/2026-05/_one_off_rebalancing_audit.py} +0 -0
- /package/scripts/ai_council/{_one_off_rule_hardening_v1.py → one_off_archive/2026-05/_one_off_rule_hardening_v1.py} +0 -0
- /package/scripts/ai_council/{_one_off_structural_open_questions.py → one_off_archive/2026-05/_one_off_structural_open_questions.py} +0 -0
- /package/scripts/ai_council/{_one_off_structural_optimization.py → one_off_archive/2026-05/_one_off_structural_optimization.py} +0 -0
- /package/scripts/ai_council/{_one_off_structural_v3_gaps.py → one_off_archive/2026-05/_one_off_structural_v3_gaps.py} +0 -0
- /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
|
-
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
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 —
|
|
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})")
|