@pm_zhousl/xinyun-ai-init 1.16.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 (176) hide show
  1. package/README.md +29 -44
  2. package/bin/xinyun-ai-init.js +121 -175
  3. package/init/xinyun-ai-init/README.md +24 -1147
  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 -242
  10. package/init/xinyun-ai-init/templates/README.template.md +4 -45
  11. package/init/xinyun-ai-init/templates/control-plane-index.template.json +15 -28
  12. package/init/xinyun-ai-init/templates/core-rules.template.md +9 -636
  13. package/init/xinyun-ai-init/templates/current-task.template.md +8 -322
  14. package/init/xinyun-ai-init/templates/manifest.template.json +35 -396
  15. package/init/xinyun-ai-init/templates/project-context.template.json +12 -0
  16. package/init/xinyun-ai-init/templates/project-index.template.json +11 -425
  17. package/init/xinyun-ai-init/templates/skill-run-record.template.json +8 -16
  18. package/init/xinyun-ai-init/templates/skill-runtime-route.template.json +3 -11
  19. package/init/xinyun-ai-init/templates/skills-assignments.template.json +3 -16
  20. package/init/xinyun-ai-init/templates/skills-registry.template.json +5 -7
  21. package/init/xinyun-ai-init/templates/task-graph.template.json +4 -15
  22. package/init/xinyun-ai-init/templates/task-index.template.json +4 -89
  23. package/init/xinyun-ai-init/version.json +8 -7
  24. package/package.json +8 -9
  25. package/scripts/publish-with-token.ps1 +6 -16
  26. package/scripts/run-v3-runtime-probe.js +8 -0
  27. package/scripts/verify-legacy-cleanup.js +24 -0
  28. package/scripts/verify-release-gate.js +11 -16
  29. package/scripts/verify-rules-tarball-security.js +1 -1
  30. package/scripts/verify-skill-runtime.js +33 -0
  31. package/scripts/verify-tarball.js +42 -0
  32. package/scripts/verify-v3-package.js +70 -0
  33. package/test-scenarios/v3.0.0-behavior-evidence.json +25 -0
  34. package/test-scenarios/v3.0.0-scenarios.md +41 -0
  35. package/init/xinyun-ai-init/prompt.md +0 -1
  36. package/init/xinyun-ai-init/rules/coordination.md +0 -33
  37. package/init/xinyun-ai-init/rules/directory-self-completion.md +0 -96
  38. package/init/xinyun-ai-init/rules/frameworks/README.md +0 -56
  39. package/init/xinyun-ai-init/rules/frameworks/backend/php/thinkphp.md +0 -42
  40. package/init/xinyun-ai-init/rules/frameworks/frontend/uni-app.md +0 -36
  41. package/init/xinyun-ai-init/rules/frameworks/frontend/wechat-miniprogram.md +0 -36
  42. package/init/xinyun-ai-init/rules/frontend-audit.md +0 -9
  43. package/init/xinyun-ai-init/rules/init-rules.md +0 -139
  44. package/init/xinyun-ai-init/rules/self-learning-rules.md +0 -16
  45. package/init/xinyun-ai-init/rules/session-workbench.md +0 -327
  46. package/init/xinyun-ai-init/rules/skills.md +0 -15
  47. package/init/xinyun-ai-init/rules/temporary-sessions.md +0 -9
  48. package/init/xinyun-ai-init/rules/upgrade-rules.md +0 -155
  49. package/init/xinyun-ai-init/rules/v1.10.2-contract.md +0 -30
  50. package/init/xinyun-ai-init/rules/v1.11.0-acceleration.md +0 -27
  51. package/init/xinyun-ai-init/rules/v1.12.0-recovery.md +0 -34
  52. package/init/xinyun-ai-init/rules/v1.12.1-provider-session-binding.md +0 -25
  53. package/init/xinyun-ai-init/rules/v1.13.0-host-adapter.md +0 -43
  54. package/init/xinyun-ai-init/rules/v1.14.0-agent-core.md +0 -35
  55. package/init/xinyun-ai-init/rules/v1.15.0-session-binding.md +0 -44
  56. package/init/xinyun-ai-init/rules/v1.16.0-capability-efficiency.md +0 -23
  57. package/init/xinyun-ai-init/scripts/README.md +0 -16
  58. package/init/xinyun-ai-init/templates/agent-capabilities.template.json +0 -27
  59. package/init/xinyun-ai-init/templates/agent-profile.template.json +0 -26
  60. package/init/xinyun-ai-init/templates/archive-transaction.template.json +0 -15
  61. package/init/xinyun-ai-init/templates/bootstrap-snapshot.template.json +0 -17
  62. package/init/xinyun-ai-init/templates/capabilities.template.json +0 -19
  63. package/init/xinyun-ai-init/templates/codegraph-incremental-record.template.json +0 -21
  64. package/init/xinyun-ai-init/templates/coordination-change-ledger.template.json +0 -12
  65. package/init/xinyun-ai-init/templates/coordination-conflict-record.template.json +0 -13
  66. package/init/xinyun-ai-init/templates/coordination-contract-snapshot.template.json +0 -9
  67. package/init/xinyun-ai-init/templates/coordination-project-facts.template.json +0 -18
  68. package/init/xinyun-ai-init/templates/coordination-transaction.template.json +0 -16
  69. package/init/xinyun-ai-init/templates/database-authorization.template.json +0 -53
  70. package/init/xinyun-ai-init/templates/final-delivery.template.json +0 -22
  71. package/init/xinyun-ai-init/templates/fixed-materialization.template.json +0 -12
  72. package/init/xinyun-ai-init/templates/framework-profile.template.json +0 -48
  73. package/init/xinyun-ai-init/templates/framework-profile.template.md +0 -39
  74. package/init/xinyun-ai-init/templates/frontend-audit.template.json +0 -27
  75. package/init/xinyun-ai-init/templates/handoff.template.json +0 -32
  76. package/init/xinyun-ai-init/templates/host-capabilities.template.json +0 -37
  77. package/init/xinyun-ai-init/templates/host-preflight.template.json +0 -33
  78. package/init/xinyun-ai-init/templates/html-migration-asset-map.template.md +0 -6
  79. package/init/xinyun-ai-init/templates/html-migration-compatibility-matrix.template.md +0 -6
  80. package/init/xinyun-ai-init/templates/html-migration-open-issues.template.md +0 -12
  81. package/init/xinyun-ai-init/templates/html-migration-pilot-plan.template.md +0 -21
  82. package/init/xinyun-ai-init/templates/html-migration-record.template.json +0 -36
  83. package/init/xinyun-ai-init/templates/html-migration-route-map.template.md +0 -6
  84. package/init/xinyun-ai-init/templates/html-migration-scope.template.md +0 -28
  85. package/init/xinyun-ai-init/templates/init-result.template.json +0 -220
  86. package/init/xinyun-ai-init/templates/initialization-cache.template.json +0 -13
  87. package/init/xinyun-ai-init/templates/initialization-change-report.template.json +0 -14
  88. package/init/xinyun-ai-init/templates/initialization-recovery.template.json +0 -29
  89. package/init/xinyun-ai-init/templates/initialization-stages.template.json +0 -18
  90. package/init/xinyun-ai-init/templates/initialization-timing.template.json +0 -16
  91. package/init/xinyun-ai-init/templates/local-query-index.template.json +0 -15
  92. package/init/xinyun-ai-init/templates/managed-baseline.template.json +0 -17
  93. package/init/xinyun-ai-init/templates/manual-session-binding.template.json +0 -45
  94. package/init/xinyun-ai-init/templates/manual-session-recovery.template.json +0 -53
  95. package/init/xinyun-ai-init/templates/onboarding-report.template.json +0 -25
  96. package/init/xinyun-ai-init/templates/orchestrator.template.json +0 -18
  97. package/init/xinyun-ai-init/templates/parallelism.template.json +0 -13
  98. package/init/xinyun-ai-init/templates/performance-metrics.template.json +0 -17
  99. package/init/xinyun-ai-init/templates/preflight-report.template.json +0 -46
  100. package/init/xinyun-ai-init/templates/project-check.template.md +0 -198
  101. package/init/xinyun-ai-init/templates/project-guide.template.md +0 -264
  102. package/init/xinyun-ai-init/templates/project-identity.template.json +0 -23
  103. package/init/xinyun-ai-init/templates/project-status.template.json +0 -53
  104. package/init/xinyun-ai-init/templates/provider-adapter.template.json +0 -38
  105. package/init/xinyun-ai-init/templates/provider-session-binding.template.json +0 -77
  106. package/init/xinyun-ai-init/templates/requirement-packet.template.json +0 -23
  107. package/init/xinyun-ai-init/templates/role-execution.template.md +0 -72
  108. package/init/xinyun-ai-init/templates/role-session-binding.template.json +0 -105
  109. package/init/xinyun-ai-init/templates/role-subtask.template.md +0 -36
  110. package/init/xinyun-ai-init/templates/session-binding.template.json +0 -35
  111. package/init/xinyun-ai-init/templates/session-candidates.template.json +0 -19
  112. package/init/xinyun-ai-init/templates/shared-project-snapshot.template.json +0 -24
  113. package/init/xinyun-ai-init/templates/takeover-baseline.template.json +0 -17
  114. package/init/xinyun-ai-init/templates/takeover-gate.template.json +0 -17
  115. package/init/xinyun-ai-init/templates/task-history.template.md +0 -35
  116. package/init/xinyun-ai-init/templates/task-lock.template.json +0 -31
  117. package/init/xinyun-ai-init/templates/task-main.template.md +0 -47
  118. package/init/xinyun-ai-init/templates/task-result.template.json +0 -23
  119. package/init/xinyun-ai-init/templates/team/README.md +0 -51
  120. package/init/xinyun-ai-init/templates/team/assignments.json +0 -49
  121. package/init/xinyun-ai-init/templates/team/confirmations/chief-engineer.md +0 -24
  122. package/init/xinyun-ai-init/templates/team/confirmations/programmer.md +0 -24
  123. package/init/xinyun-ai-init/templates/team/confirmations/project-owner.md +0 -24
  124. package/init/xinyun-ai-init/templates/team/confirmations/project-product.md +0 -24
  125. package/init/xinyun-ai-init/templates/team/confirmations/test-reviewer.md +0 -24
  126. package/init/xinyun-ai-init/templates/team/file-ownership.json +0 -32
  127. package/init/xinyun-ai-init/templates/team/members.json +0 -61
  128. package/init/xinyun-ai-init/templates/team/responsibility-matrix.json +0 -81
  129. package/init/xinyun-ai-init/templates/team/roles.json +0 -71
  130. package/init/xinyun-ai-init/templates/team/understandings/chief-engineer.md +0 -23
  131. package/init/xinyun-ai-init/templates/team/understandings/programmer.md +0 -23
  132. package/init/xinyun-ai-init/templates/team/understandings/project-owner.md +0 -23
  133. package/init/xinyun-ai-init/templates/team/understandings/project-product.md +0 -23
  134. package/init/xinyun-ai-init/templates/team/understandings/test-reviewer.md +0 -23
  135. package/init/xinyun-ai-init/templates/technical-impact-packet.template.json +0 -31
  136. package/init/xinyun-ai-init/templates/technical-landscape.template.json +0 -41
  137. package/init/xinyun-ai-init/templates/temporary-session.template.json +0 -32
  138. package/init/xinyun-ai-init/templates/workstreams-readme.template.md +0 -36
  139. package/scripts/collect-v1.10-evidence.js +0 -159
  140. package/scripts/collect-v1.14-local-evidence.js +0 -66
  141. package/scripts/collect-v1.15-real-evidence.js +0 -114
  142. package/scripts/collect-v1.16-evidence.js +0 -48
  143. package/scripts/run-v1.16-runtime-probe.js +0 -91
  144. package/scripts/verify-init-package.js +0 -2292
  145. package/scripts/verify-rules-package.js +0 -1280
  146. package/test-scenarios/README.md +0 -130
  147. package/test-scenarios/fixtures/v1.10.0-takeover-gate-fixture/README.md +0 -23
  148. package/test-scenarios/fixtures/v1.10.0-takeover-gate-fixture/after/project-status.json +0 -7
  149. package/test-scenarios/fixtures/v1.10.0-takeover-gate-fixture/before/project-status.json +0 -7
  150. package/test-scenarios/fixtures/v1.9.0-behavior-fixture/README.md +0 -9
  151. package/test-scenarios/review-template.md +0 -165
  152. package/test-scenarios/scenarios.md +0 -1273
  153. package/test-scenarios/v1.10.0-behavior-evidence.json +0 -31
  154. package/test-scenarios/v1.10.0-scenarios.md +0 -96
  155. package/test-scenarios/v1.10.2-scenarios.md +0 -38
  156. package/test-scenarios/v1.11.0-scenarios.md +0 -30
  157. package/test-scenarios/v1.12.0-behavior-evidence.json +0 -29
  158. package/test-scenarios/v1.12.0-scenarios.md +0 -51
  159. package/test-scenarios/v1.12.1-behavior-evidence.json +0 -27
  160. package/test-scenarios/v1.12.1-scenarios.md +0 -42
  161. package/test-scenarios/v1.13.0-behavior-evidence.json +0 -650
  162. package/test-scenarios/v1.13.0-scenarios.md +0 -94
  163. package/test-scenarios/v1.14.0-behavior-evidence.json +0 -780
  164. package/test-scenarios/v1.14.0-scenarios.md +0 -63
  165. package/test-scenarios/v1.15.0-behavior-evidence.json +0 -476
  166. package/test-scenarios/v1.15.0-scenarios.md +0 -57
  167. package/test-scenarios/v1.16.0-behavior-evidence.json +0 -46
  168. package/test-scenarios/v1.16.0-real-host-probe.json +0 -132
  169. package/test-scenarios/v1.16.0-scenarios.md +0 -19
  170. package/test-scenarios/v1.7.0-scenarios.md +0 -91
  171. package/test-scenarios/v1.7.1-scenarios.md +0 -130
  172. package/test-scenarios/v1.7.2-scenarios.md +0 -51
  173. package/test-scenarios/v1.7.3-scenarios.md +0 -79
  174. package/test-scenarios/v1.8.0-scenarios.md +0 -39
  175. package/test-scenarios/v1.9.0-behavior-evidence.json +0 -269
  176. package/test-scenarios/v1.9.0-scenarios.md +0 -55
