@event4u/agent-config 1.41.2 → 2.1.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.
Files changed (48) hide show
  1. package/.agent-src/commands/fix/{pr-bots.md → pr-bot-comments.md} +3 -3
  2. package/.agent-src/commands/fix/{pr.md → pr-comments.md} +6 -6
  3. package/.agent-src/commands/fix/{pr-developers.md → pr-developer-comments.md} +3 -3
  4. package/.agent-src/commands/fix.md +6 -6
  5. package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +2 -2
  6. package/.agent-src/templates/agents/agent-project-settings.example.yml +14 -0
  7. package/.agent-src/templates/scripts/work_engine/_lib/agent_settings.py +120 -11
  8. package/.claude-plugin/marketplace.json +4 -4
  9. package/CHANGELOG.md +54 -0
  10. package/README.md +39 -31
  11. package/config/agent-settings.template.yml +25 -0
  12. package/docs/architecture.md +47 -1
  13. package/docs/catalog.md +3 -3
  14. package/docs/contracts/command-clusters.md +3 -3
  15. package/docs/contracts/file-ownership-matrix.json +9 -9
  16. package/docs/customization.md +125 -9
  17. package/docs/getting-started.md +16 -25
  18. package/docs/installation.md +66 -82
  19. package/docs/migration/v1-to-v2.md +98 -0
  20. package/docs/migrations/commands-1.15.0.md +3 -3
  21. package/docs/setup/per-ide/claude-code.md +0 -17
  22. package/docs/setup/per-ide/claude-desktop.md +35 -48
  23. package/docs/setup/per-ide/windsurf.md +0 -11
  24. package/docs/skills-catalog.md +23 -2
  25. package/docs/troubleshooting.md +20 -32
  26. package/llms.txt +22 -1
  27. package/package.json +1 -6
  28. package/scripts/_cli/__init__.py +0 -0
  29. package/scripts/_cli/cmd_migrate.py +270 -0
  30. package/scripts/_cli/cmd_update.py +226 -0
  31. package/scripts/_lib/agent_settings.py +120 -11
  32. package/scripts/_lib/agents_overlay.py +109 -0
  33. package/scripts/_lib/pin_resolver.py +152 -0
  34. package/scripts/_lib/update_check.py +183 -0
  35. package/scripts/agent-config +73 -1
  36. package/scripts/check_overlay_cascade_subdirs.py +125 -0
  37. package/scripts/check_template_pin_drift.py +112 -0
  38. package/scripts/check_update_banner.py +86 -0
  39. package/scripts/install +27 -40
  40. package/scripts/install.py +17 -228
  41. package/scripts/install.sh +6 -11
  42. package/templates/agent-config-wrapper.sh +40 -25
  43. package/templates/consumer-settings/README.md +2 -2
  44. package/bin/install.php +0 -45
  45. package/composer.json +0 -33
  46. package/scripts/postinstall.sh +0 -76
  47. package/scripts/setup.sh +0 -230
  48. package/templates/global-install-manifest.yml +0 -91
@@ -97,43 +97,34 @@ def fail(msg: str) -> "None":
97
97
  # --- Package detection ---
98
98
 
99
99
  def detect_package_root(project_root: Path) -> Path:
100
- candidates = [
101
- project_root / "vendor" / "event4u" / "agent-config",
102
- project_root / "node_modules" / "@event4u" / "agent-config",
103
- ]
104
- for path in candidates:
105
- if path.is_dir():
106
- return path.resolve()
100
+ npm_path = project_root / "node_modules" / "@event4u" / "agent-config"
101
+ if npm_path.is_dir():
102
+ return npm_path.resolve()
107
103
 
108
104
  # Running from within the package itself (development mode)
109
105
  if (project_root / "config" / "profiles" / "minimal.ini").exists():
110
106
  return project_root
111
107
 
112
- fail("Could not find agent-config package. Run from a project with composer/npm install.")
108
+ fail(
109
+ "Could not find agent-config package. Install via "
110
+ "`npx @event4u/create-agent-config init` or `npm install --save-dev @event4u/agent-config`."
111
+ )
113
112
  return project_root # unreachable
