@xgent-ai/skills 0.7.8 → 0.7.9

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 (44) hide show
  1. package/README.md +8 -19
  2. package/bin/xgent-skills.js +23 -55
  3. package/dist/phoenix-diagram/VERSION.json +18 -18
  4. package/dist/phoenix-diagram/bundle/ENGINE.json +18 -18
  5. package/dist/phoenix-diagram/bundle/manifest.json +4 -4
  6. package/dist/phoenix-diagram/bundle/references/migration.md +1 -1
  7. package/dist/phoenix-diagram/bundle/scripts/phoenix-bootstrap.cjs +4 -103
  8. package/dist/phoenix-ui/THIRD-PARTY.json +1 -1
  9. package/dist/phoenix-ui/VERSION.json +19 -19
  10. package/dist/phoenix-ui/bundle/blobs/{567c3f42394f9aee9b048a001016a7ba9d8773d78027f8c995c379886634e719 → 0c95d5c7163bc162cca460e2297ab146e331fd279a738a2055643b45b161c706} +1 -1
  11. package/dist/phoenix-ui/bundle/blobs/{f077545caeb3ecb9f51a3f703a09736ca924869f4b4345c1b1ffbd9bf927a287 → 14bd6a733ce89fcd02bf6dcf0f91378625cb7739a0baaca9cb7611c332ac4580} +1 -1
  12. package/dist/phoenix-ui/bundle/blobs/{125bc7030d7f2ba789d7b1d5f474af34f8902b764e7bc351b98f1f6f55779929 → 14f329a7925311958685a16ba370c4997e924d909091e0977e5d32b3e64ecc4f} +1 -1
  13. package/dist/phoenix-ui/bundle/blobs/{8078b804bea630ed9c4ea07ec2f8ac92aba4128725860a66599eda90390d98f6 → 2b168c9ff212b8daea0d4334c94810458a683cde62f26b985855f8bf3f959db7} +1 -1
  14. package/dist/phoenix-ui/bundle/blobs/{00f4a6a630f31eb05fbdd5725e4a294233d24574bd70f4f32f4597d8256a7c1c → 2fbbc46784d2ab789675941ce81d91822ac00bd5c3e144d06743d0b8ccaccc90} +2 -17
  15. package/dist/phoenix-ui/bundle/blobs/{90f8b07ec12b78e633e306322cda58415aea70d8e0f9ff85bd439361c01dabe1 → 35913e9a7ac47f91c8af70867c71d938aa7f200b562c867fa6a43c4a6209ef61} +1 -1
  16. package/dist/phoenix-ui/bundle/blobs/{0e684572eb98d24b39cd70fa23e66c163178f5a11756dcf8a98fe2335636b33f → 3b62a4d253765fe0bdb180581cf493434d15de94e1b31470c573c0a21ece9e31} +1 -1
  17. package/dist/phoenix-ui/bundle/blobs/{68b454a99ba72c3b9b93ed96c4267847297169f6a5bf321b20e60d637c271068 → 557baa7f9ea3d863f40602f37a85e9af09f9120cfa0669b0b95539774e87b9dc} +1 -1
  18. package/dist/phoenix-ui/bundle/blobs/{c0a70ab2889e5c16e1f09249aefad948ce352c42cfe2b5afb758a4524e1b21b6 → 67fc771aa87556ecc8236c44e76251a41fa4e1809c6ad8c6a1f8eb9de91bb2d7} +1 -1
  19. package/dist/phoenix-ui/bundle/blobs/{88b150cb804bc141b80a5687a091f61b99627155c465a27adb88546ef615af92 → 6b3068f23e684f8566e7f4b069a5554624f479d57ff9073610a0917fe5b5e5af} +1 -1
  20. package/dist/phoenix-ui/bundle/blobs/{61450dbef64859e3136cd17ae3e997134d5df5721d96985c4e07367f38522129 → 6f600dfa082ed593159d3906d108baff9f4cb6bf90817a47429d613a95771d6f} +1 -1
  21. package/dist/phoenix-ui/bundle/blobs/{36310febe3e7bd4c9b21407603f2a04afefdc87b7821758bb47098b1b2876765 → 7bff2e1985a347235ef84b783f801eb3f5872d6e2a8d0278fc8c52c3cd1ac4a4} +1 -1
  22. package/dist/phoenix-ui/bundle/blobs/{e46d63e5f8a6ead1d44c8b6302a0c7247af8585b975ab5715518381777622ede → 821bda87cb9fc9f4349cf5243e18d7b4afc899ee884e33b16007ca3f1f5c9de1} +1 -1
  23. package/dist/phoenix-ui/bundle/blobs/{cadc2daefcf238aab981fa56388381d3d5ab9c0686bb9dd8187035c9933dfcee → 8291ff7c0eef14199f7d209cb8eb5e6643034c1923805b5c7023c93010d7af6e} +1 -1
  24. package/dist/phoenix-ui/bundle/blobs/{6b8d4c71355429a0b16bf367163fea46103a0a99b4e34f87608a39d85b1b980a → 94e595bc8239f72e0f5389f83aa022b18541839fb651d87b5933e89c92836a15} +1 -1
  25. package/dist/phoenix-ui/bundle/blobs/{55277fa15eb4bd2f09bf7bb24c4e79df417b06654b1e10f502c7928b192fd7cc → ba015e8aac17711d7dfbcfce3e8c92190af383af595b9ca58b7df2c0c30ba579} +1 -1
  26. package/dist/phoenix-ui/bundle/blobs/{3625c5c2750f117021232ce81c1e39ac5ef7c21445729327b88cf54dfc937df0 → fddd4bd853ad367d7f755477ee7c5ac3d0d3965c81c8a8e20eb83edc9cb7d43b} +1 -1
  27. package/dist/phoenix-ui/bundle/manifest.json +34 -34
  28. package/docs/phoenix-diagram.md +8 -6
  29. package/{skills/review-ui/references/phoenix-review.md → docs/phoenix-ui-review.md} +7 -12
  30. package/docs/phoenix-ui.md +17 -19
  31. package/package.json +2 -1
  32. package/skills/apply-ui-review/SKILL.md +1 -1
  33. package/skills/optimize-agents-md/SKILL.md +39 -21
  34. package/skills/optimize-agents-md/assets/coding-principles.md +78 -38
  35. package/skills/phoenix-diagram/ENGINE.json +18 -18
  36. package/skills/phoenix-diagram/references/migration.md +1 -1
  37. package/skills/phoenix-diagram/scripts/phoenix-bootstrap.cjs +4 -103
  38. package/skills/phoenix-ui/GENERATED.json +78 -78
  39. package/skills/phoenix-ui/SKILL.md +1 -1
  40. package/skills/phoenix-ui/scripts/ENGINE.json +19 -19
  41. package/skills/phoenix-ui/scripts/phoenix-bootstrap.cjs +2 -17
  42. package/skills/review-ui/SKILL.md +2 -1
  43. package/skills/xgent-init/SKILL.md +1 -1
  44. package/skills/review-ui/references/rules.v1.json +0 -1853
package/README.md CHANGED
@@ -22,7 +22,7 @@ npx skills add XGENT-ai/skills --skill dev-plan
22
22
 
23
23
  ## 项目辅助工具(Claude Code / Codex)
24
24
 
