@event4u/agent-config 2.3.0 → 2.4.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.
Files changed (39) hide show
  1. package/.agent-src/commands/onboard.md +14 -9
  2. package/.agent-src/skills/ai-council/SKILL.md +5 -3
  3. package/.agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  4. package/.agent-src/templates/agents/agent-project-settings.example.yml +4 -3
  5. package/.agent-src/templates/scripts/work_engine/_lib/agent_settings.py +29 -7
  6. package/.agent-src/templates/scripts/work_engine/_lib/user_global_paths.py +249 -0
  7. package/.agent-src/templates/scripts/work_engine/hooks/settings.py +8 -5
  8. package/.claude-plugin/marketplace.json +1 -1
  9. package/CHANGELOG.md +39 -0
  10. package/config/agent-settings.template.yml +5 -3
  11. package/docs/catalog.md +5 -3
  12. package/docs/contracts/installed-tools-lockfile.md +4 -0
  13. package/docs/customization.md +23 -17
  14. package/docs/decisions/ADR-007-agent-discovery-scopes.md +6 -0
  15. package/docs/decisions/ADR-009-event4u-namespace.md +188 -0
  16. package/docs/decisions/INDEX.md +1 -0
  17. package/docs/guidelines/agent-infra/installed-tools-manifest.md +1 -1
  18. package/docs/guidelines/agent-infra/layered-settings.md +6 -4
  19. package/docs/installation.md +3 -2
  20. package/docs/migration/v1-to-v2.md +45 -0
  21. package/docs/setup/per-ide/claude-desktop.md +107 -65
  22. package/package.json +1 -1
  23. package/scripts/_cli/cmd_uninstall.py +17 -7
  24. package/scripts/_cli/cmd_update.py +11 -7
  25. package/scripts/_lib/agent_settings.py +29 -7
  26. package/scripts/_lib/agents_overlay.py +15 -4
  27. package/scripts/_lib/claude_desktop_bundler.py +150 -0
  28. package/scripts/_lib/installed_lock.py +56 -4
  29. package/scripts/_lib/installed_tools.py +1 -1
  30. package/scripts/_lib/update_check.py +29 -5
  31. package/scripts/_lib/user_global_paths.py +249 -0
  32. package/scripts/ai_council/__init__.py +4 -3
  33. package/scripts/ai_council/budget_guard.py +34 -4
  34. package/scripts/ai_council/bundler.py +2 -0
  35. package/scripts/ai_council/clients.py +28 -7
  36. package/scripts/install.py +149 -49
  37. package/scripts/install_anthropic_key.sh +5 -3
  38. package/scripts/install_openai_key.sh +5 -3
  39. package/scripts/skill_trigger_eval.py +13 -2
