@sakanano-moe/agenthelm 6.6.0 → 6.8.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 +15 -15
- 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/agenthelm.py +237 -6
- package/runtime/plugins/agent-pack/runtime/agenthelm_runtime.py +19 -0
- 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/content.json +15 -0
- package/runtime/plugins/agents-init/plugin.json +2 -2
- package/runtime/plugins/agents-init/skills/agents-init/SKILL.md +14 -1
- package/runtime/plugins/agents-init/skills/agents-init/project.schema.json +18 -3
- package/runtime/plugins/agents-init/skills/agents-init/scripts/init_project.py +43 -4
- package/runtime/plugins/agents-init/skills/agents-init/scripts/scaffold.py +933 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/test_scaffold.py +551 -0
- package/runtime/plugins/agents-init/skills/agents-init/skeleton.json +64 -0
- 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/runtime/project_cli.py +69 -9
- package/runtime/plugins/project-orchestrator/runtime/project_maintain.py +380 -27
- package/runtime/plugins/project-orchestrator/runtime/project_menu.py +18 -0
- package/runtime/plugins/project-orchestrator/runtime/project_real.py +7 -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
|
@@ -34,7 +34,12 @@
|
|
|
34
34
|
"distribution": {"enum": ["marketplace", "bundled", "external"]},
|
|
35
35
|
"required": {"type": "boolean"},
|
|
36
36
|
"commit_policy": {"enum": ["explicit", "project-policy"]},
|
|
37
|
-
"push_policy": {"enum": ["explicit", "project-policy"]}
|
|
37
|
+
"push_policy": {"enum": ["explicit", "project-policy"]},
|
|
38
|
+
"source_repository": {"type": "string", "x-agenthelm-scope": "audit", "description": "审计记录:采用的来源仓库。运行时代码不读取,check 不比较,也不进脚手架指纹。"},
|
|
39
|
+
"source_ref": {"type": "string", "x-agenthelm-scope": "audit", "description": "审计记录:采用的发布 tag。同上,仅作记录。"},
|
|
40
|
+
"source_commit": {"type": "string", "x-agenthelm-scope": "audit", "description": "审计记录:采用的提交。同上,仅作记录。"},
|
|
41
|
+
"plugin_version": {"type": "string", "x-agenthelm-scope": "audit", "description": "审计记录:采用时的 project-orchestrator 版本。同上,仅作记录。"},
|
|
42
|
+
"capability_manager_version": {"type": "string", "x-agenthelm-scope": "audit", "description": "审计记录:采用时的 agent-pack 版本。同上,仅作记录。"}
|
|
38
43
|
}
|
|
39
44
|
},
|
|
40
45
|
"opinion": {
|
|
@@ -45,7 +50,8 @@
|
|
|
45
50
|
"provider": {"type": ["string", "null"]},
|
|
46
51
|
"project_overlay": {"type": ["string", "null"]},
|
|
47
52
|
"strict_mode": {"enum": ["smart", "strict", "disabled"]},
|
|
48
|
-
"loading_mode": {"enum": ["tiered", "full"]}
|
|
53
|
+
"loading_mode": {"enum": ["tiered", "full"]},
|
|
54
|
+
"tool_version": {"type": "string", "x-agenthelm-scope": "audit", "description": "审计记录:生成该 Opinion 时的工具版本;不参与比较。"}
|
|
49
55
|
}
|
|
50
56
|
},
|
|
51
57
|
"memory": {
|
|
@@ -99,7 +105,16 @@
|
|
|
99
105
|
"type": ["object", "null"], "additionalProperties": true,
|
|
100
106
|
"properties": {
|
|
101
107
|
"root": {"$ref": "#/$defs/relativePath"},
|
|
102
|
-
"artifact_mode": {"enum": ["case-local", "external-references"]}
|
|
108
|
+
"artifact_mode": {"enum": ["case-local", "external-references"]},
|
|
109
|
+
"command": {"type": ["array", "string"], "description": "项目自己的 Case 工具命令;声明后项目入口不绕过它写 Case。"},
|
|
110
|
+
"directory_naming": {
|
|
111
|
+
"type": "object", "additionalProperties": true,
|
|
112
|
+
"description": "Case 目录命名规则,check 据此检查目录名;缺省按 CASE-日期-序号-主题。fix case-naming 只给改名计划,不移动目录。",
|
|
113
|
+
"properties": {
|
|
114
|
+
"style": {"enum": ["case-id", "date-topic", "custom"]},
|
|
115
|
+
"pattern": {"type": "string", "description": "匹配整个目录名的正则;style 为 custom 时必填。"}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
103
118
|
}
|
|
104
119
|
},
|
|
105
120
|
"assets": {
|
|
@@ -715,6 +715,17 @@ def _profile_launcher(profile: dict) -> str | None:
|
|
|
715
715
|
return validate_entry_name(launcher) if isinstance(launcher, str) else None
|
|
716
716
|
|
|
717
717
|
|
|
718
|
+
def _scaffold():
|
|
719
|
+
"""scaffold.py 与本文件同目录;绑定到正在运行的本模块,保证异常类一致。"""
|
|
720
|
+
here = str(Path(__file__).resolve().parent)
|
|
721
|
+
if here not in sys.path:
|
|
722
|
+
sys.path.insert(0, here)
|
|
723
|
+
import scaffold # noqa: PLC0415
|
|
724
|
+
|
|
725
|
+
scaffold.bind(sys.modules[__name__])
|
|
726
|
+
return scaffold
|
|
727
|
+
|
|
728
|
+
|
|
718
729
|
def _unified_diff(relative: str, current: str, new: str, limit: int = 200) -> str:
|
|
719
730
|
lines = list(difflib.unified_diff(
|
|
720
731
|
current.splitlines(keepends=True), new.splitlines(keepends=True),
|
|
@@ -756,6 +767,7 @@ def refresh_project(args: argparse.Namespace, root: Path) -> tuple[int, dict]:
|
|
|
756
767
|
if accept & keep:
|
|
757
768
|
raise InitError("同一文件不能同时 --accept 与 --keep:" + ", ".join(sorted(map(str, accept & keep))))
|
|
758
769
|
plan: list[dict] = []
|
|
770
|
+
kept_before = _scaffold().keep_fingerprints(root)
|
|
759
771
|
for relative in tracked:
|
|
760
772
|
new = rendered[relative]
|
|
761
773
|
new_digest = hashlib.sha256(new.encode("utf-8")).hexdigest()
|
|
@@ -776,7 +788,10 @@ def refresh_project(args: argparse.Namespace, root: Path) -> tuple[int, dict]:
|
|
|
776
788
|
state, action = "clean", "keep" if relative in keep else "replace"
|
|
777
789
|
else:
|
|
778
790
|
state = "modified"
|
|
779
|
-
|
|
791
|
+
decided = kept_before.get(relative.as_posix()) == new_digest # 之前选过保留,且期望内容没变
|
|
792
|
+
action = "replace" if relative in accept else "keep" if relative in keep or decided else "blocked"
|
|
793
|
+
if decided and relative not in accept:
|
|
794
|
+
state = "kept-by-decision"
|
|
780
795
|
item = {"path": relative.as_posix(), "state": state, "action": action, "new_sha256": new_digest}
|
|
781
796
|
if action in {"replace", "blocked", "create"} or (state == "clean" and action == "keep"):
|
|
782
797
|
item["diff"] = _unified_diff(relative.as_posix(), current or "", new)
|
|
@@ -831,6 +846,12 @@ def refresh_project(args: argparse.Namespace, root: Path) -> tuple[int, dict]:
|
|
|
831
846
|
temporary = lock_path.with_name(f".{lock_path.name}.{os.getpid()}.tmp")
|
|
832
847
|
temporary.write_text(json_text(lock), encoding="utf-8")
|
|
833
848
|
os.replace(temporary, lock_path)
|
|
849
|
+
# 显式 --keep 的选择记入决定文件:同样的模板内容以后不再追问,模板实质变化时才再问。
|
|
850
|
+
for item in plan:
|
|
851
|
+
if Path(item["path"]) in keep and item["state"] in {"modified", "local-only", "kept-by-decision"}:
|
|
852
|
+
_scaffold().decide(root, f"content:{item['path']}", "keep", reason="refresh --keep",
|
|
853
|
+
fingerprint=item["new_sha256"], scope="project", apply=True)
|
|
854
|
+
result.setdefault("decisions", []).append(f"content:{item['path']}")
|
|
834
855
|
result["applied"] = True
|
|
835
856
|
return 0, result
|
|
836
857
|
|
|
@@ -943,8 +964,10 @@ def add_entry(args: argparse.Namespace, root: Path) -> tuple[int, dict]:
|
|
|
943
964
|
lock_path = root / CONTENT_LOCK_PATH
|
|
944
965
|
lock = _read_project_json(root, CONTENT_LOCK_PATH, "content lock") if lock_path.exists() \
|
|
945
966
|
else {"schema_version": 1, "packs": {}}
|
|
967
|
+
# 新条目只记录刚写入的启动器,它与当前模板一致,所以版本就是当前包版本;
|
|
968
|
+
# 不再写占位的 0.0.0(那会让 check 永远提示“可更新”)。
|
|
946
969
|
pack_entry = lock.setdefault("packs", {}).get(pack["id"]) or \
|
|
947
|
-
{"plugin": "agents-init", "version":
|
|
970
|
+
{"plugin": "agents-init", "version": pack["version"], "files": {}}
|
|
948
971
|
pack_entry["files"] = dict(sorted({**pack_entry.get("files", {}),
|
|
949
972
|
name: hashlib.sha256(template.encode("utf-8")).hexdigest()}.items()))
|
|
950
973
|
lock["packs"][pack["id"]] = pack_entry
|
|
@@ -964,9 +987,20 @@ def parser() -> argparse.ArgumentParser:
|
|
|
964
987
|
result.add_argument("--stack", type=split_csv)
|
|
965
988
|
result.add_argument("--runtime")
|
|
966
989
|
result.add_argument("--agent-cli", type=split_csv)
|
|
967
|
-
result.add_argument("--mode", choices=("init", "migrate", "refresh", "entry"
|
|
990
|
+
result.add_argument("--mode", choices=("init", "migrate", "refresh", "entry", "check", "add-skeleton", "profile",
|
|
991
|
+
"repair-lock", "decide", "case-naming"), default="init",
|
|
968
992
|
help="refresh:内容包更新后按 Profile 重新渲染已跟踪的模板文件;"
|
|
969
|
-
"entry:为已初始化的项目补上根目录启动器与 Profile 的 entry
|
|
993
|
+
"entry:为已初始化的项目补上根目录启动器与 Profile 的 entry 段;"
|
|
994
|
+
"check:只读,对比期望骨架清单;add-skeleton:按模板补上缺失的骨架文件;"
|
|
995
|
+
"profile:把缺失或不符的受检字段改为模板值;repair-lock:修复内容锁的 0.0.0 版本;"
|
|
996
|
+
"decide:记录或撤销对某个发现的决定;case-naming:只输出 Case 目录改名计划与受影响的引用,不移动任何目录")
|
|
997
|
+
result.add_argument("--target", action="append", default=[],
|
|
998
|
+
help="add-skeleton:骨架路径;profile:字段;decide:发现 ID(可重复;省略则处理全部可处理项)")
|
|
999
|
+
result.add_argument("--decision", choices=("skip", "keep", "later", "unskip"), help="decide:决定;unskip 撤销")
|
|
1000
|
+
result.add_argument("--reason", help="decide:理由(建议写,随项目同步)")
|
|
1001
|
+
result.add_argument("--fingerprint", help="decide:期望指纹(缺省取该发现当前的指纹)")
|
|
1002
|
+
result.add_argument("--reprompt", action="store_true", help="check:忽略已记录的决定,重新评估全部发现")
|
|
1003
|
+
result.add_argument("--state-dir", type=Path, help="机器本地决定的状态目录(缺省 $XDG_STATE_HOME 或 ~/.local/state)")
|
|
970
1004
|
result.add_argument("--entry-name", help=f"根目录启动器文件名(init 缺省 {DEFAULT_ENTRY};entry 模式必填)")
|
|
971
1005
|
result.add_argument("--no-entry", action="store_true", help="init:不生成根目录启动器")
|
|
972
1006
|
result.add_argument("--readme-entry", action="store_true",
|
|
@@ -996,6 +1030,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
996
1030
|
args = parser().parse_args(argv)
|
|
997
1031
|
try:
|
|
998
1032
|
root = validate_root(args.project)
|
|
1033
|
+
if args.mode in ("check", "add-skeleton", "profile", "repair-lock", "decide", "case-naming"):
|
|
1034
|
+
scaffold = _scaffold()
|
|
1035
|
+
code, result = scaffold.run_mode(args, root)
|
|
1036
|
+
print(json_text(result), end="") if args.output == "json" else print(scaffold.render_text(result), end="")
|
|
1037
|
+
return code
|
|
999
1038
|
if args.mode == "entry":
|
|
1000
1039
|
code, result = add_entry(args, root)
|
|
1001
1040
|
if args.output == "json":
|