@event4u/agent-config 1.41.1 → 2.0.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/templates/agents/agent-project-settings.example.yml +14 -0
- package/.agent-src/templates/scripts/work_engine/_lib/agent_settings.py +120 -11
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +31 -0
- package/README.md +34 -25
- package/config/agent-settings.template.yml +25 -0
- package/docs/architecture.md +46 -0
- package/docs/customization.md +125 -9
- package/docs/installation.md +9 -36
- package/docs/migration/v1-to-v2.md +98 -0
- package/docs/setup/per-ide/claude-code.md +0 -17
- package/docs/setup/per-ide/claude-desktop.md +35 -48
- package/docs/setup/per-ide/windsurf.md +0 -11
- package/package.json +1 -6
- package/scripts/_cli/__init__.py +0 -0
- package/scripts/_cli/cmd_migrate.py +270 -0
- package/scripts/_cli/cmd_update.py +226 -0
- package/scripts/_lib/agent_settings.py +120 -11
- package/scripts/_lib/agents_overlay.py +109 -0
- package/scripts/_lib/pin_resolver.py +152 -0
- package/scripts/_lib/update_check.py +183 -0
- package/scripts/agent-config +73 -1
- package/scripts/check_overlay_cascade_subdirs.py +125 -0
- package/scripts/check_template_pin_drift.py +112 -0
- package/scripts/check_update_banner.py +86 -0
- package/scripts/install +2 -37
- package/scripts/install.py +6 -207
- package/bin/install.php +0 -45
- package/composer.json +0 -33
- package/scripts/postinstall.sh +0 -76
- package/templates/global-install-manifest.yml +0 -91
|
@@ -26,6 +26,20 @@
|
|
|
26
26
|
|
|
27
27
|
schema_version: 1
|
|
28
28
|
|
|
29
|
+
# --- Agent config version pin (project-shared) ---
|
|
30
|
+
#
|
|
31
|
+
# Exact semver of @event4u/agent-config this project commits to. The
|
|
32
|
+
# `npx @event4u/agent-config` dispatcher re-execs at this version when
|
|
33
|
+
# the developer's locally-resolved version differs — so every
|
|
34
|
+
# contributor runs the same agent surface regardless of their npm
|
|
35
|
+
# cache state. Bump this together with a tested upgrade. Empty string
|
|
36
|
+
# disables the pin (resolver picks the latest release; only safe for
|
|
37
|
+
# greenfield projects). Ranges (^, ~, >=) are NOT supported.
|
|
38
|
+
# CI guard: a release bump of `package.json` must update this value
|
|
39
|
+
# in lockstep — see scripts/check_template_pin_drift.py (road-to-
|
|
40
|
+
# portable-runtime-and-update-check P3.3).
|
|
41
|
+
agent_config_version: "1.41.2"
|
|
42
|
+
|
|
29
43
|
# --- Project identity ---
|
|
30
44
|
project:
|
|
31
45
|
# Short slug used in agent output, prompts, and PR metadata. Keep it
|
|
@@ -3,17 +3,24 @@
|
|
|
3
3
|
Phase 1 of road-to-portable-dev-preferences. Single source of truth for
|
|
4
4
|
how scripts read agent settings — replaces ~15 ad-hoc loaders in P3.
|
|
5
5
|
|
|
6
|
-
Resolution order (
|
|
7
|
-
keys only):
|
|
6
|
+
Resolution order (deepest wins; user-global is whitelist-filtered only):
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
N. ``~/.config/agent-config/agent-settings.yml`` (user-global; whitelist only)
|
|
9
|
+
N-1. ``<repo-root>/.agent-settings.yml`` (project-wide; all keys)
|
|
10
|
+
N-2. ``<intermediate-dir>/.agent-settings.yml`` (subsystem-scoped; all keys)
|
|
11
|
+
1. ``<CWD>/.agent-settings.yml`` (deepest, wins; all keys)
|
|
12
|
+
|
|
13
|
+
``<repo-root>`` is the nearest ancestor that contains ``.git`` (directory
|
|
14
|
+
**or** file — submodule support). The walk stops there — it never drifts
|
|
15
|
+
into a parent repo or ``$HOME``. When ``cwd`` is ``None`` (default), the
|
|
16
|
+
loader behaves identically to the pre-cascade contract: project file +
|
|
17
|
+
user-global only, no ancestor walk. Back-compat is hard.
|
|
12
18
|
|
|
13
19
|
Whitelisted keys (``MERGEABLE_KEYS``) are exact dotted paths. A
|
|
14
20
|
non-whitelisted key in the user-global file is silently ignored — the
|
|
15
21
|
``verbose=True`` flag surfaces ignored paths via ``logging.info`` for
|
|
16
|
-
debugging.
|
|
22
|
+
debugging. Non-root in-project layers (intermediate + CWD) are **not**
|
|
23
|
+
whitelist-filtered — they live inside the project boundary.
|
|
17
24
|
|
|
18
25
|
Contract — pure, read-only, tolerant:
|
|
19
26
|
|
|
@@ -28,7 +35,7 @@ from __future__ import annotations
|
|
|
28
35
|
|
|
29
36
|
import logging
|
|
30
37
|
from pathlib import Path
|
|
31
|
-
from typing import Any
|
|
38
|
+
from typing import Any, Iterator
|
|
32
39
|
|
|
33
40
|
logger = logging.getLogger(__name__)
|
|
34
41
|
|
|
@@ -53,10 +60,70 @@ MERGEABLE_KEYS: tuple[str, ...] = (
|
|
|
53
60
|
_DEFAULTS: dict[str, Any] = {}
|
|
54
61
|
|
|
55
62
|
|
|
63
|
+
def find_project_root(start: Path) -> Path | None:
|
|
64
|
+
"""Walk up from ``start`` looking for ``.git`` (file or directory).
|
|
65
|
+
|
|
66
|
+
Returns the first ancestor that contains ``.git`` as a file (submodule
|
|
67
|
+
pointer) or directory (regular checkout), or ``None`` if the walk
|
|
68
|
+
reaches the filesystem root without finding one. The walk stops at
|
|
69
|
+
the project boundary — it never drifts into a parent repo or
|
|
70
|
+
``$HOME``.
|
|
71
|
+
|
|
72
|
+
Pure read-only; never touches the filesystem beyond ``exists()``
|
|
73
|
+
probes on the ``.git`` entry.
|
|
74
|
+
"""
|
|
75
|
+
current = start.resolve() if start.exists() else start
|
|
76
|
+
# ``Path.parents`` excludes ``current`` itself, so probe it first.
|
|
77
|
+
for candidate in [current, *current.parents]:
|
|
78
|
+
git_marker = candidate / ".git"
|
|
79
|
+
if git_marker.exists():
|
|
80
|
+
return candidate
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _resolve_cascade_paths(
|
|
85
|
+
cwd: Path | None,
|
|
86
|
+
project_path: Path | str | None,
|
|
87
|
+
) -> list[Path]:
|
|
88
|
+
"""Return the ordered cascade of in-project settings files (shallow → deep).
|
|
89
|
+
|
|
90
|
+
When ``cwd`` is provided and ``find_project_root(cwd)`` succeeds, the
|
|
91
|
+
list contains every ``<dir>/.agent-settings.yml`` from the repo root
|
|
92
|
+
down to ``cwd`` (inclusive on both ends), shallowest first. When
|
|
93
|
+
``cwd`` is ``None`` or no ``.git`` is reached, falls back to the
|
|
94
|
+
single legacy project path — back-compat with the pre-cascade
|
|
95
|
+
loader.
|
|
96
|
+
"""
|
|
97
|
+
if cwd is None:
|
|
98
|
+
legacy = Path(project_path) if project_path else Path(DEFAULT_PROJECT_FILE)
|
|
99
|
+
return [legacy]
|
|
100
|
+
|
|
101
|
+
root = find_project_root(cwd)
|
|
102
|
+
if root is None:
|
|
103
|
+
legacy = Path(project_path) if project_path else Path(DEFAULT_PROJECT_FILE)
|
|
104
|
+
return [legacy]
|
|
105
|
+
|
|
106
|
+
cwd_resolved = cwd.resolve()
|
|
107
|
+
# Build the chain root → … → cwd (shallowest first, deepest last).
|
|
108
|
+
chain: list[Path] = []
|
|
109
|
+
cursor = cwd_resolved
|
|
110
|
+
while True:
|
|
111
|
+
chain.append(cursor)
|
|
112
|
+
if cursor == root:
|
|
113
|
+
break
|
|
114
|
+
parent = cursor.parent
|
|
115
|
+
if parent == cursor:
|
|
116
|
+
break
|
|
117
|
+
cursor = parent
|
|
118
|
+
chain.reverse()
|
|
119
|
+
return [d / DEFAULT_PROJECT_FILE for d in chain]
|
|
120
|
+
|
|
121
|
+
|
|
56
122
|
def load_agent_settings(
|
|
57
123
|
project_path: Path | str | None = None,
|
|
58
124
|
user_global_path: Path | str | None = None,
|
|
59
125
|
verbose: bool = False,
|
|
126
|
+
cwd: Path | None = None,
|
|
60
127
|
) -> dict[str, Any]:
|
|
61
128
|
"""Return the merged settings dict.
|
|
62
129
|
|
|
@@ -65,10 +132,16 @@ def load_agent_settings(
|
|
|
65
132
|
``~/.config/agent-config/agent-settings.yml``. Both arguments accept
|
|
66
133
|
``Path`` or ``str``. Pass ``verbose=True`` to log keys present in
|
|
67
134
|
user-global that are not on the whitelist.
|
|
135
|
+
|
|
136
|
+
``cwd`` enables the in-project cascade: when provided **and**
|
|
137
|
+
``find_project_root(cwd)`` reaches a ``.git`` ancestor, the loader
|
|
138
|
+
walks every ``.agent-settings.yml`` from the repo root down to
|
|
139
|
+
``cwd`` and merges them shallowest → deepest (deepest wins).
|
|
140
|
+
Non-root layers are **not** whitelist-filtered (they live inside the
|
|
141
|
+
project boundary). When ``cwd`` is ``None`` (default), the loader
|
|
142
|
+
falls back to the single ``project_path`` behaviour — back-compat
|
|
143
|
+
with pre-cascade callers.
|
|
68
144
|
"""
|
|
69
|
-
project = _read_yaml(
|
|
70
|
-
Path(project_path) if project_path else Path(DEFAULT_PROJECT_FILE),
|
|
71
|
-
) or {}
|
|
72
145
|
user_global_raw = _read_yaml(
|
|
73
146
|
Path(user_global_path) if user_global_path else DEFAULT_USER_GLOBAL_FILE,
|
|
74
147
|
) or {}
|
|
@@ -82,12 +155,48 @@ def load_agent_settings(
|
|
|
82
155
|
sorted(ignored),
|
|
83
156
|
)
|
|
84
157
|
|
|
158
|
+
cascade = _resolve_cascade_paths(cwd, project_path)
|
|
159
|
+
|
|
85
160
|
merged: dict[str, Any] = _deep_copy_defaults(_DEFAULTS)
|
|
86
161
|
_deep_merge(merged, user_global_filtered)
|
|
87
|
-
|
|
162
|
+
for path in cascade:
|
|
163
|
+
layer = _read_yaml(path) or {}
|
|
164
|
+
if layer:
|
|
165
|
+
_deep_merge(merged, layer)
|
|
88
166
|
return merged
|
|
89
167
|
|
|
90
168
|
|
|
169
|
+
def iter_setting_overrides(
|
|
170
|
+
project_path: Path | str | None = None,
|
|
171
|
+
user_global_path: Path | str | None = None,
|
|
172
|
+
cwd: Path | None = None,
|
|
173
|
+
) -> Iterator[tuple[str, Any, Path]]:
|
|
174
|
+
"""Yield ``(dotted_key, value, source_path)`` for every leaf setting.
|
|
175
|
+
|
|
176
|
+
Walks the same cascade as :func:`load_agent_settings` and emits one
|
|
177
|
+
tuple per leaf observed at each layer (user-global → repo-root →
|
|
178
|
+
intermediates → CWD). Callers can detect overrides by grouping
|
|
179
|
+
tuples on ``dotted_key`` — the deepest tuple per group wins. Useful
|
|
180
|
+
for ``task settings:trace`` and other banner-only diagnostics.
|
|
181
|
+
Never blocks, never raises on missing files.
|
|
182
|
+
"""
|
|
183
|
+
user_global_path_resolved = (
|
|
184
|
+
Path(user_global_path) if user_global_path else DEFAULT_USER_GLOBAL_FILE
|
|
185
|
+
)
|
|
186
|
+
user_global_raw = _read_yaml(user_global_path_resolved) or {}
|
|
187
|
+
user_global_filtered, _ = _filter_whitelist(user_global_raw, MERGEABLE_KEYS)
|
|
188
|
+
if user_global_filtered:
|
|
189
|
+
for key in _leaf_paths(user_global_filtered):
|
|
190
|
+
yield key, _get_dotted(user_global_filtered, key), user_global_path_resolved
|
|
191
|
+
|
|
192
|
+
for path in _resolve_cascade_paths(cwd, project_path):
|
|
193
|
+
layer = _read_yaml(path)
|
|
194
|
+
if not layer:
|
|
195
|
+
continue
|
|
196
|
+
for key in _leaf_paths(layer):
|
|
197
|
+
yield key, _get_dotted(layer, key), path
|
|
198
|
+
|
|
199
|
+
|
|
91
200
|
def _read_yaml(path: Path) -> dict[str, Any] | None:
|
|
92
201
|
"""Best-effort YAML read; never raises. Returns ``None`` on any failure."""
|
|
93
202
|
if not path.is_file():
|
package/CHANGELOG.md
CHANGED
|
@@ -318,6 +318,37 @@ our recommendation order, not its support status.
|
|
|
318
318
|
users" tension without removing any path that an existing user
|
|
319
319
|
might rely on.
|
|
320
320
|
|
|
321
|
+
## [2.0.0](https://github.com/event4u-app/agent-config/compare/1.41.2...2.0.0) (2026-05-12)
|
|
322
|
+
|
|
323
|
+
### BREAKING CHANGES
|
|
324
|
+
|
|
325
|
+
* **install:** drop composer + npm postinstall, go npx-only ([6bc6c99](https://github.com/event4u-app/agent-config/commit/6bc6c99f57d2a2bcdab03bfacab429ce146dd9b9))
|
|
326
|
+
|
|
327
|
+
### Features
|
|
328
|
+
|
|
329
|
+
* **cli:** add update + migrate commands, version-pin resolver, CI drift guard ([b1e34fc](https://github.com/event4u-app/agent-config/commit/b1e34fcd7a03d38266e40dd053414fc9d20c9024))
|
|
330
|
+
* **update-check:** add daily npm-registry version probe + banner ([2c4d752](https://github.com/event4u-app/agent-config/commit/2c4d752d5eb28f1c460470668704f5cf403c4046))
|
|
331
|
+
* **settings:** add hierarchical project-settings cascade + agents overlay ([3296885](https://github.com/event4u-app/agent-config/commit/32968855ca17210b1594cea2e79778acb0476f0a))
|
|
332
|
+
|
|
333
|
+
### Tests
|
|
334
|
+
|
|
335
|
+
* **install:** drop postinstall.sh test cases removed in P0 ([d4e7750](https://github.com/event4u-app/agent-config/commit/d4e7750bcd13b021caa4ccaccff821dc0ae8e537))
|
|
336
|
+
|
|
337
|
+
### Chores
|
|
338
|
+
|
|
339
|
+
* **template:** mirror agent_settings cascade into work_engine template ([92f4716](https://github.com/event4u-app/agent-config/commit/92f471662c463efec0df591099a3507919396d77))
|
|
340
|
+
* **roadmap:** archive completed portable-runtime-and-update-check ([91e2e4e](https://github.com/event4u-app/agent-config/commit/91e2e4ef75042d2d19a528874ebcac4b94c4046a))
|
|
341
|
+
|
|
342
|
+
Tests: 3257 (+84 since 1.41.2)
|
|
343
|
+
|
|
344
|
+
## [1.41.2](https://github.com/event4u-app/agent-config/compare/1.41.1...1.41.2) (2026-05-12)
|
|
345
|
+
|
|
346
|
+
### Documentation
|
|
347
|
+
|
|
348
|
+
* **roadmap:** invert portable-runtime to npx-only distribution ([f8f6594](https://github.com/event4u-app/agent-config/commit/f8f65944490f69528616d21f4aa008c7c3d0bfa9))
|
|
349
|
+
|
|
350
|
+
Tests: 3173 (+0 since 1.41.1)
|
|
351
|
+
|
|
321
352
|
## [1.41.1](https://github.com/event4u-app/agent-config/compare/1.41.0...1.41.1) (2026-05-12)
|
|
322
353
|
|
|
323
354
|
### Documentation
|
package/README.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Agent Config — Governed Agent System
|
|
2
2
|
|
|
3
|
+
> ⚠️ **Breaking change in vX.0** — `agent-config` now ships as an
|
|
4
|
+
> **npx-only runtime**. `composer require` / `npm install --save-dev`
|
|
5
|
+
> are gone, the `--global` symlink scheme is retired. Existing
|
|
6
|
+
> consumers: run `npx @event4u/agent-config migrate`
|
|
7
|
+
> ([guide](docs/migration/v1-to-v2.md)). New consumers: jump to
|
|
8
|
+
> [Quickstart](#quickstart).
|
|
9
|
+
|
|
3
10
|
> **agent-config is not a runtime, but it ships a deterministic orchestration contract / state machine for host agents.**
|
|
4
11
|
|
|
5
12
|
Give your AI agents an audit-disciplined orchestration contract — testing, Git, CI, code review, and **120+ stack-aware skills** — with quality guardrails built in.
|
|
@@ -26,39 +33,40 @@ If none of those apply yet — start with the [Quickstart](#quickstart) and pick
|
|
|
26
33
|
|
|
27
34
|
## Quickstart
|
|
28
35
|
|
|
29
|
-
Two minutes from `
|
|
36
|
+
Two minutes from `npx` to a better-behaved agent — no install, no
|
|
37
|
+
vendored package, no postinstall hook.
|
|
30
38
|
|
|
31
39
|
### For teams (recommended)
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
Run once in the project root — `npx` resolves the runtime against the
|
|
42
|
+
npm registry on every invocation, and the version pin in
|
|
43
|
+
`.agent-settings.yml` keeps it reproducible:
|
|
34
44
|
|
|
35
45
|
```bash
|
|
36
|
-
#
|
|
37
|
-
|
|
46
|
+
# Bootstrap (writes .agent-settings.yml, .augment/, .claude/, …):
|
|
47
|
+
npx @event4u/agent-config init
|
|
38
48
|
|
|
39
|
-
#
|
|
40
|
-
|
|
49
|
+
# Any subsequent command:
|
|
50
|
+
npx @event4u/agent-config <command>
|
|
41
51
|
```
|
|
42
52
|
|
|
43
|
-
|
|
44
|
-
create `.agent-settings.yml`, `.vscode/settings.json`, `.augment/settings.json`,
|
|
45
|
-
and the tool-specific glue:
|
|
53
|
+
The init writes:
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
- `.agent-settings.yml` (including the `agent_config_version` pin)
|
|
56
|
+
- `.vscode/settings.json`, `.augment/settings.json`
|
|
57
|
+
- per-tool glue: `.claude/`, `.cursor/`, `.clinerules/`,
|
|
58
|
+
`.windsurfrules`, `GEMINI.md`, `.github/copilot-instructions.md`
|
|
59
|
+
|
|
60
|
+
→ Migrating from a pre-vX.0 install? See
|
|
61
|
+
[`docs/migration/v1-to-v2.md`](docs/migration/v1-to-v2.md). The one-shot
|
|
62
|
+
`npx @event4u/agent-config migrate` removes the legacy
|
|
63
|
+
`composer.json` entry / `node_modules/@event4u/agent-config`,
|
|
64
|
+
deletes the retired `~/.claude/{rules,skills}/event4u/` namespace if
|
|
65
|
+
present, and writes the new `.agent-settings.yml` shape.
|
|
57
66
|
|
|
58
|
-
**To
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
warns and continues payload-only. Task is needed only for *contributors*
|
|
67
|
+
**To run:** Node ≥ 18 and Python 3 (stdlib only — default on macOS
|
|
68
|
+
12.3+ / major Linux distros). Python missing → orchestrator warns and
|
|
69
|
+
continues payload-only. Task is needed only for *contributors*
|
|
62
70
|
rebuilding compressed content — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
63
71
|
|
|
64
72
|
**Verify hook coverage** after installing — every supported platform
|
|
@@ -410,9 +418,10 @@ kernel set: [`docs/contracts/kernel-membership.md`](docs/contracts/kernel-member
|
|
|
410
418
|
|
|
411
419
|
## Supported Tools
|
|
412
420
|
|
|
413
|
-
### Project-installed (
|
|
421
|
+
### Project-installed (`npx`)
|
|
414
422
|
|
|
415
|
-
Every developer gets the same behavior. No per-user setup needed
|
|
423
|
+
Every developer gets the same behavior. No per-user setup needed —
|
|
424
|
+
`npx @event4u/agent-config init` writes the per-tool glue listed below.
|
|
416
425
|
|
|
417
426
|
| Tool | Rules | Skills | Commands | How it works |
|
|
418
427
|
|---|---|---|---|---|
|
|
@@ -5,6 +5,18 @@
|
|
|
5
5
|
# values here directly or ask the agent (it follows the merge rules in
|
|
6
6
|
# .augment/guidelines/agent-infra/layered-settings.md).
|
|
7
7
|
|
|
8
|
+
# --- Agent config version pin ---
|
|
9
|
+
#
|
|
10
|
+
# Exact semver of the @event4u/agent-config release this project is
|
|
11
|
+
# pinned to. Used by `npx @event4u/agent-config <cmd>` to re-exec at
|
|
12
|
+
# the pinned version when the developer's local resolution differs.
|
|
13
|
+
# Empty string = unpinned (resolver picks the latest release; only safe
|
|
14
|
+
# for greenfield projects). Ranges (^, ~, >=) are NOT supported — the
|
|
15
|
+
# pin replaces lockfile determinism, so it must be exact.
|
|
16
|
+
# See docs/customization.md § "Agent config version pin" and
|
|
17
|
+
# docs/architecture.md § "npx-only distribution + version-pin governance".
|
|
18
|
+
agent_config_version: ""
|
|
19
|
+
|
|
8
20
|
# --- Cost profile ---
|
|
9
21
|
#
|
|
10
22
|
# Master switch that controls which rule tiers load each session.
|
|
@@ -376,3 +388,16 @@ memory:
|
|
|
376
388
|
# or paths outside the repo root that must not land in intake JSONL.
|
|
377
389
|
# Example: ["api[_-]?key", "/Users/[a-z]+/Library"]
|
|
378
390
|
redact_patterns: []
|
|
391
|
+
|
|
392
|
+
# --- Update check ---
|
|
393
|
+
#
|
|
394
|
+
# Daily background check against the npm registry for a newer
|
|
395
|
+
# @event4u/agent-config release. When enabled the dispatcher writes a
|
|
396
|
+
# two-line banner to stderr **after** the subcommand finishes; CI,
|
|
397
|
+
# non-TTY stdout, and `AGENT_CONFIG_NO_UPDATE_CHECK=1` always suppress
|
|
398
|
+
# it regardless of this flag. State is persisted at
|
|
399
|
+
# `~/.config/agent-config/update-check.json` (mode 0600) with a 24 h
|
|
400
|
+
# cadence. See docs/customization.md § "Update check" for the suppression
|
|
401
|
+
# matrix and the road-to-portable-runtime-and-update-check roadmap (P2).
|
|
402
|
+
update_check:
|
|
403
|
+
enabled: true
|
package/docs/architecture.md
CHANGED
|
@@ -70,6 +70,52 @@ them to the relative form when writing into `.agent-src/`. Hardcoding
|
|
|
70
70
|
`.agent-src.uncompressed/` in source frontmatter or body links is
|
|
71
71
|
forbidden and caught by `scripts/check_compressed_paths.py`.
|
|
72
72
|
|
|
73
|
+
### Distribution model — npx-only + version-pin governance
|
|
74
|
+
|
|
75
|
+
Starting with the road-to-portable-runtime cutover, the package ships
|
|
76
|
+
exclusively as a **runtime resolved by `npx @event4u/agent-config`**.
|
|
77
|
+
There is no Composer dependency, no `npm install` step, no `--global`
|
|
78
|
+
symlink scheme. Consumers run:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx @event4u/agent-config init # bootstrap a project
|
|
82
|
+
npx @event4u/agent-config <cmd> # any subsequent command
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Why local installs are gone.** Vendoring the package into every
|
|
86
|
+
consumer's `vendor/` or `node_modules/` created three problems: stale
|
|
87
|
+
runtimes diverging from the published version, build-system coupling
|
|
88
|
+
(Composer post-install hooks, npm postinstall scripts), and a parallel
|
|
89
|
+
"global install" scheme that copied curated skills into `~/.claude/`,
|
|
90
|
+
`~/.cursor/`, `~/.codeium/windsurf/` under an `event4u/` namespace.
|
|
91
|
+
Maintenance cost was high, the abstractions leaked across surfaces, and
|
|
92
|
+
debugging "which version is loaded" was non-trivial. `npx` resolves the
|
|
93
|
+
runtime per invocation against a single npm registry source, which
|
|
94
|
+
collapses all three failure modes.
|
|
95
|
+
|
|
96
|
+
**How the pin replaces lockfile determinism.** A consumer-managed
|
|
97
|
+
`composer.lock` / `package-lock.json` previously froze the runtime
|
|
98
|
+
version per repo. Under npx-only, the equivalent role is played by the
|
|
99
|
+
`agent_config_version` field in the consumer's `.agent-settings.yml`
|
|
100
|
+
(see `config/agent-settings.template.yml`). The dispatcher reads the
|
|
101
|
+
pin on every invocation and re-execs `npx @event4u/agent-config@<pin>`
|
|
102
|
+
if the resolved version diverges (P3.2 pin-resolver). The pin lives in
|
|
103
|
+
the consumer's repo, is reviewed in PRs, and survives `npx`'s own
|
|
104
|
+
cache eviction.
|
|
105
|
+
|
|
106
|
+
**Q1 council rejection + override + pin as substitute.** During Q1
|
|
107
|
+
planning, the architecture council rejected the npx-only proposal on
|
|
108
|
+
the grounds that `npx` resolution introduces a "09:00 vs 09:15
|
|
109
|
+
release skew" window where two developers running the same command
|
|
110
|
+
minutes apart could see different runtimes. The user overrode the
|
|
111
|
+
rejection on the condition that an explicit version pin replaces
|
|
112
|
+
lockfile determinism — the council's concern is real, but the pin
|
|
113
|
+
collapses the skew window to whatever the project's PR cadence is.
|
|
114
|
+
Pin drift across developers becomes a reviewable config change in
|
|
115
|
+
`.agent-settings.yml` rather than an invisible registry-resolution
|
|
116
|
+
race. ADR-pending entry will record the trade-off in full once P3 is
|
|
117
|
+
green.
|
|
118
|
+
|
|
73
119
|
### Cloud-bundle pipeline
|
|
74
120
|
|
|
75
121
|
`task build-cloud-bundles-all` produces one ZIP per skill at
|
package/docs/customization.md
CHANGED
|
@@ -67,30 +67,76 @@ personal.autonomy
|
|
|
67
67
|
caveman.speak_scope
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
**Merge order** (lowest → highest precedence):
|
|
70
|
+
**Merge order** (lowest → highest precedence; every layer optional):
|
|
71
71
|
|
|
72
72
|
```
|
|
73
73
|
1. Package defaults (shipped by event4u/agent-config)
|
|
74
74
|
2. ~/.config/agent-config/agent-settings.yml (user-global · whitelist-filtered)
|
|
75
|
-
3. <
|
|
75
|
+
3. <repo-root>/.agent-settings.yml (project-wide · all keys)
|
|
76
|
+
4. <intermediate-dir>/.agent-settings.yml (subsystem-scoped · all keys · optional)
|
|
77
|
+
5. <CWD>/.agent-settings.yml (deepest · all keys · wins)
|
|
76
78
|
```
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
`<repo-root>` is the nearest ancestor of the CWD that contains a `.git`
|
|
81
|
+
directory **or** file (submodule support). The walk stops there — it
|
|
82
|
+
never drifts into a parent repo or `$HOME`. Callers that omit the
|
|
83
|
+
``cwd`` argument get the legacy two-layer behaviour (user-global +
|
|
84
|
+
single project file) — back-compat is hard.
|
|
85
|
+
|
|
86
|
+
Project-local values **always win** over user-global. The user-global
|
|
87
|
+
file is a fallback, never a lock. Non-whitelisted keys in the
|
|
88
|
+
user-global file are dropped without error — adding `personal.theme`
|
|
89
|
+
there has no effect.
|
|
90
|
+
|
|
91
|
+
**Whitelist asymmetry.** The six-key whitelist applies **only** to the
|
|
92
|
+
user-global layer. Non-root in-project layers (intermediate +
|
|
93
|
+
``<CWD>``) carry arbitrary keys — they live inside the project
|
|
94
|
+
boundary, are tracked in git, and reviewed in PRs like any other
|
|
95
|
+
config. Use a subdirectory `.agent-settings.yml` to scope a single
|
|
96
|
+
field (e.g. a `cost_profile` override for `services/heavy-ml/`) without
|
|
97
|
+
duplicating the root file.
|
|
98
|
+
|
|
99
|
+
The user-global file is created **only on explicit opt-in via
|
|
100
|
+
`/onboard`**. The loader at
|
|
101
|
+
[`scripts/_lib/agent_settings.py`](../scripts/_lib/agent_settings.py)
|
|
85
102
|
is **read-only** — no script can create or mutate it without an
|
|
86
103
|
explicit `/onboard` confirmation. Edit the file by hand for mid-life
|
|
87
104
|
changes; `/sync-agent-settings` stays project-scoped and never touches
|
|
88
105
|
user-global state.
|
|
89
106
|
|
|
107
|
+
### Agent config version pin
|
|
108
|
+
|
|
109
|
+
The top-level `agent_config_version` key pins the project to an exact
|
|
110
|
+
release of `@event4u/agent-config`. Under the npx-only distribution
|
|
111
|
+
model (see [`docs/architecture.md`](architecture.md) §
|
|
112
|
+
*"npx-only distribution + version-pin governance"*), there is no
|
|
113
|
+
local `node_modules/` or `vendor/` lockfile to anchor the runtime —
|
|
114
|
+
the pin is the substitute mechanism.
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
agent_config_version: "2.0.3" # exact semver, no ranges
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Rules:
|
|
121
|
+
|
|
122
|
+
- **Exact semver only.** Ranges (`^2.0`, `~2.0.3`, `>=2.0`) are
|
|
123
|
+
rejected — the pin must be reproducible across the team.
|
|
124
|
+
- **Empty string = unpinned.** The resolver picks the latest release
|
|
125
|
+
on every invocation. Only safe for greenfield projects; production
|
|
126
|
+
consumers should pin.
|
|
127
|
+
- **Owned by the project, not the developer.** Lives in
|
|
128
|
+
`.agent-settings.yml` (committed), reviewed in PRs like any other
|
|
129
|
+
config change. Never merged from `~/.config/agent-config/agent-settings.yml`.
|
|
130
|
+
- **Resolver enforcement.** `npx @event4u/agent-config <cmd>`
|
|
131
|
+
compares the resolved CLI version against the pin; mismatch
|
|
132
|
+
triggers a re-exec at the pinned version
|
|
133
|
+
(`npx @event4u/agent-config@<pin> <cmd>`).
|
|
134
|
+
|
|
90
135
|
### Available settings
|
|
91
136
|
|
|
92
137
|
| Setting | Default | Description |
|
|
93
138
|
|---|---|---|
|
|
139
|
+
| `agent_config_version` | *(empty)* | Exact semver pin of the agent-config release (see above). Empty = unpinned. |
|
|
94
140
|
| `cost_profile` | `minimal` | Token budget (`minimal`, `balanced`, `full`, `custom`) |
|
|
95
141
|
| `personal.user_name` | *(empty)* | User's first name for personalized responses |
|
|
96
142
|
| `personal.minimal_output` | `true` | Suppress intermediate output |
|
|
@@ -254,6 +300,76 @@ agents/
|
|
|
254
300
|
|
|
255
301
|
Module-level documentation goes into `app/Modules/*/agents/`.
|
|
256
302
|
|
|
303
|
+
### `agents/` overlay cascade
|
|
304
|
+
|
|
305
|
+
A subset of `agents/` subdirs participates in the same deepest-wins
|
|
306
|
+
cascade as `.agent-settings.yml` (see *"User-global DX-comfort
|
|
307
|
+
defaults"* above). The cascade is **per-file** by basename — the
|
|
308
|
+
deepest existing `agents/<kind>/<name>.md` wins; the rest are silently
|
|
309
|
+
shadowed.
|
|
310
|
+
|
|
311
|
+
| Subdir | Cascade? | User-global allowed? | Why |
|
|
312
|
+
|---|---|---|---|
|
|
313
|
+
| `agents/overrides/` | ✅ Yes — deepest wins by basename. | ✅ Yes — weakest layer. | Personal developer overrides. |
|
|
314
|
+
| `agents/contexts/` | ✅ Yes — deepest wins by basename. | ❌ No — project-shaped. | Shared knowledge; would leak across projects. |
|
|
315
|
+
| `agents/decisions/` | ✅ Yes — deepest wins by basename. | ❌ No — project-shaped ADRs. | Decisions are repo-bound. |
|
|
316
|
+
| `agents/roadmaps/` | ❌ No — project-rooted only. | ❌ No. | Active delivery plans. |
|
|
317
|
+
| `agents/state/`, `agents/memory/`, `agents/work_engine/`, `agents/.agent-prices.md`, `agents/council-*/` | ❌ No — stateful / session-scoped. | ❌ No. | Per-session state, not shareable. |
|
|
318
|
+
|
|
319
|
+
**User-global asymmetry.** `~/.config/agent-config/agents/overrides/`
|
|
320
|
+
is the only user-global overlay path consulted by the loader. Files
|
|
321
|
+
under `~/.config/agent-config/agents/contexts/` or
|
|
322
|
+
`.../agents/decisions/` are silently skipped — these kinds are
|
|
323
|
+
project-shaped and must not leak across projects.
|
|
324
|
+
|
|
325
|
+
The resolver lives at
|
|
326
|
+
[`scripts/_lib/agents_overlay.py`](../scripts/_lib/agents_overlay.py)
|
|
327
|
+
and is enforced by `scripts/check_overlay_cascade_subdirs.py` — drift
|
|
328
|
+
between the code constants (`CASCADE_ELIGIBLE_KINDS`,
|
|
329
|
+
`USER_GLOBAL_OVERLAY_KINDS`) and the table above breaks the build.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Update check
|
|
334
|
+
|
|
335
|
+
`npx @event4u/agent-config <cmd>` checks the npm registry once per
|
|
336
|
+
24 h for a newer release of the package and, when one is available,
|
|
337
|
+
writes a two-line banner to **stderr** *after* the subcommand has
|
|
338
|
+
finished. There is no prompt — the user updates when they want with
|
|
339
|
+
`npx @event4u/agent-config update` (Phase 3).
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
ℹ️ agent-config 1.42.0 available (you have 1.38.0).
|
|
343
|
+
Update: npx @event4u/agent-config update
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
State is persisted at `~/.config/agent-config/update-check.json`
|
|
347
|
+
(mode `0600`) — sibling of `anthropic.key`, `council-spend.jsonl`.
|
|
348
|
+
The fetch is hard-capped at 1 s and silent on any error.
|
|
349
|
+
|
|
350
|
+
### Suppression matrix
|
|
351
|
+
|
|
352
|
+
The banner is silently skipped when **any** of the following match:
|
|
353
|
+
|
|
354
|
+
| Condition | Reason |
|
|
355
|
+
|---|---|
|
|
356
|
+
| `CI=1` / `CI=true` / `GITHUB_ACTIONS=true` | CI noise, breaks log scrapers. |
|
|
357
|
+
| `stdout` is not a TTY | Piped / redirected output must stay clean. |
|
|
358
|
+
| `AGENT_CONFIG_NO_UPDATE_CHECK=1` | Per-invocation escape hatch. |
|
|
359
|
+
| `update_check.enabled: false` in `.agent-settings.yml` | Project / user opt-out. |
|
|
360
|
+
| Registry call exceeds 1 s | Network must never delay `npx`. |
|
|
361
|
+
| Registry call raises any exception | Best-effort — failure is silent. |
|
|
362
|
+
|
|
363
|
+
`update_check.enabled` is a **project-scoped** key — it is *not* on
|
|
364
|
+
the user-global whitelist (see [§ Agent Settings](#agent-settings)).
|
|
365
|
+
Each project decides; the user-global file cannot flip it on or off
|
|
366
|
+
for unrelated projects.
|
|
367
|
+
|
|
368
|
+
The decision logic lives at
|
|
369
|
+
[`scripts/_lib/update_check.py`](../scripts/_lib/update_check.py); the
|
|
370
|
+
dispatcher integration lives in [`scripts/agent-config`](../scripts/agent-config)
|
|
371
|
+
(`run_update_check_banner`).
|
|
372
|
+
|
|
257
373
|
---
|
|
258
374
|
|
|
259
375
|
← [Back to README](../README.md)
|
package/docs/installation.md
CHANGED
|
@@ -518,8 +518,6 @@ Options:
|
|
|
518
518
|
--quiet Suppress non-error output
|
|
519
519
|
--skip-sync Skip payload sync (install.sh)
|
|
520
520
|
--skip-bridges Skip bridge files (install.py)
|
|
521
|
-
--global Ship kernel rules + curated skills to user-scope dirs
|
|
522
|
-
--uninstall With --global: remove the event4u/ namespace dir
|
|
523
521
|
--help, -h Show this help
|
|
524
522
|
```
|
|
525
523
|
|
|
@@ -528,41 +526,16 @@ The underlying stages keep their own CLI surfaces:
|
|
|
528
526
|
|
|
529
527
|
---
|
|
530
528
|
|
|
531
|
-
## Global user-level install
|
|
529
|
+
## Global user-level install — retired
|
|
532
530
|
|
|
533
|
-
`--global`
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
# Scope to specific surfaces (mirrors the project install --tools flag).
|
|
542
|
-
bash scripts/install --global --tools=claude-code,cursor
|
|
543
|
-
|
|
544
|
-
# Remove only what we put there — never touches user files.
|
|
545
|
-
bash scripts/install --global --uninstall
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
| Surface | Target directory |
|
|
549
|
-
| ------------- | --------------------------------------------------------------- |
|
|
550
|
-
| Claude Code | `~/.claude/rules/event4u/`, `~/.claude/skills/event4u/` |
|
|
551
|
-
| Cursor | `~/.cursor/rules/imported/event4u/{rules,skills}/` |
|
|
552
|
-
| Windsurf | `~/.codeium/windsurf/global_workflows/event4u/{rules,skills}/` |
|
|
553
|
-
| Fallback | `~/.config/agent-config/{rules,skills}/event4u/` |
|
|
554
|
-
|
|
555
|
-
The fallback path is always written so an editor we don't yet know
|
|
556
|
-
about can still pick the files up.
|
|
557
|
-
|
|
558
|
-
**Curation source:** `templates/global-install-manifest.yml`. Edit
|
|
559
|
-
post-install to grow or shrink the global set; re-run `--global` to
|
|
560
|
-
re-project. `--uninstall` only removes the `event4u/` namespace —
|
|
561
|
-
user-added rules / skills under sibling paths stay untouched.
|
|
562
|
-
|
|
563
|
-
**When to use:** running multiple unrelated projects where a per-project
|
|
564
|
-
install is overkill, or wiring up a new editor (Claude Desktop, Cursor)
|
|
565
|
-
that benefits from a baseline set of skills out of the box.
|
|
531
|
+
The previous `--global` symlink scheme (kernel rules + curated skills
|
|
532
|
+
copied into `~/.claude/`, `~/.cursor/`, `~/.codeium/windsurf/`, and
|
|
533
|
+
`~/.config/agent-config/` under an `event4u/` namespace) has been
|
|
534
|
+
**retired** under the npx-only distribution model. Run
|
|
535
|
+
`npx @event4u/agent-config init` per project instead; the
|
|
536
|
+
`agent_config_version` pin in `.agent-settings.yml` keeps every
|
|
537
|
+
invocation reproducible. See [`migration/v1-to-v2.md`](migration/v1-to-v2.md)
|
|
538
|
+
for the upgrade path.
|
|
566
539
|
|
|
567
540
|
---
|
|
568
541
|
|