@pm_zhousl/xinyun-ai-init 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/README.md +29 -39
  2. package/bin/xinyun-ai-init.js +121 -175
  3. package/init/xinyun-ai-init/README.md +26 -144
  4. package/init/xinyun-ai-init/rules/v3-skill-runtime.md +45 -0
  5. package/init/xinyun-ai-init/runtime/migrate-v3.js +115 -0
  6. package/init/xinyun-ai-init/runtime/route-skill.js +69 -0
  7. package/init/xinyun-ai-init/runtime-manifest.json +26 -0
  8. package/init/xinyun-ai-init/skills/registry.json +112 -0
  9. package/init/xinyun-ai-init/templates/AGENTS.template.md +4 -27
  10. package/init/xinyun-ai-init/templates/README.template.md +3 -31
  11. package/init/xinyun-ai-init/templates/control-plane-index.template.json +5 -24
  12. package/init/xinyun-ai-init/templates/core-rules.template.md +9 -57
  13. package/init/xinyun-ai-init/templates/current-task.template.md +3 -19
  14. package/init/xinyun-ai-init/templates/manifest.template.json +13 -67
  15. package/init/xinyun-ai-init/templates/project-context.template.json +3 -11
  16. package/init/xinyun-ai-init/templates/project-index.template.json +5 -51
  17. package/init/xinyun-ai-init/templates/skill-run-record.template.json +4 -16
  18. package/init/xinyun-ai-init/templates/skill-runtime-route.template.json +3 -24
  19. package/init/xinyun-ai-init/templates/skills-assignments.template.json +3 -21
  20. package/init/xinyun-ai-init/templates/skills-registry.template.json +5 -29
  21. package/init/xinyun-ai-init/templates/task-graph.template.json +3 -5
  22. package/init/xinyun-ai-init/templates/task-index.template.json +3 -31
  23. package/init/xinyun-ai-init/version.json +8 -8
  24. package/package.json +8 -14
  25. package/scripts/run-v3-runtime-probe.js +8 -0
  26. package/scripts/verify-legacy-cleanup.js +24 -0
  27. package/scripts/verify-release-gate.js +10 -23
  28. package/scripts/verify-rules-tarball-security.js +1 -1
  29. package/scripts/verify-skill-runtime.js +33 -0
  30. package/scripts/verify-tarball.js +42 -0
  31. package/scripts/verify-v3-package.js +70 -0
  32. package/test-scenarios/v3.0.0-behavior-evidence.json +25 -0
  33. package/test-scenarios/v3.0.0-scenarios.md +41 -0
  34. package/init/xinyun-ai-init/prompt.md +0 -1
  35. package/init/xinyun-ai-init/rules/coordination.md +0 -43
  36. package/init/xinyun-ai-init/rules/directory-self-completion.md +0 -100
  37. package/init/xinyun-ai-init/rules/frameworks/README.md +0 -56
  38. package/init/xinyun-ai-init/rules/frameworks/backend/php/thinkphp.md +0 -42
  39. package/init/xinyun-ai-init/rules/frameworks/frontend/uni-app.md +0 -36
  40. package/init/xinyun-ai-init/rules/frameworks/frontend/wechat-miniprogram.md +0 -36
  41. package/init/xinyun-ai-init/rules/frontend-audit.md +0 -9
  42. package/init/xinyun-ai-init/rules/init-rules.md +0 -145
  43. package/init/xinyun-ai-init/rules/self-learning-rules.md +0 -16
  44. package/init/xinyun-ai-init/rules/session-workbench.md +0 -333
  45. package/init/xinyun-ai-init/rules/skills.md +0 -23
  46. package/init/xinyun-ai-init/rules/temporary-sessions.md +0 -13
  47. package/init/xinyun-ai-init/rules/upgrade-rules.md +0 -161
  48. package/init/xinyun-ai-init/rules/v1.10.2-contract.md +0 -30
  49. package/init/xinyun-ai-init/rules/v1.11.0-acceleration.md +0 -27
  50. package/init/xinyun-ai-init/rules/v1.12.0-recovery.md +0 -34
  51. package/init/xinyun-ai-init/rules/v1.12.1-provider-session-binding.md +0 -25
  52. package/init/xinyun-ai-init/rules/v1.13.0-host-adapter.md +0 -43
  53. package/init/xinyun-ai-init/rules/v1.14.0-agent-core.md +0 -35
  54. package/init/xinyun-ai-init/rules/v1.15.0-session-binding.md +0 -44
  55. package/init/xinyun-ai-init/rules/v1.16.0-capability-efficiency.md +0 -23
  56. package/init/xinyun-ai-init/rules/v2.0.0-shared-project-conversations.md +0 -68
  57. package/init/xinyun-ai-init/rules/v2.0.0-single-session-skills.md +0 -89
  58. package/init/xinyun-ai-init/scripts/README.md +0 -16
  59. package/init/xinyun-ai-init/templates/agent-capabilities.template.json +0 -27
  60. package/init/xinyun-ai-init/templates/agent-profile.template.json +0 -21
  61. package/init/xinyun-ai-init/templates/archive-transaction.template.json +0 -15
  62. package/init/xinyun-ai-init/templates/bootstrap-snapshot.template.json +0 -22
  63. package/init/xinyun-ai-init/templates/capabilities.template.json +0 -19
  64. package/init/xinyun-ai-init/templates/codegraph-incremental-record.template.json +0 -21
  65. package/init/xinyun-ai-init/templates/conversation-binding.template.json +0 -26
  66. package/init/xinyun-ai-init/templates/conversation-event.template.json +0 -24
  67. package/init/xinyun-ai-init/templates/conversation-registry.template.json +0 -15
  68. package/init/xinyun-ai-init/templates/conversation-stream-head.template.json +0 -14
  69. package/init/xinyun-ai-init/templates/conversation-sync-cursor.template.json +0 -13
  70. package/init/xinyun-ai-init/templates/coordination-change-ledger.template.json +0 -15
  71. package/init/xinyun-ai-init/templates/coordination-conflict-record.template.json +0 -18
  72. package/init/xinyun-ai-init/templates/coordination-contract-snapshot.template.json +0 -9
  73. package/init/xinyun-ai-init/templates/coordination-project-facts.template.json +0 -20
  74. package/init/xinyun-ai-init/templates/coordination-transaction.template.json +0 -16
  75. package/init/xinyun-ai-init/templates/database-authorization.template.json +0 -53
  76. package/init/xinyun-ai-init/templates/final-delivery.template.json +0 -22
  77. package/init/xinyun-ai-init/templates/fixed-materialization.template.json +0 -12
  78. package/init/xinyun-ai-init/templates/framework-profile.template.json +0 -48
  79. package/init/xinyun-ai-init/templates/framework-profile.template.md +0 -39
  80. package/init/xinyun-ai-init/templates/frontend-audit.template.json +0 -27
  81. package/init/xinyun-ai-init/templates/handoff.template.json +0 -27
  82. package/init/xinyun-ai-init/templates/host-capabilities.template.json +0 -37
  83. package/init/xinyun-ai-init/templates/host-preflight.template.json +0 -33
  84. package/init/xinyun-ai-init/templates/html-migration-asset-map.template.md +0 -6
  85. package/init/xinyun-ai-init/templates/html-migration-compatibility-matrix.template.md +0 -6
  86. package/init/xinyun-ai-init/templates/html-migration-open-issues.template.md +0 -12
  87. package/init/xinyun-ai-init/templates/html-migration-pilot-plan.template.md +0 -21
  88. package/init/xinyun-ai-init/templates/html-migration-record.template.json +0 -36
  89. package/init/xinyun-ai-init/templates/html-migration-route-map.template.md +0 -6
  90. package/init/xinyun-ai-init/templates/html-migration-scope.template.md +0 -28
  91. package/init/xinyun-ai-init/templates/init-result.template.json +0 -25
  92. package/init/xinyun-ai-init/templates/initialization-cache.template.json +0 -13
  93. package/init/xinyun-ai-init/templates/initialization-change-report.template.json +0 -14
  94. package/init/xinyun-ai-init/templates/initialization-recovery.template.json +0 -24
  95. package/init/xinyun-ai-init/templates/initialization-stages.template.json +0 -18
  96. package/init/xinyun-ai-init/templates/initialization-timing.template.json +0 -16
  97. package/init/xinyun-ai-init/templates/local-query-index.template.json +0 -15
  98. package/init/xinyun-ai-init/templates/managed-baseline.template.json +0 -17
  99. package/init/xinyun-ai-init/templates/manual-session-binding.template.json +0 -45
  100. package/init/xinyun-ai-init/templates/manual-session-recovery.template.json +0 -53
  101. package/init/xinyun-ai-init/templates/multi-conversation-migration.template.json +0 -24
  102. package/init/xinyun-ai-init/templates/onboarding-report.template.json +0 -30
  103. package/init/xinyun-ai-init/templates/orchestrator.template.json +0 -28
  104. package/init/xinyun-ai-init/templates/parallelism.template.json +0 -13
  105. package/init/xinyun-ai-init/templates/performance-metrics.template.json +0 -17
  106. package/init/xinyun-ai-init/templates/preflight-report.template.json +0 -25
  107. package/init/xinyun-ai-init/templates/project-check.template.md +0 -56
  108. package/init/xinyun-ai-init/templates/project-guide.template.md +0 -57
  109. package/init/xinyun-ai-init/templates/project-identity.template.json +0 -23
  110. package/init/xinyun-ai-init/templates/project-status.template.json +0 -28
  111. package/init/xinyun-ai-init/templates/provider-adapter.template.json +0 -25
  112. package/init/xinyun-ai-init/templates/provider-session-binding.template.json +0 -77
  113. package/init/xinyun-ai-init/templates/re-takeover-record.template.json +0 -37
  114. package/init/xinyun-ai-init/templates/requirement-packet.template.json +0 -23
  115. package/init/xinyun-ai-init/templates/role-execution.template.md +0 -72
  116. package/init/xinyun-ai-init/templates/role-session-binding.template.json +0 -105
  117. package/init/xinyun-ai-init/templates/role-subtask.template.md +0 -36
  118. package/init/xinyun-ai-init/templates/session-binding.template.json +0 -38
  119. package/init/xinyun-ai-init/templates/session-candidates.template.json +0 -19
  120. package/init/xinyun-ai-init/templates/shared-project-snapshot.template.json +0 -24
  121. package/init/xinyun-ai-init/templates/single-session-migration.template.json +0 -31
  122. package/init/xinyun-ai-init/templates/sync-check.template.json +0 -23
  123. package/init/xinyun-ai-init/templates/takeover-baseline.template.json +0 -17
  124. package/init/xinyun-ai-init/templates/takeover-gate.template.json +0 -22
  125. package/init/xinyun-ai-init/templates/task-history.template.md +0 -35
  126. package/init/xinyun-ai-init/templates/task-lease.template.json +0 -19
  127. package/init/xinyun-ai-init/templates/task-lock.template.json +0 -31
  128. package/init/xinyun-ai-init/templates/task-main.template.md +0 -38
  129. package/init/xinyun-ai-init/templates/task-result.template.json +0 -23
  130. package/init/xinyun-ai-init/templates/team/README.md +0 -51
  131. package/init/xinyun-ai-init/templates/team/assignments.json +0 -49
  132. package/init/xinyun-ai-init/templates/team/confirmations/chief-engineer.md +0 -24
  133. package/init/xinyun-ai-init/templates/team/confirmations/programmer.md +0 -24
  134. package/init/xinyun-ai-init/templates/team/confirmations/project-owner.md +0 -24
  135. package/init/xinyun-ai-init/templates/team/confirmations/project-product.md +0 -24
  136. package/init/xinyun-ai-init/templates/team/confirmations/test-reviewer.md +0 -24
  137. package/init/xinyun-ai-init/templates/team/file-ownership.json +0 -32
  138. package/init/xinyun-ai-init/templates/team/members.json +0 -61
  139. package/init/xinyun-ai-init/templates/team/responsibility-matrix.json +0 -81
  140. package/init/xinyun-ai-init/templates/team/roles.json +0 -71
  141. package/init/xinyun-ai-init/templates/team/understandings/chief-engineer.md +0 -23
  142. package/init/xinyun-ai-init/templates/team/understandings/programmer.md +0 -23
  143. package/init/xinyun-ai-init/templates/team/understandings/project-owner.md +0 -23
  144. package/init/xinyun-ai-init/templates/team/understandings/project-product.md +0 -23
  145. package/init/xinyun-ai-init/templates/team/understandings/test-reviewer.md +0 -23
  146. package/init/xinyun-ai-init/templates/technical-impact-packet.template.json +0 -31
  147. package/init/xinyun-ai-init/templates/technical-landscape.template.json +0 -41
  148. package/init/xinyun-ai-init/templates/temporary-session.template.json +0 -32
  149. package/init/xinyun-ai-init/templates/workstreams-readme.template.md +0 -36
  150. package/scripts/collect-v1.10-evidence.js +0 -159
  151. package/scripts/collect-v1.14-local-evidence.js +0 -66
  152. package/scripts/collect-v1.15-real-evidence.js +0 -114
  153. package/scripts/collect-v1.16-evidence.js +0 -48
  154. package/scripts/collect-v2-evidence.js +0 -35
  155. package/scripts/run-v1.16-runtime-probe.js +0 -91
  156. package/scripts/run-v2-runtime-probe.js +0 -110
  157. package/scripts/verify-init-package.js +0 -2297
  158. package/scripts/verify-multi-conversation-contract.js +0 -61
  159. package/scripts/verify-re-takeover-contract.js +0 -54
  160. package/scripts/verify-rules-package.js +0 -1287
  161. package/scripts/verify-v2-package.js +0 -210
  162. package/scripts/verify-v2-tarball.js +0 -73
  163. package/test-scenarios/README.md +0 -130
  164. package/test-scenarios/fixtures/v1.10.0-takeover-gate-fixture/README.md +0 -23
  165. package/test-scenarios/fixtures/v1.10.0-takeover-gate-fixture/after/project-status.json +0 -7
  166. package/test-scenarios/fixtures/v1.10.0-takeover-gate-fixture/before/project-status.json +0 -7
  167. package/test-scenarios/fixtures/v1.9.0-behavior-fixture/README.md +0 -9
  168. package/test-scenarios/multi-conversation-evidence.json +0 -26
  169. package/test-scenarios/multi-conversation-scenarios.md +0 -16
  170. package/test-scenarios/re-takeover-evidence.json +0 -20
  171. package/test-scenarios/re-takeover-scenarios.md +0 -33
  172. package/test-scenarios/review-template.md +0 -165
  173. package/test-scenarios/scenarios.md +0 -1273
  174. package/test-scenarios/v1.10.0-behavior-evidence.json +0 -31
  175. package/test-scenarios/v1.10.0-scenarios.md +0 -96
  176. package/test-scenarios/v1.10.2-scenarios.md +0 -38
  177. package/test-scenarios/v1.11.0-scenarios.md +0 -30
  178. package/test-scenarios/v1.12.0-behavior-evidence.json +0 -29
  179. package/test-scenarios/v1.12.0-scenarios.md +0 -51
  180. package/test-scenarios/v1.12.1-behavior-evidence.json +0 -27
  181. package/test-scenarios/v1.12.1-scenarios.md +0 -42
  182. package/test-scenarios/v1.13.0-behavior-evidence.json +0 -650
  183. package/test-scenarios/v1.13.0-scenarios.md +0 -94
  184. package/test-scenarios/v1.14.0-behavior-evidence.json +0 -780
  185. package/test-scenarios/v1.14.0-scenarios.md +0 -63
  186. package/test-scenarios/v1.15.0-behavior-evidence.json +0 -476
  187. package/test-scenarios/v1.15.0-scenarios.md +0 -57
  188. package/test-scenarios/v1.16.0-behavior-evidence.json +0 -46
  189. package/test-scenarios/v1.16.0-real-host-probe.json +0 -132
  190. package/test-scenarios/v1.16.0-scenarios.md +0 -19
  191. package/test-scenarios/v1.7.0-scenarios.md +0 -91
  192. package/test-scenarios/v1.7.1-scenarios.md +0 -130
  193. package/test-scenarios/v1.7.2-scenarios.md +0 -51
  194. package/test-scenarios/v1.7.3-scenarios.md +0 -79
  195. package/test-scenarios/v1.8.0-scenarios.md +0 -39
  196. package/test-scenarios/v1.9.0-behavior-evidence.json +0 -269
  197. package/test-scenarios/v1.9.0-scenarios.md +0 -55
  198. package/test-scenarios/v2.0.0-behavior-evidence.json +0 -40
  199. package/test-scenarios/v2.0.0-scenarios.md +0 -21
