@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.
- package/.agent-src/commands/fix/{pr-bots.md → pr-bot-comments.md} +3 -3
- package/.agent-src/commands/fix/{pr.md → pr-comments.md} +6 -6
- package/.agent-src/commands/fix/{pr-developers.md → pr-developer-comments.md} +3 -3
- package/.agent-src/commands/fix.md +6 -6
- package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +2 -2
- 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 +4 -4
- package/CHANGELOG.md +54 -0
- package/README.md +39 -31
- package/config/agent-settings.template.yml +25 -0
- package/docs/architecture.md +47 -1
- package/docs/catalog.md +3 -3
- package/docs/contracts/command-clusters.md +3 -3
- package/docs/contracts/file-ownership-matrix.json +9 -9
- package/docs/customization.md +125 -9
- package/docs/getting-started.md +16 -25
- package/docs/installation.md +66 -82
- package/docs/migration/v1-to-v2.md +98 -0
- package/docs/migrations/commands-1.15.0.md +3 -3
- 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/docs/skills-catalog.md +23 -2
- package/docs/troubleshooting.md +20 -32
- package/llms.txt +22 -1
- 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 +27 -40
- package/scripts/install.py +17 -228
- package/scripts/install.sh +6 -11
- package/templates/agent-config-wrapper.sh +40 -25
- package/templates/consumer-settings/README.md +2 -2
- package/bin/install.php +0 -45
- package/composer.json +0 -33
- package/scripts/postinstall.sh +0 -76
- package/scripts/setup.sh +0 -230
- package/templates/global-install-manifest.yml +0 -91
package/scripts/install.py
CHANGED
|
@@ -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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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(
|
|
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
|
-
|
|
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 ==
|
|
132
|
-
|
|
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
|
|
1210
|
+
# --- Global user-level install — RETIRED (road-to-portable-runtime P0.5) ---
|
|
1221
1211
|
#
|
|
1222
|
-
#
|
|
1223
|
-
#
|
|
1224
|
-
#
|
|
1225
|
-
#
|
|
1226
|
-
#
|
|
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
|
|
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()
|
package/scripts/install.sh
CHANGED
|
@@ -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
|
|
5
|
-
#
|
|
6
|
-
#
|
|
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
|
|
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
|
|
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
|
|
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
|
-
#
|
|
6
|
-
#
|
|
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
|
-
|
|
21
|
-
"$
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
local
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
|
28
|
+
These tools do **not** support plugins yet. Use the classic installer:
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
|
|
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
|
-
}
|
package/scripts/postinstall.sh
DELETED
|
@@ -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
|