25
- 本仓库同时以 [`@xgent-ai/skills`](https://www.npmjs.com/package/@xgent-ai/skills) 发布到 npm,自带 `xgent-skills` 命令,可为任意项目安装 XGENT 的 Claude Code hooks(当前包含 statusline)、在项目 `.claude/settings.json` 中启用对应配置,装上随包的 [Phoenix UI](#phoenix-ui) 设计工具,并可选创建通用的 `AGENTS.md`、安装 Codex 或 Claude Code 的 goal 上下文收尾提醒。npm 发布包与仓库的 `xgent-skills install` 统一默认安装 [Phoenix Diagram](docs/phoenix-diagram.md):
25
+ 本仓库同时以 [`@xgent-ai/skills`](https://www.npmjs.com/package/@xgent-ai/skills) 发布到 npm,自带 `xgent-skills` 命令,可为任意项目安装 XGENT 的 Claude Code hooks(当前包含 statusline)、在项目 `.claude/settings.json` 中启用对应配置,装上随包的 [Phoenix UI](#phoenix-ui) 设计工具,并可选安装 Codex 或 Claude Code 的 goal 上下文收尾提醒。npm 发布包与仓库的 `xgent-skills install` 统一默认安装 [Phoenix Diagram](docs/phoenix-diagram.md):
26
26
 
27
27
  ```bash
28
28
  # 在目标项目根目录执行(也可显式传目录:npx @xgent-ai/skills install <dir>)
@@ -42,8 +42,6 @@ npx @xgent-ai/skills install
42
42
  | `--confirm-remove-archify` | 与迁移配用的显式确认;旧 skill 不备份,回滚不重建,非交互迁移必须提供 |
43
43
  | `--release-dir=DIR` | 离线安装 Phoenix UI engine:使用已核验的 release 目录,不联网 |
44
44
  | `--diagram-release-dir=DIR` | 为 Phoenix Diagram 使用匹配的完整离线目录 |
45
- | `--xgent-init` | 直接创建仓根 `AGENTS.md` (已有则跳过),不再询问 |
46
- | `--no-xgent-init` | 跳过创建 `AGENTS.md`,不再询问 |
47
45
  | `--providers=a,b` | 指定 Phoenix 工具的 harness 目录(如 `--providers=.claude,.cursor`);`codex` 识别 `.agents/.codex`,Diagram skill 只写 `.agents`;默认按已有目录选择,空项目用 `.claude` |
48
46
 
49
47
  Phoenix UI 和 Phoenix Diagram skill 均由 npm 安装器随匹配引擎安装与更新,已通过 `metadata.internal: true` 从 `npx skills add XGENT-ai/skills` 的默认发现列表排除,避免独立更新 skill 后与引擎版本不一致。
@@ -52,15 +50,7 @@ Phoenix UI 和 Phoenix Diagram skill 均由 npm 安装器随匹配引擎安装
52
50
 
53
51
  普通安装不移除 Archify。`--migrate-archify` 使用独立的 Diagram 迁移分支,仅处理精确项目/provider 的完整已知来源;自定义、链接、外部管理与个人入口保留。TTY 拒绝或 EOF 不迁移;`--no-phoenix-diagram` 仍跳过并保留。旧入口移除后需要时按原来源重装,新 Diagram 入口的恢复/回滚见[迁移说明](docs/phoenix-diagram.md#旧入口迁移与安装恢复)。仓库只保留新活跃入口,不代表其他项目或个人旧安装已被处理。
54
52
 
55
- `install` 会询问是否同时创建仓根 `AGENTS.md`,输入 `y` 或 `yes` 确认,回车默认跳过。确认后直接复制 [通用模板](skills/xgent-init/references/external-app-AGENTS.template.md),只创建 `AGENTS.md`,不安装 `xgent-init` skill,不生成 `CLAUDE.md` / `PRODUCT.md` / `DESIGN.md`。已有 `AGENTS.md` 时直接跳过并保留原文。
56
-
57
- 非交互环境默认跳过,可用 `--xgent-init` 显式创建,或用 `--no-xgent-init` 跳过询问。文件位于仓根,不受 `--providers` 影响。模板没有待填占位,通用编码准则支持不同 coding agent;Portal App 的 onebox 联调与有前端改动时的真实浏览器验收分别按适用条件执行。
58
-
59
- Portal 的产品和设计规范仍保留在 [PRODUCT 模板](skills/xgent-init/references/external-app-PRODUCT.template.md) 与 [DESIGN 模板](skills/xgent-init/references/external-app-DESIGN.template.md) 中。需要生成项目上下文时,显式安装并在 Agent 对话中使用 [xgent-init](skills/xgent-init/SKILL.md),它读取清单与代码事实,补齐 PRODUCT.md / DESIGN.md (无前端的 service 型不新生成 DESIGN.md),已有文件保留:
60
-
61
- ```bash
62
- npx skills add XGENT-ai/skills --skill xgent-init
63
- ```
53
+ `install` 不创建或修改 `AGENTS.md`,也不询问是否安装模板。安装完成后会提示:可在 agent 对话中使用 [optimize-agents-md](skills/optimize-agents-md/SKILL.md),以 opinionated DSL(Odyssey/ODC)改良已有的 `AGENTS.md`;不喜欢这套风格可以忽略。Claude Code 用 `/optimize-agents-md`,Codex 用 `$optimize-agents-md`,由用户自行选择是否使用。
64
54
 
65
55
  ## Phoenix UI
66
56
 
@@ -71,12 +61,11 @@ Phoenix UI 是 XGENT 自主维护的设计工具:从 [impeccable](https://github
71
61
  Phoenix UI 适用于独立网站、应用和嵌入式界面等 UI 项目。`phoenix-ui init` 可独立完成产品上下文初始化,不依赖 `xgent-init`。subagent 与并行策略由 coding agent 自主安排,Phoenix 不额外要求询问或等待授权。
72
62
 
73
63
  - **engine**:按包内 `dist/phoenix-ui/VERSION.json` 的摘要校验后放进 `~/.phoenix-ui/bin/<版本>/<平台>/<engineSha256>/`(`PHOENIX_UI_HOME` 可改),新旧项目的引擎可共存,支持 darwin-arm64、darwin-x64、linux-x64、linux-arm64;不支持 Windows。不可用时整个安装在写项目前停止,可改用 `--release-dir=DIR` 离线安装或 `--no-phoenix-ui`。hook 与 context 运行时不联网补下载。
74
- - **已有工具**:安装时发现 impeccable 会询问是否替换。选择替换则直接删除所选 provider 的项目级及个人级旧入口和旧 hooks,不备份、不留转跳入口;选择保留则共存,不阻止 Phoenix 安装,无交互环境默认保留。没有安装事务状态的 Phoenix 源码副本直接删除后重装;已有回执的 Phoenix 工具文件被其他渠道覆盖时,显式安装会按当前包刷新,并保留重装前内容供回滚。业务源码、旧工具的数据和文档不改动;共享配置中的其他 hooks 保留。
75
- - **回滚**:安装输出给出事务 ID、备份位置与完整命令,例如 `npm exec --package=@xgent-ai/skills@<版本> -- phoenix-ui migrate --rollback <事务ID> --project <项目>`;回滚只撤销本次安装的条目,后来新增的配置保留。
64
+ - **已有工具**:Phoenix UI 和 Diagram 都是可重新安装的工具:安装直接覆盖工具文件,不备份旧版、不创建安装事务目录,不提供普通安装回滚。中断后重跑安装即可;成功安装会清理旧安装记录和备份。业务源码、工具生成的业务数据及其他 hooks 保留。发现 impeccable 时仍可选择替换或共存,无交互环境默认保留。
76
65
 
77
- 安装、迁移、回滚、支持矩阵与渠道说明见 [docs/phoenix-ui.md](docs/phoenix-ui.md)。
66
+ 安装、迁移、支持矩阵与渠道说明见 [docs/phoenix-ui.md](docs/phoenix-ui.md)。
78
67
 
79
- managed UI 循环先核对固定包 engine,再用 `phoenix-ui review --help` 和 `phoenix-ui review schema --json` 发现 verbs 与输入形状;后者是 typed examples,样例不是真实证据。具体调用与恢复边界见 [Phoenix 评审协议](skills/review-ui/references/phoenix-review.md)。
68
+ 选择 Phoenix CLI 管理评审记录时,用 `phoenix-ui review --help` 和 `phoenix-ui review schema --json` 发现 verbs 与输入形状;后者是 typed examples,样例不是真实证据。具体调用与恢复边界见 [Phoenix 评审协议](docs/phoenix-ui-review.md)。
80
69
 
81
70
  ## Skills 列表
82
71
 
@@ -117,7 +106,7 @@ managed UI 循环先核对固定包 engine,再用 `phoenix-ui review --help`
117
106
  4. `review-code <计划>` 从完成记录登记的提交与未提交改动确定范围:需要修改时 `apply-code-review <报告>`(按计划约定提交修复并登记 SHA),再回到 `review-code <计划>` 复审,直到通过验收或你明确本期放过。
118
107
  5. (可选)计划涉及界面时,`review-ui <计划>` 做 UI/UX 验收:按影响范围自动检查与采集代表帧,同页同状态共用截图;复用有效历史证据及已通过结论,默认一位全新上下文的独立评审,不按接近门槛自动增加评审。需要修改时 `apply-ui-review <报告>`(按计划约定提交并登记 `UI修复` SHA),再回到 `review-ui <计划>` 复审,直到通过(默认每个单元 ≥ 8/10 且没有未关闭的 P0/P1)、停滞后交你裁定,或你明确放过。UI 修复带来代码改动时,再用 `review-code <计划>` 复审这些改动。 需补证时同样交给 `apply-ui-review <报告>`,由执行 agent 补齐环境、数据与实测材料、处理差距后交回 `review-ui <计划或原范围>` 独立复审;只缺独立评审时续接 review-ui。仅无法自行取得的权限、资料或产品决定请求用户提供。没有代码、证据或阻塞条件变化时不新增轮次。
119
108
 
120
- UI 循环可沿用人工 Markdown,或通过匹配的 Phoenix engine 管理证据与轮次。已有 managed 索引时从原轮与 revision 续接;engine 缺失、版本不匹配或状态损坏时保持只读,恢复固定版本后再继续。人工历史只经明确的 `review import` 接管,不能清零未知次数;CLI 操作成功不代表 UI 通过,真实取证和独立评审仍是判定依据。
109
+ UI 验收是独立的 agent 工作流,依据目标、真实证据和 Markdown 历史继续,不绑定工具、规则版本或文档指纹。可选用 Phoenix CLI 管理记录;不可用时保留其状态文件,继续记录评审与修复结果。CLI 操作成功不代表 UI 通过。
121
110
  6. 通过后,计划来自 PRD 时可选 `review-prd-dev-gaps <计划>`;生成 gaps plan 时从第 2 步的 `review-dev-plan <gaps 计划>` 继续。
122
111
 
123
112
  **跨项目入口按角色固定**:提出方始终用 `crd` 起草/修订需求、整合接收方返回稿、答复反问;接收方始终用 `crd-respond <CRD>` 评估/答复需求、续接提出方澄清或回告实现结果。双方收到文件或追加消息都不切换角色,归属不明先核实。接收方默认在原文末追加有证据的评估答复,需改本方代码时实际调用 `dev-plan`(未决契约按就绪门标 `Blocked`),计划记录来源 CR、承接请求/子范围、回写文件与顶部完成回写约定,生成计划从第 2 步继续;完成回告核对适用证据后追加 RESP,不重开规划或改写历史。仅原样追加/收录不重新评估或生成计划,仅评估不落盘,仅答复不生成计划,不自动发送或实施。
@@ -218,7 +207,7 @@ $agi-mode <任务描述> 或 sdlc [流程/阶段/问题] 或 init-sdlc [PRD/计
218
207
 
219
208
  只需安装 `agi-mode` 即可使用上述流程;保留的 `autoresearch`、`arena`、`reflect` 名称是兼容入口,需要同时安装 `agi-mode`。它们不再独立维护执行规则。
220
209
 
221
- 明确要求优化仓库指令时,可使用 `$optimize-agents-md [repo 路径] [文件范围] [仅诊断]`,默认处理当前代码仓库的根 AGENTS.md。它默认可发现,普通编码、初始化或全局规则修改不进入该流程;仅诊断保持只读。优化时使用已安装的 `use-prompt-dsl` 的 `compose`(默认操作)将原则压缩为 Odyssey RULE 片段(直接嵌入 AGENTS.md,无外层包装),按语义去重并补齐 Think Before Coding 与十二项 Coding Principles,保留项目事实、授权和作用域。缺少 DSL skill 时停止,建议完整安装本仓 skills;安装检查和优化流程只属于 skill,不写进目标 AGENTS.md。无需同仓评测任务或远端配置,文件精简不等于已证明行为质量提升。
210
+ 明确要求优化仓库指令时,可使用 `$optimize-agents-md [repo 路径] [文件范围] [仅诊断]`,默认处理当前代码仓库的根 AGENTS.md。它默认可发现,普通编码、初始化或全局规则修改不进入该流程;仅诊断保持只读。优化时使用已安装的 `use-prompt-dsl` 的 `compose`(默认操作)将原则压缩为 Odyssey RULE 片段(直接嵌入 AGENTS.md,无外层包装),按语义去重并补齐原则素材中缺失的编码准则,保留项目事实、授权和作用域。缺少 DSL skill 时停止,建议完整安装本仓 skills;安装检查和优化流程只属于 skill,不写进目标 AGENTS.md。无需同仓评测任务或远端配置,文件精简不等于已证明行为质量提升。
222
211
 
223
212
  独立的 agi-mode [远端执行工具](skills/agi-mode/references/remote-evolution.md) 提供 `validate / run / resume / apply`,优化器、被测 agent 与固定验收使用 SSH 主机上的独立环境。默认搜索 keep 只更新探索最佳,独立确认和原则核对通过后才在已有写回授权内按 CAS 采用;显式获准的 v2 `bounded_exploration_v1` 仅允许质量门通过、来源精度不足时探索一个候选,不晋级最佳、不确认/采用,整体 inconclusive。不自动提交、发布或安装全局配置。验收范围与运行结果见 [CHANGELOG.md](CHANGELOG.md) 及其引用的阶段记录。
224
213
 
@@ -267,7 +256,7 @@ v2自有仓任务可显式使用`case.platform=linux/arm64`和匹配的固定镜
267
256
 
268
257
  | Skill | 说明 |
269
258
  | --- | --- |
270
- | [xgent-init](skills/xgent-init/SKILL.md) | 内部初始化 skill,补齐 `AGENTS.md` / `PRODUCT.md` / `DESIGN.md`,保留 Portal 产品边界与设计规范;`install` 仅直接创建 AGENTS.md |
259
+ | [xgent-init](skills/xgent-init/SKILL.md) | 内部初始化 skill,补齐 `AGENTS.md` / `PRODUCT.md` / `DESIGN.md`,保留 Portal 产品边界与设计规范 |
271
260
  | [portal-dev-setup](skills/portal-dev-setup/SKILL.md) | 在 App 仓库中启动、体检和排查 Docker 一盒门户,完成本地联调 |
272
261
  | [portal-external-app](skills/portal-external-app/SKILL.md) | 接入以 Docker 镜像交付的外部服务,核对 manifest、注册、路由、权限与交付契约 |
273
262
  | [portal-micro-app](skills/portal-micro-app/SKILL.md) | 开发嵌入门户的微应用前端,处理 SDK 握手、导航、授权与 iframe 交互 |
@@ -13,7 +13,6 @@ const readline = require('node:readline');
13
13
 
14
14
  const PKG_ROOT = path.join(__dirname, '..');
15
15
  const HOOKS_SRC_DIR = path.join(PKG_ROOT, '.claude', 'hooks');
16
- const AGENTS_TEMPLATE = path.join(PKG_ROOT, 'skills', 'xgent-init', 'references', 'external-app-AGENTS.template.md');
17
16
 
18
17
  // 写入目标项目 .claude/settings.json 的配置,按顶层 key 合并:
19
18
  // 这里列出的 key 以本包为准覆盖,其余已有配置保持不动。
@@ -26,7 +25,7 @@ const MANAGED_SETTINGS = {
26
25
  };
27
26
 
28
27
  // ─── Phoenix UI ─────────────────────────────────────────────────────────────
29
- // Phoenix 的受管安装、hooks 与回执由 Rust 执行器事务化完成。
28
+ // Phoenix 工具文件由 Rust 执行器直接覆盖,共享 hooks 只更新本工具条目。
30
29
  // 旧工具是否删除由 npm 安装入口询问,工具清理不备份、不迁移旧数据。
31
30
 
32
31
  const bootstrap = require('../skills/phoenix-ui/scripts/phoenix-bootstrap.cjs');
@@ -101,10 +100,8 @@ function preparePhoenixTools(project, providers) {
101
100
  }
102
101
  }
103
102
  }
104
- if (!fs.existsSync(path.join(project, '.phoenix-ui/install'))) {
105
- const file = checked(project, `${provider}/skills/phoenix-ui`);
106
- if (present(file)) refresh.push(file);
107
- }
103
+ const file = checked(project, `${provider}/skills/phoenix-ui`);
104
+ if (fs.lstatSync(file, { throwIfNoEntry: false })?.isSymbolicLink()) refresh.push(file);
108
105
  }
109
106
  const oldCommand = value => typeof value === 'string'
110
107
  && /(?:skills[\\/]impeccable[\\/]scripts[\\/](?:impeccable(?:\.cmd)?|hook(?:-(?:probe|before-edit|after-edit|stop))?\.mjs)|(?:^|[\s"'])impeccable\s+hook\b)/.test(value);
@@ -201,17 +198,15 @@ function runDiagram(binary, args) {
201
198
 
202
199
  function prepareDiagramTools(project, providers) {
203
200
  const refresh = [];
204
- if (!fs.lstatSync(path.join(project, '.phoenix-diagram/install'), { throwIfNoEntry: false })) {
205
- for (const provider of providers.filter(name => name !== '.codex')) {
206
- const relative = `${provider}/skills/phoenix-diagram`;
207
- const file = path.join(project, relative);
208
- const stat = fs.lstatSync(file, { throwIfNoEntry: false });
209
- if (!stat) continue;
210
- for (let dir = path.dirname(file); dir !== project; dir = path.dirname(dir)) {
211
- if (fs.lstatSync(dir).isSymbolicLink()) throw phoenixFailure(3, `工具目录的父路径是符号链接,未修改: ${dir}`);
212
- }
213
- refresh.push({ file, relative, linked: stat.isSymbolicLink() });
201
+ for (const provider of providers.filter(name => name !== '.codex')) {
202
+ const relative = `${provider}/skills/phoenix-diagram`;
203
+ const file = path.join(project, relative);
204
+ const stat = fs.lstatSync(file, { throwIfNoEntry: false });
205
+ if (!stat?.isSymbolicLink()) continue;
206
+ for (let dir = path.dirname(file); dir !== project; dir = path.dirname(dir)) {
207
+ if (fs.lstatSync(dir).isSymbolicLink()) throw phoenixFailure(3, `工具目录的父路径是符号链接,未修改: ${dir}`);
214
208
  }
209
+ refresh.push({ file, relative, linked: true });
215
210
  }
216
211
  return {
217
212
  covers(conflict) {
@@ -242,10 +237,7 @@ async function prepareDiagram(targetDir, options) {
242
237
  const tools = prepareDiagramTools(targetDir, planned.report.providers || []);
243
238
  const reinstallable = planned.code === 3 && planned.report.status === 'conflict'
244
239
  && planned.report.conflicts.length > 0 && planned.report.conflicts.every(conflict => tools.covers(conflict));
245
- // A known pending transaction is recovered only by the later explicit apply.
246
- const recoverable = planned.code === 3 && planned.report.status === 'recovery-required'
247
- && planned.report.conflicts?.length === 0;
248
- if (!reinstallable && ((planned.code !== 0 && !recoverable) || !['planned', 'unchanged', 'recovery-required'].includes(planned.report.status))) {
240
+ if (!reinstallable && (planned.code !== 0 || !['planned', 'unchanged'].includes(planned.report.status))) {
249
241
  printDiagram(planned.report, ready.manifest);
250
242
  throw phoenixFailure(planned.code || 3, 'Phoenix Diagram 预检失败');
251
243
  }
@@ -345,6 +337,8 @@ function printPhoenix(report, manifest) {
345
337
  console.log(` 事务 ${report.transactionId};备份 ${report.backups}`);
346
338
  const project = bootstrap.shellQuote(report.project);
347
339
  console.log(` 回滚 npm exec --package=@xgent-ai/skills@${npmPackageVersion} -- phoenix-ui migrate --rollback ${report.transactionId} --project ${project}`);
340
+ }
341
+ if (['installed', 'integration-pending'].includes(report.status)) {
348
342
  for (const provider of skillProviders) console.log(` 重载 ${reloadStep(provider)}`);
349
343
  }
350
344
  }
@@ -359,17 +353,6 @@ async function confirm(question, skipMessage, answers) {
359
353
  return /^(y|yes)$/i.test(value.trim());
360
354
  }
361
355
 
362
- function createAgents(targetDir) {
363
- const dest = path.join(targetDir, 'AGENTS.md');
364
- try {
365
- fs.copyFileSync(AGENTS_TEMPLATE, dest, fs.constants.COPYFILE_EXCL);
366
- console.log(' 创建 AGENTS.md');
367
- } catch (error) {
368
- if (error.code !== 'EEXIST') throw error;
369
- console.log(' 跳过 AGENTS.md (已存在,保留原文)');
370
- }
371
- }
372
-
373
356
  function usage() {
374
357
  console.log(`用法: npx @xgent-ai/skills <command>
375
358
 
@@ -377,8 +360,7 @@ function usage() {
377
360
  install [dir] 为目标项目(默认当前目录)安装 .claude/hooks 下的全部
378
361
  hook,在 .claude/settings.json 中启用对应配置,并安装
379
362
  Phoenix UI(设计 skill 与 hooks;engine 校验后
380
- 放入用户 cache;旧 impeccable 可选择替换或共存),
381
- ${includesDiagram ? ' 以及 Phoenix Diagram(十类图解 skill 与独立原生 engine),\n' : ''} 并询问是否创建通用的 AGENTS.md
363
+ 放入用户 cache;旧 impeccable 可选择替换或共存)${includesDiagram ? ',\n 以及 Phoenix Diagram(十类图解 skill 与独立原生 engine)' : ''}
382
364
  help 显示本帮助
383
365
 
384
366
  install 选项:
@@ -387,14 +369,12 @@ install 选项:
387
369
  --replace-impeccable 删除项目及个人级旧 impeccable 工具和 hooks,不备份
388
370
  --keep-impeccable 保留 impeccable,与 Phoenix UI 共存(无交互时默认)
389
371
  ${includesDiagram ? ` --no-phoenix-diagram 跳过 Phoenix Diagram(不删除已装版本)
390
- --migrate-archify 仅执行 Diagram 预检/确认/迁移,跳过 hooks/settings/AGENTS/UI;
372
+ --migrate-archify 仅执行 Diagram 预检/确认/迁移,跳过 hooks/settings/UI;
391
373
  使用已存在且匹配包内固定版本的引擎,不自动获取
392
374
  --confirm-remove-archify 仅配 --migrate-archify,确认移除所选项目/provider 旧入口;
393
375
  不备份 Archify,回滚不重建旧入口,需要时按原来源重装
394
376
  ` : ''} --release-dir=DIR 离线安装 Phoenix UI engine:使用已核验的 release 目录,不联网
395
- ${includesDiagram ? ' --diagram-release-dir=DIR 离线安装 Phoenix Diagram:使用匹配的完整目录,不联网\n' : ''} --xgent-init 创建 AGENTS.md (已有则跳过),不再询问
396
- --no-xgent-init 跳过 AGENTS.md,不再询问
397
- --providers=a,b 指定 Phoenix 工具装进哪些 harness 目录(默认按项目里已有的
377
+ ${includesDiagram ? ' --diagram-release-dir=DIR 离线安装 Phoenix Diagram:使用匹配的完整目录,不联网\n' : ''} --providers=a,b 指定 Phoenix 工具装进哪些 harness 目录(默认按项目里已有的
398
378
  目录判断,都没有时装 .claude;codex 同时选 .agents 与 .codex)
399
379
  `);
400
380
  }
@@ -402,7 +382,7 @@ ${includesDiagram ? ' --diagram-release-dir=DIR 离线安装 Phoenix Diagram:
402
382
  async function install(args) {
403
383
  const flags = args.filter((a) => a.startsWith('--'));
404
384
  const dirArg = args.find((a) => !a.startsWith('--'));
405
- const unknown = flags.filter((f) => !['--no-phoenix-ui', '--no-impeccable', '--replace-impeccable', '--keep-impeccable', '--xgent-init', '--no-xgent-init',
385
+ const unknown = flags.filter((f) => !['--no-phoenix-ui', '--no-impeccable', '--replace-impeccable', '--keep-impeccable',
406
386
  ...(includesDiagram ? ['--no-phoenix-diagram', '--migrate-archify', '--confirm-remove-archify'] : [])].includes(f)
407
387
  && !['--providers=', '--release-dir=', ...(includesDiagram ? ['--diagram-release-dir='] : [])].some((p) => f.startsWith(p)));
408
388
  if (unknown.length > 0) {
@@ -411,10 +391,6 @@ async function install(args) {
411
391
  process.exit(1);
412
392
  }
413
393
  if (flags.includes('--confirm-remove-archify') && !flags.includes('--migrate-archify')) throw phoenixFailure(2, '--confirm-remove-archify 需要 --migrate-archify');
414
- if (flags.includes('--xgent-init') && flags.includes('--no-xgent-init')) {
415
- console.error('错误: --xgent-init 与 --no-xgent-init 不能同时使用');
416
- process.exit(1);
417
- }
418
394
  if (flags.includes('--replace-impeccable') && flags.includes('--keep-impeccable')) {
419
395
  throw phoenixFailure(1, '--replace-impeccable 与 --keep-impeccable 不能同时使用');
420
396
  }
@@ -472,24 +448,18 @@ async function install(args) {
472
448
  process.exit(1);
473
449
  }
474
450
  }
475
- const agentsExists = fs.existsSync(path.join(targetDir, 'AGENTS.md'));
476
- const askAgents = !flags.includes('--xgent-init') && !flags.includes('--no-xgent-init') && !agentsExists;
477
451
  const phoenix = options.phoenix ? await preparePhoenix(targetDir, options) : null;
478
452
  const diagram = options.diagram ? await prepareDiagram(targetDir, options) : null;
479
453
  const askReplace = phoenix?.tools.hasLegacy && !flags.includes('--replace-impeccable') && !flags.includes('--keep-impeccable');
480
- const rl = process.stdin.isTTY && (askAgents || askReplace)
454
+ const rl = process.stdin.isTTY && askReplace
481
455
  ? readline.createInterface({ input: process.stdin, output: process.stdout }) : null;
482
456
  const answers = rl?.[Symbol.asyncIterator]();
483
457
  rl?.once('SIGINT', () => { rl.close(); process.exit(130); });
484
- let agents;
485
458
  let replace = flags.includes('--replace-impeccable');
486
459
  try {
487
460
  if (askReplace) replace = await confirm(
488
461
  '发现旧 impeccable,是否删除并替换为 Phoenix UI(含个人级入口,不备份)? [y/N] ',
489
462
  ' 保留 impeccable:无交互环境默认共存;可用 --replace-impeccable 替换', answers);
490
- agents = flags.includes('--xgent-init') || (askAgents && await confirm(
491
- '是否同时创建通用的 AGENTS.md (含 Portal 联调与前端验收约定)? [y/N] ',
492
- ' 跳过 AGENTS.md:非交互环境,可加 --xgent-init 创建', answers));
493
463
  } finally {
494
464
  rl?.close();
495
465
  }
@@ -524,16 +494,13 @@ async function install(args) {
524
494
  console.log(' 写入 .claude/settings.json (statusLine)');
525
495
  }
526
496
 
527
- if (agents) createAgents(targetDir);
528
- else if (agentsExists && !flags.includes('--no-xgent-init')) console.log(' 跳过 AGENTS.md (已存在,保留原文)');
529
-
530
- // XGENT 部分已完成;Phoenix 在锁内重读这些文件后再应用,中断时可重试。
497
+ // XGENT 部分已完成;Phoenix 直接覆盖工具文件,中断后重跑。
531
498
  if (phoenix) {
532
499
  let applied;
533
500
  try {
534
501
  applied = runPhoenix(phoenix.binary, phoenix.args);
535
502
  } catch (error) {
536
- console.log(`未完成: XGENT 部分已安装;Phoenix UI 待恢复(安装被中断,可能已部分写入),按下方错误处理后重跑本命令,会先按日志恢复(可重复执行)。${diagram ? 'Phoenix Diagram 未执行。' : ''}`);
503
+ console.log(`未完成: XGENT 部分已安装;Phoenix UI 安装未完成,处理下方错误后重跑本命令覆盖安装。${diagram ? 'Phoenix Diagram 未执行。' : ''}`);
537
504
  throw error;
538
505
  }
539
506
  printPhoenix(applied.report, phoenix.manifest);
@@ -551,7 +518,7 @@ async function install(args) {
551
518
  try {
552
519
  applied = runDiagram(diagram.binary, diagram.args);
553
520
  } catch (error) {
554
- console.log(`未完成: ${completed};Phoenix Diagram 可能已部分写入,按错误和项目日志重跑恢复。`);
521
+ console.log(`未完成: ${completed};Phoenix Diagram 安装未完成,处理错误后重跑本命令覆盖安装。`);
555
522
  throw error;
556
523
  }
557
524
  printDiagram(applied.report, diagram.manifest);
@@ -562,6 +529,7 @@ async function install(args) {
562
529
  }
563
530
  }
564
531
  console.log(`完成: ${targetDir}`);
532
+ console.log(' 可选 在 agent 对话中用 /optimize-agents-md(Codex 用 $optimize-agents-md)以 opinionated DSL 改良已有 AGENTS.md;不喜欢这套风格可忽略。');
565
533
  }
566
534
 
567
535
  const [cmd, ...args] = process.argv.slice(2);
@@ -3,37 +3,37 @@
3
3
  "distribution": "release",
4
4
  "tool": "phoenix-diagram",
5
5
  "toolVersion": "0.2.0",
6
- "npmPackageVersion": "0.7.8",
7
- "sourceCommit": "4e2a4e4dccbbceb84152eb93bc74662aa4b759d0",
8
- "sourceInputSha256": "51b65bd3f53b989e1f242cae99542ed67c5fe3e855edf906f7f5fab23939ab94",
6
+ "npmPackageVersion": "0.7.9",
7
+ "sourceCommit": "7229a8ba08f7d37bc37530e00e4d933903f1b5cf",
8
+ "sourceInputSha256": "910df91b4e857da6e30d8982aea308d2e1bf2d99c2d7644cb5455b37762740d9",
9
9
  "inputContractSchema": 1,
10
10
  "receiptSchema": 1,
11
11
  "viewerRevision": "phoenix-v2",
12
- "bundleSha256": "58d1580a3a443f79a3aa41ee2c9000542de5a8d4b799377a4be673298ad8589e",
12
+ "bundleSha256": "e573234de7918dc87e1630c30a4ea1ba5862cc647dc0023a6d578ccb114e2ab6",
13
13
  "engines": {
14
14
  "darwin-arm64": {
15
15
  "asset": "phoenix-diagram-darwin-arm64",
16
- "size": 17566448,
17
- "sha256": "f85846e9a0967847eaf3f3b0a290d4009c069821c028495b047db358176ce17c",
18
- "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.8/darwin-arm64/phoenix-diagram-darwin-arm64"
16
+ "size": 17549968,
17
+ "sha256": "5f432a278a433bd456e8b65e0eb7a4128665f31d8c992609d6eb1cd9c48b7429",
18
+ "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.9/ci-41410951647/darwin-arm64/phoenix-diagram-darwin-arm64"
19
19
  },
20
20
  "darwin-x64": {
21
21
  "asset": "phoenix-diagram-darwin-x64",
22
- "size": 19576864,
23
- "sha256": "b3df962d82d3f731d9641184cceec5516c082aa1fbc871ac41692cc7acd85dc8",
24
- "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.8/darwin-x64/phoenix-diagram-darwin-x64"
25
- },
26
- "linux-x64": {
27
- "asset": "phoenix-diagram-linux-x64",
28
- "size": 21880592,
29
- "sha256": "59ef4d6ac3f08e000c387a668915e31a8f44a83cc01ba9b89381fadc193e09f6",
30
- "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.8/linux-x64/phoenix-diagram-linux-x64"
22
+ "size": 19556424,
23
+ "sha256": "236384da35f4f65e892966b5912b1c1631fb4819194a7d51085076f2bc3b1a79",
24
+ "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.9/ci-41410951647/darwin-x64/phoenix-diagram-darwin-x64"
31
25
  },
32
26
  "linux-arm64": {
33
27
  "asset": "phoenix-diagram-linux-arm64",
34
28
  "size": 19406792,
35
- "sha256": "f86c8610dde3af5c01cf70275d3a8241d35e937916e34367963c1a728bfba7ac",
36
- "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.8/linux-arm64/phoenix-diagram-linux-arm64"
29
+ "sha256": "0df998aed2c0d1968a94151e01e1be0ea73b62f279ff3f33fbaa2ec037e3c215",
30
+ "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.9/ci-41410951647/linux-arm64/phoenix-diagram-linux-arm64"
31
+ },
32
+ "linux-x64": {
33
+ "asset": "phoenix-diagram-linux-x64",
34
+ "size": 21861536,
35
+ "sha256": "502c95c538ab6db7c4b3de80c65e7188a84a4326ba6a5a965a1c23e7b6033037",
36
+ "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.9/ci-41410951647/linux-x64/phoenix-diagram-linux-x64"
37
37
  }
38
38
  }
39
39
  }
@@ -3,37 +3,37 @@
3
3
  "distribution": "release",
4
4
  "tool": "phoenix-diagram",
5
5
  "toolVersion": "0.2.0",
6
- "npmPackageVersion": "0.7.8",
7
- "sourceCommit": "4e2a4e4dccbbceb84152eb93bc74662aa4b759d0",
8
- "sourceInputSha256": "51b65bd3f53b989e1f242cae99542ed67c5fe3e855edf906f7f5fab23939ab94",
6
+ "npmPackageVersion": "0.7.9",
7
+ "sourceCommit": "7229a8ba08f7d37bc37530e00e4d933903f1b5cf",
8
+ "sourceInputSha256": "910df91b4e857da6e30d8982aea308d2e1bf2d99c2d7644cb5455b37762740d9",
9
9
  "inputContractSchema": 1,
10
10
  "receiptSchema": 1,
11
11
  "viewerRevision": "phoenix-v2",
12
- "bundleSha256": "58d1580a3a443f79a3aa41ee2c9000542de5a8d4b799377a4be673298ad8589e",
12
+ "bundleSha256": "e573234de7918dc87e1630c30a4ea1ba5862cc647dc0023a6d578ccb114e2ab6",
13
13
  "engines": {
14
14
  "darwin-arm64": {
15
15
  "asset": "phoenix-diagram-darwin-arm64",
16
- "size": 17566448,
17
- "sha256": "f85846e9a0967847eaf3f3b0a290d4009c069821c028495b047db358176ce17c",
18
- "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.8/darwin-arm64/phoenix-diagram-darwin-arm64"
16
+ "size": 17549968,
17
+ "sha256": "5f432a278a433bd456e8b65e0eb7a4128665f31d8c992609d6eb1cd9c48b7429",
18
+ "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.9/ci-41410951647/darwin-arm64/phoenix-diagram-darwin-arm64"
19
19
  },
20
20
  "darwin-x64": {
21
21
  "asset": "phoenix-diagram-darwin-x64",
22
- "size": 19576864,
23
- "sha256": "b3df962d82d3f731d9641184cceec5516c082aa1fbc871ac41692cc7acd85dc8",
24
- "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.8/darwin-x64/phoenix-diagram-darwin-x64"
25
- },
26
- "linux-x64": {
27
- "asset": "phoenix-diagram-linux-x64",
28
- "size": 21880592,
29
- "sha256": "59ef4d6ac3f08e000c387a668915e31a8f44a83cc01ba9b89381fadc193e09f6",
30
- "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.8/linux-x64/phoenix-diagram-linux-x64"
22
+ "size": 19556424,
23
+ "sha256": "236384da35f4f65e892966b5912b1c1631fb4819194a7d51085076f2bc3b1a79",
24
+ "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.9/ci-41410951647/darwin-x64/phoenix-diagram-darwin-x64"
31
25
  },
32
26
  "linux-arm64": {
33
27
  "asset": "phoenix-diagram-linux-arm64",
34
28
  "size": 19406792,
35
- "sha256": "f86c8610dde3af5c01cf70275d3a8241d35e937916e34367963c1a728bfba7ac",
36
- "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.8/linux-arm64/phoenix-diagram-linux-arm64"
29
+ "sha256": "0df998aed2c0d1968a94151e01e1be0ea73b62f279ff3f33fbaa2ec037e3c215",
30
+ "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.9/ci-41410951647/linux-arm64/phoenix-diagram-linux-arm64"
31
+ },
32
+ "linux-x64": {
33
+ "asset": "phoenix-diagram-linux-x64",
34
+ "size": 21861536,
35
+ "sha256": "502c95c538ab6db7c4b3de80c65e7188a84a4326ba6a5a965a1c23e7b6033037",
36
+ "url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.9/ci-41410951647/linux-x64/phoenix-diagram-linux-x64"
37
37
  }
38
38
  }
39
39
  }
@@ -137,8 +137,8 @@
137
137
  },
138
138
  {
139
139
  "path": "references/migration.md",
140
- "size": 6211,
141
- "sha256": "9aad9f2d99eb2f2da9d2f2d656a7a7f01179862a815a6b07f3bf450fe565453a",
140
+ "size": 6209,
141
+ "sha256": "61f88625e1ffcb278db47ca1ddc7ba94ea9b1c459dfecd023125b05195c8a969",
142
142
  "mode": 420
143
143
  },
144
144
  {
@@ -233,8 +233,8 @@
233
233
  },
234
234
  {
235
235
  "path": "scripts/phoenix-bootstrap.cjs",
236
- "size": 45211,
237
- "sha256": "35fe87eb4e534470bc3bb2e6cc05e5483796694a8dd58f01c7702f3129132b71",
236
+ "size": 35018,
237
+ "sha256": "2b0e6f09be39d414d1d3928ed8ef8bd05f76504a03b039bca6200e5691ab0d31",
238
238
  "mode": 420
239
239
  },
240
240
  {
@@ -36,7 +36,7 @@ dry-run只报告计划与冲突,零写入;遇pending只报告,不恢复。
36
36
 
37
37
  recover不接受dry-run;recover/rollback都不接受bundle-root、providers或confirm-remove-archify。它们核已存在的固定正确引擎与本地事务,不下载、不更新、不重装旧skill,不要求当前active安装已完整提交。
38
38
 
39
- 普通 install 的 pending 恢复仍按其回执重跑原显式 install;`migrate --recover ID` 用于已确认的迁移或正在回滚的指定事务,不能代替普通安装恢复。优先执行对应回执的 `recoveryCommand`,不要只因有 journal ID 就改用迁移恢复命令。
39
+ 普通 install 直接覆盖工具文件,不建立事务;中断后重跑即可。成功安装会清理旧安装与迁移状态,不保留历史回滚入口。`migrate --recover ID` 仅用于仍存在的已确认迁移或回滚事务,优先执行该迁移回执的 `recoveryCommand`。
40
40
 
41
41
  - Recover继续指定已确认事务的安全剩余步骤;同一确认范围不重复索取确认。后来新增/改写、删除后重建或父目录身份变化保留并报告recovery-required;不凭stdout推断哪些文件已删。
42
42
  - Rollback仅回滚**当前committed事务**的新入口受管写入;包括仍为v1的当前事务经原生引擎验证并扩展后的回滚,不是跳过后来安装去回滚任意历史ID。后来修改在预检时冲突,先保持零变更;中途身份变化仍保留拒绝。成功rolled-back,同一ID可幂等重试。
@@ -353,106 +353,12 @@ function projectRoot(start) {
353
353
  let directory = initial;
354
354
  if (!fs.statSync(initial).isDirectory()) throw failure(`Project is not a directory: ${start}`, 2);
355
355
  for (;;) {
356
- if (exists(path.join(directory, '.phoenix-diagram/install/active.json')) || exists(path.join(directory, '.git'))) return directory;
356
+ if (exists(path.join(directory, '.git'))) return directory;
357
357
  const parent = path.dirname(directory);
358
358
  if (parent === directory) return initial;
359
359
  directory = parent;
360
360
  }
361
361
  }
362
- function physical(stat) { return { device: stat.dev.toString(), inode: stat.ino.toString() }; }
363
- function identity(snapshot) {
364
- const { stat, bytes } = snapshot;
365
- return { ...physical(stat), links: Number(stat.nlink), mode: Number(stat.mode & 0o777n), uid: Number(stat.uid), sha256: sha256(bytes), bytes: bytes.length };
366
- }
367
- function same(left, right) { return JSON.stringify(left) === JSON.stringify(right); }
368
- function sealed(file) {
369
- const snapshot = readRegular(file, MAX_ENGINE, 0o600);
370
- const data = JSON.parse(snapshot.bytes);
371
- if (!object(data) || !SHA.test(data.checksum || '') || sha256(encoded({ ...data, checksum: '' })) !== data.checksum || !snapshot.bytes.equals(encoded(data))) throw failure(`Invalid canonical installation ownership/journal checksum: ${file}`);
372
- if (snapshot.stat.uid !== BigInt(process.getuid())) throw failure(`Installation state belongs to a different owner: ${file}`);
373
- return { ...snapshot, data };
374
- }
375
- function managed(name) {
376
- if (!relative(name)) return false;
377
- const [provider, skills, tool, ...rest] = name.split('/');
378
- return PROVIDERS.includes(provider) && provider !== '.codex' && skills === 'skills' && tool === TOOL && allowed(rest.join('/'), true);
379
- }
380
- function verifiedJournalPublicationEntry(journals, name) {
381
- const parent = fs.lstatSync(journals, { bigint: true });
382
- if (!parent.isDirectory() || parent.isSymbolicLink() || parent.uid !== BigInt(process.getuid()) || (parent.mode & 0o7777n) !== 0o700n) throw failure('Installation journal directory is not private.');
383
- if (name === '.phoenix-diagram-output-cleanup') {
384
- const directory = childFile(journals, name), stat = fs.lstatSync(directory, { bigint: true });
385
- if (!stat.isDirectory() || stat.isSymbolicLink() || stat.uid !== parent.uid || (stat.mode & 0o7777n) !== 0o700n) throw failure('Unknown journal publication cleanup directory.');
386
- const marker = readRegular(childFile(directory, 'owner-v1.json'), 16 * 1024, 0o600);
387
- const owner = JSON.parse(marker.bytes);
388
- const expected = { schemaVersion: 1, tool: TOOL, kind: 'output-cleanup-directory', parentPath: journals, parent: physical(parent), directory: physical(stat), recordIdentity: physical(marker.stat), checksum: '' };
389
- expected.checksum = sha256(Buffer.from(JSON.stringify(expected)));
390
- if (marker.stat.uid !== parent.uid || !marker.bytes.equals(encoded(expected)) || !same(owner, expected)) throw failure('Journal publication cleanup owner binding or checksum differs.');
391
- if (fs.readdirSync(directory).some(entry => entry !== 'owner-v1.json')) throw failure('Journal publication cleanup requires explicit native recovery; unknown records are preserved.');
392
- return true;
393
- }
394
- if (name.startsWith('.phoenix-diagram-lock-')) {
395
- const marker = readRegular(childFile(journals, name), 16 * 1024, 0o600), lock = JSON.parse(marker.bytes);
396
- if (!/^[a-f0-9]{32}\.json$/.test(lock.name || '') || name !== `.phoenix-diagram-lock-${sha256(lock.name)}.lock` || !exists(childFile(journals, lock.name))) throw failure('Unknown journal publication lock target or filename.');
397
- const expected = { schemaVersion: 1, tool: TOOL, kind: 'output-lock', name: lock.name, parentDevice: parent.dev.toString(), parentInode: parent.ino.toString() };
398
- if (marker.stat.uid !== parent.uid || !marker.bytes.equals(Buffer.from(JSON.stringify(expected)))) throw failure('Journal publication lock identity or bytes differ.');
399
- return true;
400
- }
401
- return false;
402
- }
403
- function verifyReceipt(project, manifest, options = {}) {
404
- const receiptPath = '.phoenix-diagram/install/active.json';
405
- const file = path.join(project, receiptPath);
406
- if (!exists(file)) {
407
- if (options.installedSkillRoot) throw failure('Installed skill has no diagram ownership receipt.');
408
- return null;
409
- }
410
- const snapshot = sealed(childFile(project, receiptPath));
411
- const active = snapshot.data; const version = manifest.data;
412
- const transaction = active.transactionId;
413
- if (![1, 2].includes(active.schemaVersion) || active.tool !== TOOL || active.toolVersion !== TOOL_VERSION || !/^[a-f0-9]{32}$/.test(transaction || '') || !same(active.project, physical(fs.statSync(project, { bigint: true })))
414
- || active.npmPackageVersion !== version.npmPackageVersion || active.bundleManifestSha256 !== version.bundleSha256 || active.engineManifestSha256 !== manifest.hash
415
- || active.engine?.sourceInputSha256 !== version.sourceInputSha256 || active.engine?.inputContractSchema !== 1 || active.engine?.receiptSchema !== 1 || active.engine?.viewerRevision !== 'phoenix-v2'
416
- || !Object.values(version.engines).some(meta => meta.sha256 === active.engine?.binary?.sha256 && meta.size === active.engine?.binary?.bytes)
417
- || !Array.isArray(active.providers) || !active.providers.length || new Set(active.providers).size !== active.providers.length || active.providers.some(provider => !PROVIDERS.includes(provider))
418
- || active.journal !== `.phoenix-diagram/install/journals/${transaction}.json` || active.backup !== `.phoenix-diagram/install/transactions/${transaction}/before`
419
- || !Array.isArray(active.ownership) || !active.ownership.length || active.ownership.length > 200000
420
- || active.planSha256 !== sha256(encoded([active.bundleManifestSha256, active.engineManifestSha256, active.providers, active.ownership]))) throw failure('Project ownership requires a different or invalid Phoenix Diagram package/contract identity.');
421
- const journal = sealed(childFile(project, active.journal)).data;
422
- if (![1, 2].includes(journal.schemaVersion) || journal.tool !== TOOL || journal.transactionId !== transaction || journal.phase !== 'committed' || journal.pending !== null || !Array.isArray(journal.operations) || !journal.operations.length || journal.completed !== journal.operations.length
423
- || !same(journal.project, active.project) || !same(journal.active, active)
424
- || !same(journal.transaction, physical(fs.statSync(path.dirname(path.join(project, active.backup)), { bigint: true })))
425
- || journal.operations[journal.operations.length - 1].path !== receiptPath || !same(journal.operations[journal.operations.length - 1].after, identity(snapshot))) throw failure('Installed ownership is not bound to a completed native journal.');
426
- if (journal.migration && (journal.schemaVersion !== 2 || journal.migration.confirmed !== true || !Array.isArray(journal.migration.roots) || journal.migration.roots.some(root => root.kind !== 'legacy-root-remove' || !active.providers.some(provider => root.path === `${provider}/skills/archify`) || !['legacy-2.17', 'legacy-2.17-when-to-use', 'reference-3.1.0'].includes(root.source) || root.pending !== null || !Array.isArray(root.entries) || !root.entries.length || root.entries.some(entry => entry.removed !== true)))) throw failure('Legacy removal journal has not committed its confirmed scope.');
427
- let previous; const owned = new Map();
428
- for (const entry of active.ownership) {
429
- if (!managed(entry.path) || !active.providers.includes(entry.path.split('/')[0]) || previous && Buffer.compare(Buffer.from(previous), Buffer.from(entry.path)) >= 0 || ![0o644, 0o755].includes(entry.identity?.mode)) throw failure(`Unsafe managed ownership path: ${entry.path}`);
430
- const actual = readRegular(childFile(project, entry.path), MAX_ENGINE, entry.identity.mode);
431
- if (!same(identity(actual), entry.identity)) throw failure(`Managed ownership identity or digest changed: ${entry.path}`);
432
- owned.set(entry.path, entry.identity); previous = entry.path;
433
- }
434
- for (const [index, operation] of journal.operations.entries()) {
435
- if ((journal.schemaVersion === 2 ? operation.kind !== 'file-write' : operation.kind !== undefined)) throw failure('Unknown native journal operation kind.');
436
- if (operation.stagePath !== `after/${index}` || operation.retiredPath !== `retired/${index}` || operation.undoPath !== `undo/${index}` || operation.backupPath !== (operation.before ? `before/${index}` : null)) throw failure('Journal staging/backup paths differ from native ownership.');
437
- if (index + 1 < journal.operations.length && (!managed(operation.path) || !same(owned.get(operation.path), operation.after))) throw failure(`Journal operation differs from managed ownership: ${operation.path}`);
438
- }
439
- const journals = path.join(project, '.phoenix-diagram/install/journals');
440
- for (const name of fs.readdirSync(journals)) {
441
- if (verifiedJournalPublicationEntry(journals, name)) continue;
442
- if (!/^[a-f0-9]{32}\.json$/.test(name)) throw failure(`Unknown installation journal entry: ${name}`);
443
- const other = sealed(childFile(journals, name)).data;
444
- if (![1, 2].includes(other.schemaVersion) || other.tool !== TOOL || !same(other.project, active.project) || !['committed', 'recovered'].includes(other.phase)) throw failure('Diagram installation requires explicit native recovery before ordinary use.');
445
- }
446
- if (options.installedSkillRoot) {
447
- const relativeRoot = path.relative(project, options.installedSkillRoot).split(path.sep).join('/');
448
- if (!owned.has(`${relativeRoot}/ENGINE.json`) || !owned.has(`${relativeRoot}/SKILL.md`)) throw failure('Launcher skill root is not managed by this project ownership record.');
449
- const engine = readRegular(childFile(options.installedSkillRoot, 'ENGINE.json'), 1024 * 1024).bytes;
450
- if (!engine.equals(manifest.bytes)) throw failure('Installed ENGINE bytes differ from their native ownership identity.');
451
- inventory(options.installedSkillRoot, new Set([...owned.keys()].filter(name => name.startsWith(`${relativeRoot}/`)).map(name => name.slice(relativeRoot.length + 1))));
452
- }
453
- return active;
454
- }
455
-
456
362
  function exists(file) {
457
363
  try { fs.lstatSync(file); return true; } catch (error) { if (error.code === 'ENOENT') return false; throw error; }
458
364
  }
@@ -474,12 +380,7 @@ async function preflight(options = {}) {
474
380
  verifyBundle(bundleRoot, manifest);
475
381
  legalResources(path.dirname(versionManifestPath));
476
382
  if (options.releaseDir && options.allowInstall !== true) verifyOffline(path.resolve(options.releaseDir), manifest, verifyBundle(bundleRoot, manifest));
477
- // An explicit package upgrade lets native --dry-run validate the prior
478
- // ownership. Ordinary use never opts out of the current receipt binding.
479
- if (options.checkReceipt !== false) verifyReceipt(project, manifest);
480
383
  } else {
481
- if (!options.project && !exists(path.join(project, '.phoenix-diagram/install/active.json'))) project = projectRoot(skillDir);
482
- if (options.checkReceipt !== false) verifyReceipt(project, manifest, { installedSkillRoot: skillDir });
483
384
  bundleRoot = skillDir;
484
385
  if (options.releaseDir && options.allowInstall !== true) verifyOffline(path.resolve(options.releaseDir), manifest, null);
485
386
  }
@@ -551,14 +452,14 @@ async function main(args, options = {}) {
551
452
  }
552
453
  if (!requested) throw failure('Engine installation requires an explicit --project DIR.', 2);
553
454
  project = projectRoot(requested);
554
- const ready = await preflight({ ...options, project, releaseDir, env, allowInstall: true, checkReceipt: true });
455
+ const ready = await preflight({ ...options, project, releaseDir, env, allowInstall: true });
555
456
  manifest = ready.manifest; target = ready.target;
556
457
  const result = { schemaVersion: 1, tool: TOOL, toolVersion: TOOL_VERSION, command: 'engine install', status: 'installed', scope: 'engine-cache', diagnostics: [], npmPackageVersion: ready.version.npmPackageVersion, target, enginePath: ready.enginePath, project: ready.project };
557
458
  process.stdout.write(json ? encoded(result) : `Phoenix Diagram engine ${TOOL_VERSION} ready: ${ready.enginePath}\n`);
558
459
  return 0;
559
460
  }
560
461
  const migration = migrationOptions(args);
561
- const ready = await preflight({ ...options, ...(migration ? { project: migration.project, exactProject: true, ...(migration.from && migration.bundleRoot ? { bundleRoot: path.resolve(migration.bundleRoot) } : {}) } : {}), env, allowInstall: false, checkReceipt: !['install', 'migrate'].includes(args[0]) });
462
+ const ready = await preflight({ ...options, ...(migration ? { project: migration.project, exactProject: true, ...(migration.from && migration.bundleRoot ? { bundleRoot: path.resolve(migration.bundleRoot) } : {}) } : {}), env, allowInstall: false });
562
463
  manifest = ready.manifest; target = ready.target; project = ready.project;
563
464
  const childArgs = [...args];
564
465
  if ((args[0] === 'install' || migration?.from) && !args.some(arg => arg === '--bundle-root' || arg.startsWith('--bundle-root='))) childArgs.push('--bundle-root', ready.bundleRoot);
@@ -585,5 +486,5 @@ async function main(args, options = {}) {
585
486
  }
586
487
  }
587
488
 
588
- module.exports = { TARGETS, platformTarget, manifestAt, verifyBundle, verifyReceipt, verifyEngine, enginePath, installEngine, preflight, setupCommand, shellQuote, proxyFor, checkedUrl, sha256, migrationOptions, main };
489
+ module.exports = { TARGETS, platformTarget, manifestAt, verifyBundle, verifyEngine, enginePath, installEngine, preflight, setupCommand, shellQuote, proxyFor, checkedUrl, sha256, migrationOptions, main };
589
490
  if (require.main === module) main(process.argv.slice(2)).then(code => { process.exitCode = code; }).catch(error => { process.stderr.write(`Phoenix Diagram: ${error.message}\n`); process.exitCode = 4; });
@@ -6621,7 +6621,7 @@
6621
6621
  {
6622
6622
  "path": "tools/phoenix-ui/crates/live/assets/detect-antipatterns-browser.js",
6623
6623
  "importedPath": "crates/live/assets/detect-antipatterns-browser.js",
6624
- "sha256": "33b8a7c6aee916580e63f6bdcfac372defd8b1dd7dfd287f343eae6b6c30d40a",
6624
+ "sha256": "cde4b96028f395b47a3264f65c458014ea123e579f68475c33ab59aac581756f",
6625
6625
  "license": "Apache-2.0",
6626
6626
  "licenses": [
6627
6627
  {