package/README.md CHANGED
@@ -1,68 +1,58 @@
1
- # @pm_zhousl/xinyun-ai-init
1
+ # @pm_zhousl/xinyun-ai-init v3
2
2
 
3
- 欣芸技术开发 Agent 流初始化包。v2 为每个项目维护一个共享项目上下文,允许一个或多个普通 Codex 对话接入;各对话按正式任务动态执行 Skills,并通过独立事件流和增量游标自动共享有价值结果,不再创建固定角色或角色专属会话。
3
+ 欣芸技术开发 Skill Runtime 初始化包。v3 将项目接管、任务分析、技术设计、实现、验证、安全审查、发布审查和交接同步封装为可直接路由的 Skills。
4
4
 
5
- ## Codex 一句话入口
5
+ ## 使用
6
6
 
7
- 用 Codex 打开项目后,直接发送:
8
-
9
- ```md
10
- 请执行 `npx --yes @pm_zhousl/xinyun-ai-init@latest --force`,然后读取 `xinyun-ai-init/README.md`,按其中规则初始化或升级当前项目。
7
+ ```bash
8
+ npx --yes @pm_zhousl/xinyun-ai-init@latest --force --offline
11
9
  ```
12
10
 
13
- ## 手动使用
11
+ 默认只 materialize `xinyun-ai-init/` 中的核心 Runtime,不创建 `xinyun-ai/`,不修改业务代码,也不读取整套历史兼容材料。
12
+
13
+ 初始化后,宿主 AI 使用:
14
14
 
15
15
  ```bash