114
113
 
115
114
 
116
115
  def detect_package_type(package_root: Path) -> str:
117
- parts = package_root.parts
118
- if "vendor" in parts:
119
- return "composer"
120
- if "node_modules" in parts:
116
+ if "node_modules" in package_root.parts:
121
117
  return "npm"
122
118
  return "local"
123
119
 
124
120
 
125
121
  def detect_package_type_for_project(project_root: Path, package_root: Path) -> str:
126
122
  """Determine package type based on where the package lives relative to the project."""
127
- composer_path = (project_root / "vendor" / "event4u" / "agent-config").resolve()
128
123
  npm_path = (project_root / "node_modules" / "@event4u" / "agent-config").resolve()
129
124
  package_resolved = package_root.resolve()
130
125
 
131
- if package_resolved == composer_path or composer_path.exists():
132
- if package_resolved == composer_path:
133
- return "composer"
134
- if package_resolved == npm_path or npm_path.exists():
135
- if package_resolved == npm_path:
136
- return "npm"
126
+ if package_resolved == npm_path:
127
+ return "npm"
137
128
  return detect_package_type(package_root)
138
129
 
139
130
 
@@ -446,7 +437,6 @@ def ensure_agent_settings(project_root: Path, package_root: Path, profile: str,
446
437
 
447
438
  def ensure_vscode_bridge(project_root: Path, package_type: str, force: bool) -> None:
448
439
  plugin_paths = {
449
- "composer": "./vendor/event4u/agent-config/plugin/agent-config",
450
440
  "npm": "./node_modules/@event4u/agent-config/plugin/agent-config",
451
441
  }
452
442
  plugin_path = plugin_paths.get(package_type, "./plugin/agent-config")
@@ -1217,188 +1207,13 @@ def _smoke_test_hooks(project_root: Path, package_root: Path) -> int:
1217
1207
  return 1 if failed else 0
1218
1208
 
1219
1209
 
1220
- # --- Global user-level install (Phase 3 road-to-simplicity-and-everywhere) ---
1210
+ # --- Global user-level install RETIRED (road-to-portable-runtime P0.5) ---
1221
1211
  #
1222
- # `scripts/install --global` ships kernel rules + a curated top-N of skills
1223
- # into per-tool user-scope directories so the agent has them in every
1224
- # project on the machine. Curation lives in templates/global-install-manifest.yml.
1225
- #
1226
- # Files are written under an `event4u/` namespace so `--global --uninstall`
1227
- # can wipe the namespace dir without touching user-added files.
1228
-
1229
- GLOBAL_NAMESPACE = "event4u"
1230
- GLOBAL_MANIFEST_REL = Path("templates") / "global-install-manifest.yml"
1231
-
1232
- # Per-tool global directories. Each surface gets a `rules/` and `skills/`
1233
- # bucket under the event4u/ namespace. claude-desktop reuses claude-code's
1234
- # ~/.claude/ (the two surfaces share the dir per Anthropic's docs).
1235
- GLOBAL_TOOL_DIRS: dict[str, dict[str, Path]] = {
1236
- "claude-code": {
1237
- "rules": Path.home() / ".claude" / "rules" / GLOBAL_NAMESPACE,
1238
- "skills": Path.home() / ".claude" / "skills" / GLOBAL_NAMESPACE,
1239
- },
1240
- "cursor": {
1241
- "rules": Path.home() / ".cursor" / "rules" / "imported" / GLOBAL_NAMESPACE / "rules",
1242
- "skills": Path.home() / ".cursor" / "rules" / "imported" / GLOBAL_NAMESPACE / "skills",
1243
- },
1244
- "windsurf": {
1245
- "rules": Path.home() / ".codeium" / "windsurf" / "global_workflows" / GLOBAL_NAMESPACE / "rules",
1246
- "skills": Path.home() / ".codeium" / "windsurf" / "global_workflows" / GLOBAL_NAMESPACE / "skills",
1247
- },
1248
- "fallback": {
1249
- "rules": Path.home() / ".config" / "agent-config" / "rules" / GLOBAL_NAMESPACE,
1250
- "skills": Path.home() / ".config" / "agent-config" / "skills" / GLOBAL_NAMESPACE,
1251
- },
1252
- }
1253
-
1254
-
1255
- def _load_global_manifest(package_root: Path) -> dict:
1256
- """Parse templates/global-install-manifest.yml without a YAML dep.
1257
-
1258
- Tiny hand-rolled parser — only handles the manifest's flat shape:
1259
- `key: value`, `- item`, and `- key: value` indented under a parent
1260
- list. We avoid pulling in PyYAML so install.py stays zero-dep.
1261
- """
1262
- path = package_root / GLOBAL_MANIFEST_REL
1263
- if not path.is_file():
1264
- fail(f"global manifest missing: {path}")
1265
- out: dict = {"kernel_rules": [], "top_skills": []}
1266
- section: str | None = None
1267
- pending: dict | None = None
1268
- for raw in path.read_text(encoding="utf-8").splitlines():
1269
- line = raw.split("#", 1)[0].rstrip()
1270
- if not line.strip():
1271
- continue
1272
- if line.startswith("kernel_rules:"):
1273
- section = "kernel_rules"; pending = None; continue
1274
- if line.startswith("top_skills:"):
1275
- section = "top_skills"; pending = None; continue
1276
- stripped = line.lstrip()
1277
- indent = len(line) - len(stripped)
1278
- if section == "kernel_rules" and stripped.startswith("- "):
1279
- out["kernel_rules"].append(stripped[2:].strip())
1280
- elif section == "top_skills":
1281
- if stripped.startswith("- id:"):
1282
- if pending is not None:
1283
- out["top_skills"].append(pending)
1284
- pending = {"id": stripped.split(":", 1)[1].strip(), "surfaces": []}
1285
- elif pending is not None and stripped.startswith("surfaces:"):
1286
- raw_list = stripped.split(":", 1)[1].strip()
1287
- if raw_list.startswith("[") and raw_list.endswith("]"):
1288
- pending["surfaces"] = [s.strip() for s in raw_list[1:-1].split(",") if s.strip()]
1289
- elif indent == 0 and not stripped.startswith("-"):
1290
- section = None
1291
- if pending is not None:
1292
- out["top_skills"].append(pending)
1293
- return out
1294
-
1295
-
1296
- def _resolve_skill_source(package_root: Path, skill_id: str) -> Path | None:
1297
- """Locate a skill's SKILL.md in the package. Prefers .claude/skills/."""
1298
- for base in (package_root / ".claude" / "skills",
1299
- package_root / ".agent-src" / "skills"):
1300
- candidate = base / skill_id / "SKILL.md"
1301
- if candidate.is_file():
1302
- return candidate.parent
1303
- return None
1304
-
1305
-
1306
- def _resolve_rule_source(package_root: Path, rule_id: str) -> Path | None:
1307
- """Locate a rule's .md in the package. Prefers .agent-src/rules/."""
1308
- for base in (package_root / ".agent-src" / "rules",
1309
- package_root / ".augment" / "rules"):
1310
- candidate = base / f"{rule_id}.md"
1311
- if candidate.is_file():
1312
- return candidate
1313
- return None
1314
-
1315
-
1316
- def _global_targets(tools: set[str]) -> dict[str, dict[str, Path]]:
1317
- """Return the subset of GLOBAL_TOOL_DIRS to write to. Fallback always on."""
1318
- targets: dict[str, dict[str, Path]] = {"fallback": GLOBAL_TOOL_DIRS["fallback"]}
1319
- for tool_id, dirs in GLOBAL_TOOL_DIRS.items():
1320
- if tool_id == "fallback":
1321
- continue
1322
- # claude-desktop shares claude-code's dir — both flip the same target.
1323
- if tool_id == "claude-code" and ("claude-code" in tools or "claude-desktop" in tools):
1324
- targets[tool_id] = dirs
1325
- elif tool_id in tools:
1326
- targets[tool_id] = dirs
1327
- return targets
1328
-
1329
-
1330
- def install_global(package_root: Path, tools: set[str], force: bool) -> int:
1331
- """S13/S14: ship kernel rules + curated skills to user-scope dirs."""
1332
- import shutil
1333
- manifest = _load_global_manifest(package_root)
1334
- targets = _global_targets(tools)
1335
-
1336
- if not QUIET:
1337
- info(f"Global install — surfaces: {', '.join(sorted(targets))}")
1338
- info(f" rules: {len(manifest['kernel_rules'])}")
1339
- info(f" skills: {len(manifest['top_skills'])}")
1340
-
1341
- for surface, dirs in targets.items():
1342
- for kind in ("rules", "skills"):
1343
- dirs[kind].mkdir(parents=True, exist_ok=True)
1344
- # Rules: copy each kernel rule (file).
1345
- for rule_id in manifest["kernel_rules"]:
1346
- src = _resolve_rule_source(package_root, rule_id)
1347
- if src is None:
1348
- warn(f"global: rule '{rule_id}' missing in package — skipped")
1349
- continue
1350
- dst = dirs["rules"] / f"{rule_id}.md"
1351
- if dst.exists() and not force and dst.read_bytes() == src.read_bytes():
1352
- continue
1353
- dst.write_bytes(src.read_bytes())
1354
- # Skills: copy each curated skill (directory).
1355
- for entry in manifest["top_skills"]:
1356
- if surface != "fallback" and surface not in entry.get("surfaces", []):
1357
- continue
1358
- src_dir = _resolve_skill_source(package_root, entry["id"])
1359
- if src_dir is None:
1360
- warn(f"global: skill '{entry['id']}' missing in package — skipped")
1361
- continue
1362
- dst_dir = dirs["skills"] / entry["id"]
1363
- if dst_dir.exists() and not force:
1364
- shutil.rmtree(dst_dir)
1365
- shutil.copytree(src_dir, dst_dir)
1366
- if not QUIET:
1367
- success(f" {surface}: {dirs['rules']}, {dirs['skills']}")
1368
- return 0
1369
-
1370
-
1371
- def uninstall_global(tools: set[str]) -> int:
1372
- """S15: remove the event4u/ namespace dir from each enabled surface."""
1373
- import shutil
1374
- targets = _global_targets(tools)
1375
- removed: list[str] = []
1376
- # Collect ancestor dirs named GLOBAL_NAMESPACE so we can drop the
1377
- # shared parent (e.g. cursor/windsurf put rules + skills as siblings
1378
- # under one event4u/ dir; removing both leaves a stranded namespace
1379
- # dir). We never delete anything not literally named event4u/.
1380
- namespace_parents: set[Path] = set()
1381
- for surface, dirs in targets.items():
1382
- for kind in ("rules", "skills"):
1383
- d = dirs[kind]
1384
- if d.exists():
1385
- shutil.rmtree(d)
1386
- removed.append(str(d))
1387
- for ancestor in d.parents:
1388
- if ancestor.name == GLOBAL_NAMESPACE:
1389
- namespace_parents.add(ancestor)
1390
- for parent in namespace_parents:
1391
- if parent.is_dir() and not any(parent.iterdir()):
1392
- parent.rmdir()
1393
- removed.append(str(parent))
1394
- if not QUIET:
1395
- if removed:
1396
- for r in removed:
1397
- success(f"removed {r}")
1398
- else:
1399
- skip("global uninstall: nothing to remove")
1400
- return 0
1401
-
1212
+ # The `--global` symlink-install scheme was retired under the npx-only
1213
+ # distribution model. The curated manifest (templates/global-install-manifest.yml)
1214
+ # and its helpers/dispatch are gone. The replacement is `npx @event4u/agent-config`
1215
+ # resolving the runtime per-invocation; the project's `.agent-settings.yml`
1216
+ # carries the version pin.
1402
1217
 
1403
1218
  # --- Argument parsing ---
1404
1219
 
@@ -1457,21 +1272,6 @@ def parse_options(argv: list[str]) -> argparse.Namespace:
1457
1272
  action="store_true",
1458
1273
  help="skip the post-install hook smoke test (default: dry-fire dispatch:hook against every installed bridge)",
1459
1274
  )
