@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.
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Shared agent configuration \u2014 skills for AI coding tools (Claude Code, Augment, Cursor, Cline, Windsurf, Gemini CLI).",
9
- "version": "5.10.0",
9
+ "version": "5.10.1",
10
10
  "keywords": [
11
11
  "agent-config",
12
12
  "skills",
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
@@ -1,6 +1,6 @@
1
1
  # Discovery — Deprecation Report
2
2
 
3
- - Generated: `2026-06-02T12:33:08Z`
3
+ - Generated: `2026-06-02T13:30:57Z`
4
4
  - Deprecated artefacts: **0**
5
5
 
6
6
  _None. Tree is clean._
@@ -10004,7 +10004,7 @@
10004
10004
  "reason": "scaffold for new SKILL.md authoring"
10005
10005
  }
10006
10006
  ],
10007
- "generated_at": "2026-06-02T12:33:08Z",
10007
+ "generated_at": "2026-06-02T13:30:57Z",
10008
10008
  "packs": [
10009
10009
  {
10010
10010
  "artefact_count": 85,
@@ -1 +1 @@
1
- b30fc8174fa095e23447c3419bbbd0b610fb33e895bc5df99ea6160cb6dac1c2 discovery-manifest.json
1
+ 4ab7f294f2a0fdb6122559d63cdbc8fb6cb7ac64661141535bc217431cfe8e01 discovery-manifest.json
@@ -1,6 +1,6 @@
1
1
  # Discovery Manifest — Summary
2
2
 
3
- - Generated: `2026-06-02T12:33:08Z`
3
+ - Generated: `2026-06-02T13:30:57Z`
4
4
  - Scanner: `ca7acd2ec7af`
5
5
  - Artefacts: **453**
6
6
  - Unassigned: **0**
@@ -1,6 +1,6 @@
1
1
  # Discovery — Orphan Report
2
2
 
3
- - Generated: `2026-06-02T12:33:08Z`
3
+ - Generated: `2026-06-02T13:30:57Z`
4
4
  - Orphan artefacts: **0**
5
5
 
6
6
  > An orphan is an artefact whose declared pack has no other members.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "checksum": "sha256:b55ddc4224e45ec7627ece7c2fd6f043df8c7030efbe50df87573f0da4de4173",
3
- "generated_at": "2026-06-02T12:33:08Z",
3
+ "generated_at": "2026-06-02T13:30:57Z",
4
4
  "packs": [
5
5
  {
6
6
  "artefact_count": 85,
@@ -1,6 +1,6 @@
1
1
  # Discovery — Trust Report
2
2
 
3
- - Generated: `2026-06-02T12:33:08Z`
3
+ - Generated: `2026-06-02T13:30:57Z`
4
4
  - Workspaces tracked: **8**
5
5
  - Human-review-required artefacts: **2**
6
6
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "checksum": "sha256:b55ddc4224e45ec7627ece7c2fd6f043df8c7030efbe50df87573f0da4de4173",
3
- "generated_at": "2026-06-02T12:33:08Z",
3
+ "generated_at": "2026-06-02T13:30:57Z",
4
4
  "scanner_version": "ca7acd2ec7af",
5
5
  "workspaces": [
6
6
  {
@@ -9,7 +9,7 @@
9
9
  "homepage": "https://github.com/event4u-app/agent-config#readme",
10
10
  "name": "@event4u/agent-config",
11
11
  "repository": "https://github.com/event4u-app/agent-config",
12
- "version": "5.10.0"
12
+ "version": "5.10.1"
13
13
  },
14
14
  "registries": [
15
15
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@event4u/agent-config",
3
- "version": "5.10.0",
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,
@@ -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
- Two side effects, in order:
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
@@ -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())