16
- npx --yes @pm_zhousl/xinyun-ai-init@latest --force
16
+ node xinyun-ai-init/runtime/route-skill.js --facts task-facts.json
17
17
  ```
18
18
 
19
- 执行后会在当前目录生成 `xinyun-ai-init/`。
19
+ ## 参数
20
+
21
+ - `--offline`:跳过 npm registry 版本检查。
22
+ - `--no-version-check`:跳过版本检查。
23
+ - `--strict-version`:发现本地版本落后时退出。
24
+ - `--force`:覆盖脚本托管文件;用户自行修改的文件仍会记录冲突。
20
25
 
21
26
  ## 维护者校验
22
27
 
23
28
  ```bash
24
- npm run verify:init
25
- npm run verify:rules
26
- npm run verify:v2
29
+ npm run verify:v3
30
+ npm run verify:runtime
31
+ npm run verify:cleanup
27
32
  npm run verify:rules:security
33
+ npm pack --dry-run
28
34
  ```
29
35
 
30
- 上述校验为只读规则包一致性检查,不自动修复、不安装依赖、不修改 Git 或业务代码。
31
-
32
- ## 注意
33
-
34
- 该 CLI 只释放初始化包,不修改业务代码、不创建 `xinyun-ai/`。
35
-
36
- 完整执行要求已经内置在 `xinyun-ai-init/README.md`、`rules/` 和 `templates/` 中。后续不需要复制长提示词,只需要让 AI 读取本地释放出来的 `xinyun-ai-init/README.md`。
37
-
38
- CLI 执行时会显示当前版本,并尽量检测 npm 线上最新版本;检测失败不会阻塞初始化。
36
+ ## v3 升级
39
37
 
40
- 用户发送 Codex 一句话入口后,AI 可按初始化包规则创建或补齐最小 `xinyun-ai/`。正式任务必须先更新目标项目自己的任务文件、索引、任务图和当前任务;这不代表授权修改业务代码。
41
-
42
- 可选参数:
38
+ 旧项目升级先预览:
43
39
 
44
40
  ```bash
