@event4u/agent-config 1.41.1 → 2.0.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.
@@ -1217,188 +1217,13 @@ def _smoke_test_hooks(project_root: Path, package_root: Path) -> int:
1217
1217
  return 1 if failed else 0
1218
1218
 
1219
1219
 
1220
- # --- Global user-level install (Phase 3 road-to-simplicity-and-everywhere) ---
1220
+ # --- Global user-level install RETIRED (road-to-portable-runtime P0.5) ---
1221
1221
  #
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
-
1222
+ # The `--global` symlink-install scheme was retired under the npx-only
1223
+ # distribution model. The curated manifest (templates/global-install-manifest.yml)
1224
+ # and its helpers/dispatch are gone. The replacement is `npx @event4u/agent-config`
1225
+ # resolving the runtime per-invocation; the project's `.agent-settings.yml`
1226
+ # carries the version pin.
1402
1227
 
1403
1228
  # --- Argument parsing ---
1404
1229
 
@@ -1457,21 +1282,6 @@ def parse_options(argv: list[str]) -> argparse.Namespace:
1457
1282
  action="store_true",
1458
1283
  help="skip the post-install hook smoke test (default: dry-fire dispatch:hook against every installed bridge)",
1459
1284
  )
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
1285
  return parser.parse_args(argv)
1476
1286
 
1477
1287
 
@@ -1539,17 +1349,6 @@ def main(argv: list[str]) -> int:
1539
1349
  info(f"Profile: {opts.profile}")
1540
1350
  print()
1541
1351
 
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
1352
  ensure_agent_settings(project_root, package_root, opts.profile, opts.force)
1554
1353
 
1555
1354
  tools = _parse_tools(opts.tools)
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
@@ -1,91 +0,0 @@
1
- # Global Install Manifest — road-to-simplicity-and-everywhere Phase 3
2
- #
3
- # Curated subset of rules + skills shipped to user-scope directories
4
- # (~/.claude/, ~/.cursor/, ~/.codeium/windsurf/, ~/.config/agent-config/)
5
- # by `scripts/install --global`. Without curation we'd dump 200+ skills
6
- # into user dirs — that is the failure mode anthropics/claude-code#53950
7
- # flagged.
8
- #
9
- # Schema (v1):
10
- # kernel_rules: list[str] — rule IDs from router.json's `kernel`.
11
- # Always shipped to every enabled surface.
12
- # top_skills: list[entry] — curated universal skills.
13
- # entry.id : skill directory under .agent-src/skills/
14
- # entry.surfaces : subset of [claude-code, cursor, windsurf, fallback]
15
- # — fallback is always written.
16
- #
17
- # Source: kernel_rules mirrors router.json; top_skills is a 15-pick
18
- # distilled from .agent-src/skills/ — universal coding-loop helpers
19
- # (work, commit, PR, review, quality, analyze, handoff). Per-stack
20
- # skills (laravel, react-shadcn-ui, php-coder) stay project-local.
21
- #
22
- # Per-tool target paths (handled by scripts/install.py global subcommand):
23
- # claude-code : ~/.claude/skills/event4u/<id>/SKILL.md
24
- # ~/.claude/rules/event4u/<rule>.md
25
- # cursor : ~/.cursor/rules/imported/event4u/<rule>.mdc
26
- # ~/.cursor/skills/event4u/<id>/ (skills surface; mirrors agent-os)
27
- # windsurf : ~/.codeium/windsurf/global_workflows/event4u/<id>.md
28
- # fallback : ~/.config/agent-config/{rules,skills}/event4u/
29
- #
30
- # All writes are namespaced under `event4u/` so `--global --uninstall`
31
- # can `rm -rf` the namespace dir without touching user-added files.
32
-
33
- schema_version: 1
34
-
35
- # Always-loaded rules — mirrors router.json's `kernel` array.
36
- # Symlinked / copied into every enabled surface's rules dir.
37
- kernel_rules:
38
- - agent-authority
39
- - ask-when-uncertain
40
- - commit-policy
41
- - direct-answers
42
- - language-and-tone
43
- - no-cheap-questions
44
- - non-destructive-by-default
45
- - scope-control
46
- - verify-before-complete
47
-
48
- # Top-N curated skills. The 15 picks below are coding-loop universals:
49
- # the agent reaches for them on almost any task, regardless of stack.
50
- # Per-stack and domain skills are deliberately omitted — they belong in
51
- # project-local installs where the stack signal is unambiguous.
52
- top_skills:
53
- # --- Workflow drivers (most-touched commands) ---
54
- - id: work
55
- surfaces: [claude-code, cursor, windsurf, fallback]
56
- - id: commit
57
- surfaces: [claude-code, cursor, windsurf, fallback]
58
- - id: create-pr
59
- surfaces: [claude-code, cursor, windsurf, fallback]
60
- - id: review-changes
61
- surfaces: [claude-code, cursor, windsurf, fallback]
62
- - id: quality-fix
63
- surfaces: [claude-code, cursor, windsurf, fallback]
64
-
65
- # --- Session / context management ---
66
- - id: agent-handoff
67
- surfaces: [claude-code, cursor, windsurf, fallback]
68
- - id: agent-status
69
- surfaces: [claude-code, cursor, windsurf, fallback]
70
- - id: project-analyze
71
- surfaces: [claude-code, cursor, windsurf, fallback]
72
- - id: project-health
73
- surfaces: [claude-code, cursor, windsurf, fallback]
74
-
75
- # --- Investigation / debugging ---
76
- - id: bug-investigate
77
- surfaces: [claude-code, cursor, windsurf, fallback]
78
- - id: bug-fix
79
- surfaces: [claude-code, cursor, windsurf, fallback]
80
- - id: systematic-debugging
81
- surfaces: [claude-code, cursor, windsurf, fallback]
82
-
83
- # --- Ticket / PR drivers ---
84
- - id: implement-ticket
85
- surfaces: [claude-code, cursor, windsurf, fallback]
86
- - id: prepare-for-review
87
- surfaces: [claude-code, cursor, windsurf, fallback]
88
-
89
- # --- Onboarding (single command, surfaces a project) ---
90
- - id: onboard
91
- surfaces: [claude-code, cursor, windsurf, fallback]