@sakanano-moe/agenthelm 6.5.2 → 6.6.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/bin/agenthelm.js +1 -1
- package/package.json +2 -2
- package/runtime/index.json +80 -0
- package/runtime/plugins/agent-pack/.codex-plugin/plugin.json +19 -0
- package/runtime/plugins/agent-pack/.mcp.json +12 -0
- package/runtime/plugins/agent-pack/hooks/hooks.json +16 -0
- package/runtime/plugins/agent-pack/mcp.json +14 -0
- package/runtime/plugins/agent-pack/plugin.json +20 -0
- package/runtime/plugins/agent-pack/references/capability-config.md +124 -0
- package/runtime/{agenthelm.py → plugins/agent-pack/runtime/agenthelm.py} +111 -28
- package/runtime/plugins/agent-pack/runtime/agenthelm_runtime.py +388 -0
- package/runtime/plugins/agent-pack/skills/agent-pack/SKILL.md +46 -0
- package/runtime/plugins/agents-init/.codex-plugin/plugin.json +22 -0
- package/runtime/plugins/agents-init/content.json +31 -0
- package/runtime/plugins/agents-init/plugin.json +19 -0
- package/runtime/plugins/agents-init/skills/agents-init/AGENT.template.md +376 -0
- package/runtime/plugins/agents-init/skills/agents-init/SKILL.md +119 -0
- package/runtime/plugins/agents-init/skills/agents-init/index.html +357 -0
- package/runtime/plugins/agents-init/skills/agents-init/launcher.sh +93 -0
- package/runtime/plugins/agents-init/skills/agents-init/project.schema.json +171 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/init_project.py +1190 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/test_init_project.py +678 -0
- package/runtime/plugins/agents-init/skills/agents-init/skill.json +122 -0
- package/runtime/plugins/opinion-manager/.codex-plugin/plugin.json +26 -0
- package/runtime/plugins/opinion-manager/catalog/README.md +15 -0
- package/runtime/plugins/opinion-manager/catalog/rules/.gitkeep +0 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.align-requirements/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.assumptions-authority/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.batch-questions/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.bilingual-terms/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.calculations/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.chinese-response/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.clean-deliverable/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.complete-options/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.conclusion-first/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.concrete-titles/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.consistent-items/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.device-readable/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.editable-diagrams/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.evidence-inference/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.maintenance-chinese/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.maintenance-project-language/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.markdown-structure/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.portable-files/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.report-context/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.report-formats/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.report-review/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.response-brief/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.response-dense/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.rules-tools/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.semantic-wrap/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.solutions-proportional/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.solutions-three-level/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.tables-compact/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.tables-list-first/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.test-evidence/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.versioned-files/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/templates/.gitkeep +0 -0
- package/runtime/plugins/opinion-manager/catalog/templates/delivery-standard/compact-tables/versions/1.0.0.json +21 -0
- package/runtime/plugins/opinion-manager/catalog/templates/delivery-standard/list-first/versions/1.0.0.json +21 -0
- package/runtime/plugins/opinion-manager/catalog/templates/development-standard/chinese-maintenance/versions/1.0.0.json +15 -0
- package/runtime/plugins/opinion-manager/catalog/templates/development-standard/project-language/versions/1.0.0.json +15 -0
- package/runtime/plugins/opinion-manager/catalog/templates/response-style/brief-chinese/versions/1.0.0.json +19 -0
- package/runtime/plugins/opinion-manager/catalog/templates/response-style/dense-chinese/versions/1.0.0.json +19 -0
- package/runtime/plugins/opinion-manager/catalog/templates/scenario/concise-project/versions/1.0.0.json +39 -0
- package/runtime/plugins/opinion-manager/catalog/templates/scenario/reviewed-project/versions/1.0.0.json +39 -0
- package/runtime/plugins/opinion-manager/catalog/templates/work-method/proportional/versions/1.0.0.json +20 -0
- package/runtime/plugins/opinion-manager/catalog/templates/work-method/three-level/versions/1.0.0.json +20 -0
- package/runtime/plugins/opinion-manager/content.json +63 -0
- package/runtime/plugins/opinion-manager/plugin.json +18 -0
- package/runtime/plugins/opinion-manager/references/lock.schema.json +14 -0
- package/runtime/plugins/opinion-manager/references/onboarding.md +57 -0
- package/runtime/plugins/opinion-manager/references/profile.schema.json +56 -0
- package/runtime/plugins/opinion-manager/references/rule.schema.json +16 -0
- package/runtime/plugins/opinion-manager/references/template.schema.json +71 -0
- package/runtime/plugins/opinion-manager/references/versioned-template.schema.json +19 -0
- package/runtime/plugins/opinion-manager/references/versioning.md +263 -0
- package/runtime/plugins/opinion-manager/runtime/constants.py +7 -0
- package/runtime/plugins/opinion-manager/runtime/context.py +180 -0
- package/runtime/plugins/opinion-manager/runtime/lifecycle.py +395 -0
- package/runtime/plugins/opinion-manager/runtime/opinion_manager.py +419 -0
- package/runtime/plugins/opinion-manager/runtime/versioned.py +705 -0
- package/runtime/plugins/opinion-manager/skills/opinion-manager/SKILL.md +62 -0
- package/runtime/plugins/opinion-manager/skills/opinion-manager/agents/openai.yaml +4 -0
- package/runtime/plugins/opinion-manager/templates/.gitkeep +1 -0
- package/runtime/plugins/project-orchestrator/.codex-plugin/plugin.json +18 -0
- package/runtime/plugins/project-orchestrator/content.json +37 -0
- package/runtime/plugins/project-orchestrator/plugin.json +19 -0
- package/runtime/plugins/project-orchestrator/references/asset-synonyms.example.json +8 -0
- package/runtime/plugins/project-orchestrator/references/case-workspaces.md +159 -0
- package/runtime/plugins/project-orchestrator/references/case.schema.json +31 -0
- package/runtime/plugins/project-orchestrator/runtime/assets.py +1288 -0
- package/runtime/plugins/project-orchestrator/runtime/case.py +557 -0
- package/runtime/plugins/project-orchestrator/runtime/case_workspace.py +350 -0
- package/runtime/plugins/project-orchestrator/runtime/memory.py +363 -0
- package/runtime/plugins/project-orchestrator/runtime/project_base.py +107 -0
- package/runtime/plugins/project-orchestrator/runtime/project_cli.py +599 -0
- package/runtime/plugins/project-orchestrator/runtime/project_demo.py +504 -0
- package/runtime/plugins/project-orchestrator/runtime/project_maintain.py +470 -0
- package/runtime/plugins/project-orchestrator/runtime/project_menu.py +394 -0
- package/runtime/plugins/project-orchestrator/runtime/project_real.py +522 -0
- package/runtime/plugins/project-orchestrator/runtime/project_write.py +329 -0
- package/runtime/plugins/project-orchestrator/runtime/wf.py +439 -0
- package/runtime/plugins/project-orchestrator/skills/project-orchestrator/SKILL.md +230 -0
- package/runtime/plugins/project-orchestrator/skills/project-orchestrator/agents/openai.yaml +4 -0
- package/runtime/plugins/project-orchestrator/templates/CASE.md +39 -0
- /package/runtime/{agent_pack_config.py → plugins/agent-pack/runtime/agent_pack_config.py} +0 -0
- /package/runtime/{mcp_env_launcher.py → plugins/agent-pack/runtime/mcp_env_launcher.py} +0 -0
- /package/runtime/{mcp_server.py → plugins/agent-pack/runtime/mcp_server.py} +0 -0
package/bin/agenthelm.js
CHANGED
|
@@ -5,7 +5,7 @@ const { spawnSync } = require("node:child_process");
|
|
|
5
5
|
const { existsSync } = require("node:fs");
|
|
6
6
|
const { join } = require("node:path");
|
|
7
7
|
|
|
8
|
-
const script = join(__dirname, "..", "runtime", "agenthelm.py");
|
|
8
|
+
const script = join(__dirname, "..", "runtime", "plugins", "agent-pack", "runtime", "agenthelm.py");
|
|
9
9
|
if (!existsSync(script)) {
|
|
10
10
|
console.error("agenthelm: runtime is missing from this package; reinstall @sakanano-moe/agenthelm.");
|
|
11
11
|
process.exit(2);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sakanano-moe/agenthelm",
|
|
3
|
-
"version": "6.
|
|
4
|
-
"description": "agenthelm
|
|
3
|
+
"version": "6.6.0",
|
|
4
|
+
"description": "agenthelm 命令行:安装、检查与更新 agenthelm 插件;用户级运行时;不需要 AI Agent 的项目入口(agenthelm project)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
7
7
|
"agenthelm": "bin/agenthelm.js"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"site": {
|
|
3
|
+
"id": "agenthelm",
|
|
4
|
+
"name": "agenthelm",
|
|
5
|
+
"description": "跨 Agent 的项目管理插件集:项目契约、生命周期、扩展能力与行为规则。",
|
|
6
|
+
"version": "0.7.0",
|
|
7
|
+
"updated_at": "2026-10-25",
|
|
8
|
+
"homepage": "./index.html",
|
|
9
|
+
"domain": "agenthelm.sakanano.moe",
|
|
10
|
+
"legacy_domain": "skill.sakanano.moe"
|
|
11
|
+
},
|
|
12
|
+
"plugin": {
|
|
13
|
+
"manifest": "./plugins/agents-init/plugin.json",
|
|
14
|
+
"version": "6.6.0",
|
|
15
|
+
"install_unit": "marketplace",
|
|
16
|
+
"bundled_plugins": [
|
|
17
|
+
"./plugins/project-orchestrator/plugin.json",
|
|
18
|
+
"./plugins/agent-pack/plugin.json",
|
|
19
|
+
"./plugins/opinion-manager/plugin.json"
|
|
20
|
+
],
|
|
21
|
+
"agent_install": "./INSTALL.md",
|
|
22
|
+
"llms": "./llms.txt",
|
|
23
|
+
"bootstrap_and_init": "./scripts/bootstrap_and_init.py",
|
|
24
|
+
"marketplace": "./.claude-plugin/marketplace.json",
|
|
25
|
+
"latest": "./latest.json",
|
|
26
|
+
"cli": "npx @sakanano-moe/agenthelm"
|
|
27
|
+
},
|
|
28
|
+
"skills": [
|
|
29
|
+
{
|
|
30
|
+
"id": "agents-init",
|
|
31
|
+
"name": "agents-init v6 / Project Profile 初始化与迁移",
|
|
32
|
+
"description": "创建或迁移项目骨架;写入前验证同包 project-orchestrator、agent-pack 与 opinion-manager。",
|
|
33
|
+
"version": "6.6.0",
|
|
34
|
+
"status": "stable",
|
|
35
|
+
"entrypoints": {
|
|
36
|
+
"browser": "./plugins/agents-init/skills/agents-init/index.html",
|
|
37
|
+
"agent": "./plugins/agents-init/skills/agents-init/SKILL.md",
|
|
38
|
+
"metadata": "./plugins/agents-init/skills/agents-init/skill.json",
|
|
39
|
+
"template": "./plugins/agents-init/skills/agents-init/AGENT.template.md"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "project-orchestrator",
|
|
44
|
+
"name": "project-orchestrator / 项目生命周期编排",
|
|
45
|
+
"description": "按项目模式组织 Case 工作目录或兼容既有 Task/Case;提供检索、无损迁移、验证与受控 Git 交付;附不需要 AI Agent 的项目入口(找资料、看进展、指南、检查、维护与记录)。",
|
|
46
|
+
"version": "1.8.0",
|
|
47
|
+
"status": "stable",
|
|
48
|
+
"entrypoints": {
|
|
49
|
+
"agent": "./plugins/project-orchestrator/skills/project-orchestrator/SKILL.md",
|
|
50
|
+
"metadata": "./plugins/project-orchestrator/plugin.json",
|
|
51
|
+
"runtime": "./plugins/project-orchestrator/runtime/project_cli.py"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "agent-pack",
|
|
56
|
+
"name": "agent-pack / Agent 扩展能力管理",
|
|
57
|
+
"description": "在项目与 Codex / Cursor / Claude 之间盘点、校验、安装、同步、迁移与对账 Plugin、Skill、MCP;agenthelm 命令行与用户级运行时。",
|
|
58
|
+
"version": "1.7.0",
|
|
59
|
+
"status": "stable",
|
|
60
|
+
"entrypoints": {
|
|
61
|
+
"agent": "./plugins/agent-pack/skills/agent-pack/SKILL.md",
|
|
62
|
+
"metadata": "./plugins/agent-pack/plugin.json",
|
|
63
|
+
"mcp": "./plugins/agent-pack/mcp.json",
|
|
64
|
+
"reference": "./plugins/agent-pack/references/capability-config.md"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "opinion-manager",
|
|
69
|
+
"name": "opinion-manager / Agent 行为规则管理",
|
|
70
|
+
"description": "三个首次主入口与可选记忆候选;一次内容确认后分别保存 Profile、正文与锁,支持精确版本和个人修改保护;可选的分层加载与项目私有的候选、灰度记账。",
|
|
71
|
+
"version": "0.4.1",
|
|
72
|
+
"status": "experimental",
|
|
73
|
+
"entrypoints": {
|
|
74
|
+
"agent": "./plugins/opinion-manager/skills/opinion-manager/SKILL.md",
|
|
75
|
+
"metadata": "./plugins/opinion-manager/plugin.json",
|
|
76
|
+
"runtime": "./plugins/opinion-manager/runtime/opinion_manager.py"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-pack",
|
|
3
|
+
"version": "1.7.0",
|
|
4
|
+
"description": "Deterministic Agent-extension capability management for configured workspaces.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "lc4t"
|
|
7
|
+
},
|
|
8
|
+
"skills": "./skills/",
|
|
9
|
+
"mcpServers": "./.mcp.json",
|
|
10
|
+
"interface": {
|
|
11
|
+
"displayName": "Agent Pack",
|
|
12
|
+
"shortDescription": "盘点 / 校验 / 安装 / 同步 / 迁移 Agent Plugin、Skill 与 MCP",
|
|
13
|
+
"longDescription": "确定性的 Agent 扩展能力管理器:在项目、插件目录与 Codex / Cursor 客户端之间 inventory / doctor / bootstrap / sync / transfer / reconcile。所有写操作默认 dry-run,重名与含密钥的 MCP 条目失败关闭。项目生命周期(Task/Case/路由/验证/Git)由 project-orchestrator 承担,不在本包内。",
|
|
14
|
+
"developerName": "lc4t",
|
|
15
|
+
"category": "Developer Tools",
|
|
16
|
+
"capabilities": ["capability-inventory", "capability-doctor", "plugin-bootstrap", "client-sync", "skill-transfer", "capability-reconcile"],
|
|
17
|
+
"defaultPrompt": "Use $agent-pack to inventory this project's Agent Plugins, Skills, and MCP servers and report drift against the declared contract."
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "startup",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "python3 \"${CLAUDE_PLUGIN_ROOT}/runtime/agenthelm.py\" check --hook --hook-format claude-hook",
|
|
10
|
+
"timeout": 10
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
|
|
3
|
+
"mcpServers": {
|
|
4
|
+
"agent-pack": {
|
|
5
|
+
"type": "stdio",
|
|
6
|
+
"command": "python3",
|
|
7
|
+
"args": ["${PLUGIN_ROOT}/runtime/mcp_server.py"],
|
|
8
|
+
"env": {
|
|
9
|
+
"PYTHONPATH": "${PLUGIN_ROOT}/runtime"
|
|
10
|
+
},
|
|
11
|
+
"cwd": "${PLUGIN_ROOT}"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
+
"name": "agent-pack",
|
|
4
|
+
"version": "1.7.0",
|
|
5
|
+
"description": "Deterministic Agent-extension capability management: inventory, doctor, bootstrap, sync, transfer, and reconcile Agent Plugins / Skills / MCP servers across Codex, Cursor, Claude Code, and project sources. Ships a stdio MCP.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "lc4t"
|
|
8
|
+
},
|
|
9
|
+
"repository": "https://github.com/lc4t/agenthelm",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"agent-pack",
|
|
13
|
+
"agent-plugins",
|
|
14
|
+
"mcp",
|
|
15
|
+
"capability-management",
|
|
16
|
+
"inventory",
|
|
17
|
+
"doctor",
|
|
18
|
+
"bootstrap"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Agent 能力配置
|
|
2
|
+
|
|
3
|
+
仅在盘点、迁移、同步或验证 Agent Plugin、Skill、MCP 时读取本参考。
|
|
4
|
+
|
|
5
|
+
## 可移植项目结构
|
|
6
|
+
|
|
7
|
+
`agents-init v5` 创建以下能力包:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
.agents/
|
|
11
|
+
├── plugin.json
|
|
12
|
+
├── skills/
|
|
13
|
+
├── mcp.json
|
|
14
|
+
└── moe.sakanano.agent-pack/
|
|
15
|
+
└── project.json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- `plugin.json` 与 `mcp.json` 面向 Agent Plugins 1.0.0。
|
|
19
|
+
- `skills/` 的直接子目录包含 `SKILL.md`。
|
|
20
|
+
- `project.json` 是客户端扩展,也是机器可读的 Project Profile。
|
|
21
|
+
- 个人偏好和凭据禁止进入可移植公开模板。
|
|
22
|
+
|
|
23
|
+
## CLI
|
|
24
|
+
|
|
25
|
+
从已加载 Skill 解析插件根目录,再运行:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json inventory --source project:/path/to/project
|
|
29
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json inventory --source client:codex
|
|
30
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json inventory --source client:cursor
|
|
31
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json inventory --source client:claude
|
|
32
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json doctor --project /path/to/project
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
runtime 尚未安装时,bootstrap 包含本 Skill 的插件:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
python3 /path/to/agent-pack/runtime/agent_pack_config.py --output json bootstrap \
|
|
39
|
+
--plugin /path/to/agent-pack --client codex
|
|
40
|
+
python3 /path/to/agent-pack/runtime/agent_pack_config.py --output json bootstrap \
|
|
41
|
+
--plugin /path/to/agent-pack --client codex --apply
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`--client` 可取 `codex`、`cursor`、`claude`。Claude Code 没有 Agent Plugins 加载器,`bootstrap --client claude` 把插件投影成它实际读取的三处:
|
|
45
|
+
|
|
46
|
+
- `~/.claude/skills/<skill>` → 源插件 `skills/<skill>` 的符号链接;
|
|
47
|
+
- `~/.claude/agent-pack/plugins/<name>` → 源插件根目录的符号链接(安装记录,`inventory --source client:claude` 从这里读已安装版本);
|
|
48
|
+
- 插件 `mcp.json` 中每个 server 写入 `~/.claude.json#mcpServers`(user scope):`${PLUGIN_ROOT}` 展开为安装记录路径,`streamable-http` 映射为 `http`,丢弃 Claude 不支持的 `cwd`;其他 `${VAR}` 原样保留,由 Claude 启动时从进程环境展开。写入保留文件原权限。
|
|
49
|
+
|
|
50
|
+
Claude 端是**活链接**,不固定副本:源目录改动立即生效,版本即源 `plugin.json#version`。已存在但内容不同的链接或 MCP 条目需要 `--replace`,旧内容进入 `~/.agent-pack/recovery/<stamp>-claude-<name>/`(`replaced-skills/`、`replaced-plugins/`、`replaced-mcp.json`)。装完需重启 Claude Code 才会加载新 Skill / MCP。`sync` / `reconcile` 的项目包仍只支持 Codex 与 Cursor。
|
|
51
|
+
|
|
52
|
+
在来源与已初始化项目之间迁移选中的组件:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 预演
|
|
56
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json transfer \
|
|
57
|
+
--from client:codex --to-project /path/to/project --skill example-skill
|
|
58
|
+
|
|
59
|
+
# 审阅预演结果后再应用
|
|
60
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json transfer \
|
|
61
|
+
--from project:/path/to/source --to-project /path/to/destination \
|
|
62
|
+
--skill example-skill --mcp example-server --apply
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
为客户端构建并安装项目的可移植能力:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# 预演
|
|
69
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json sync \
|
|
70
|
+
--project /path/to/project --client cursor
|
|
71
|
+
|
|
72
|
+
# 获得授权后应用
|
|
73
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json sync \
|
|
74
|
+
--project /path/to/project --client cursor --apply
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
迁移时使用 `reconcile` 分类目标,并归档明确选择的旧组件。默认只生成只读计划:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json reconcile \
|
|
81
|
+
--project /path/to/project --client cursor \
|
|
82
|
+
--retire-skill work-engine --retire-skill ai-config-sync
|
|
83
|
+
|
|
84
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json reconcile \
|
|
85
|
+
--project /path/to/project --client cursor \
|
|
86
|
+
--retire-skill work-engine --retire-skill ai-config-sync --apply
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
只退役旧组件、不安装或替换项目包时加 `--retire-only`(至少需要一个 `--retire-skill` / `--retire-plugin`):
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
python3 <plugin-root>/runtime/agent_pack_config.py --output json reconcile \
|
|
93
|
+
--project /path/to/project --client cursor --retire-plugin project-runtime --retire-only --apply
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
结果把组件分为 `exact`、`conflict`、`missing`、`native_only` 与 `blocked`。替换包和明确退役的组件移动到 `~/.agent-pack/recovery/`;事务失败时自动恢复。
|
|
97
|
+
|
|
98
|
+
Codex 同步会写入 `~/.agents/plugins/plugins/<name>` 可移植包、`.codex-plugin` adapter 与 `~/.agents/plugins/marketplace.json` 的 `personal` 条目。之后需在 Codex 桌面版的插件设置里启用 / 重新同步 `<name>@personal`(并确认 `~/.codex/config.toml` 有 `[plugins."<name>@personal"] enabled = true`)来激活;同步只更新 marketplace 源,Codex 自身缓存 `~/.codex/plugins/cache/personal/` 要到下次激活或重启才追平。homebrew 版 `codex-cli` 无 `codex plugin` 子命令,`plugin` / `marketplace` 命令与 UI 随 Codex 版本变化,以当前 Codex 文档为准。Cursor 同步会在本地插件根目录安装真实的 Agent Plugin 目录。
|
|
99
|
+
|
|
100
|
+
## MCP Tool
|
|
101
|
+
|
|
102
|
+
随附的 stdio server 暴露以下 Tool:
|
|
103
|
+
|
|
104
|
+
- `agent_pack_inventory`
|
|
105
|
+
- `agent_pack_doctor`
|
|
106
|
+
- `agent_pack_transfer`
|
|
107
|
+
- `agent_pack_sync`
|
|
108
|
+
- `agent_pack_bootstrap`
|
|
109
|
+
- `agent_pack_reconcile`
|
|
110
|
+
|
|
111
|
+
所有会修改状态的 MCP Tool 默认使用 `apply=false`。必须先展示并审阅操作计划,才能使用 `apply=true` 重试。
|
|
112
|
+
|
|
113
|
+
## 安全与碰撞规则
|
|
114
|
+
|
|
115
|
+
- 禁止把原生客户端 MCP 配置中的凭据复制进可移植包。
|
|
116
|
+
- 拒绝包含疑似密钥环境变量、header 或 token 字面值的 MCP 条目。
|
|
117
|
+
- 迁移期间禁止跟随指向来源 Skill 目录之外的 symlink。
|
|
118
|
+
- 禁止静默覆盖现有 Skill、MCP 名称、Agent Plugin、客户端包或 marketplace 条目。
|
|
119
|
+
- 在 `native_mcp_sources` 中声明历史或客户端专属 MCP 文件。可移植条目成为打包候选;包含密钥的条目保持为 `blocked`/doctor warning,并留在客户端本地。
|
|
120
|
+
- 原生 MCP 来源可以使用 `__PROJECT_DIR__` 与 `__REPO_ROOT__`;源文件保留占位符,生成客户端包时解析为当前本地项目路径。
|
|
121
|
+
- 把 `credential_env_file` 设置为项目相对路径的 dotenv 文件,权限必须为 `0600`。生成包只保存 `${VAR}` 引用;本地 launcher 在 MCP 启动时读取值,禁止复制进 Git 或插件 manifest。
|
|
122
|
+
- 使用 `mcp_client_policy.<server>.include` 或 `.exclude` 完成客户端路由。禁止在 MCP 定义中放入 adapter 专属 `_skip` 字段。
|
|
123
|
+
- `${PLUGIN_ROOT}` 用于不可变包文件,`${PLUGIN_DATA}` 用于客户端管理的可写状态。
|
|
124
|
+
- 客户端原生 adapter 只在安装阶段使用;Agent Plugins 1.0 文件保持权威源地位。
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""agenthelm:在 Claude Code、Codex、Cursor 间安装、检查与更新 agenthelm 插件。
|
|
3
3
|
|
|
4
|
-
``
|
|
4
|
+
``project`` 打开不需要 AI Agent 的项目自助入口(由 project-orchestrator 实现);``runtime``
|
|
5
|
+
管理用户级运行时。``check`` 只读:比较各客户端已安装的插件版本、项目里由模板渲染的文件,
|
|
5
6
|
以及 Opinion 来源与云端 ``latest.json``。``install``、``update``、``migrate``
|
|
6
7
|
默认只预览,``--apply`` 才执行。通过 ``npx @sakanano-moe/agenthelm <命令>`` 或
|
|
7
8
|
``python3 <agent-pack>/runtime/agenthelm.py <命令>`` 运行。``--hook`` 供会话开始时静默调用:
|
|
@@ -255,7 +256,9 @@ def plugin_findings(latest: dict, installed: list[dict]) -> list[dict]:
|
|
|
255
256
|
for item in installed:
|
|
256
257
|
target = latest.get("plugins", {}).get(item["plugin"], {}).get("version")
|
|
257
258
|
current = item.get("version") or ""
|
|
258
|
-
if
|
|
259
|
+
if item["client"] == "legacy":
|
|
260
|
+
status = "legacy" # a pre-marketplace copy: it gets migrated, not updated
|
|
261
|
+
elif target is None:
|
|
259
262
|
status = "unknown"
|
|
260
263
|
elif is_newer(target, current):
|
|
261
264
|
status = "behind"
|
|
@@ -421,7 +424,11 @@ def render_text(report: dict) -> str:
|
|
|
421
424
|
if not report["plugins"]:
|
|
422
425
|
lines.append("未发现已安装的 agenthelm 插件。")
|
|
423
426
|
for item in report["plugins"]:
|
|
424
|
-
label = {"behind": "可更新", "current": "最新", "ahead": "高于云端", "unknown": "版本未知"
|
|
427
|
+
label = {"behind": "可更新", "current": "最新", "ahead": "高于云端", "unknown": "版本未知",
|
|
428
|
+
"legacy": "旧方式安装,待迁移"}[item["status"]]
|
|
429
|
+
if item["status"] == "legacy":
|
|
430
|
+
lines.append(f" [旧方式] {item['plugin']} {item.get('version')} {label}")
|
|
431
|
+
continue
|
|
425
432
|
line = f" [{item['client']}] {item['plugin']} {item.get('version')} → {item.get('latest')} {label}"
|
|
426
433
|
if item["status"] == "behind":
|
|
427
434
|
kinds = {"code": "代码", "content": "仅模板/样例", "both": "代码与模板"}
|
|
@@ -436,8 +443,6 @@ def render_text(report: dict) -> str:
|
|
|
436
443
|
hint = UPDATE_HINTS.get(item["client"])
|
|
437
444
|
if hint:
|
|
438
445
|
line += f"\n 更新:{hint.format(plugin=item['plugin'])}"
|
|
439
|
-
elif item["client"] == "legacy":
|
|
440
|
-
line += f"\n 旧方式安装:{UPDATE_HINTS['legacy']}"
|
|
441
446
|
lines.append(line)
|
|
442
447
|
for item in report["content"]:
|
|
443
448
|
if item.get("pack") is None:
|
|
@@ -458,6 +463,8 @@ def render_text(report: dict) -> str:
|
|
|
458
463
|
if not report["plugins"]:
|
|
459
464
|
lines.append(f"运行 `{CLI} install` 预览安装。")
|
|
460
465
|
else:
|
|
466
|
+
if any(item["status"] == "legacy" for item in report["plugins"]):
|
|
467
|
+
lines.append(f"旧方式安装可用 `{UPDATE_HINTS['legacy']}` 预览迁移。")
|
|
461
468
|
lines.append(one_line(report) or "全部为最新。")
|
|
462
469
|
return "\n".join(lines) + "\n"
|
|
463
470
|
|
|
@@ -512,9 +519,9 @@ def github_slug(latest: dict) -> str:
|
|
|
512
519
|
|
|
513
520
|
def detect_clients(env: Env) -> list[str]:
|
|
514
521
|
found = []
|
|
515
|
-
if
|
|
522
|
+
if find_cli(env, "claude") or env.claude_dir.is_dir():
|
|
516
523
|
found.append("claude")
|
|
517
|
-
if
|
|
524
|
+
if find_cli(env, "codex") or env.codex_dir.is_dir():
|
|
518
525
|
found.append("codex")
|
|
519
526
|
if (env.home / ".cursor").is_dir():
|
|
520
527
|
found.append("cursor")
|
|
@@ -608,8 +615,34 @@ class Step:
|
|
|
608
615
|
return data
|
|
609
616
|
|
|
610
617
|
|
|
611
|
-
def
|
|
612
|
-
|
|
618
|
+
def known_cli_locations(env: Env, name: str) -> list[Path]:
|
|
619
|
+
"""Where a client's command line lives when it is not on PATH (GUI apps, per-user installs)."""
|
|
620
|
+
home = env.home
|
|
621
|
+
if name == "codex":
|
|
622
|
+
bundle = "ChatGPT.app/Contents/Resources/codex-cli/bin/codex"
|
|
623
|
+
return [Path("/Applications") / bundle, home / "Applications" / bundle,
|
|
624
|
+
home / ".local/bin/codex", Path("/opt/homebrew/bin/codex"), Path("/usr/local/bin/codex")]
|
|
625
|
+
return [home / ".local/bin" / name, Path("/opt/homebrew/bin") / name, Path("/usr/local/bin") / name]
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def find_cli(env: Env, name: str) -> str | None:
|
|
629
|
+
"""A client's command line: ``AGENTHELM_<NAME>`` override, then PATH, then the usual app locations."""
|
|
630
|
+
override = env.get(f"AGENTHELM_{name.upper()}")
|
|
631
|
+
if override and os.access(override, os.X_OK):
|
|
632
|
+
return override
|
|
633
|
+
on_path = shutil.which(name, path=env.get("PATH") or "")
|
|
634
|
+
if on_path:
|
|
635
|
+
return on_path
|
|
636
|
+
for candidate in known_cli_locations(env, name):
|
|
637
|
+
if candidate.is_file() and os.access(candidate, os.X_OK):
|
|
638
|
+
return str(candidate)
|
|
639
|
+
return None
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def _cli(env: Env, name: str, *args: str) -> tuple[list[str], str | None]:
|
|
643
|
+
"""The command (using the resolved executable) and why it cannot run, if it cannot."""
|
|
644
|
+
found = find_cli(env, name)
|
|
645
|
+
return [found or name, *args], (None if found else f"未找到 {name} 命令(已查 PATH 与常见安装位置;可用 AGENTHELM_{name.upper()} 指定)")
|
|
613
646
|
|
|
614
647
|
|
|
615
648
|
def install_steps(env: Env, latest: dict, clients: list[str], plugins: list[str], source: Path | None,
|
|
@@ -618,17 +651,17 @@ def install_steps(env: Env, latest: dict, clients: list[str], plugins: list[str]
|
|
|
618
651
|
steps: list[Step] = []
|
|
619
652
|
for client in clients:
|
|
620
653
|
if client == "claude":
|
|
621
|
-
|
|
622
|
-
|
|
654
|
+
command, blocked = _cli(env, "claude", "plugin", "marketplace", "add", slug)
|
|
655
|
+
steps.append(Step("install", "claude", f"Claude Code 添加 agenthelm 市场({slug})", command, blocked=blocked))
|
|
623
656
|
for plugin in plugins:
|
|
624
|
-
|
|
625
|
-
|
|
657
|
+
command, blocked = _cli(env, "claude", "plugin", "install", f"{plugin}@{MARKETPLACE}")
|
|
658
|
+
steps.append(Step("install", "claude", f"Claude Code 安装 {plugin}", command, blocked=blocked))
|
|
626
659
|
elif client == "codex":
|
|
627
|
-
|
|
628
|
-
|
|
660
|
+
command, blocked = _cli(env, "codex", "plugin", "marketplace", "add", slug)
|
|
661
|
+
steps.append(Step("install", "codex", f"Codex 添加 agenthelm 市场({slug})", command, blocked=blocked))
|
|
629
662
|
for plugin in plugins:
|
|
630
|
-
|
|
631
|
-
|
|
663
|
+
command, blocked = _cli(env, "codex", "plugin", "add", f"{plugin}@{MARKETPLACE}")
|
|
664
|
+
steps.append(Step("install", "codex", f"Codex 安装 {plugin}", command, blocked=blocked))
|
|
632
665
|
elif client == "cursor":
|
|
633
666
|
steps.append(cursor_step(latest, plugins, source, home, "安装"))
|
|
634
667
|
return steps
|
|
@@ -655,17 +688,16 @@ def update_steps(env: Env, report: dict, project: Path | None, source: Path | No
|
|
|
655
688
|
if only in (None, "plugins"):
|
|
656
689
|
claude = sorted({item["plugin"] for item in behind if item["client"] == "claude"})
|
|
657
690
|
if claude:
|
|
658
|
-
|
|
659
|
-
|
|
691
|
+
command, blocked = _cli(env, "claude", "plugin", "marketplace", "update", MARKETPLACE)
|
|
692
|
+
steps.append(Step("update", "claude", "刷新 Claude Code 的 agenthelm 市场", command, blocked=blocked))
|
|
660
693
|
for item in [entry for entry in behind if entry["client"] == "claude"]:
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
command += ["--scope", item["scope"]]
|
|
694
|
+
extra = ["--scope", item["scope"]] if item.get("scope") and item["scope"] != "user" else []
|
|
695
|
+
command, blocked = _cli(env, "claude", "plugin", "update", f"{item['plugin']}@{MARKETPLACE}", *extra)
|
|
664
696
|
steps.append(Step("update", "claude", f"Claude Code 更新 {item['plugin']} → {item['latest']}",
|
|
665
|
-
command, blocked=
|
|
697
|
+
command, blocked=blocked))
|
|
666
698
|
if any(item["client"] == "codex" for item in behind):
|
|
667
|
-
|
|
668
|
-
|
|
699
|
+
command, blocked = _cli(env, "codex", "plugin", "marketplace", "upgrade", MARKETPLACE)
|
|
700
|
+
steps.append(Step("update", "codex", "Codex 升级 agenthelm 市场与已装插件", command, blocked=blocked))
|
|
669
701
|
cursor = sorted({item["plugin"] for item in behind if item["client"] == "cursor"})
|
|
670
702
|
if cursor:
|
|
671
703
|
steps.append(cursor_step(latest, cursor, source, home, "更新"))
|
|
@@ -799,6 +831,10 @@ def legacy_artifacts(env: Env, names: set[str]) -> list[dict]:
|
|
|
799
831
|
"path": str(env.home / ".agents/plugins/marketplace.json")})
|
|
800
832
|
for name in _codex_personal_sections(env, names):
|
|
801
833
|
artifacts.append({"kind": "codex-config-section", "name": name, "path": str(env.codex_dir / "config.toml")})
|
|
834
|
+
for name in sorted(names):
|
|
835
|
+
cache = env.codex_dir / "plugins" / "cache" / "personal" / name
|
|
836
|
+
if cache.is_dir() and not cache.is_symlink():
|
|
837
|
+
artifacts.append({"kind": "codex-personal-cache", "name": name, "path": str(cache)})
|
|
802
838
|
try:
|
|
803
839
|
servers = read_json(env.home / ".claude.json").get("mcpServers", {})
|
|
804
840
|
except (OSError, ValueError, AttributeError):
|
|
@@ -827,10 +863,17 @@ def retire_legacy(env: Env, artifacts: list[dict], recovery: Path) -> list[dict]
|
|
|
827
863
|
done: list[dict] = []
|
|
828
864
|
backed_up: set[str] = set()
|
|
829
865
|
|
|
866
|
+
def inside_recovery(path: Path) -> Path:
|
|
867
|
+
"""The path below the recovery folder: home-relative, or absolute for locations outside home."""
|
|
868
|
+
try:
|
|
869
|
+
return path.relative_to(env.home)
|
|
870
|
+
except ValueError:
|
|
871
|
+
return Path("external") / str(path).lstrip("/")
|
|
872
|
+
|
|
830
873
|
def backup(path: Path) -> None:
|
|
831
874
|
if str(path) in backed_up:
|
|
832
875
|
return
|
|
833
|
-
target = recovery / "files" / path
|
|
876
|
+
target = recovery / "files" / inside_recovery(path)
|
|
834
877
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
835
878
|
shutil.copy2(path, target)
|
|
836
879
|
backed_up.add(str(path))
|
|
@@ -840,8 +883,8 @@ def retire_legacy(env: Env, artifacts: list[dict], recovery: Path) -> list[dict]
|
|
|
840
883
|
if item.get("manual"):
|
|
841
884
|
done.append({**item, "status": "manual"})
|
|
842
885
|
continue
|
|
843
|
-
if item["kind"] in {"plugin-copy", "plugin-record", "claude-skill-link"}:
|
|
844
|
-
target = recovery / "moved" / path
|
|
886
|
+
if item["kind"] in {"plugin-copy", "plugin-record", "claude-skill-link", "codex-personal-cache"}:
|
|
887
|
+
target = recovery / "moved" / inside_recovery(path)
|
|
845
888
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
846
889
|
os.replace(path, target)
|
|
847
890
|
done.append({**item, "status": "moved", "to": str(target)})
|
|
@@ -961,6 +1004,10 @@ def parser() -> argparse.ArgumentParser:
|
|
|
961
1004
|
doctor_parser = sub.add_parser("doctor", help="报告重复安装、版本不一致、旧方式残留与检查开关")
|
|
962
1005
|
common(doctor_parser)
|
|
963
1006
|
|
|
1007
|
+
sub.add_parser("runtime", add_help=False,
|
|
1008
|
+
help="用户级运行时:install / update / use / status(日常只需要 Python,不经过 npx)")
|
|
1009
|
+
sub.add_parser("project", add_help=False,
|
|
1010
|
+
help="项目自助入口:找资料、看进展、项目指南、检查(不需要 AI Agent;详见 project --help)")
|
|
964
1011
|
locate_parser = sub.add_parser("locate", help="输出插件的已安装位置(最新优先),供下游脚本查找")
|
|
965
1012
|
locate_parser.add_argument("plugin")
|
|
966
1013
|
locate_parser.add_argument("--version", help="只要这个版本")
|
|
@@ -992,7 +1039,43 @@ def _print_steps(results: list[dict], apply: bool) -> None:
|
|
|
992
1039
|
print("以上为预览;确认后追加 --apply 执行。")
|
|
993
1040
|
|
|
994
1041
|
|
|
1042
|
+
def run_project(argv: list[str]) -> int:
|
|
1043
|
+
"""``agenthelm project``: hand over to project-orchestrator's project entry, which owns it."""
|
|
1044
|
+
installed = installed_plugins(current_env(), {"project-orchestrator"})
|
|
1045
|
+
copies = [PLUGIN_ROOT.parent / "project-orchestrator", *(Path(item["path"]) for item in installed)]
|
|
1046
|
+
# Older copies predate the project entry; only those that ship it count.
|
|
1047
|
+
usable = [path for path in copies if (path / "runtime" / "project_cli.py").is_file()]
|
|
1048
|
+
if not usable:
|
|
1049
|
+
stale = any((path / "plugin.json").is_file() for path in copies)
|
|
1050
|
+
found = "(已安装的 project-orchestrator 版本过旧)" if stale else ""
|
|
1051
|
+
print(f"agenthelm project 需要 project-orchestrator 的项目入口,但没有找到{found}。\n"
|
|
1052
|
+
f" · 更新插件:{CLI} update,或从发布包安装运行时(见 INSTALL.md)", file=sys.stderr)
|
|
1053
|
+
return 3
|
|
1054
|
+
# Prefer the copy shipped next to this agent-pack (same release), then the newest installed one.
|
|
1055
|
+
sibling = PLUGIN_ROOT.parent / "project-orchestrator"
|
|
1056
|
+
newest = sibling if sibling in usable else \
|
|
1057
|
+
max(usable, key=lambda path: version_key(_manifest_version(path) or "") or (0,))
|
|
1058
|
+
sys.path.insert(0, str(newest / "runtime"))
|
|
1059
|
+
import project_cli # noqa: PLC0415 located at run time
|
|
1060
|
+
|
|
1061
|
+
project_cli.HOST = sys.modules[__name__]
|
|
1062
|
+
return project_cli.main(argv)
|
|
1063
|
+
|
|
1064
|
+
|
|
1065
|
+
def run_runtime(argv: list[str]) -> int:
|
|
1066
|
+
"""``agenthelm runtime``: the user-level runtime that project launchers use without any AI client."""
|
|
1067
|
+
sys.path.insert(0, str(RUNTIME_DIR))
|
|
1068
|
+
import agenthelm_runtime # noqa: PLC0415 - shipped next to this file
|
|
1069
|
+
|
|
1070
|
+
return agenthelm_runtime.main(argv, sys.modules[__name__])
|
|
1071
|
+
|
|
1072
|
+
|
|
995
1073
|
def main(argv: list[str] | None = None) -> int:
|
|
1074
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
1075
|
+
if argv[:1] == ["project"]:
|
|
1076
|
+
return run_project(argv[1:])
|
|
1077
|
+
if argv[:1] == ["runtime"]:
|
|
1078
|
+
return run_runtime(argv[1:])
|
|
996
1079
|
args = parser().parse_args(argv)
|
|
997
1080
|
env = current_env(args.home)
|
|
998
1081
|
home = env.home
|