@event4u/agent-config 5.10.0 → 5.10.1
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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +13 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +1 -1
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +1 -1
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +1 -1
- package/dist/mcp/registry-manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
- package/scripts/_cli/cmd_refresh.py +7 -1
- package/scripts/_cli/cmd_upgrade.py +31 -1
- package/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
- package/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
- package/scripts/_lib/cli_wrapper.py +64 -0
- package/scripts/hook_manifest.yaml +16 -7
- package/scripts/wrapper_freshness_hook.py +86 -0
package/CHANGELOG.md
CHANGED
|
@@ -820,6 +820,19 @@ our recommendation order, not its support status.
|
|
|
820
820
|
> that forces a new era split (`# Era: 5.10.x`, etc.) — see
|
|
821
821
|
> [`docs/contracts/CHANGELOG-conventions.md § Era splits`](docs/contracts/CHANGELOG-conventions.md).
|
|
822
822
|
|
|
823
|
+
## [5.10.1](https://github.com/event4u-app/agent-config/compare/5.10.0...5.10.1) (2026-06-02)
|
|
824
|
+
|
|
825
|
+
### Bug Fixes
|
|
826
|
+
|
|
827
|
+
* **hooks:** self-heal stale ./agent-config wrapper on session_start ([c36ad4d](https://github.com/event4u-app/agent-config/commit/c36ad4dbfbccfcaac017a4f8e2cb526223cbf5fa))
|
|
828
|
+
* **cli:** refresh project ./agent-config wrapper on upgrade and refresh --project ([abf2936](https://github.com/event4u-app/agent-config/commit/abf293657cc6630aca548d5afa9385d8c7690522))
|
|
829
|
+
|
|
830
|
+
### Chores
|
|
831
|
+
|
|
832
|
+
* **git:** untrack agents/runtime council artifacts ([b58705d](https://github.com/event4u-app/agent-config/commit/b58705d96bd4ee813f2565ee73e4f9f484fa720d))
|
|
833
|
+
|
|
834
|
+
Tests: 5512 (+12 since 5.10.0)
|
|
835
|
+
|
|
823
836
|
## [5.10.0](https://github.com/event4u-app/agent-config/compare/5.9.0...5.10.0) (2026-06-02)
|
|
824
837
|
|
|
825
838
|
### Features
|
|
@@ -10004,7 +10004,7 @@
|
|
|
10004
10004
|
"reason": "scaffold for new SKILL.md authoring"
|
|
10005
10005
|
}
|
|
10006
10006
|
],
|
|
10007
|
-
"generated_at": "2026-06-
|
|
10007
|
+
"generated_at": "2026-06-02T13:30:57Z",
|
|
10008
10008
|
"packs": [
|
|
10009
10009
|
{
|
|
10010
10010
|
"artefact_count": 85,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4ab7f294f2a0fdb6122559d63cdbc8fb6cb7ac64661141535bc217431cfe8e01 discovery-manifest.json
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@event4u/agent-config",
|
|
3
|
-
"version": "5.10.
|
|
3
|
+
"version": "5.10.1",
|
|
4
4
|
"description": "Universal AI Agent OS \u2014 audited skills, governance rules, commands, and templates for AI coding tools (Claude Code, Cursor, Windsurf, Copilot).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
Binary file
|
|
@@ -84,7 +84,7 @@ def _is_source_repo(project_root: Path) -> bool:
|
|
|
84
84
|
def _refresh_project(project_root: Path, out, err) -> int:
|
|
85
85
|
# Imported lazily: scripts.install is large and only needed for --project.
|
|
86
86
|
from scripts import install as installer
|
|
87
|
-
from scripts._lib import installed_lock
|
|
87
|
+
from scripts._lib import cli_wrapper, installed_lock
|
|
88
88
|
|
|
89
89
|
if _is_source_repo(project_root):
|
|
90
90
|
print("ℹ️ refresh --project skipped: this is the agent-config package "
|
|
@@ -114,6 +114,12 @@ def _refresh_project(project_root: Path, out, err) -> int:
|
|
|
114
114
|
encoding="utf-8")
|
|
115
115
|
print(f"✅ overrides scaffold: {overrides}", file=out)
|
|
116
116
|
|
|
117
|
+
# Re-stamp the ``./agent-config`` wrapper from the canonical template so
|
|
118
|
+
# an older, fallback-less wrapper cannot linger and break the hooks.
|
|
119
|
+
wrapper = cli_wrapper.install_cli_wrapper(project_root)
|
|
120
|
+
if wrapper is not None:
|
|
121
|
+
print(f"✅ ./agent-config wrapper refreshed: {wrapper}", file=out)
|
|
122
|
+
|
|
117
123
|
rc = _sync_gitignore(project_root, out, err)
|
|
118
124
|
if rc != 0:
|
|
119
125
|
return rc
|
|
@@ -8,12 +8,16 @@ goal: it installs the latest published package globally and re-runs the
|
|
|
8
8
|
global install so new skills / rules / hooks reach every consumer at
|
|
9
9
|
once.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Side effects, in order:
|
|
12
12
|
|
|
13
13
|
1. ``npm install -g @event4u/agent-config@latest`` — refresh the global
|
|
14
14
|
binary on PATH (the binary the Claude plugin hook resolves).
|
|
15
15
|
2. ``agent-config global`` (→ ``install.py --global``) — refresh the
|
|
16
16
|
global root (``~/.event4u/agent-config/``) + regenerate plugin hooks.
|
|
17
|
+
3. If run from inside a consumer project that already has a
|
|
18
|
+
``./agent-config`` wrapper, re-stamp that wrapper from the canonical
|
|
19
|
+
template so an older, fallback-less copy cannot linger and break the
|
|
20
|
+
hooks. Skipped in the source repo and never creates a wrapper.
|
|
17
21
|
|
|
18
22
|
The **Claude marketplace plugin** updates on Claude Code's own cadence,
|
|
19
23
|
independent of npm; ``upgrade`` cannot drive it. ``agent-config doctor``
|
|
@@ -72,12 +76,36 @@ def _agent_config_bin() -> str:
|
|
|
72
76
|
Path(__file__).resolve().parents[2] / "agent-config")
|
|
73
77
|
|
|
74
78
|
|
|
79
|
+
def _maybe_refresh_project_wrapper(project_root: Path, out, err) -> None:
|
|
80
|
+
"""Re-stamp the project-local ``./agent-config`` wrapper after a global
|
|
81
|
+
upgrade — but only when the upgrade was run from inside a consumer
|
|
82
|
+
project that *already* has a wrapper. Never creates one (that is an
|
|
83
|
+
install action, out of scope), never touches the source repo.
|
|
84
|
+
|
|
85
|
+
This closes the gap where ``upgrade`` refreshed the global binary while
|
|
86
|
+
an older, fallback-less project wrapper kept breaking the Claude hooks.
|
|
87
|
+
"""
|
|
88
|
+
from scripts._lib import cli_wrapper
|
|
89
|
+
from scripts._cli.cmd_refresh import _is_source_repo
|
|
90
|
+
|
|
91
|
+
if _is_source_repo(project_root):
|
|
92
|
+
return
|
|
93
|
+
if not (project_root / "agent-config").is_file():
|
|
94
|
+
return # not a consumer project root (or never installed) — leave it
|
|
95
|
+
if not cli_wrapper.needs_refresh(project_root):
|
|
96
|
+
return
|
|
97
|
+
wrapper = cli_wrapper.install_cli_wrapper(project_root)
|
|
98
|
+
if wrapper is not None:
|
|
99
|
+
print(f"✅ refreshed stale project wrapper: {wrapper}", file=out)
|
|
100
|
+
|
|
101
|
+
|
|
75
102
|
def main(
|
|
76
103
|
argv: Optional[list[str]] = None,
|
|
77
104
|
*,
|
|
78
105
|
runner: Runner = _default_runner,
|
|
79
106
|
fetcher=update_check.fetch_latest_from_npm,
|
|
80
107
|
installed: Optional[str] = None,
|
|
108
|
+
project_root: Optional[Path] = None,
|
|
81
109
|
out=sys.stdout,
|
|
82
110
|
err=sys.stderr,
|
|
83
111
|
) -> int:
|
|
@@ -128,6 +156,8 @@ def main(
|
|
|
128
156
|
f"{' '.join(cmd)}", file=err)
|
|
129
157
|
return 1
|
|
130
158
|
|
|
159
|
+
_maybe_refresh_project_wrapper(project_root or Path.cwd(), out, err)
|
|
160
|
+
|
|
131
161
|
print("✅ agent-config upgraded. Run `agent-config doctor` to verify "
|
|
132
162
|
"PATH + plugin parity.", file=out)
|
|
133
163
|
return 0
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Install / refresh the consumer-facing ``./agent-config`` wrapper.
|
|
2
|
+
|
|
3
|
+
Python counterpart to ``install_cli_wrapper`` in ``scripts/install.sh``.
|
|
4
|
+
The wrapper is gitignored and meant to be regenerated on every install,
|
|
5
|
+
but the normal update cadence (``upgrade``, ``refresh --project``) never
|
|
6
|
+
re-ran the bash installer — so an older, fallback-less wrapper could
|
|
7
|
+
linger in a consumer project and break every Claude hook (the hook
|
|
8
|
+
resolves the master CLI *through* this wrapper). These helpers let the
|
|
9
|
+
update commands re-stamp the wrapper from the canonical template.
|
|
10
|
+
|
|
11
|
+
The template is the single source of truth (``templates/agent-config-wrapper.sh``);
|
|
12
|
+
the installer copies it verbatim with no substitution, so refreshing is a
|
|
13
|
+
plain copy + ``chmod``.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import shutil
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
# scripts/_lib/cli_wrapper.py → parents[2] is the package root.
|
|
22
|
+
_PACKAGE_ROOT = Path(__file__).resolve().parents[2]
|
|
23
|
+
_TEMPLATE = _PACKAGE_ROOT / "templates" / "agent-config-wrapper.sh"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def template_path() -> Path:
|
|
27
|
+
"""Absolute path to the canonical wrapper template."""
|
|
28
|
+
return _TEMPLATE
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _target(project_root: Path) -> Path:
|
|
32
|
+
return Path(project_root) / "agent-config"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def needs_refresh(project_root: Path) -> bool:
|
|
36
|
+
"""True when the project wrapper is missing or differs from the template.
|
|
37
|
+
|
|
38
|
+
Returns ``False`` when the template itself is unavailable (corrupt /
|
|
39
|
+
maintainer-only checkout) — there is nothing to refresh *to*.
|
|
40
|
+
"""
|
|
41
|
+
if not _TEMPLATE.is_file():
|
|
42
|
+
return False
|
|
43
|
+
target = _target(project_root)
|
|
44
|
+
if not target.is_file():
|
|
45
|
+
return True
|
|
46
|
+
try:
|
|
47
|
+
return target.read_text(encoding="utf-8") != _TEMPLATE.read_text(
|
|
48
|
+
encoding="utf-8")
|
|
49
|
+
except OSError:
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def install_cli_wrapper(project_root: Path) -> Optional[Path]:
|
|
54
|
+
"""Copy the canonical wrapper template to ``<project_root>/agent-config``.
|
|
55
|
+
|
|
56
|
+
Returns the written target path, or ``None`` when the template is
|
|
57
|
+
missing (corrupt package / maintainer checkout without templates).
|
|
58
|
+
"""
|
|
59
|
+
if not _TEMPLATE.is_file():
|
|
60
|
+
return None
|
|
61
|
+
target = _target(project_root)
|
|
62
|
+
shutil.copyfile(_TEMPLATE, target)
|
|
63
|
+
target.chmod(0o755)
|
|
64
|
+
return target
|
|
@@ -62,16 +62,25 @@ concerns:
|
|
|
62
62
|
script: scripts/profile_staleness_hook.py
|
|
63
63
|
args: []
|
|
64
64
|
fail_closed: false
|
|
65
|
+
# Defense-in-depth twin of the update-command wrapper refresh
|
|
66
|
+
# (cmd_upgrade / cmd_refresh). On session_start, re-stamps a stale
|
|
67
|
+
# project-local ./agent-config wrapper from the canonical template so
|
|
68
|
+
# an outdated, fallback-less copy cannot keep breaking the hooks.
|
|
69
|
+
# Never creates a wrapper, never touches the source repo, fail-open.
|
|
70
|
+
wrapper-freshness:
|
|
71
|
+
script: scripts/wrapper_freshness_hook.py
|
|
72
|
+
args: []
|
|
73
|
+
fail_closed: false
|
|
65
74
|
|
|
66
75
|
platforms:
|
|
67
76
|
augment:
|
|
68
|
-
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness]
|
|
77
|
+
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness]
|
|
69
78
|
session_end: [chat-history]
|
|
70
79
|
stop: [chat-history, verify-before-complete]
|
|
71
80
|
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff]
|
|
72
81
|
|
|
73
82
|
claude:
|
|
74
|
-
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness]
|
|
83
|
+
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness]
|
|
75
84
|
session_end: [chat-history]
|
|
76
85
|
stop: [chat-history, verify-before-complete]
|
|
77
86
|
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff]
|
|
@@ -92,7 +101,7 @@ platforms:
|
|
|
92
101
|
# Decision matrix + upstream blockers tracked in
|
|
93
102
|
# agents/settings/contexts/chat-history-platform-hooks.md § Cowork.
|
|
94
103
|
cowork:
|
|
95
|
-
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness]
|
|
104
|
+
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness]
|
|
96
105
|
session_end: [chat-history]
|
|
97
106
|
stop: [chat-history, verify-before-complete]
|
|
98
107
|
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff]
|
|
@@ -106,7 +115,7 @@ platforms:
|
|
|
106
115
|
# IDE-only — CLI-only users fall back to /checkpoint per
|
|
107
116
|
# agents/settings/contexts/chat-history-platform-hooks.md.
|
|
108
117
|
cursor:
|
|
109
|
-
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness]
|
|
118
|
+
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness]
|
|
110
119
|
session_end: [chat-history]
|
|
111
120
|
stop: [chat-history, verify-before-complete]
|
|
112
121
|
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff]
|
|
@@ -121,7 +130,7 @@ platforms:
|
|
|
121
130
|
# both map to session_start. TaskCancel maps to stop because the
|
|
122
131
|
# session is interrupted with partial state (mirrors Augment Stop).
|
|
123
132
|
cline:
|
|
124
|
-
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness]
|
|
133
|
+
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness]
|
|
125
134
|
session_end: [chat-history]
|
|
126
135
|
stop: [chat-history, verify-before-complete]
|
|
127
136
|
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff]
|
|
@@ -140,7 +149,7 @@ platforms:
|
|
|
140
149
|
# surface to record verification commands; documented limitation).
|
|
141
150
|
# minimal-safe-diff is omitted entirely on Windsurf for the same reason.
|
|
142
151
|
windsurf:
|
|
143
|
-
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, profile-staleness]
|
|
152
|
+
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, profile-staleness, wrapper-freshness]
|
|
144
153
|
stop: [chat-history, verify-before-complete]
|
|
145
154
|
user_prompt_submit: [chat-history, verify-before-complete]
|
|
146
155
|
|
|
@@ -155,7 +164,7 @@ platforms:
|
|
|
155
164
|
# turn-check semantics. AfterAgent fires when the agent loop ends
|
|
156
165
|
# — this is our `stop` slot.
|
|
157
166
|
gemini:
|
|
158
|
-
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness]
|
|
167
|
+
session_start: [chat-history, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness]
|
|
159
168
|
session_end: [chat-history]
|
|
160
169
|
stop: [chat-history, verify-before-complete]
|
|
161
170
|
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""session_start concern — keep the project-local ``./agent-config`` fresh.
|
|
3
|
+
|
|
4
|
+
Defense-in-depth twin of the update-command refresh (``upgrade`` /
|
|
5
|
+
``refresh --project``). On every session_start the dispatcher runs this in
|
|
6
|
+
the consumer workspace; if a ``./agent-config`` wrapper exists there and
|
|
7
|
+
differs from the canonical template, it is re-stamped so an outdated,
|
|
8
|
+
fallback-less copy cannot keep breaking the hooks.
|
|
9
|
+
|
|
10
|
+
Bootstrapping note: this can only heal a wrapper functional enough to
|
|
11
|
+
invoke the dispatcher in the first place (the current template's global +
|
|
12
|
+
npx fallbacks guarantee that). A *completely* broken wrapper never reaches
|
|
13
|
+
this concern — that recovery path is ``agent-config upgrade`` /
|
|
14
|
+
``refresh --project``.
|
|
15
|
+
|
|
16
|
+
Contract: never creates a wrapper where none exists (that is an install
|
|
17
|
+
action); never touches the agent-config source repo; always fail-open
|
|
18
|
+
(exit 0) — hook self-heal must not block a session.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
import sys
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
# Make `scripts` importable when invoked as a bare script path.
|
|
29
|
+
_REPO = Path(__file__).resolve().parent.parent
|
|
30
|
+
if str(_REPO) not in sys.path:
|
|
31
|
+
sys.path.insert(0, str(_REPO))
|
|
32
|
+
|
|
33
|
+
EXIT_ALLOW = 0
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _project_root() -> Path:
|
|
37
|
+
env = os.environ.get("CLAUDE_PROJECT_DIR") or os.environ.get(
|
|
38
|
+
"AGENT_CONFIG_PROJECT_DIR")
|
|
39
|
+
if env:
|
|
40
|
+
return Path(env)
|
|
41
|
+
return Path.cwd()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main(argv: list[str] | None = None) -> int:
|
|
45
|
+
ap = argparse.ArgumentParser(
|
|
46
|
+
description="Project ./agent-config wrapper freshness (session_start).")
|
|
47
|
+
ap.add_argument("--root", default=None)
|
|
48
|
+
args, _ = ap.parse_known_args(argv)
|
|
49
|
+
|
|
50
|
+
# Drain stdin (the dispatcher passes a JSON envelope); we do not need it.
|
|
51
|
+
try:
|
|
52
|
+
if not sys.stdin.isatty():
|
|
53
|
+
sys.stdin.read()
|
|
54
|
+
except Exception:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
from scripts._lib import cli_wrapper
|
|
59
|
+
from scripts._cli.cmd_refresh import _is_source_repo
|
|
60
|
+
except Exception: # pragma: no cover — defensive; never block the loop
|
|
61
|
+
return EXIT_ALLOW
|
|
62
|
+
|
|
63
|
+
root = Path(args.root) if args.root else _project_root()
|
|
64
|
+
try:
|
|
65
|
+
if _is_source_repo(root):
|
|
66
|
+
return EXIT_ALLOW
|
|
67
|
+
if not (root / "agent-config").is_file():
|
|
68
|
+
return EXIT_ALLOW # no wrapper here — never create one
|
|
69
|
+
if not cli_wrapper.needs_refresh(root):
|
|
70
|
+
return EXIT_ALLOW
|
|
71
|
+
wrapper = cli_wrapper.install_cli_wrapper(root)
|
|
72
|
+
except Exception:
|
|
73
|
+
return EXIT_ALLOW # fail-open — never block the session
|
|
74
|
+
|
|
75
|
+
if wrapper is not None:
|
|
76
|
+
print(f"[wrapper] re-stamped stale ./agent-config at {wrapper}",
|
|
77
|
+
file=sys.stderr)
|
|
78
|
+
sys.stdout.write(json.dumps({
|
|
79
|
+
"decision": "allow",
|
|
80
|
+
"reason": f"refreshed stale ./agent-config wrapper at {wrapper}",
|
|
81
|
+
}))
|
|
82
|
+
return EXIT_ALLOW
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
raise SystemExit(main())
|