package/README.md CHANGED
@@ -1,73 +1,58 @@
1
- # @pm_zhousl/xinyun-ai-init
1
+ # @pm_zhousl/xinyun-ai-init v3
2
2
 
3
- 欣芸技术开发 Agent 流初始化包。
3
+ 欣芸技术开发 Skill Runtime 初始化包。v3 将项目接管、任务分析、技术设计、实现、验证、安全审查、发布审查和交接同步封装为可直接路由的 Skills。
4
4
 
5
- ## Codex 一句话入口
6
-
7
- 用 Codex 打开项目后,直接发送:
8
-
9
- ```md
10
- 请执行 `npx --yes @pm_zhousl/xinyun-ai-init@latest --force`,然后读取 `xinyun-ai-init/README.md`,按其中规则初始化或升级当前项目。
11
- ```
12
-
13
- ## 手动使用
5
+ ## 使用
14
6
 
15
7
  ```bash
16
- npx --yes @pm_zhousl/xinyun-ai-init@latest --force
8
+ npx --yes @pm_zhousl/xinyun-ai-init@latest --force --offline
17
9
  ```
18
10
 
19
- 执行后会在当前目录生成 `xinyun-ai-init/`。
11
+ 默认只 materialize `xinyun-ai-init/` 中的核心 Runtime,不创建 `xinyun-ai/`,不修改业务代码,也不读取整套历史兼容材料。
20
12
 