1460
- parser.add_argument(
1461
- "--global",
1462
- dest="global_install",
1463
- action="store_true",
1464
- help=(
1465
- "Phase-3 mode: ship kernel rules + curated top-N skills to user-scope "
1466
- "dirs (~/.claude/, ~/.cursor/, ~/.codeium/windsurf/, ~/.config/agent-config/) "
1467
- "so the agent has them in every project. Curation: templates/global-install-manifest.yml."
1468
- ),
1469
- )
1470
- parser.add_argument(
1471
- "--uninstall",
1472
- action="store_true",
1473
- help="With --global: remove the event4u/ namespace dir from each enabled surface.",
1474
- )
1475
1275
  return parser.parse_args(argv)
1476
1276
 
1477
1277
 
@@ -1539,17 +1339,6 @@ def main(argv: list[str]) -> int:
1539
1339
  info(f"Profile: {opts.profile}")
1540
1340
  print()
1541
1341
 
1542
- # --global: short-circuits the project-bridge path. Ships kernel rules
1543
- # + curated skills to user-scope dirs and exits. --uninstall pairs
1544
- # with --global to remove the namespace dir.
1545
- if opts.global_install:
1546
- tools = _parse_tools(opts.tools)
1547
- if opts.uninstall:
1548
- return uninstall_global(tools)
1549
- return install_global(package_root, tools, opts.force)
1550
- if opts.uninstall:
1551
- fail("--uninstall is only valid combined with --global")
1552
-
1553
1342
  ensure_agent_settings(project_root, package_root, opts.profile, opts.force)