@@ -2092,36 +2092,41 @@ PROJECT_BRIDGE_MARKERS = {
2092
2092
  # ``_write_claude_desktop_marker`` rather than via this map.
2093
2093
  #
2094
2094
  # Tools that follow the markdown-skills convention (anchors lifted from
2095
- # nextlevelbuilder/ui-ux-pro-max-skill) deploy ``.claude/skills``
2096
- # the universal Anthropic-shaped skill bundle — into ``<anchor>/skills/``
2097
- # (or ``<anchor>/steering/`` for kiro). ``.claude/rules`` is also copied
2095
+ # nextlevelbuilder/ui-ux-pro-max-skill) deploy the universal Anthropic-
2096
+ # shaped skill bundle — sourced from ``.agent-src/`` (the npm-shipped
2097
+ # canonical asset tree) into ``<anchor>/skills/`` (or
2098
+ # ``<anchor>/steering/`` for kiro). ``.agent-src/rules`` is also copied
2098
2099
  # where the destination is a true rules-aware tool root.
2100
+ #
2101
+ # All source paths reference ``.agent-src/<subdir>`` because that is the
2102
+ # only asset tree included in the npm tarball (see ``package.json#files``).
2103
+ # The legacy ``.augment/``, ``.claude/``, ``.cursor/`` projections only
2104
+ # exist in the development checkout — they are not shipped.
2099
2105
  _CLAUDE_SKILL_BUNDLE: list[tuple[str, str]] = [
2100
- (".claude/rules", "rules"),
2101
- (".claude/skills", "skills"),
2102
- (".claude/personas", "personas"),
2106
+ (".agent-src/rules", "rules"),
2107
+ (".agent-src/skills", "skills"),
2108
+ (".agent-src/personas", "personas"),
2103
2109
  ]
2104
2110
  GLOBAL_DEPLOY_SOURCES: dict[str, list[tuple[str, str]]] = {
2105
2111
  "claude-code": _CLAUDE_SKILL_BUNDLE,
2106
2112
  "augment": [
2107
- (".augment/rules", "rules"),
2108
- (".augment/skills", "skills"),
2109
- (".augment/commands", "commands"),
2110
- (".augment/contexts", "contexts"),
2111
- (".augment/personas", "personas"),
2112
- (".augment/templates", "templates"),
2113
+ (".agent-src/rules", "rules"),
2114
+ (".agent-src/skills", "skills"),
2115
+ (".agent-src/commands", "commands"),
2116
+ (".agent-src/contexts", "contexts"),
2117
+ (".agent-src/personas", "personas"),
2118
+ (".agent-src/templates", "templates"),
2113
2119
  ],
2114
2120
  "cursor": [
2115
- (".cursor/rules", "rules"),
2116
- (".cursor/commands", "commands"),
2117
- (".cursor/personas", "personas"),
2121
+ (".agent-src/rules", "rules"),
2122
+ (".agent-src/commands", "commands"),
2123
+ (".agent-src/personas", "personas"),
2118
2124
  ],
2119
2125
  "windsurf": [
2120
- (".windsurf/rules", "rules"),
2121
- (".windsurf/workflows", "workflows"),
2126
+ (".agent-src/rules", "rules"),
2122
2127
  ],
2123
2128
  "cline": [
2124
- (".clinerules", ""),
2129
+ (".agent-src/rules", ""),
2125
2130
  ],
2126
2131
  # Markdown-skills tools — mirror the universal skill bundle into the
2127
2132
  # tool-specific anchor. Subpath matches the reference repo's
@@ -2142,9 +2147,9 @@ GLOBAL_DEPLOY_SOURCES: dict[str, list[tuple[str, str]]] = {
2142
2147
  # Kiro reads from `steering/` not `skills/` (per
2143
2148
  # platforms/kiro.json#folderStructure.skillPath).
2144
2149
  "kiro": [
2145
- (".claude/rules", "rules"),
2146
- (".claude/skills", "steering"),
2147
- (".claude/personas", "personas"),
2150
+ (".agent-src/rules", "rules"),
2151
+ (".agent-src/skills", "steering"),
2152
+ (".agent-src/personas", "personas"),
2148
2153
  ],
2149
2154
  }
2150
2155
 
@@ -2158,17 +2163,34 @@ _CLAUDE_DESKTOP_MARKER_TEMPLATE = """\
2158
2163
 
2159
2164
  Installed by `@event4u/agent-config` (user scope, ADR-007).
2160
2165
 
2161
- - Lockfile: `{lockfile}`
2162
- - Anchor: `{anchor}`
2166
+ - Lockfile: `{lockfile}`
2167
+ - Anchor: `{anchor}`
2168
+ - Skill bundles: `{bundles_dir}` ({bundle_count} ZIPs)
2169
+
2170
+ ## Import skills into Claude Desktop
2171
+
2172
+ Claude Desktop has no filesystem skill-discovery convention — skills are
2173
+ imported manually via the Customize → Skills UI.
2174
+
2175
+ 1. Open Claude Desktop → **Settings → Customize → Skills**.
2176
+ 2. Click the **Upload skill** button.
2177
+ 3. Browse to `{bundles_dir}` and pick the `<skill-name>.zip` files you
2178
+ want to install. One ZIP = one skill.
2179
+ 4. Repeat per skill. Claude Desktop keeps each upload until you remove it.
2163
2180
 
2164
- Claude Desktop has no native rules / skills filesystem convention; this
2165
- file is informational. Rules and skills for AI coding tools are deployed
2166
- to their respective user-scope directories (`~/.claude/`, `~/.augment/`,
2167
- `~/.cursor/`, `~/.codeium/windsurf/`, `~/Documents/Cline/Rules/`).
2181
+ The bundle directory is regenerated on every
2182
+ `npx @event4u/agent-config init --tools=claude-desktop` run (only
2183
+ changed skills are rewritten content-hash idempotency).
2168
2184
 
2169
2185
  To remove this marker, delete this file.
2170
2186
  """
2171
2187
 
2188
+ #: Subpath under ``~/.event4u/agent-config/`` where Claude Desktop ZIP
2189
+ #: bundles are written. Kept separate from the per-tool USER_SCOPE_PATHS
2190
+ #: anchor (which is the Claude Desktop config dir) because bundles are
2191
+ #: package-owned, not Claude-owned, content.
2192
+ _CLAUDE_DESKTOP_BUNDLES_SUBPATH = "claude-desktop/bundles"
2193
+
2172
2194
 
2173
2195
  def _bridge_marker(tool_id: str, scope: str) -> str:
2174
2196
  """Return the canonical bridge-marker path for ``(tool_id, scope)``.
@@ -2477,6 +2499,24 @@ def _load_installed_tools_module():
2477
2499
  return installed_tools
2478
2500
 
2479
2501
 
2502
+ def _load_user_global_paths_module():
2503
+ """Lazy-import ``scripts._lib.user_global_paths`` (Phase 3 migration shim)."""
2504
+ pkg_root = str(Path(__file__).resolve().parents[1])
2505
+ if pkg_root not in sys.path:
2506
+ sys.path.insert(0, pkg_root)
2507
+ from scripts._lib import user_global_paths # noqa: WPS433 — lazy by design
2508
+ return user_global_paths
2509
+
2510
+
2511
+ def _load_claude_desktop_bundler_module():
2512
+ """Lazy-import ``scripts._lib.claude_desktop_bundler`` (Phase 4 ZIP bundler)."""
2513
+ pkg_root = str(Path(__file__).resolve().parents[1])
2514
+ if pkg_root not in sys.path:
2515
+ sys.path.insert(0, pkg_root)
2516
+ from scripts._lib import claude_desktop_bundler # noqa: WPS433 — lazy by design
2517
+ return claude_desktop_bundler
2518
+
2519
+
2480
2520
  def _sha256_of_file(path: Path) -> Optional[str]:
2481
2521
  """Return the hex SHA-256 of ``path`` content, or ``None`` if unreadable.
2482
2522
 
@@ -2794,29 +2834,68 @@ def _copy_dir_dereferencing_symlinks(
2794
2834
  return (written, skipped, written_paths)
2795
2835
 
2796
2836
 
2837
+ def _claude_desktop_bundles_dir() -> Path:
2838
+ """Return the canonical bundle output dir under the event4u namespace.
2839
+
2840
+ Located via :func:`user_global_paths.write_target` so the path
2841
+ honours the ``EVENT4U_HOME`` env override used by tests.
2842
+ """
2843
+ paths_mod = _load_user_global_paths_module()
2844
+ return paths_mod.write_target(_CLAUDE_DESKTOP_BUNDLES_SUBPATH)
2845
+
2846
+
2797
2847
  def _write_claude_desktop_marker(
2798
- force: bool, lockfile_path: Path,
2848
+ force: bool,
2849
+ lockfile_path: Path,
2850
+ *,
2851
+ bundles_dir: Path,
2852
+ bundle_count: int,
2799
2853
  ) -> tuple[int, int, list[Path]]:
2800
2854
  """Write the Claude Desktop user-scope marker file.
2801
2855
 
2802
2856
  Returns ``(written, skipped, written_paths)`` for symmetry with the
2803
- tree copier (P1.4). The marker is a single Markdown file; existing
2804
- files are preserved unless ``force=True``.
2857
+ tree copier (P1.4). The marker points users at ``bundles_dir`` for
2858
+ the Customize Skills import flow (Phase 4). Existing markers are
2859
+ overwritten unconditionally because the bundle count is part of the
2860
+ body and we want it to stay current.
2805
2861
  """
2806
2862
  anchor = Path(USER_SCOPE_PATHS["claude-desktop"]).expanduser()
2807
2863
  target = anchor / "agent-config.md"
2808
- decision = _resolve_file_conflict(target, force_hint=force)
2809
- if decision == "skip":
2810
- return (0, 1, [])
2811
2864
  anchor.mkdir(parents=True, exist_ok=True)
2812
2865
  body = _CLAUDE_DESKTOP_MARKER_TEMPLATE.format(
2813
2866
  lockfile=str(lockfile_path),
2814
2867
  anchor=str(anchor),
2868
+ bundles_dir=str(bundles_dir),
2869
+ bundle_count=bundle_count,
2815
2870
  )
2816
2871
  target.write_text(body, encoding="utf-8")
2817
2872
  return (1, 0, [target])
2818
2873
 
2819
2874
 
2875
+ def _deploy_claude_desktop(
2876
+ force: bool, package_root: Path, lockfile_path: Path,
2877
+ ) -> tuple[int, int, str, list[Path]]:
2878
+ """Build skill ZIP bundles + write the marker for ``claude-desktop``.
2879
+
2880
+ Phase 4 of road-to-event4u-namespace-and-claude-desktop. Bundles
2881
+ land in ``~/.event4u/agent-config/claude-desktop/bundles/``; the
2882
+ marker file in the Claude Desktop config dir points at them with
2883
+ Customize → Skills import instructions.
2884
+
2885
+ Returns ``(bundle_count, 0, "deployed", [bundles_dir, marker])``.
2886
+ The ``deployed`` status replaces the v2.3 ``marker``-only status.
2887
+ """
2888
+ bundler = _load_claude_desktop_bundler_module()
2889
+ bundles_dir = _claude_desktop_bundles_dir()
2890
+ bundler.build_skill_bundles(package_root, bundles_dir, force=force)
2891
+ # Count total existing ZIPs (idempotent runs may not rewrite any).
2892
+ bundle_count = sum(1 for _ in bundles_dir.glob("*.zip")) if bundles_dir.is_dir() else 0
2893
+ _, _, marker_paths = _write_claude_desktop_marker(
2894
+ force, lockfile_path, bundles_dir=bundles_dir, bundle_count=bundle_count,
2895
+ )
2896
+ return (bundle_count, 0, "deployed", [bundles_dir, *marker_paths])
2897
+
2898
+
2820
2899
  def _deploy_global_content(
2821
2900
  tools: set[str],
2822
2901
  force: bool,
@@ -2827,20 +2906,21 @@ def _deploy_global_content(
2827
2906
 
2828
2907
  For each tool in ``tools`` that has a ``GLOBAL_DEPLOY_SOURCES`` entry,
2829
2908
  copies the listed package subtrees into ``USER_SCOPE_PATHS[tool_id]``
2830
- (expanded). For ``claude-desktop`` writes the marker file. For tools
2831
- without a deployment plan (e.g. ``copilot``), records a ``hint`` status
2832
- so the caller can print an actionable next step.
2909
+ (expanded). For ``claude-desktop`` builds per-skill ZIP bundles under
2910
+ ``~/.event4u/agent-config/claude-desktop/bundles/`` and writes the
2911
+ marker file pointing at them (Phase 4). For tools without a deployment
2912
+ plan (e.g. ``copilot``), records a ``hint`` status so the caller can
2913
+ print an actionable next step.
2833
2914
 
2834
2915
  Returns ``{tool_id: (written, skipped, status, written_paths)}``
2835
- where ``status`` is one of ``deployed``, ``marker``, ``hint``,
2836
- ``unsupported`` and ``written_paths`` is the absolute path list of
2837
- every file the deploy actually wrote (P1.4).
2916
+ where ``status`` is one of ``deployed``, ``hint``, ``unsupported``
2917
+ and ``written_paths`` is the absolute path list of every file the
2918
+ deploy actually wrote (P1.4).
2838
2919
  """
2839
2920
  results: dict[str, tuple[int, int, str, list[Path]]] = {}
2840
2921
  for tool_id in sorted(tools):
2841
2922
  if tool_id == "claude-desktop":
2842
- w, s, paths = _write_claude_desktop_marker(force, lockfile_path)
2843
- results[tool_id] = (w, s, "marker", paths)
2923
+ results[tool_id] = _deploy_claude_desktop(force, package_root, lockfile_path)
2844
2924
  continue
2845
2925
  plan = GLOBAL_DEPLOY_SOURCES.get(tool_id)
2846
2926
  if plan is None:
@@ -2879,8 +2959,9 @@ def install_global(
2879
2959
  ) -> int:
2880
2960
  """User-scope install path (ADR-007 + Phase 1.6 lockfile lifecycle).
2881
2961
 
2882
- Reads ``~/.config/agent-config/installed.lock`` first. A recorded
2883
- version that does not match the running package version refuses the
2962
+ Reads ``~/.event4u/agent-config/installed.lock`` first (with a read
2963
+ fallback to the legacy ``~/.config/agent-config/installed.lock``). A
2964
+ recorded version that does not match the running package version refuses the
2884
2965
  install with a remediation hint unless ``--force`` is passed. On
2885
2966
  success the lockfile is rewritten atomically with the current
2886
2967
  package version + the union of previously-recorded and now-installed
@@ -2894,17 +2975,33 @@ def install_global(
2894
2975
  presence of ``.agent-settings.yml``), the project-scope manifest at
2895
2976
  ``agents/installed-tools.lock`` is also refreshed with ``scope=global``
2896
2977
  entries per ADR-008 Phase 3.2.
2978
+
2979
+ Phase 3 namespace migration: before any lockfile read, the legacy
2980
+ ``~/.config/agent-config/`` tree (pre-2.4 installs) is migrated into
2981
+ ``~/.event4u/agent-config/`` so subsequent reads land on the canonical
2982
+ path. The migration is idempotent and leaves a ``MIGRATED.md``
2983
+ breadcrumb behind; the legacy tree is never auto-deleted.
2897
2984
  """
2985
+ paths_mod = _load_user_global_paths_module()
2986
+ migrated = paths_mod.migrate_legacy_namespace()
2987
+ if migrated and not QUIET:
2988
+ info(
2989
+ "🔁 Migrated user-global config to "
2990
+ f"{paths_mod.event4u_root()} (legacy "
2991
+ f"{paths_mod.legacy_xdg_root()} preserved as fallback)"
2992
+ )
2993
+
2898
2994
  lock_mod = _load_installed_lock_module()
2899
2995
  installed_version = lock_mod.current_package_version()
2900
- lock_path = lock_mod.lockfile_path()
2901
- ok, recorded = lock_mod.check_version(installed_version, path=lock_path)
2996
+ read_path = lock_mod.lockfile_path()
2997
+ write_path = lock_mod.lockfile_write_path()
2998
+ ok, recorded = lock_mod.check_version(installed_version, path=read_path)
2902
2999
 
2903
3000
  if not ok and not force:
2904
3001
  if not QUIET:
2905
3002
  print()
2906
3003
  warn("Refusing global install: lockfile version mismatch.")
2907
- info(f" Lockfile: {lock_path}")
3004
+ info(f" Lockfile: {read_path}")
2908
3005
  info(f" Recorded version: {recorded}")
2909
3006
  info(f" Current package: {installed_version}")
2910
3007
  info(" Fix: run `agent-config update`")
@@ -2922,10 +3019,10 @@ def install_global(
2922
3019
  continue
2923
3020
  print(f" {tool_id:<15} → {anchor}")
2924
3021
 
2925
- existing = lock_mod.read_lockfile(path=lock_path) or {}
3022
+ existing = lock_mod.read_lockfile(path=read_path) or {}
2926
3023
  existing_tools = list(existing.get("tools", []))
2927
3024
  merged_tools = sorted(set(existing_tools) | set(tools))
2928
- written = lock_mod.write_lockfile(installed_version, merged_tools, path=lock_path)
3025
+ written = lock_mod.write_lockfile(installed_version, merged_tools, path=write_path)
2929
3026
 
2930
3027
  if not QUIET:
2931
3028
  print()
@@ -2945,7 +3042,10 @@ def install_global(
2945
3042
  for tool_id in sorted(deploy_results):
2946
3043
  w, s, status, _ = deploy_results[tool_id]
2947
3044
  anchor = USER_SCOPE_PATHS.get(tool_id, "")
2948
- if status == "deployed":
3045
+ if status == "deployed" and tool_id == "claude-desktop":
3046
+ bundles_dir = _claude_desktop_bundles_dir()
3047
+ print(f" {tool_id:<15} → {bundles_dir} ({w} bundles)")
3048
+ elif status == "deployed":
2949
3049
  print(f" {tool_id:<15} → {anchor} ({w} files, {s} skipped)")
2950
3050
  elif status == "marker":
2951
3051
  print(f" {tool_id:<15} → {anchor}agent-config.md ({'written' if w else 'skipped'})")
@@ -3,10 +3,12 @@
3
3
  #
4
4
  # Reads the key with `read -s` so it never echoes to the terminal and
5
5
  # never lands in shell history or scrollback. Writes atomically to
6
- # ~/.config/agent-config/anthropic.key with mode 0600.
6
+ # ~/.event4u/agent-config/anthropic.key with mode 0600. The legacy
7
+ # ~/.config/agent-config/anthropic.key is read as a fallback by the
8
+ # loaders so pre-2.4 installs keep working until the namespace shim runs.
7
9
  #
8
10
  # Contract — companion to scripts/skill_trigger_eval.py:
9
- # - File path: $HOME/.config/agent-config/anthropic.key
11
+ # - File path: $HOME/.event4u/agent-config/anthropic.key
10
12
  # - File mode: 0600 (owner read/write only)
11
13
  # - Key format: must start with `sk-ant-`
12
14
  # - No --force, no --yes, no env-var bypass. Piped stdin is rejected.
@@ -16,7 +18,7 @@
16
18
 
17
19
  set -euo pipefail
18
20
 
19
- TARGET_DIR="${HOME}/.config/agent-config"
21
+ TARGET_DIR="${HOME}/.event4u/agent-config"
20
22
  TARGET_FILE="${TARGET_DIR}/anthropic.key"
21
23
 
22
24
  # ── controlling-terminal requirement ─────────────────────────────────────
@@ -3,10 +3,12 @@
3
3
  #
4
4
  # Reads the key with `read -s` so it never echoes to the terminal and
5
5
  # never lands in shell history or scrollback. Writes atomically to
6
- # ~/.config/agent-config/openai.key with mode 0600.
6
+ # ~/.event4u/agent-config/openai.key with mode 0600. The legacy
7
+ # ~/.config/agent-config/openai.key is read as a fallback by the loaders
8
+ # so pre-2.4 installs keep working until the namespace shim runs.
7
9
  #
8
10
  # Contract — companion to scripts/ai_council/clients.py:
9
- # - File path: $HOME/.config/agent-config/openai.key
11
+ # - File path: $HOME/.event4u/agent-config/openai.key
10
12
  # - File mode: 0600 (owner read/write only)
11
13
  # - Key format: must start with `sk-`
12
14
  # - No --force, no --yes, no env-var bypass. Piped stdin is rejected.
@@ -16,7 +18,7 @@
16
18
 
17
19
  set -euo pipefail
18
20
 
19
- TARGET_DIR="${HOME}/.config/agent-config"
21
+ TARGET_DIR="${HOME}/.event4u/agent-config"
20
22
  TARGET_FILE="${TARGET_DIR}/openai.key"
21
23
 
22
24
  # ── controlling-terminal requirement ─────────────────────────────────────
@@ -46,7 +46,16 @@ PRICE_PER_MTOK_OUT = {"claude-sonnet-4-5": 15.0, "claude-opus-4": 75.0}
46
46
 
47
47
  # On-disk key file. Companion: scripts/install_anthropic_key.sh writes it
48
48
  # with mode 0600; load_anthropic_key() refuses to read anything else.
49
- ANTHROPIC_KEY_PATH = Path.home() / ".config" / "agent-config" / "anthropic.key"
49
+ # Resolution prefers the new namespace (``~/.event4u/agent-config/``) and
50
+ # falls back to the legacy ``~/.config/agent-config/`` so pre-2.4 keys
51
+ # stay usable until the user runs the migration shim.
52
+ from scripts._lib import user_global_paths # noqa: E402
53
+
54
+ ANTHROPIC_KEY_FILENAME = "anthropic.key"
55
+ ANTHROPIC_KEY_PATH = (
56
+ user_global_paths.resolve_with_fallback(ANTHROPIC_KEY_FILENAME)
57
+ or user_global_paths.write_target(ANTHROPIC_KEY_FILENAME)
58
+ )
50
59
  # Token heuristics used for the *pre-run* cost preview. Real billing
51
60
  # comes from the API response once the user has confirmed.
52
61
  TOKENS_PER_CHAR = 0.25 # ~4 chars per token, industry rule of thumb.
@@ -569,7 +578,9 @@ def build_arg_parser() -> argparse.ArgumentParser:
569
578
  default=ANTHROPIC_KEY_PATH,
570
579
  help=(
571
580
  "Override the key file location. Default: "
572
- "~/.config/agent-config/anthropic.key. Mode 0600 required."
581
+ "~/.event4u/agent-config/anthropic.key (legacy "
582
+ "~/.config/agent-config/anthropic.key read as fallback). "
583
+ "Mode 0600 required."
573
584
  ),
574
585
  )
575
586
  return parser