45
- --no-version-check
46
- --strict-version
41
+ node xinyun-ai-init/runtime/migrate-v3.js --project-root <project-root>
47
42
  ```
48
43
 
49
- ## 维护者发布
50
-
51
- 发布新版时,在本包目录执行:
44
+ 确认清理后执行:
52
45
 
53
- ```powershell
54
- npm run publish:token
46
+ ```bash
47
+ node xinyun-ai-init/runtime/migrate-v3.js --project-root <project-root> --apply
55
48
  ```
56
49
 
57
- 脚本会提示输入 npm token,临时写入系统临时目录下的 `.npmrc`,发布完成或失败后自动删除该文件。
58
- `.npmrc` 位于系统临时目录,不覆盖项目或用户现有 npm 配置。无需执行 `npm login`,也无需预先设置环境变量;按提示只输入一次 token。脚本会校验 token 的发布权限、发布包,并在完成或失败后清除临时凭据。npm publish 必须已获得用户单独授权。
59
-
60
- `publish:token` 不自动执行真实宿主或第二会话发布验证。需要单独审查发布证据时,可手动运行 `npm run verify:release`,该检查结果不会替代用户发布授权。
50
+ 升级会合并必要的项目事实和任务结果,然后移除已知的 team、角色任务和角色会话运行目录。v3 不再提供旧兼容运行路径。
61
51
 