1554
1343
 
1555
1344
  tools = _parse_tools(opts.tools)
@@ -1605,7 +1394,7 @@ def main(argv: list[str]) -> int:
1605
1394
  print()
1606
1395
  print(" Next steps:")
1607
1396
  print(" • Commit .agent-settings.yml and bridge files to your repo")
1608
- print(" • New team members just run composer install / npm install — done")
1397
+ print(" • New team members run `npx @event4u/create-agent-config init` — done")
1609
1398
  print(" • Inspect hook coverage: ./agent-config hooks:status")
1610
1399
  print(" • Full walkthrough: https://github.com/event4u-app/agent-config/blob/main/docs/getting-started.md")
1611
1400
  print()
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bash
2
2
  # install.sh — Agent-config payload sync (one of two installer stages).
3
3
  #
4
- # Reads from vendor's .agent-src/ (fallback: .augment/ for pre-2.0 packages) and
5
- # writes the target project's .augment/ tree: copies rules, symlinks everything
6
- # else. When augment.rules_use_symlinks: true is set in the target's
4
+ # Reads from the package's .agent-src/ and writes the target project's
5
+ # .augment/ tree: copies rules, symlinks everything else. When
6
+ # augment.rules_use_symlinks: true is set in the target's
7
7
  # .agent-settings.yml, rules are symlinked instead of copied.
