@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.
- package/README.md +8 -19
- package/bin/xgent-skills.js +23 -55
- package/dist/phoenix-diagram/VERSION.json +18 -18
- package/dist/phoenix-diagram/bundle/ENGINE.json +18 -18
- package/dist/phoenix-diagram/bundle/manifest.json +4 -4
- package/dist/phoenix-diagram/bundle/references/migration.md +1 -1
- package/dist/phoenix-diagram/bundle/scripts/phoenix-bootstrap.cjs +4 -103
- package/dist/phoenix-ui/THIRD-PARTY.json +1 -1
- package/dist/phoenix-ui/VERSION.json +19 -19
- package/dist/phoenix-ui/bundle/blobs/{567c3f42394f9aee9b048a001016a7ba9d8773d78027f8c995c379886634e719 → 0c95d5c7163bc162cca460e2297ab146e331fd279a738a2055643b45b161c706} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{f077545caeb3ecb9f51a3f703a09736ca924869f4b4345c1b1ffbd9bf927a287 → 14bd6a733ce89fcd02bf6dcf0f91378625cb7739a0baaca9cb7611c332ac4580} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{125bc7030d7f2ba789d7b1d5f474af34f8902b764e7bc351b98f1f6f55779929 → 14f329a7925311958685a16ba370c4997e924d909091e0977e5d32b3e64ecc4f} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{8078b804bea630ed9c4ea07ec2f8ac92aba4128725860a66599eda90390d98f6 → 2b168c9ff212b8daea0d4334c94810458a683cde62f26b985855f8bf3f959db7} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{00f4a6a630f31eb05fbdd5725e4a294233d24574bd70f4f32f4597d8256a7c1c → 2fbbc46784d2ab789675941ce81d91822ac00bd5c3e144d06743d0b8ccaccc90} +2 -17
- package/dist/phoenix-ui/bundle/blobs/{90f8b07ec12b78e633e306322cda58415aea70d8e0f9ff85bd439361c01dabe1 → 35913e9a7ac47f91c8af70867c71d938aa7f200b562c867fa6a43c4a6209ef61} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{0e684572eb98d24b39cd70fa23e66c163178f5a11756dcf8a98fe2335636b33f → 3b62a4d253765fe0bdb180581cf493434d15de94e1b31470c573c0a21ece9e31} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{68b454a99ba72c3b9b93ed96c4267847297169f6a5bf321b20e60d637c271068 → 557baa7f9ea3d863f40602f37a85e9af09f9120cfa0669b0b95539774e87b9dc} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{c0a70ab2889e5c16e1f09249aefad948ce352c42cfe2b5afb758a4524e1b21b6 → 67fc771aa87556ecc8236c44e76251a41fa4e1809c6ad8c6a1f8eb9de91bb2d7} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{88b150cb804bc141b80a5687a091f61b99627155c465a27adb88546ef615af92 → 6b3068f23e684f8566e7f4b069a5554624f479d57ff9073610a0917fe5b5e5af} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{61450dbef64859e3136cd17ae3e997134d5df5721d96985c4e07367f38522129 → 6f600dfa082ed593159d3906d108baff9f4cb6bf90817a47429d613a95771d6f} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{36310febe3e7bd4c9b21407603f2a04afefdc87b7821758bb47098b1b2876765 → 7bff2e1985a347235ef84b783f801eb3f5872d6e2a8d0278fc8c52c3cd1ac4a4} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{e46d63e5f8a6ead1d44c8b6302a0c7247af8585b975ab5715518381777622ede → 821bda87cb9fc9f4349cf5243e18d7b4afc899ee884e33b16007ca3f1f5c9de1} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{cadc2daefcf238aab981fa56388381d3d5ab9c0686bb9dd8187035c9933dfcee → 8291ff7c0eef14199f7d209cb8eb5e6643034c1923805b5c7023c93010d7af6e} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{6b8d4c71355429a0b16bf367163fea46103a0a99b4e34f87608a39d85b1b980a → 94e595bc8239f72e0f5389f83aa022b18541839fb651d87b5933e89c92836a15} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{55277fa15eb4bd2f09bf7bb24c4e79df417b06654b1e10f502c7928b192fd7cc → ba015e8aac17711d7dfbcfce3e8c92190af383af595b9ca58b7df2c0c30ba579} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{3625c5c2750f117021232ce81c1e39ac5ef7c21445729327b88cf54dfc937df0 → fddd4bd853ad367d7f755477ee7c5ac3d0d3965c81c8a8e20eb83edc9cb7d43b} +1 -1
- package/dist/phoenix-ui/bundle/manifest.json +34 -34
- package/docs/phoenix-diagram.md +8 -6
- package/{skills/review-ui/references/phoenix-review.md → docs/phoenix-ui-review.md} +7 -12
- package/docs/phoenix-ui.md +17 -19
- package/package.json +2 -1
- package/skills/apply-ui-review/SKILL.md +1 -1
- package/skills/optimize-agents-md/SKILL.md +39 -21
- package/skills/optimize-agents-md/assets/coding-principles.md +78 -38
- package/skills/phoenix-diagram/ENGINE.json +18 -18
- package/skills/phoenix-diagram/references/migration.md +1 -1
- package/skills/phoenix-diagram/scripts/phoenix-bootstrap.cjs +4 -103
- package/skills/phoenix-ui/GENERATED.json +78 -78
- package/skills/phoenix-ui/SKILL.md +1 -1
- package/skills/phoenix-ui/scripts/ENGINE.json +19 -19
- package/skills/phoenix-ui/scripts/phoenix-bootstrap.cjs +2 -17
- package/skills/review-ui/SKILL.md +2 -1
- package/skills/xgent-init/SKILL.md +1 -1
- 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)
|
|
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`
|
|
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
|
-
-
|
|
75
|
-
- **回滚**:安装输出给出事务 ID、备份位置与完整命令,例如 `npm exec --package=@xgent-ai/skills@<版本> -- phoenix-ui migrate --rollback <事务ID> --project <项目>`;回滚只撤销本次安装的条目,后来新增的配置保留。
|
|
64
|
+
- **已有工具**:Phoenix UI 和 Diagram 都是可重新安装的工具:安装直接覆盖工具文件,不备份旧版、不创建安装事务目录,不提供普通安装回滚。中断后重跑安装即可;成功安装会清理旧安装记录和备份。业务源码、工具生成的业务数据及其他 hooks 保留。发现 impeccable 时仍可选择替换或共存,无交互环境默认保留。
|
|
76
65
|
|
|
77
|
-
|
|
66
|
+
安装、迁移、支持矩阵与渠道说明见 [docs/phoenix-ui.md](docs/phoenix-ui.md)。
|
|
78
67
|
|
|
79
|
-
|
|
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
|
|
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
|
|
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
|
|
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 交互 |
|
package/bin/xgent-skills.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
105
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
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/
|
|
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' : ''} --
|
|
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',
|
|
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 &&
|
|
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
|
-
|
|
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
|
|
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.
|
|
7
|
-
"sourceCommit": "
|
|
8
|
-
"sourceInputSha256": "
|
|
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": "
|
|
12
|
+
"bundleSha256": "e573234de7918dc87e1630c30a4ea1ba5862cc647dc0023a6d578ccb114e2ab6",
|
|
13
13
|
"engines": {
|
|
14
14
|
"darwin-arm64": {
|
|
15
15
|
"asset": "phoenix-diagram-darwin-arm64",
|
|
16
|
-
"size":
|
|
17
|
-
"sha256": "
|
|
18
|
-
"url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.
|
|
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":
|
|
23
|
-
"sha256": "
|
|
24
|
-
"url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.
|
|
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": "
|
|
36
|
-
"url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.
|
|
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.
|
|
7
|
-
"sourceCommit": "
|
|
8
|
-
"sourceInputSha256": "
|
|
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": "
|
|
12
|
+
"bundleSha256": "e573234de7918dc87e1630c30a4ea1ba5862cc647dc0023a6d578ccb114e2ab6",
|
|
13
13
|
"engines": {
|
|
14
14
|
"darwin-arm64": {
|
|
15
15
|
"asset": "phoenix-diagram-darwin-arm64",
|
|
16
|
-
"size":
|
|
17
|
-
"sha256": "
|
|
18
|
-
"url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.
|
|
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":
|
|
23
|
-
"sha256": "
|
|
24
|
-
"url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.
|
|
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": "
|
|
36
|
-
"url": "https://download.xgent.ai/vendor/phoenix-diagram/v0.7.
|
|
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":
|
|
141
|
-
"sha256": "
|
|
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":
|
|
237
|
-
"sha256": "
|
|
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
|
|
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, '.
|
|
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
|
|
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
|
|
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,
|
|
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": "
|
|
6624
|
+
"sha256": "cde4b96028f395b47a3264f65c458014ea123e579f68475c33ab59aac581756f",
|
|
6625
6625
|
"license": "Apache-2.0",
|
|
6626
6626
|
"licenses": [
|
|
6627
6627
|
{
|