62
- 发布前预演:
52
+ ## 发布
63
53
 
64
54
  ```powershell
65
- npm run publish:token:dry
55
+ npm run publish:token
66
56
  ```
67
57
 
68
- 预演同样只提示一次 token,但只执行 npm 的 `--dry-run`,不会发布版本。
58
+ 发布只要求在安全提示中输入一次 token,不需要 `npm login`、环境变量或第二会话验证。npm publish 仍需用户针对目标版本单独授权。
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const fs = require("fs");
4
+ const crypto = require("crypto");
4
5
  const https = require("https");
5
6
  const os = require("os");
6
7
  const path = require("path");
7
8
 
8
9
  const packageJson = require("../package.json");
9
-
10
10
  const args = new Set(process.argv.slice(2));
11
11
  const force = args.has("--force") || args.has("-f");
12
+ const offline = args.has("--offline");
12
13
  const noVersionCheck = args.has("--no-version-check");
13
14
  const strictVersion = args.has("--strict-version");
14
15
  const cwd = process.cwd();
@@ -16,240 +17,185 @@ const source = path.resolve(__dirname, "..", "init", "xinyun-ai-init");
16
17
  const target = path.join(cwd, "xinyun-ai-init");
17
18
  const packageName = packageJson.name;
18
19
  const currentVersion = packageJson.version;
19
- const versionChannel = "latest";
20
20
  const registryMetadataUrl = "https://registry.npmjs.org/@pm_zhousl%2Fxinyun-ai-init";
21
21
  const updateCheckTtlMs = 24 * 60 * 60 * 1000;
22
22
 
23
- function copyDir(src, dest) {
24
- fs.mkdirSync(dest, { recursive: true });
25
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
26
- const srcPath = path.join(src, entry.name);
27
- const destPath = path.join(dest, entry.name);
28
- if (entry.isDirectory()) {
29
- copyDir(srcPath, destPath);
30
- } else {
31
- fs.copyFileSync(srcPath, destPath);
32
- }
33
- }
23
+ function hashFile(filePath) {
24
+ return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
34
25
  }
35
26
 