8
8
  # Creates tool-specific directories for Claude Code, Cursor, Cline, Windsurf, Gemini.
9
9
  #
@@ -91,9 +91,6 @@ parse_args() {
91
91
  if [[ -z "$TARGET_DIR" ]]; then
92
92
  if [[ -n "${PROJECT_ROOT:-}" ]]; then
93
93
  TARGET_DIR="$PROJECT_ROOT"
94
- elif [[ "$SOURCE_DIR" == */vendor/event4u/agent-config ]]; then
95
- # Composer: vendor/event4u/agent-config → project root is 3 levels up
96
- TARGET_DIR="$(cd "$SOURCE_DIR/../../.." && pwd)"
97
94
  elif [[ "$SOURCE_DIR" == */node_modules/@event4u/agent-config ]]; then
98
95
  # npm (scoped): node_modules/@event4u/agent-config → project root is 3 levels up
99
96
  TARGET_DIR="$(cd "$SOURCE_DIR/../../.." && pwd)"
@@ -106,13 +103,11 @@ parse_args() {
106
103
  fi
107
104
  fi
108
105
 
109
- # Resolve source layout: prefer .agent-src/ (v2.0+), fallback to .augment/ (pre-2.0).
106
+ # Resolve source layout. .agent-src/ is the only supported source since v2.0.
110
107
  if [[ -d "$SOURCE_DIR/.agent-src" ]]; then
111
108
  SOURCE_PAYLOAD="$SOURCE_DIR/.agent-src"
112
- elif [[ -d "$SOURCE_DIR/.augment" ]]; then
113
- SOURCE_PAYLOAD="$SOURCE_DIR/.augment"
114
109
  else
115
- log_error "Source payload not found: $SOURCE_DIR/.agent-src (or .augment)"
110
+ log_error "Source payload not found: $SOURCE_DIR/.agent-src"
116
111
  exit 1
117
112
  fi
118
113
  }
@@ -706,7 +701,7 @@ ensure_gitignore() {
706
701
 
707
702
  # Install the consumer-facing CLI wrapper `./agent-config` at the project
708
703
  # root. Gitignored, overwritten on every install, delegates to the master
709
- # CLI shipped in the package (node_modules or vendor).
704
+ # CLI shipped in the package (node_modules) or fetched on demand via npx.
710
705
  install_cli_wrapper() {
711
706
  local project_root="$1"
712
707
  local template="$SOURCE_DIR/templates/agent-config-wrapper.sh"
@@ -2,8 +2,13 @@
2
2
  # agent-config — thin wrapper for the event4u/agent-config CLI.
3
3
  #
4
4
  # Auto-generated by the package installer. Delegates to the master CLI
5
- # shipped inside the installed package (node_modules or vendor). Do NOT
6
- # edit this file is gitignored and regenerated on every install.
5
+ # in this priority:
6
+ # 1. $AGENT_CONFIG_MASTER (escape hatch for tests / custom checkouts)
7
+ # 2. ./node_modules/@event4u/agent-config/scripts/agent-config (npm install)
8
+ # 3. globally-installed `agent-config` on $PATH (npm i -g)
9
+ # 4. `npx --yes @event4u/agent-config@latest` (last-resort, network)
10
+ #
11
+ # Do NOT edit — this file is gitignored and regenerated on every install.
7
12
  #
8
13
  # Usage:
9
14
  # ./agent-config help
@@ -17,31 +22,41 @@ set -euo pipefail
17
22
  SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
23
 
19
24
  locate_master() {
20
- local candidates=(
21
- "$SELF_DIR/node_modules/@event4u/agent-config/scripts/agent-config"
22
- "$SELF_DIR/vendor/event4u/agent-config/scripts/agent-config"
23
- )
24
- local c
25
- for c in "${candidates[@]}"; do
26
- if [[ -x "$c" ]]; then
27
- printf '%s' "$c"
28
- return 0
29
- fi
30
- done
25
+ if [[ -n "${AGENT_CONFIG_MASTER:-}" && -x "$AGENT_CONFIG_MASTER" ]]; then
26
+ printf '%s' "$AGENT_CONFIG_MASTER"
27
+ return 0
28
+ fi
29
+ local local_npm="$SELF_DIR/node_modules/@event4u/agent-config/scripts/agent-config"
30
+ if [[ -x "$local_npm" ]]; then
31
+ printf '%s' "$local_npm"
32
+ return 0
33
+ fi
34
+ local on_path
35
+ if on_path="$(command -v agent-config 2>/dev/null)" && [[ -n "$on_path" && "$on_path" != "$SELF_DIR/agent-config" ]]; then
36
+ printf '%s' "$on_path"
37
+ return 0
38
+ fi
31
39
  return 1
32
40
  }
33
41
 
34
- if ! MASTER="$(locate_master)"; then
35
- cat >&2 <<EOF
36
- ❌ agent-config: master CLI not found.
37
- Tried:
38
- - node_modules/@event4u/agent-config/scripts/agent-config
39
- - vendor/event4u/agent-config/scripts/agent-config
40
- Reinstall the package, then retry:
41
- npm install # for Node projects
42
- composer install # for PHP projects
43
- EOF
44
- exit 127
42
+ if MASTER="$(locate_master)"; then
43
+ exec "$MASTER" "$@"
44
+ fi
45
+
46
+ if command -v npx >/dev/null 2>&1; then
47
+ exec npx --yes @event4u/agent-config@latest "$@"
45
48
  fi
46
49
 
47
- exec "$MASTER" "$@"
50
+ cat >&2 <<EOF
51
+ ❌ agent-config: master CLI not found.
52
+ Tried, in order:
53
+ - \$AGENT_CONFIG_MASTER
54
+ - ./node_modules/@event4u/agent-config/scripts/agent-config
55
+ - agent-config on \$PATH (npm i -g @event4u/agent-config)
56
+ - npx @event4u/agent-config@latest (npx not installed)
57
+
58
+ Install via:
59
+ npx @event4u/create-agent-config init # one-shot setup
60
+ npm install -g @event4u/agent-config # global CLI
61
+ EOF
62
+ exit 127
@@ -25,10 +25,10 @@ instead of copying files via `install.sh`.
25
25
 
26
26
  ### Cursor / Cline / Windsurf / Augment VSCode
27
27
 
28
- These tools do **not** support plugins yet. Use the classic `install.sh` approach:
28
+ These tools do **not** support plugins yet. Use the classic installer:
29
29
 
30
30
  ```bash
31
- bash vendor/event4u/agent-config/scripts/install.sh --target .
31
+ npx @event4u/create-agent-config init --tools=cursor,cline,windsurf,augment
32
32
  ```
33
33
 
34
34
  ## What the plugin provides
package/bin/install.php DELETED
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env php
2
- <?php
3
-
4
- declare(strict_types=1);
5
-
6
- /**
7
- * Agent Config — Project Installer (Composer wrapper)
8
- *
9
- * Thin wrapper that delegates to scripts/install, the primary orchestrator.
10
- * The orchestrator chains payload sync (install.sh) and bridge generation
11
- * (install.py); this wrapper only exists so that PHP/Composer users can
12
- * invoke it via `php vendor/bin/install.php` without knowing about bash.
13
- *
14
- * Usage:
15
- * php vendor/bin/install.php # full install (every tool)
16
- * php vendor/bin/install.php --profile=balanced # pick cost profile
17
- * php vendor/bin/install.php --tools=claude-code,cursor # only those two surfaces
18
- * php vendor/bin/install.php --tools=cursor --yes # CI-friendly, single tool
19
- * php vendor/bin/install.php --list-tools # show valid tool IDs
20
- * php vendor/bin/install.php --force # overwrite existing bridges
21
- * php vendor/bin/install.php --skip-bridges # payload only
22
- * php vendor/bin/install.php --skip-sync # bridges only
23
- * php vendor/bin/install.php --help # full option list
24
- *
25
- * Valid --tools IDs (default: all):
26
- * claude-code, claude-desktop, cursor, windsurf, cline,
27
- * gemini-cli, copilot, augment, aider, codex, all
28
- */
29
-
30
- $packageRoot = dirname(__DIR__);
31
- $installer = $packageRoot . '/scripts/install';
32
-
33
- if (! file_exists($installer)) {
34
- fwrite(STDERR, " ❌ Installer script not found: {$installer}\n");
35
- exit(1);
36
- }
37
-
38
- // Forward all CLI arguments to the bash orchestrator. Drop $argv[0] (this script).
39
- $forwarded = array_slice($argv, 1);
40
- $command = array_merge(['bash', $installer], $forwarded);
41
- $escaped = array_map('escapeshellarg', $command);
42
-
43
- $exitCode = 0;
44
- passthru(implode(' ', $escaped), $exitCode);
45
- exit($exitCode);
package/composer.json DELETED
@@ -1,33 +0,0 @@
1
- {
2
- "name": "event4u/agent-config",
3
- "description": "Shared agent configuration — skills, rules, commands, guidelines, and templates for AI coding tools",
4
- "type": "library",
5
- "license": "MIT",
6
- "require": {
7
- "php": ">=8.0"
8
- },
9
- "suggest": {
10
- "@event4u/agent-memory": "Optional MCP-based memory backend (npm: @event4u/agent-memory ^1.1.0). Adds persistent agent learnings across sessions. Install with `npm install --save-dev @event4u/agent-memory` if your team wants the memory layer; otherwise agent-config falls back to file-based memory."
11
- },
12
- "bin": [
13
- "bin/install.php",
14
- "scripts/agent-config"
15
- ],
16
- "archive": {
17
- "exclude": [
18
- "/.agent-src.uncompressed",
19
- "/.compression-hashes.json",
20
- "/agents",
21
- "/tests",
22
-
23
- "/Taskfile.yml",
24
- "/.github",
25
- "/.idea",
26
- "/.claude",
27
- "/.cursor",
28
- "/.clinerules",
29
- "/.windsurfrules",
30
- "/GEMINI.md"
31
- ]
32
- }
33
- }
@@ -1,76 +0,0 @@
1
- #!/usr/bin/env bash
2
- # postinstall.sh — npm postinstall wrapper for scripts/install
3
- #
4
- # Runs the orchestrator and decides on an exit strategy that is honest
5
- # without breaking `npm install` for unrelated reasons:
6
- #
7
- # - Success → exit 0, silent.
8
- # - Soft failure → exit 0, print actionable hint. The orchestrator
9
- # already handles environment issues (no python3)
10
- # internally and continues with the payload sync.
11
- # - Hard failure → print a loud error block, print the captured output,
12
- # exit 0. `npm install` keeps working; the developer
13
- # sees exactly what failed and the command to retry.
14
- #
15
- # Delegates to scripts/install (the orchestrator). Never calls install.sh
16
- # or install.py directly.
17
-
18
- set -u
19
-
20
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21
- INSTALLER="$SCRIPT_DIR/install"
22
-
23
- if [[ ! -f "$INSTALLER" ]]; then
24
- # Nothing to do — shipping this wrapper without the installer would be a
25
- # packaging bug, not a consumer problem.
26
- echo "agent-config postinstall: $INSTALLER missing, skipping." >&2
27
- exit 0
28
- fi
29
-
30
- LOG="$(mktemp -t agent-config-postinstall.XXXXXX.log 2>/dev/null || mktemp)"
31
- trap 'rm -f "$LOG"' EXIT
32
-
33
- bash "$INSTALLER" --quiet >"$LOG" 2>&1
34
- CODE=$?
35
- if [[ $CODE -eq 0 ]]; then
36
- # Optional companion: @event4u/agent-memory. Suggest it once, only if
37
- # the consumer hasn't already installed it (locally or on PATH). The
38
- # hint is purely informational; agent-config falls back to file-based
39
- # memory when the backend is absent.
40
- if ! command -v memory >/dev/null 2>&1 \
41
- && ! command -v agent-memory >/dev/null 2>&1 \
42
- && [[ ! -d "$SCRIPT_DIR/../../@event4u/agent-memory" ]]; then
43
- cat >&2 <<'HINT'
44
- 💡 agent-config tip: install @event4u/agent-memory for persistent agent
45
- learnings across sessions (optional, dev-only):
46
-
47
- npm install --save-dev @event4u/agent-memory
48
-
49
- Skip if you don't need it — agent-config falls back to file-based memory.
50
- HINT
51
- fi
52
- exit 0
53
- fi
54
-
55
- cat >&2 <<EOF
56
-
57
- ────────────────────────────────────────────────────────────────
58
- ⚠️ agent-config: postinstall failed (exit $CODE)
59
- ────────────────────────────────────────────────────────────────
60
- Output:
61
- $(sed 's/^/ /' "$LOG")
62
-
63
- The rest of \`npm install\` continues — but agent-config was NOT
64
- installed. Re-run manually to see the full trace:
65
-
66
- bash node_modules/@event4u/agent-config/scripts/install
67
-
68
- If this keeps happening, please open an issue:
69
- https://github.com/event4u-app/agent-config/issues
70
- ────────────────────────────────────────────────────────────────
71
-
72
- EOF
73
-
74
- # Exit 0 so npm install doesn't break for unrelated consumer workflows.
75
- # The loud block above makes the failure visible — unlike `|| true`.
76
- exit 0