@sakanano-moe/agenthelm 6.6.0 → 6.7.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 (23) hide show
  1. package/README.md +68 -0
  2. package/README.zh-CN.md +28 -0
  3. package/package.json +2 -2
  4. package/runtime/index.json +14 -14
  5. package/runtime/plugins/agent-pack/.codex-plugin/plugin.json +11 -4
  6. package/runtime/plugins/agent-pack/plugin.json +1 -1
  7. package/runtime/plugins/agent-pack/references/capability-config.md +29 -5
  8. package/runtime/plugins/agent-pack/runtime/agent_pack_claude.py +448 -0
  9. package/runtime/plugins/agent-pack/runtime/agent_pack_config.py +178 -22
  10. package/runtime/plugins/agent-pack/runtime/mcp_server.py +30 -8
  11. package/runtime/plugins/agent-pack/skills/agent-pack/SKILL.md +2 -2
  12. package/runtime/plugins/agents-init/.codex-plugin/plugin.json +5 -5
  13. package/runtime/plugins/agents-init/plugin.json +2 -2
  14. package/runtime/plugins/agents-init/skills/agents-init/SKILL.md +1 -1
  15. package/runtime/plugins/agents-init/skills/agents-init/skill.json +3 -3
  16. package/runtime/plugins/opinion-manager/.codex-plugin/plugin.json +5 -5
  17. package/runtime/plugins/opinion-manager/plugin.json +1 -1
  18. package/runtime/plugins/opinion-manager/skills/opinion-manager/SKILL.md +1 -1
  19. package/runtime/plugins/opinion-manager/skills/opinion-manager/agents/openai.yaml +2 -2
  20. package/runtime/plugins/project-orchestrator/.codex-plugin/plugin.json +10 -4
  21. package/runtime/plugins/project-orchestrator/plugin.json +1 -1
  22. package/runtime/plugins/project-orchestrator/skills/project-orchestrator/SKILL.md +1 -1
  23. package/runtime/plugins/project-orchestrator/skills/project-orchestrator/agents/openai.yaml +2 -2
@@ -1019,8 +1019,15 @@ def install_plugin(
1019
1019
  apply: bool = False,
1020
1020
  replace: bool = False,
1021
1021
  recovery_root: Path | None = None,
1022
+ skills: Iterable[str] = (),
1023
+ mcps: Iterable[str] = (),
1024
+ reset_overrides: bool = False,
1022
1025
  ) -> dict[str, Any]:
1023
- """Bootstrap a portable plugin into a supported client without overwriting."""
1026
+ """Bootstrap a portable plugin into a supported client without overwriting.
1027
+
1028
+ For Claude Code, ``skills`` / ``mcps`` limit the install to those components
1029
+ and local credential values on MCP entries are kept unless ``reset_overrides``.
1030
+ """
1024
1031
  plugin = plugin.expanduser().resolve()
1025
1032
  manifest, issues = validate_plugin_manifest(plugin)
1026
1033
  if issues:
@@ -1030,8 +1037,11 @@ def install_plugin(
1030
1037
  home = (home or Path.home()).expanduser().resolve()
1031
1038
  if client == "claude":
1032
1039
  return _install_plugin_claude(
1033
- plugin, name, home=home, apply=apply, replace=replace, recovery_root=recovery_root
1040
+ plugin, name, home=home, apply=apply, replace=replace, recovery_root=recovery_root,
1041
+ select_skills=set(skills), select_mcps=set(mcps), reset_overrides=reset_overrides,
1034
1042
  )
1043
+ if skills or mcps:
1044
+ raise AgentPackError("--skill / --mcp selection is supported for the claude client only")
1035
1045
  destination = _client_package_destination(client, home, name)
1036
1046
  action = "install-plugin"
1037
1047
  if destination.exists() or destination.is_symlink():
@@ -1103,6 +1113,9 @@ def _install_plugin_claude(
1103
1113
  apply: bool,
1104
1114
  replace: bool,
1105
1115
  recovery_root: Path | None,
1116
+ select_skills: set[str] | None = None,
1117
+ select_mcps: set[str] | None = None,
1118
+ reset_overrides: bool = False,
1106
1119
  ) -> dict[str, Any]:
1107
1120
  """Install a plugin into Claude Code as live links plus user-scope MCP entries.
1108
1121
 
@@ -1112,24 +1125,42 @@ def _install_plugin_claude(
1112
1125
  record ``~/.claude/agent-pack/plugins/<name>`` (symlink to the source root)
1113
1126
  that inventory uses to report the installed version.
1114
1127
  """
1128
+ import agent_pack_claude # noqa: PLC0415 - shipped next to this file
1129
+
1130
+ select_skills, select_mcps = select_skills or set(), select_mcps or set()
1131
+ selective = bool(select_skills or select_mcps)
1115
1132
  record = _claude_plugin_records(home) / name
1116
1133
  links: list[tuple[str, str, Path, Path]] = [("plugin", name, record, plugin)]
1134
+ available_skills: set[str] = set()
1117
1135
  skills_root = plugin / "skills"
1118
1136
  if skills_root.is_dir() and not skills_root.is_symlink():
1119
1137
  for child in sorted(skills_root.iterdir(), key=lambda item: item.name):
1120
1138
  if child.is_dir() and not child.name.startswith(".") and (child / "SKILL.md").is_file():
1121
- links.append(("skill", child.name, home / ".claude" / "skills" / child.name, child))
1139
+ available_skills.add(child.name)
1140
+ if not selective or child.name in select_skills:
1141
+ links.append(("skill", child.name, home / ".claude" / "skills" / child.name, child))
1142
+ unknown_skills = select_skills - available_skills
1143
+ if unknown_skills:
1144
+ raise AgentPackError("plugin has no such Skill: " + ", ".join(sorted(unknown_skills)))
1122
1145
 
1123
1146
  config_path = _claude_user_config(home)
1124
1147
  servers: dict[str, dict[str, Any]] = {}
1125
1148
  mcp_path = plugin / "mcp.json"
1126
1149
  if mcp_path.is_file():
1127
1150
  data_root = home / ".claude" / "agent-pack" / "data" / name
1128
- for server, raw in sorted(_read_json(mcp_path).get("mcpServers", {}).items()):
1151
+ declared = _read_json(mcp_path).get("mcpServers", {})
1152
+ unknown_mcps = select_mcps - set(declared)
1153
+ if unknown_mcps:
1154
+ raise AgentPackError("plugin has no such MCP server: " + ", ".join(sorted(unknown_mcps)))
1155
+ for server, raw in sorted(declared.items()):
1156
+ if selective and server not in select_mcps:
1157
+ continue
1129
1158
  parsed = _portable_mcp(server, raw)
1130
1159
  if not parsed.portable:
1131
1160
  raise AgentPackError(f"MCP {server} is not portable: " + "; ".join(parsed.issues))
1132
1161
  servers[server] = _claude_mcp_config(parsed.config, record, data_root)
1162
+ elif select_mcps:
1163
+ raise AgentPackError("plugin has no mcp.json: " + ", ".join(sorted(select_mcps)))
1133
1164
 
1134
1165
  config: dict[str, Any] = {}
1135
1166
  if servers and config_path.exists():
@@ -1151,16 +1182,17 @@ def _install_plugin_claude(
1151
1182
  else:
1152
1183
  action = "link"
1153
1184
  operations.append({"action": f"{action}-{kind}", "client": "claude", "name": item_name, "to": str(path)})
1185
+ entries: dict[str, dict[str, Any]] = {}
1154
1186
  for server, rendered in servers.items():
1155
- current = existing_servers.get(server)
1156
- if current is None:
1157
- action = "add"
1158
- elif current == rendered:
1159
- action = "keep"
1160
- else:
1161
- action = "replace"
1187
+ state, entries[server], kept = agent_pack_claude.classify_mcp(
1188
+ rendered, existing_servers.get(server), reset=reset_overrides)
1189
+ action = {"differ": "replace"}.get(state, state)
1190
+ if action == "replace":
1162
1191
  conflicts.append(f"mcp {server}: {config_path}")
1163
- operations.append({"action": f"{action}-mcp", "client": "claude", "name": server, "to": str(config_path)})
1192
+ operation = {"action": f"{action}-mcp", "client": "claude", "name": server, "to": str(config_path)}
1193
+ if kept:
1194
+ operation["kept_overrides"] = kept
1195
+ operations.append(operation)
1164
1196
  if conflicts and not replace:
1165
1197
  raise AgentPackError("Claude client already has different content: " + "; ".join(conflicts))
1166
1198
  result: dict[str, Any] = {"ok": True, "applied": apply, "operations": operations}
@@ -1192,8 +1224,8 @@ def _install_plugin_claude(
1192
1224
  os.symlink(target, path, target_is_directory=True)
1193
1225
  created.append(path)
1194
1226
  changed = {
1195
- server: rendered for server, rendered in servers.items()
1196
- if existing_servers.get(server) != rendered
1227
+ server: entry for server, entry in entries.items()
1228
+ if existing_servers.get(server) != entry
1197
1229
  }
1198
1230
  if changed:
1199
1231
  replaced = {server: existing_servers[server] for server in changed if server in existing_servers}
@@ -1287,12 +1319,23 @@ def reconcile_project(
1287
1319
  recovery_root: Path | None = None,
1288
1320
  apply: bool = False,
1289
1321
  retire_only: bool = False,
1322
+ replace: bool = False,
1323
+ reset_overrides: bool = False,
1290
1324
  ) -> dict[str, Any]:
1291
1325
  """Reconcile one project package and explicitly selected legacy components.
1292
1326
 
1293
1327
  ``retire_only`` archives the selected legacy components without installing
1294
- or replacing the project package itself.
1328
+ or replacing the project package itself. Claude Code is handled by
1329
+ ``agent_pack_claude`` (dedicated package, MCP entries, Skill links);
1330
+ ``replace`` and ``reset_overrides`` only apply there.
1295
1331
  """
1332
+ if client == "claude":
1333
+ import agent_pack_claude # noqa: PLC0415 - shipped next to this file
1334
+
1335
+ return agent_pack_claude.sync_project(
1336
+ project, home=home, apply=apply, replace=replace, reset_overrides=reset_overrides,
1337
+ recovery_root=recovery_root, retire_skills=retire_skills, retire_plugins=retire_plugins,
1338
+ retire_only=retire_only)
1296
1339
  layout = load_project_layout(project)
1297
1340
  authority = inventory_project(layout.root)
1298
1341
  package_name = _package_name(layout.root)
@@ -1402,8 +1445,12 @@ def sync_project(
1402
1445
  *,
1403
1446
  home: Path | None = None,
1404
1447
  apply: bool = False,
1448
+ replace: bool = False,
1449
+ reset_overrides: bool = False,
1450
+ recovery_root: Path | None = None,
1405
1451
  ) -> dict[str, Any]:
1406
- return reconcile_project(project, client, home=home, apply=apply)
1452
+ return reconcile_project(project, client, home=home, apply=apply, replace=replace,
1453
+ reset_overrides=reset_overrides, recovery_root=recovery_root)
1407
1454
 
1408
1455
 
1409
1456
  def _update_personal_marketplace(home: Path, package_name: str) -> None:
@@ -1425,7 +1472,81 @@ def _update_personal_marketplace(home: Path, package_name: str) -> None:
1425
1472
  _write_json_atomic(marketplace, payload)
1426
1473
 
1427
1474
 
1428
- def doctor(project: Path) -> dict[str, Any]:
1475
+ def _skill_fingerprints(package: Path) -> dict[str, str]:
1476
+ root = package / "skills"
1477
+ if not root.is_dir() or root.is_symlink():
1478
+ return {}
1479
+ found = {}
1480
+ for child in sorted(root.iterdir()):
1481
+ if child.is_dir() and not child.is_symlink() and (child / "SKILL.md").is_file():
1482
+ try:
1483
+ found[child.name] = _tree_fingerprint(child)
1484
+ except AgentPackError:
1485
+ found[child.name] = "unreadable"
1486
+ return found
1487
+
1488
+
1489
+ def _package_mcps(package: Path) -> dict[str, Any]:
1490
+ path = package / "mcp.json"
1491
+ if not path.is_file() or path.is_symlink():
1492
+ return {}
1493
+ try:
1494
+ servers = _read_json(path).get("mcpServers", {})
1495
+ except AgentPackError:
1496
+ return {}
1497
+ return servers if isinstance(servers, dict) else {}
1498
+
1499
+
1500
+ def client_drift(project: Path, client: str, home: Path | None = None) -> dict[str, Any]:
1501
+ """Compare what a client has installed for this project with the project's authority.
1502
+
1503
+ Codex and Cursor load the project package directory, so the installed package
1504
+ is compared with a freshly built one (Skills, MCP servers, the rest of the
1505
+ files). Claude Code is compared entry by entry by ``agent_pack_claude``.
1506
+ Same-name MCP servers configured by hand in the client are listed as native.
1507
+ """
1508
+ if client == "claude":
1509
+ import agent_pack_claude # noqa: PLC0415 - shipped next to this file
1510
+
1511
+ return agent_pack_claude.drift(project, home)
1512
+ home = (home or Path.home()).expanduser().resolve()
1513
+ layout = load_project_layout(project)
1514
+ authority = inventory_project(layout.root)
1515
+ name = _package_name(layout.root)
1516
+ destination = _client_package_destination(client, home, name)
1517
+ report: dict[str, Any] = {"client": client, "package": name, "missing": [], "different": [], "overrides": [],
1518
+ "stale": [], "native": [], "excluded": [], "dangling": [], "coupled": None}
1519
+ report["excluded"] = sorted(item.name for item in authority.mcps
1520
+ if item.portable and not _mcp_allowed(layout, item.name, client))
1521
+ with tempfile.TemporaryDirectory(prefix="agent-pack-drift-") as temporary:
1522
+ expected = _build_package(authority, name, Path(temporary), client)
1523
+ if destination.is_symlink() and not destination.exists():
1524
+ report["dangling"].append(f"package {name}: {destination}")
1525
+ elif not destination.is_dir():
1526
+ report["missing"].append(f"package {name}")
1527
+ report["missing"] += [f"skill {skill}" for skill in _skill_fingerprints(expected)]
1528
+ report["missing"] += [f"mcp {server}" for server in _package_mcps(expected)]
1529
+ elif _tree_fingerprint(expected) != _tree_fingerprint(destination):
1530
+ want, have = _skill_fingerprints(expected), _skill_fingerprints(destination)
1531
+ report["missing"] += [f"skill {skill}" for skill in sorted(set(want) - set(have))]
1532
+ report["stale"] += [f"skill {skill}" for skill in sorted(set(have) - set(want))]
1533
+ report["different"] += [f"skill {skill}" for skill in sorted(set(want) & set(have))
1534
+ if want[skill] != have[skill]]
1535
+ want_mcps, have_mcps = _package_mcps(expected), _package_mcps(destination)
1536
+ report["missing"] += [f"mcp {server}" for server in sorted(set(want_mcps) - set(have_mcps))]
1537
+ report["stale"] += [f"mcp {server}" for server in sorted(set(have_mcps) - set(want_mcps))]
1538
+ report["different"] += [f"mcp {server}" for server in sorted(set(want_mcps) & set(have_mcps))
1539
+ if want_mcps[server] != have_mcps[server]]
1540
+ if not (report["missing"] or report["stale"] or report["different"]):
1541
+ report["different"].append(f"package {name}: other files differ")
1542
+ native = _codex_mcps(home) if client == "codex" else _json_client_mcps(home / ".cursor" / "mcp.json")
1543
+ report["native"] = sorted(native)
1544
+ report["in_sync"] = not (report["missing"] or report["different"] or report["stale"] or report["dangling"])
1545
+ return report
1546
+
1547
+
1548
+ def doctor(project: Path, clients: Iterable[str] = (), home: Path | None = None) -> dict[str, Any]:
1549
+ """Validate the project; with ``clients``, also report each client's drift from it."""
1429
1550
  layout = load_project_layout(project)
1430
1551
  inventory = inventory_project(layout.root)
1431
1552
  issues = list(inventory.issues)
@@ -1465,7 +1586,17 @@ def doctor(project: Path) -> dict[str, Any]:
1465
1586
  issues.extend(f"credential env file missing key: {name}" for name in sorted(missing))
1466
1587
  except AgentPackError as exc:
1467
1588
  issues.append(str(exc))
1468
- return {
1589
+ reports: dict[str, Any] = {}
1590
+ for client in dict.fromkeys(clients):
1591
+ report = client_drift(layout.root, client, home)
1592
+ reports[client] = report
1593
+ for kind in ("missing", "different", "stale", "dangling"):
1594
+ issues.extend(f"{client}: {kind} {item}" for item in report[kind])
1595
+ if report["coupled"]:
1596
+ issues.append(f"{client}: install record points into another package directory: {report['coupled']}")
1597
+ warnings.extend(f"{client}: kept local override {item['name']} ({', '.join(item['keys'])})"
1598
+ for item in report["overrides"])
1599
+ result: dict[str, Any] = {
1469
1600
  "ok": not issues,
1470
1601
  "source": inventory.source,
1471
1602
  "counts": {
@@ -1476,6 +1607,9 @@ def doctor(project: Path) -> dict[str, Any]:
1476
1607
  "issues": sorted(set(issues)),
1477
1608
  "warnings": sorted(set(warnings)),
1478
1609
  }
1610
+ if reports:
1611
+ result["clients"] = reports
1612
+ return result
1479
1613
 
1480
1614
 
1481
1615
  def _print(value: dict[str, Any], output: str) -> None:
@@ -1508,6 +1642,9 @@ def build_parser() -> argparse.ArgumentParser:
1508
1642
 
1509
1643
  doctor_parser = sub.add_parser("doctor")
1510
1644
  doctor_parser.add_argument("--project", type=Path, default=Path.cwd())
1645
+ doctor_parser.add_argument("--client", action="append", default=[], choices=("codex", "cursor", "claude"),
1646
+ help="also compare this client's installed capabilities with the project (repeatable)")
1647
+ doctor_parser.add_argument("--home", type=Path)
1511
1648
 
1512
1649
  transfer_parser = sub.add_parser("transfer")
1513
1650
  transfer_parser.add_argument("--from", dest="source", required=True)
@@ -1519,9 +1656,14 @@ def build_parser() -> argparse.ArgumentParser:
1519
1656
 
1520
1657
  sync_parser = sub.add_parser("sync")
1521
1658
  sync_parser.add_argument("--project", type=Path, default=Path.cwd())
1522
- sync_parser.add_argument("--client", choices=("codex", "cursor"), required=True)
1659
+ sync_parser.add_argument("--client", choices=("codex", "cursor", "claude"), required=True)
1523
1660
  sync_parser.add_argument("--home", type=Path)
1524
1661
  sync_parser.add_argument("--apply", action="store_true")
1662
+ sync_parser.add_argument("--replace", action="store_true",
1663
+ help="claude: also replace Skills / MCP entries this package did not install")
1664
+ sync_parser.add_argument("--reset-overrides", action="store_true",
1665
+ help="claude: drop local credential values (headers / env) instead of keeping them")
1666
+ sync_parser.add_argument("--recovery-root", type=Path)
1525
1667
 
1526
1668
  bootstrap_parser = sub.add_parser("bootstrap")
1527
1669
  bootstrap_parser.add_argument("--plugin", type=Path, required=True)
@@ -1530,10 +1672,18 @@ def build_parser() -> argparse.ArgumentParser:
1530
1672
  bootstrap_parser.add_argument("--apply", action="store_true")
1531
1673
  bootstrap_parser.add_argument("--replace", action="store_true")
1532
1674
  bootstrap_parser.add_argument("--recovery-root", type=Path)
1675
+ bootstrap_parser.add_argument("--skill", action="append", default=[], help="claude: install only this Skill")
1676
+ bootstrap_parser.add_argument("--mcp", action="append", default=[], help="claude: install only this MCP server")
1677
+ bootstrap_parser.add_argument("--reset-overrides", action="store_true",
1678
+ help="claude: drop local credential values (headers / env) instead of keeping them")
1533
1679
 
1534
1680
  reconcile_parser = sub.add_parser("reconcile")
1535
1681
  reconcile_parser.add_argument("--project", type=Path, default=Path.cwd())
1536
- reconcile_parser.add_argument("--client", choices=("codex", "cursor"), required=True)
1682
+ reconcile_parser.add_argument("--client", choices=("codex", "cursor", "claude"), required=True)
1683
+ reconcile_parser.add_argument("--replace", action="store_true",
1684
+ help="claude: also replace Skills / MCP entries this package did not install")
1685
+ reconcile_parser.add_argument("--reset-overrides", action="store_true",
1686
+ help="claude: drop local credential values (headers / env) instead of keeping them")
1537
1687
  reconcile_parser.add_argument("--home", type=Path)
1538
1688
  reconcile_parser.add_argument("--retire-skill", action="append", default=[])
1539
1689
  reconcile_parser.add_argument("--retire-plugin", action="append", default=[])
@@ -1554,7 +1704,7 @@ def main(argv: list[str] | None = None) -> int:
1554
1704
  result = inventory_source(args.source, args.home).to_dict()
1555
1705
  result["ok"] = not result["issues"]
1556
1706
  elif args.command == "doctor":
1557
- result = doctor(args.project)
1707
+ result = doctor(args.project, args.client, args.home)
1558
1708
  elif args.command == "transfer":
1559
1709
  if not args.skill and not args.mcp:
1560
1710
  raise AgentPackError("select at least one --skill or --mcp")
@@ -1567,7 +1717,8 @@ def main(argv: list[str] | None = None) -> int:
1567
1717
  )
1568
1718
  elif args.command == "sync":
1569
1719
  result = sync_project(
1570
- args.project, args.client, home=args.home, apply=args.apply
1720
+ args.project, args.client, home=args.home, apply=args.apply, replace=args.replace,
1721
+ reset_overrides=args.reset_overrides, recovery_root=args.recovery_root,
1571
1722
  )
1572
1723
  elif args.command == "bootstrap":
1573
1724
  result = install_plugin(
@@ -1577,6 +1728,9 @@ def main(argv: list[str] | None = None) -> int:
1577
1728
  apply=args.apply,
1578
1729
  replace=args.replace,
1579
1730
  recovery_root=args.recovery_root,
1731
+ skills=args.skill,
1732
+ mcps=args.mcp,
1733
+ reset_overrides=args.reset_overrides,
1580
1734
  )
1581
1735
  else:
1582
1736
  result = reconcile_project(
@@ -1588,6 +1742,8 @@ def main(argv: list[str] | None = None) -> int:
1588
1742
  recovery_root=args.recovery_root,
1589
1743
  apply=args.apply,
1590
1744
  retire_only=args.retire_only,
1745
+ replace=args.replace,
1746
+ reset_overrides=args.reset_overrides,
1591
1747
  )
1592
1748
  _print(result, args.output)
1593
1749
  return 0 if result.get("ok", True) else 1
@@ -43,7 +43,7 @@ TOOLS = [
43
43
  {
44
44
  "name": "agent_pack_bootstrap",
45
45
  "title": "Install Agent Plugin",
46
- "description": "Dry-run or install a portable plugin, including agent-pack itself, into Codex, Cursor, or Claude Code without overwriting an existing package.",
46
+ "description": "Dry-run or install a portable plugin, including agent-pack itself, into Codex, Cursor, or Claude Code without overwriting an existing package. For Claude Code, skills / mcps limit the install to those components, and local credential values on MCP entries are kept unless resetOverrides.",
47
47
  "inputSchema": {
48
48
  "type": "object",
49
49
  "properties": {
@@ -53,6 +53,9 @@ TOOLS = [
53
53
  "apply": {"type": "boolean", "default": False},
54
54
  "replace": {"type": "boolean", "default": False},
55
55
  "recoveryRoot": {"type": "string"},
56
+ "skills": {"type": "array", "items": {"type": "string"}, "default": []},
57
+ "mcps": {"type": "array", "items": {"type": "string"}, "default": []},
58
+ "resetOverrides": {"type": "boolean", "default": False},
56
59
  },
57
60
  "required": ["plugin", "client"],
58
61
  "additionalProperties": False,
@@ -62,12 +65,14 @@ TOOLS = [
62
65
  {
63
66
  "name": "agent_pack_reconcile",
64
67
  "title": "Reconcile Project and Client Capabilities",
65
- "description": "Classify capability drift, safely replace the managed project package, and archive explicitly selected legacy Skills or plugins with rollback. apply defaults to false.",
68
+ "description": "Classify capability drift, safely replace the managed project package, and archive explicitly selected legacy Skills or plugins with rollback. Claude Code gets its own package, MCP entries and Skill links; local credential values are kept unless resetOverrides. apply defaults to false.",
66
69
  "inputSchema": {
67
70
  "type": "object",
68
71
  "properties": {
69
72
  "project": {"type": "string"},
70
- "client": {"type": "string", "enum": ["codex", "cursor"]},
73
+ "client": {"type": "string", "enum": ["codex", "cursor", "claude"]},
74
+ "replace": {"type": "boolean", "default": False},
75
+ "resetOverrides": {"type": "boolean", "default": False},
71
76
  "home": {"type": "string"},
72
77
  "retireSkills": {"type": "array", "items": {"type": "string"}, "default": []},
73
78
  "retirePlugins": {"type": "array", "items": {"type": "string"}, "default": []},
@@ -97,10 +102,15 @@ TOOLS = [
97
102
  {
98
103
  "name": "agent_pack_doctor",
99
104
  "title": "Validate Project Capabilities",
100
- "description": "Validate an initialized project's Agent Plugin, Skill, MCP, duplicate-name, portability, and secret boundaries.",
105
+ "description": "Validate an initialized project's Agent Plugin, Skill, MCP, duplicate-name, portability, and secret boundaries. With clients, also report each client's missing, stale and different components and kept local overrides.",
101
106
  "inputSchema": {
102
107
  "type": "object",
103
- "properties": {"project": {"type": "string"}},
108
+ "properties": {
109
+ "project": {"type": "string"},
110
+ "clients": {"type": "array", "items": {"type": "string", "enum": ["codex", "cursor", "claude"]},
111
+ "default": []},
112
+ "home": {"type": "string"},
113
+ },
104
114
  "required": ["project"],
105
115
  "additionalProperties": False,
106
116
  },
@@ -127,14 +137,17 @@ TOOLS = [
127
137
  {
128
138
  "name": "agent_pack_sync",
129
139
  "title": "Sync Project Capabilities to an Agent Client",
130
- "description": "Dry-run or install a project's portable capability package into Codex or Cursor. apply defaults to false and existing targets are never overwritten.",
140
+ "description": "Dry-run or install a project's portable capability package into Codex, Cursor or Claude Code. apply defaults to false; replaced content goes to the recovery directory. Claude Code keeps local credential values unless resetOverrides, and only replaces entries it did not install with replace.",
131
141
  "inputSchema": {
132
142
  "type": "object",
133
143
  "properties": {
134
144
  "project": {"type": "string"},
135
- "client": {"type": "string", "enum": ["codex", "cursor"]},
145
+ "client": {"type": "string", "enum": ["codex", "cursor", "claude"]},
136
146
  "home": {"type": "string"},
137
147
  "apply": {"type": "boolean", "default": False},
148
+ "replace": {"type": "boolean", "default": False},
149
+ "resetOverrides": {"type": "boolean", "default": False},
150
+ "recoveryRoot": {"type": "string"},
138
151
  },
139
152
  "required": ["project", "client"],
140
153
  "additionalProperties": False,
@@ -174,6 +187,9 @@ def _call(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
174
187
  apply=bool(arguments.get("apply", False)),
175
188
  replace=bool(arguments.get("replace", False)),
176
189
  recovery_root=recovery,
190
+ skills=arguments.get("skills", []),
191
+ mcps=arguments.get("mcps", []),
192
+ reset_overrides=bool(arguments.get("resetOverrides", False)),
177
193
  )
178
194
  if name == "agent_pack_reconcile":
179
195
  recovery_value = arguments.get("recoveryRoot")
@@ -187,6 +203,8 @@ def _call(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
187
203
  recovery_root=recovery,
188
204
  apply=bool(arguments.get("apply", False)),
189
205
  retire_only=bool(arguments.get("retireOnly", False)),
206
+ replace=bool(arguments.get("replace", False)),
207
+ reset_overrides=bool(arguments.get("resetOverrides", False)),
190
208
  )
191
209
  if name == "agent_pack_inventory":
192
210
  inventory = inventory_source(arguments["source"], home)
@@ -194,7 +212,7 @@ def _call(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
194
212
  payload["ok"] = not payload["issues"]
195
213
  return payload
196
214
  if name == "agent_pack_doctor":
197
- return doctor(Path(arguments["project"]))
215
+ return doctor(Path(arguments["project"]), arguments.get("clients", []), home)
198
216
  if name == "agent_pack_transfer":
199
217
  source = inventory_source(arguments["source"], home)
200
218
  return transfer_to_project(
@@ -205,11 +223,15 @@ def _call(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
205
223
  apply=bool(arguments.get("apply", False)),
206
224
  )
207
225
  if name == "agent_pack_sync":
226
+ recovery_value = arguments.get("recoveryRoot")
208
227
  return sync_project(
209
228
  Path(arguments["project"]),
210
229
  arguments["client"],
211
230
  home=home,
212
231
  apply=bool(arguments.get("apply", False)),
232
+ replace=bool(arguments.get("replace", False)),
233
+ reset_overrides=bool(arguments.get("resetOverrides", False)),
234
+ recovery_root=Path(recovery_value) if isinstance(recovery_value, str) else None,
213
235
  )
214
236
  raise AgentPackError(f"unknown tool: {name}")
215
237
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agent-pack
3
- description: 确定性地盘点、校验、安装、同步、迁移与对账 Agent Plugin、Skill 与 MCP server——在项目、插件目录与 Codex / Cursor / Claude 等客户端之间。当需要发现、导入、导出、同步或验证 Agent 能力,或诊断某台机器的扩展安装漂移时使用。项目生命周期(Task/Case/路由/验证/Git)不在本 Skill 内——那是 project-orchestrator。
3
+ description: Deterministically inventory, verify, install, sync, migrate and reconcile Agent Plugins, Skills and MCP servers across projects, plugin directories and clients such as Codex, Cursor and Claude. Use when discovering, importing, exporting, syncing or verifying Agent capabilities, or diagnosing extension drift on a machine. The project lifecycle (Task/Case/routing/verification/Git) is not part of this Skill; that is project-orchestrator. 中文:确定性地盘点、校验、安装、同步、迁移与对账 Agent Plugin、Skill 与 MCP server——在项目、插件目录与 Codex / Cursor / Claude 等客户端之间。当需要发现、导入、导出、同步或验证 Agent 能力,或诊断某台机器的扩展安装漂移时使用。项目生命周期(Task/Case/路由/验证/Git)不在本 Skill 内——那是 project-orchestrator。
4
4
  ---
5
5
 
6
6
  # Agent Pack(Agent 扩展能力管理)
@@ -29,7 +29,7 @@ description: 确定性地盘点、校验、安装、同步、迁移与对账 Age
29
29
  6. 只应用明确选择的组件;
30
30
  7. 对目标运行 `doctor`。
31
31
 
32
- 目标客户端尚未安装某包时,从可信本地插件根目录使用 `bootstrap`。`bootstrap` 支持 `codex` / `cursor` / `claude`;Claude 端写入 `~/.claude/skills` 符号链接与 `~/.claude.json` user-scope MCP,装完需重启 Claude Code。处理已有客户端时优先使用 `reconcile`:审阅组件状态,明确列出要退役的旧 Skill/plugin,再执行应用。只退役、不安装项目包时加 `--retire-only`。用户验收迁移前,已退役或替换的内容必须保留在报告的恢复目录(`~/.agent-pack/recovery/`)中。
32
+ 目标客户端尚未安装某包时,从可信本地插件根目录使用 `bootstrap`。`bootstrap` 支持 `codex` / `cursor` / `claude`;Claude 端写入 `~/.claude/skills` 符号链接与 `~/.claude.json` user-scope MCP,可用 `--skill` / `--mcp` 只装选中的组件,本机认证值默认保留(`--reset-overrides` 才丢弃),装完需重启 Claude Code。把项目能力同步到 Claude 用 `sync --client claude`:它为 Claude 单独生成包,只增删改本包装过的条目,不要用 `bootstrap` 指向 Codex 的包。核对客户端是否与项目一致用 `doctor --client <客户端>`。处理已有客户端时优先使用 `reconcile`:审阅组件状态,明确列出要退役的旧 Skill/plugin,再执行应用。只退役、不安装项目包时加 `--retire-only`。用户验收迁移前,已退役或替换的内容必须保留在报告的恢复目录(`~/.agent-pack/recovery/`)中。
33
33
 
34
34
  ## 打包与协议
35
35
 
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "agents-init",
3
- "version": "6.6.0",
4
- "description": "初始化或迁移 Agent 项目契约;同一分发包附带 project-orchestrator、agent-pack 与 opinion-manager。",
3
+ "version": "6.6.1",
4
+ "description": "Initialize or migrate an Agent project contract; ships with project-orchestrator, agent-pack and opinion-manager.",
5
5
  "author": {
6
6
  "name": "lc4t"
7
7
  },
8
8
  "skills": "./skills/",
9
9
  "interface": {
10
10
  "displayName": "Agents Init",
11
- "shortDescription": "初始化或迁移项目骨架与 Project Profile",
12
- "longDescription": "用 agents-init 创建或迁移可移植项目契约。project-orchestrator 管理项目生命周期,agent-pack 管理 Plugin/Skill/MCP,opinion-manager 管理用户与 Agent 之间的行为规则;三者作为独立 Plugin 随同分发。",
11
+ "shortDescription": "Initialize or migrate the project skeleton and Project Profile",
12
+ "longDescription": "Use agents-init to create or migrate a portable project contract, including a root launcher that opens the project menu without any AI agent. project-orchestrator manages the project lifecycle, agent-pack manages Plugins/Skills/MCP, and opinion-manager manages behavior rules between the user and the Agent; all three ship alongside it as separate plugins. 中文:用 agents-init 创建或迁移可移植项目契约(含不需要 AI Agent 就能打开项目菜单的根目录启动器)。project-orchestrator 管理项目生命周期,agent-pack 管理 Plugin/Skill/MCP,opinion-manager 管理用户与 Agent 之间的行为规则;三者作为独立 Plugin 随同分发。",
13
13
  "developerName": "lc4t",
14
14
  "category": "Developer Tools",
15
15
  "capabilities": [
@@ -17,6 +17,6 @@
17
17
  "project-migration",
18
18
  "opinion-configuration"
19
19
  ],
20
- "defaultPrompt": "使用 $agents-init 初始化或迁移项目,并通过 $opinion-manager 配置当前环境的 Agent 行为规则。"
20
+ "defaultPrompt": "Use $agents-init to initialize or migrate this project, then use $opinion-manager to configure the Agent behavior rules for this environment."
21
21
  }
22
22
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "agents-init",
4
- "version": "6.6.0",
5
- "description": "初始化或迁移 Agent 项目契约;同一分发包附带 project-orchestrator、agent-pack 与 opinion-manager。",
4
+ "version": "6.6.1",
5
+ "description": "Initialize or migrate an Agent project contract (AGENTS.md, Project Profile, root launcher); ships with project-orchestrator, agent-pack and opinion-manager.",
6
6
  "author": {
7
7
  "name": "lc4t"
8
8
  },
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agents-init
3
- description: 初始化仓库或将既有仓库迁移到 agents-init v6 项目骨架。用户要求初始化 Agent 协作、创建 AGENTS.md、增加 Project Profile(项目配置)或迁移旧版 agents-init 结构(含 v5 的 project-runtime Profile)时使用。本 Skill 只负责结构;project-orchestrator 负责项目生命周期,agent-pack 负责 Plugin/Skill/MCP,opinion-manager 负责用户确认的 Agent 行为规则。
3
+ description: Initialize a repository, or migrate an existing one, to the agents-init v6 project skeleton. Use when the user asks to set up Agent collaboration, create AGENTS.md, add a Project Profile, add a root launcher for the project menu, or migrate an older agents-init layout (including the v5 project-runtime Profile). This Skill only handles structure; project-orchestrator owns the project lifecycle, agent-pack owns Plugins/Skills/MCP, and opinion-manager owns user-confirmed Agent behavior rules. 中文:初始化仓库或将既有仓库迁移到 agents-init v6 项目骨架。用户要求初始化 Agent 协作、创建 AGENTS.md、增加 Project Profile(项目配置)或迁移旧版 agents-init 结构(含 v5 的 project-runtime Profile)时使用。本 Skill 只负责结构;project-orchestrator 负责项目生命周期,agent-pack 负责 Plugin/Skill/MCP,opinion-manager 负责用户确认的 Agent 行为规则。
4
4
  ---
5
5
 
6
6
  # Agents Init(项目初始化)
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "id": "agents-init",
3
- "name": "agents-init v6 / Project Profile 初始化与迁移",
4
- "description": "创建或迁移通用项目骨架与 Project Profile;随包 opinion-manager 管理用户确认的 Agent 行为规则。",
3
+ "name": "agents-init v6 / Project Profile initialization and migration",
4
+ "description": "Create or migrate a general project skeleton and Project Profile; the bundled opinion-manager manages user-confirmed Agent behavior rules.",
5
5
  "schema_version": "1.0",
6
- "version": "6.6.0",
6
+ "version": "6.6.1",
7
7
  "status": "stable",
8
8
  "language": "zh-CN",
9
9
  "license": "MIT",
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "opinion-manager",
3
- "version": "0.4.1",
4
- "description": "选择模板变体与精确版本,管理个人 Profile、锁定快照和升级预览。",
3
+ "version": "0.4.2",
4
+ "description": "Choose template variants and exact versions; manage personal Profiles, locked snapshots and upgrade previews.",
5
5
  "author": {
6
6
  "name": "lc4t"
7
7
  },
8
8
  "skills": "./skills/",
9
9
  "interface": {
10
10
  "displayName": "Opinion Manager",
11
- "shortDescription": "生成、组合、审查与维护 Agent 行为规则",
12
- "longDescription": "提供经过批准的可选规则与模板;三个主入口为直接用模板、选择部分条目、从空白开始,另可基于已授权记忆提出候选。完整内容集中确认,工具分别核验指纹;支持不可变版本、个人 Profile、锁和升级保护。公开内容仍需用户逐文件批准。",
11
+ "shortDescription": "Generate, compose, review and maintain Agent behavior rules",
12
+ "longDescription": "Offers approved, selectable rules and templates. Three main entry points: use a template as is, pick some of its items, or start from blank; candidates can also be proposed from authorized memory. The full content is confirmed once, and each fingerprint is verified by the tool; supports immutable versions, personal Profiles, locks and upgrade protection. Public content still needs the user's file-by-file approval. 中文:提供经过批准的可选规则与模板;三个主入口为直接用模板、选择部分条目、从空白开始,另可基于已授权记忆提出候选。完整内容集中确认,工具分别核验指纹;支持不可变版本、个人 Profile、锁和升级保护。公开内容仍需用户逐文件批准。",
13
13
  "developerName": "lc4t",
14
14
  "category": "Productivity",
15
15
  "capabilities": [
@@ -21,6 +21,6 @@
21
21
  "opinion-lock",
22
22
  "upgrade-preview"
23
23
  ],
24
- "defaultPrompt": "使用 $opinion-manager 为当前环境生成或维护 OPINION.md。"
24
+ "defaultPrompt": "Use $opinion-manager to generate or maintain OPINION.md for this environment."
25
25
  }
26
26
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "opinion-manager",
4
- "version": "0.4.1",
4
+ "version": "0.4.2",
5
5
  "description": "Manage Agent behavior rules through templates, partial selection, blank iteration, and authorized-memory candidates, with complete previews and exact version locks.",
6
6
  "author": {
7
7
  "name": "lc4t"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: opinion-manager
3
- description: 生成、组合、审查和维护用户与 Agent 之间的行为规则。用户要求创建或更新 OPINION.md、导入既有说明、直接用模板、选择部分条目、从空白开始、基于已授权记忆提出候选规则、维护个人 Profile、检查升级与冲突或按当前 Opinion 审查交付物时使用。
3
+ description: Generate, compose, review and maintain behavior rules between the user and the Agent. Use when the user asks to create or update OPINION.md, import existing instructions, apply a template directly, pick some of its items, start from blank, propose rule candidates from authorized memory, maintain a personal Profile, check upgrades and conflicts, or review a deliverable against the current Opinion. 中文:生成、组合、审查和维护用户与 Agent 之间的行为规则。用户要求创建或更新 OPINION.md、导入既有说明、直接用模板、选择部分条目、从空白开始、基于已授权记忆提出候选规则、维护个人 Profile、检查升级与冲突或按当前 Opinion 审查交付物时使用。
4
4
  ---
5
5
 
6
6
  # Opinion Manager(Agent 行为规则管理)
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Opinion Manager"
3
- short_description: "选择规则模板变体和版本,生成完整 Opinion 并审阅升级"
4
- default_prompt: "使用 $opinion-manager 为当前环境生成或维护 OPINION.md。"
3
+ short_description: "Generate, compose, review and maintain Agent behavior rules"
4
+ default_prompt: "Use $opinion-manager to generate or maintain OPINION.md for this environment."