36
- function compareVersions(a, b) {
37
- const pa = String(a).split(".").map((part) => Number.parseInt(part, 10) || 0);
38
- const pb = String(b).split(".").map((part) => Number.parseInt(part, 10) || 0);
39
- const length = Math.max(pa.length, pb.length);
40
-
41
- for (let index = 0; index < length; index += 1) {
42
- const av = pa[index] || 0;
43
- const bv = pb[index] || 0;
44
- if (av > bv) return 1;
45
- if (av < bv) return -1;
46
- }
47
-
48
- return 0;
27
+ function copyFileAtomic(sourcePath, targetPath) {
28
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
29
+ const tempPath = `${targetPath}.${process.pid}.tmp`;
30
+ fs.copyFileSync(sourcePath, tempPath);
31
+ fs.renameSync(tempPath, targetPath);
49
32
  }
50
33
 
51
- function fetchLatestVersion(timeoutMs = 1500) {
52
- return new Promise((resolve) => {
53
- const request = https.get(
54
- registryMetadataUrl,
55
- { headers: { Accept: "application/json", "User-Agent": "xinyun-ai-init" } },
56
- (response) => {
57
- let body = "";
58
-
59
- response.setEncoding("utf8");
60
- response.on("data", (chunk) => {
61
- body += chunk;
62
- });
63
- response.on("end", () => {
64
- if (response.statusCode < 200 || response.statusCode >= 300) {
65
- resolve(null);
66
- return;
67
- }
68
-
69
- try {
70
- const data = JSON.parse(body);
71
- resolve(data && data["dist-tags"] ? data["dist-tags"].latest : null);
72
- } catch (error) {
73
- resolve(null);
74
- }
75
- });
76
- }
77
- );
78
-
79
- request.setTimeout(timeoutMs, () => {
80
- request.destroy();
81
- resolve(null);
82
- });
83
-
84
- request.on("error", () => {
85
- resolve(null);
86
- });
87
- });
34
+ function readJson(filePath, fallback) {
35
+ try {
36
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
37
+ } catch (error) {
38
+ return fallback;
39
+ }
88
40
  }
89
41
 
90
42
  function getUpdateCachePath() {
91
43
  if (process.platform === "win32" && process.env.LOCALAPPDATA) {
92
44
  return path.join(process.env.LOCALAPPDATA, "xinyun-ai-init", "update-check.json");
93
45
  }
94
-
95
46
  if (process.env.XDG_CACHE_HOME) {
96
47
  return path.join(process.env.XDG_CACHE_HOME, "xinyun-ai-init", "update-check.json");
97
48
  }
98
-
99
49
  return path.join(os.homedir(), ".cache", "xinyun-ai-init", "update-check.json");
100
50
  }
101
51
 
102
52
  function readUpdateCache() {
103
- try {
104
- const cachePath = getUpdateCachePath();
105
- if (!fs.existsSync(cachePath)) {
106
- return null;
107
- }
108
- return JSON.parse(fs.readFileSync(cachePath, "utf8"));
109
- } catch (error) {
110
- return null;
111
- }
53
+ return readJson(getUpdateCachePath(), null);
112
54
  }
113
55
 
114
56
  function writeUpdateCache(cache) {
115
57
  try {
116
58
  const cachePath = getUpdateCachePath();
117
- const cacheDir = path.dirname(cachePath);
118
- fs.mkdirSync(cacheDir, { recursive: true });
59
+ fs.mkdirSync(path.dirname(cachePath), { recursive: true });
119
60
  const tempPath = `${cachePath}.${process.pid}.tmp`;
120
61
  fs.writeFileSync(tempPath, `${JSON.stringify(cache, null, 2)}\n`, "utf8");
121
62
  fs.renameSync(tempPath, cachePath);
122
- return true;
123
63
  } catch (error) {
124
- return false;
64
+ // A cache is an optimization. Failure must not block initialization.
125
65
  }
126
66
  }
127
67
 
128
- function shouldNotifyUpdate(cache, observedVersion, nowMs) {
129
- if (!cache || cache.packageName !== packageName || cache.channel !== versionChannel) {
130
- return true;
131
- }
132
-
133
- if (cache.lastNotifiedVersion !== observedVersion || !cache.lastNotifiedAt) {
134
- return true;
135
- }
136
-
137
- const notifiedAt = Date.parse(cache.lastNotifiedAt);
138
- if (Number.isNaN(notifiedAt)) {
139
- return true;
68
+ function compareVersions(a, b) {
69
+ const left = String(a).split(".").map((part) => Number.parseInt(part, 10) || 0);
70
+ const right = String(b).split(".").map((part) => Number.parseInt(part, 10) || 0);
71
+ const length = Math.max(left.length, right.length);
72
+ for (let index = 0; index < length; index += 1) {
73
+ if ((left[index] || 0) > (right[index] || 0)) return 1;
74
+ if ((left[index] || 0) < (right[index] || 0)) return -1;
140
75
  }
141
-
142
- return nowMs - notifiedAt >= updateCheckTtlMs;
76
+ return 0;
143
77
  }
144
78
 
145
- function buildUpdateCache(observedVersion, checkStatus, notification, nowIso, previousCache) {
146
- const lastNotifiedVersion = notification
147
- ? observedVersion
148
- : previousCache && previousCache.lastNotifiedVersion
149
- ? previousCache.lastNotifiedVersion
150
- : null;
151
- const lastNotifiedAt = notification
152
- ? nowIso
153
- : previousCache && previousCache.lastNotifiedAt
154
- ? previousCache.lastNotifiedAt
155
- : null;
156
-
157
- return {
158
- schemaVersion: 1,
159
- packageName,
160
- channel: versionChannel,
161
- lastCheckedAt: nowIso,
162
- observedVersion: observedVersion || null,
163
- lastNotifiedVersion,
164
- lastNotifiedAt,
165
- checkStatus,
166
- };
79
+ function fetchLatestVersion(timeoutMs = 1200) {
80
+ return new Promise((resolve) => {
81
+ const request = https.get(
82
+ registryMetadataUrl,
83
+ { headers: { Accept: "application/json", "User-Agent": "xinyun-ai-init" } },
84
+ (response) => {
85
+ let body = "";
86
+ response.setEncoding("utf8");
87
+ response.on("data", (chunk) => { body += chunk; });
88
+ response.on("end", () => {
89
+ if (response.statusCode < 200 || response.statusCode >= 300) return resolve(null);
90
+ try {
91
+ const data = JSON.parse(body);
92
+ resolve(data && data["dist-tags"] ? data["dist-tags"].latest : null);
93
+ } catch (error) {
94
+ resolve(null);
95
+ }
96
+ });
97
+ }
98
+ );
99
+ request.setTimeout(timeoutMs, () => { request.destroy(); resolve(null); });
100
+ request.on("error", () => resolve(null));
101
+ });
167
102
  }
168
103
 
169
104
  async function checkVersion() {
170
105
  console.log(`[xinyun-ai-init] Current version: ${currentVersion}`);
171
-
172
- if (noVersionCheck) {
173
- console.log("[xinyun-ai-init] Version check skipped.");
106
+ const cache = readUpdateCache();
107
+ const now = Date.now();
108
+ const cacheFresh = cache && cache.packageName === packageName && cache.observedVersion &&
109
+ Date.parse(cache.lastCheckedAt || "") > now - updateCheckTtlMs;
110
+
111
+ if (offline || noVersionCheck || cacheFresh) {
112
+ if (cacheFresh) console.log(`[xinyun-ai-init] Using cached latest version: ${cache.observedVersion}`);
113
+ else console.log("[xinyun-ai-init] Version check skipped.");
174
114
  return true;
175
115
  }
176
116
 
177
117
  const latestVersion = await fetchLatestVersion();
178
- const nowMs = Date.now();
179
- const nowIso = new Date(nowMs).toISOString();
180
-
118
+ const nowIso = new Date().toISOString();
181
119
  if (!latestVersion) {
182
- console.log("[xinyun-ai-init] Could not check latest npm version. Continue with local version.");
183
- writeUpdateCache(buildUpdateCache(null, "unknown", false, nowIso, readUpdateCache()));
120
+ console.log("[xinyun-ai-init] Version check unavailable. Continue with local version.");
121
+ writeUpdateCache({ schemaVersion: 1, packageName, observedVersion: null, lastCheckedAt: nowIso, checkStatus: "unknown" });
184
122
  return true;
185
123
  }
186
124
 
187
125
  console.log(`[xinyun-ai-init] Latest version: ${latestVersion}`);
188
-
126
+ writeUpdateCache({ schemaVersion: 1, packageName, observedVersion: latestVersion, lastCheckedAt: nowIso, checkStatus: "current" });
127
+ if (compareVersions(currentVersion, latestVersion) < 0 && strictVersion) {
128
+ console.error("[xinyun-ai-init] Strict version mode is enabled. Stop because local version is outdated.");
129
+ return false;
130
+ }
189
131
  if (compareVersions(currentVersion, latestVersion) < 0) {
190
- const cache = readUpdateCache();
191
- const shouldNotify = shouldNotifyUpdate(cache, latestVersion, nowMs);
192
-
193
- if (shouldNotify) {
194
- console.log("[xinyun-ai-init] A newer version is available. Recommended command:");
195
- console.log(`npx --yes ${packageName}@latest --force`);
196
- console.log("[xinyun-ai-init] After upgrading the npm package, ask AI to reread xinyun-ai-init/README.md and check whether xinyun-ai/ needs a controlled upgrade.");
197
- } else {
198
- console.log("[xinyun-ai-init] A newer version is available, but the same update was already shown within 24 hours.");
199
- }
200
-
201
- writeUpdateCache(buildUpdateCache(latestVersion, "available", shouldNotify, nowIso, cache));
202
-
203
- if (strictVersion) {
204
- console.error("[xinyun-ai-init] Strict version mode is enabled. Stop because local version is outdated.");
205
- return false;
206
- }
207
-
208
- return true;
132
+ console.log(`[xinyun-ai-init] A newer version is available: npx --yes ${packageName}@latest --force`);
209
133
  }
210
-
211
- writeUpdateCache(buildUpdateCache(latestVersion, "current", false, nowIso, readUpdateCache()));
212
134
  return true;
213
135
  }
214
136
 
215
- async function main() {
216
- const canContinue = await checkVersion();
217
- if (!canContinue) {
218
- process.exit(1);
137
+ function materialize() {
138
+ const manifestPath = path.join(source, "runtime-manifest.json");
139
+ const manifest = readJson(manifestPath, null);
140
+ if (!manifest || !Array.isArray(manifest.files)) {
141
+ throw new Error("Missing or invalid runtime-manifest.json");
219
142
  }
220
143
 
221
- if (!fs.existsSync(source)) {
222
- console.error("[xinyun-ai-init] Init package missing:", source);
223
- console.error("[xinyun-ai-init] Please reinstall or run with @latest.");
224
- process.exit(1);
225
- }
226
-
227
- if (fs.existsSync(target)) {
228
- if (!force) {
229
- console.log("[xinyun-ai-init] xinyun-ai-init/ already exists. Skip overwrite.");
230
- console.log(`[xinyun-ai-init] To overwrite, run: npx --yes ${packageName}@latest --force`);
231
- console.log("");
232
- printNextStep();
233
- return;
144
+ const statePath = path.join(target, ".runtime-state.json");
145
+ const previous = readJson(statePath, { schemaVersion: 1, files: {} });
146
+ const next = {
147
+ schemaVersion: 1,
148
+ packageName,
149
+ packageVersion: currentVersion,
150
+ managedFiles: manifest.files,
151
+ files: {},
152
+ updatedAt: new Date().toISOString()
153
+ };
154
+ let copied = 0;
155
+ let skipped = 0;
156
+
157
+ fs.mkdirSync(target, { recursive: true });
158
+ for (const relative of manifest.files) {
159
+ const sourcePath = path.join(source, relative);
160
+ const targetPath = path.join(target, relative);
161
+ const sourceHash = hashFile(sourcePath);
162
+ const existingHash = fs.existsSync(targetPath) ? hashFile(targetPath) : null;
163
+ const previousEntry = previous.files && previous.files[relative];
164
+ const userModified = previousEntry && existingHash && existingHash !== previousEntry.targetHash;
165
+
166
+ if (userModified && !force) {
167
+ next.files[relative] = { sourceHash, targetHash: existingHash, status: "conflict" };
168
+ skipped += 1;
169
+ continue;
170
+ }
171
+ if (!existingHash || !previousEntry || previousEntry.sourceHash !== sourceHash || existingHash !== sourceHash || force) {
172
+ copyFileAtomic(sourcePath, targetPath);
173
+ copied += 1;
174
+ } else {
175
+ skipped += 1;
234
176
  }
235
- fs.rmSync(target, { recursive: true, force: true });
177
+ next.files[relative] = { sourceHash, targetHash: hashFile(targetPath), status: "managed" };
236
178
  }
237
-
238
- copyDir(source, target);
239
- console.log("[xinyun-ai-init] Created init package:", target);
240
- console.log("[xinyun-ai-init] This CLI only creates xinyun-ai-init/. It does not create xinyun-ai/ or modify business code.");
241
- console.log("");
242
- printNextStep();
179
+ copyFileAtomic(manifestPath, path.join(target, "runtime-manifest.json"));
180
+ next.files["runtime-manifest.json"] = { sourceHash: hashFile(manifestPath), targetHash: hashFile(path.join(target, "runtime-manifest.json")), status: "managed" };
181
+ const tempState = `${statePath}.${process.pid}.tmp`;
182
+ fs.writeFileSync(tempState, `${JSON.stringify(next, null, 2)}\n`, "utf8");
183
+ fs.renameSync(tempState, statePath);
184
+ return { copied, skipped, conflicts: Object.values(next.files).filter((item) => item.status === "conflict").length };
243
185
  }
244
186
 
245
187
  function printNextStep() {
246
- console.log("Next step: send this message to Codex / Cursor / Claude Code:");
247
- console.log("");
248
- console.log("请执行 `npx --yes @pm_zhousl/xinyun-ai-init@latest --force`,然后读取 `xinyun-ai-init/README.md`,按其中规则初始化或升级当前项目。");
249
- console.log("");
250
- console.log("The full workflow is in xinyun-ai-init/README.md. No long prompt is needed.");
251
- console.log("After this message, the AI may create or update the minimal xinyun-ai/ workspace, but must not modify business code without confirmation.");
252
- console.log("");
188
+ console.log("Next step: ask the host AI to read xinyun-ai-init/README.md and call the bundled Skill Runtime.");
189
+ console.log("The default route command is: node xinyun-ai-init/runtime/route-skill.js --facts <task-facts.json>");
190
+ }
191
+
192
+ async function main() {
193
+ if (!(await checkVersion())) process.exit(1);
194
+ if (!fs.existsSync(source)) throw new Error(`Init package missing: ${source}`);
195
+ const result = materialize();
196
+ console.log(`[xinyun-ai-init] Materialized v${currentVersion}: ${result.copied} updated, ${result.skipped} reused, ${result.conflicts} conflicts.`);
197
+ console.log("[xinyun-ai-init] This CLI only manages xinyun-ai-init/. It does not modify business code.");
198
+ printNextStep();
253
199
  }
254
200
 
255
201
  main().catch((error) => {