21
- ## 维护者校验
13
+ 初始化后,宿主 AI 使用:
22
14
 
23
15
  ```bash
24
- npm run verify:init
25
- npm run verify:rules
26
- npm run verify:rules -- --json
27
- npm run verify:rules -- --tarball ./pm_zhousl-xinyun-ai-init-1.10.1.tgz
16
+ node xinyun-ai-init/runtime/route-skill.js --facts task-facts.json
28
17
  ```
29
18
 
30
- `verify:rules` 为只读规则包一致性检查,不自动修复、不安装依赖、不修改 Git 或业务代码。
31
-
32
- ## 注意
19
+ ## 参数
33
20
 
34
- 该 CLI 只释放初始化包,不修改业务代码、不创建 `xinyun-ai/`。
21
+ - `--offline`:跳过 npm registry 版本检查。
22
+ - `--no-version-check`:跳过版本检查。
23
+ - `--strict-version`:发现本地版本落后时退出。
24
+ - `--force`:覆盖脚本托管文件;用户自行修改的文件仍会记录冲突。
35
25
 
36
- 完整执行要求已经内置在 `xinyun-ai-init/README.md`、`rules/` 和 `templates/` 中。后续不需要复制长提示词,只需要让 AI 读取本地释放出来的 `xinyun-ai-init/README.md`。
26
+ ## 维护者校验
37
27
 
