@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.
- package/README.md +68 -0
- package/README.zh-CN.md +28 -0
- package/package.json +2 -2
- package/runtime/index.json +14 -14
- package/runtime/plugins/agent-pack/.codex-plugin/plugin.json +11 -4
- package/runtime/plugins/agent-pack/plugin.json +1 -1
- package/runtime/plugins/agent-pack/references/capability-config.md +29 -5
- package/runtime/plugins/agent-pack/runtime/agent_pack_claude.py +448 -0
- package/runtime/plugins/agent-pack/runtime/agent_pack_config.py +178 -22
- package/runtime/plugins/agent-pack/runtime/mcp_server.py +30 -8
- package/runtime/plugins/agent-pack/skills/agent-pack/SKILL.md +2 -2
- package/runtime/plugins/agents-init/.codex-plugin/plugin.json +5 -5
- package/runtime/plugins/agents-init/plugin.json +2 -2
- package/runtime/plugins/agents-init/skills/agents-init/SKILL.md +1 -1
- package/runtime/plugins/agents-init/skills/agents-init/skill.json +3 -3
- package/runtime/plugins/opinion-manager/.codex-plugin/plugin.json +5 -5
- package/runtime/plugins/opinion-manager/plugin.json +1 -1
- package/runtime/plugins/opinion-manager/skills/opinion-manager/SKILL.md +1 -1
- package/runtime/plugins/opinion-manager/skills/opinion-manager/agents/openai.yaml +2 -2
- package/runtime/plugins/project-orchestrator/.codex-plugin/plugin.json +10 -4
- package/runtime/plugins/project-orchestrator/plugin.json +1 -1
- package/runtime/plugins/project-orchestrator/skills/project-orchestrator/SKILL.md +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
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
|
-
|
|
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:
|
|
1196
|
-
if existing_servers.get(server) !=
|
|
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
|
|
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
|
-
|
|
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": {
|
|
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
|
|
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:
|
|
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
|
|
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.
|
|
4
|
-
"description": "
|
|
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": "
|
|
12
|
-
"longDescription": "
|
|
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": "
|
|
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.
|
|
5
|
-
"description": "
|
|
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:
|
|
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": "
|
|
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.
|
|
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.
|
|
4
|
-
"description": "
|
|
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": "
|
|
12
|
-
"longDescription": "
|
|
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": "
|
|
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.
|
|
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:
|
|
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: "
|
|
4
|
-
default_prompt: "
|
|
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."
|