38
- CLI 执行时会显示当前版本,并尽量检测 npm 线上最新版本;检测失败不会阻塞初始化。
28
+ ```bash
29
+ npm run verify:v3
30
+ npm run verify:runtime
31
+ npm run verify:cleanup
32
+ npm run verify:rules:security
33
+ npm pack --dry-run
34
+ ```
39
35
 
40
- 用户发送 Codex 一句话入口后,AI 可按初始化包规则创建或补齐最小 `xinyun-ai/`;这不代表授权修改业务代码。
36
+ ## v3 升级
41
37
 
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`,发布完成或失败后自动删除 `.npmrc`。
58
- 发布脚本会先执行 `npm run verify:release`;v1.16 要求可审计的真实宿主运行证据,且只有证据完整时才允许进入发布流程。未验证范围必须继续保留在证据记录中。
50
+ 升级会合并必要的项目事实和任务结果,然后移除已知的 team、角色任务和角色会话运行目录。v3 不再提供旧兼容运行路径。
59
51
 
60
- 如果需要让 AI 代执行,可先在当前终端设置临时环境变量:
52
+ ## 发布
61
53
 
62
54
  ```powershell
63
- $env:NPM_TOKEN="你的 npm token"
64
55
  npm run publish:token
65
- $env:NPM_TOKEN=$null
66
56
  ```
67
57
 
68
- 发布前预演:
69
-
70
- ```powershell
71
- npm run verify:release
72
- npm run publish:token:dry
73
- ```
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) => {