@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.
- package/README.md +29 -44
- package/bin/xinyun-ai-init.js +121 -175
- package/init/xinyun-ai-init/README.md +24 -1147
- package/init/xinyun-ai-init/rules/v3-skill-runtime.md +45 -0
- package/init/xinyun-ai-init/runtime/migrate-v3.js +115 -0
- package/init/xinyun-ai-init/runtime/route-skill.js +69 -0
- package/init/xinyun-ai-init/runtime-manifest.json +26 -0
- package/init/xinyun-ai-init/skills/registry.json +112 -0
- package/init/xinyun-ai-init/templates/AGENTS.template.md +4 -242
- package/init/xinyun-ai-init/templates/README.template.md +4 -45
- package/init/xinyun-ai-init/templates/control-plane-index.template.json +15 -28
- package/init/xinyun-ai-init/templates/core-rules.template.md +9 -636
- package/init/xinyun-ai-init/templates/current-task.template.md +8 -322
- package/init/xinyun-ai-init/templates/manifest.template.json +35 -396
- package/init/xinyun-ai-init/templates/project-context.template.json +12 -0
- package/init/xinyun-ai-init/templates/project-index.template.json +11 -425
- package/init/xinyun-ai-init/templates/skill-run-record.template.json +8 -16
- package/init/xinyun-ai-init/templates/skill-runtime-route.template.json +3 -11
- package/init/xinyun-ai-init/templates/skills-assignments.template.json +3 -16
- package/init/xinyun-ai-init/templates/skills-registry.template.json +5 -7
- package/init/xinyun-ai-init/templates/task-graph.template.json +4 -15
- package/init/xinyun-ai-init/templates/task-index.template.json +4 -89
- package/init/xinyun-ai-init/version.json +8 -7
- package/package.json +8 -9
- package/scripts/publish-with-token.ps1 +6 -16
- package/scripts/run-v3-runtime-probe.js +8 -0
- package/scripts/verify-legacy-cleanup.js +24 -0
- package/scripts/verify-release-gate.js +11 -16
- package/scripts/verify-rules-tarball-security.js +1 -1
- package/scripts/verify-skill-runtime.js +33 -0
- package/scripts/verify-tarball.js +42 -0
- package/scripts/verify-v3-package.js +70 -0
- package/test-scenarios/v3.0.0-behavior-evidence.json +25 -0
- package/test-scenarios/v3.0.0-scenarios.md +41 -0
- package/init/xinyun-ai-init/prompt.md +0 -1
- package/init/xinyun-ai-init/rules/coordination.md +0 -33
- package/init/xinyun-ai-init/rules/directory-self-completion.md +0 -96
- package/init/xinyun-ai-init/rules/frameworks/README.md +0 -56
- package/init/xinyun-ai-init/rules/frameworks/backend/php/thinkphp.md +0 -42
- package/init/xinyun-ai-init/rules/frameworks/frontend/uni-app.md +0 -36
- package/init/xinyun-ai-init/rules/frameworks/frontend/wechat-miniprogram.md +0 -36
- package/init/xinyun-ai-init/rules/frontend-audit.md +0 -9
- package/init/xinyun-ai-init/rules/init-rules.md +0 -139
- package/init/xinyun-ai-init/rules/self-learning-rules.md +0 -16
- package/init/xinyun-ai-init/rules/session-workbench.md +0 -327
- package/init/xinyun-ai-init/rules/skills.md +0 -15
- package/init/xinyun-ai-init/rules/temporary-sessions.md +0 -9
- package/init/xinyun-ai-init/rules/upgrade-rules.md +0 -155
- package/init/xinyun-ai-init/rules/v1.10.2-contract.md +0 -30
- package/init/xinyun-ai-init/rules/v1.11.0-acceleration.md +0 -27
- package/init/xinyun-ai-init/rules/v1.12.0-recovery.md +0 -34
- package/init/xinyun-ai-init/rules/v1.12.1-provider-session-binding.md +0 -25
- package/init/xinyun-ai-init/rules/v1.13.0-host-adapter.md +0 -43
- package/init/xinyun-ai-init/rules/v1.14.0-agent-core.md +0 -35
- package/init/xinyun-ai-init/rules/v1.15.0-session-binding.md +0 -44
- package/init/xinyun-ai-init/rules/v1.16.0-capability-efficiency.md +0 -23
- package/init/xinyun-ai-init/scripts/README.md +0 -16
- package/init/xinyun-ai-init/templates/agent-capabilities.template.json +0 -27
- package/init/xinyun-ai-init/templates/agent-profile.template.json +0 -26
- package/init/xinyun-ai-init/templates/archive-transaction.template.json +0 -15
- package/init/xinyun-ai-init/templates/bootstrap-snapshot.template.json +0 -17
- package/init/xinyun-ai-init/templates/capabilities.template.json +0 -19
- package/init/xinyun-ai-init/templates/codegraph-incremental-record.template.json +0 -21
- package/init/xinyun-ai-init/templates/coordination-change-ledger.template.json +0 -12
- package/init/xinyun-ai-init/templates/coordination-conflict-record.template.json +0 -13
- package/init/xinyun-ai-init/templates/coordination-contract-snapshot.template.json +0 -9
- package/init/xinyun-ai-init/templates/coordination-project-facts.template.json +0 -18
- package/init/xinyun-ai-init/templates/coordination-transaction.template.json +0 -16
- package/init/xinyun-ai-init/templates/database-authorization.template.json +0 -53
- package/init/xinyun-ai-init/templates/final-delivery.template.json +0 -22
- package/init/xinyun-ai-init/templates/fixed-materialization.template.json +0 -12
- package/init/xinyun-ai-init/templates/framework-profile.template.json +0 -48
- package/init/xinyun-ai-init/templates/framework-profile.template.md +0 -39
- package/init/xinyun-ai-init/templates/frontend-audit.template.json +0 -27
- package/init/xinyun-ai-init/templates/handoff.template.json +0 -32
- package/init/xinyun-ai-init/templates/host-capabilities.template.json +0 -37
- package/init/xinyun-ai-init/templates/host-preflight.template.json +0 -33
- package/init/xinyun-ai-init/templates/html-migration-asset-map.template.md +0 -6
- package/init/xinyun-ai-init/templates/html-migration-compatibility-matrix.template.md +0 -6
- package/init/xinyun-ai-init/templates/html-migration-open-issues.template.md +0 -12
- package/init/xinyun-ai-init/templates/html-migration-pilot-plan.template.md +0 -21
- package/init/xinyun-ai-init/templates/html-migration-record.template.json +0 -36
- package/init/xinyun-ai-init/templates/html-migration-route-map.template.md +0 -6
- package/init/xinyun-ai-init/templates/html-migration-scope.template.md +0 -28
- package/init/xinyun-ai-init/templates/init-result.template.json +0 -220
- package/init/xinyun-ai-init/templates/initialization-cache.template.json +0 -13
- package/init/xinyun-ai-init/templates/initialization-change-report.template.json +0 -14
- package/init/xinyun-ai-init/templates/initialization-recovery.template.json +0 -29
- package/init/xinyun-ai-init/templates/initialization-stages.template.json +0 -18
- package/init/xinyun-ai-init/templates/initialization-timing.template.json +0 -16
- package/init/xinyun-ai-init/templates/local-query-index.template.json +0 -15
- package/init/xinyun-ai-init/templates/managed-baseline.template.json +0 -17
- package/init/xinyun-ai-init/templates/manual-session-binding.template.json +0 -45
- package/init/xinyun-ai-init/templates/manual-session-recovery.template.json +0 -53
- package/init/xinyun-ai-init/templates/onboarding-report.template.json +0 -25
- package/init/xinyun-ai-init/templates/orchestrator.template.json +0 -18
- package/init/xinyun-ai-init/templates/parallelism.template.json +0 -13
- package/init/xinyun-ai-init/templates/performance-metrics.template.json +0 -17
- package/init/xinyun-ai-init/templates/preflight-report.template.json +0 -46
- package/init/xinyun-ai-init/templates/project-check.template.md +0 -198
- package/init/xinyun-ai-init/templates/project-guide.template.md +0 -264
- package/init/xinyun-ai-init/templates/project-identity.template.json +0 -23
- package/init/xinyun-ai-init/templates/project-status.template.json +0 -53
- package/init/xinyun-ai-init/templates/provider-adapter.template.json +0 -38
- package/init/xinyun-ai-init/templates/provider-session-binding.template.json +0 -77
- package/init/xinyun-ai-init/templates/requirement-packet.template.json +0 -23
- package/init/xinyun-ai-init/templates/role-execution.template.md +0 -72
- package/init/xinyun-ai-init/templates/role-session-binding.template.json +0 -105
- package/init/xinyun-ai-init/templates/role-subtask.template.md +0 -36
- package/init/xinyun-ai-init/templates/session-binding.template.json +0 -35
- package/init/xinyun-ai-init/templates/session-candidates.template.json +0 -19
- package/init/xinyun-ai-init/templates/shared-project-snapshot.template.json +0 -24
- package/init/xinyun-ai-init/templates/takeover-baseline.template.json +0 -17
- package/init/xinyun-ai-init/templates/takeover-gate.template.json +0 -17
- package/init/xinyun-ai-init/templates/task-history.template.md +0 -35
- package/init/xinyun-ai-init/templates/task-lock.template.json +0 -31
- package/init/xinyun-ai-init/templates/task-main.template.md +0 -47
- package/init/xinyun-ai-init/templates/task-result.template.json +0 -23
- package/init/xinyun-ai-init/templates/team/README.md +0 -51
- package/init/xinyun-ai-init/templates/team/assignments.json +0 -49
- package/init/xinyun-ai-init/templates/team/confirmations/chief-engineer.md +0 -24
- package/init/xinyun-ai-init/templates/team/confirmations/programmer.md +0 -24
- package/init/xinyun-ai-init/templates/team/confirmations/project-owner.md +0 -24
- package/init/xinyun-ai-init/templates/team/confirmations/project-product.md +0 -24
- package/init/xinyun-ai-init/templates/team/confirmations/test-reviewer.md +0 -24
- package/init/xinyun-ai-init/templates/team/file-ownership.json +0 -32
- package/init/xinyun-ai-init/templates/team/members.json +0 -61
- package/init/xinyun-ai-init/templates/team/responsibility-matrix.json +0 -81
- package/init/xinyun-ai-init/templates/team/roles.json +0 -71
- package/init/xinyun-ai-init/templates/team/understandings/chief-engineer.md +0 -23
- package/init/xinyun-ai-init/templates/team/understandings/programmer.md +0 -23
- package/init/xinyun-ai-init/templates/team/understandings/project-owner.md +0 -23
- package/init/xinyun-ai-init/templates/team/understandings/project-product.md +0 -23
- package/init/xinyun-ai-init/templates/team/understandings/test-reviewer.md +0 -23
- package/init/xinyun-ai-init/templates/technical-impact-packet.template.json +0 -31
- package/init/xinyun-ai-init/templates/technical-landscape.template.json +0 -41
- package/init/xinyun-ai-init/templates/temporary-session.template.json +0 -32
- package/init/xinyun-ai-init/templates/workstreams-readme.template.md +0 -36
- package/scripts/collect-v1.10-evidence.js +0 -159
- package/scripts/collect-v1.14-local-evidence.js +0 -66
- package/scripts/collect-v1.15-real-evidence.js +0 -114
- package/scripts/collect-v1.16-evidence.js +0 -48
- package/scripts/run-v1.16-runtime-probe.js +0 -91
- package/scripts/verify-init-package.js +0 -2292
- package/scripts/verify-rules-package.js +0 -1280
- package/test-scenarios/README.md +0 -130
- package/test-scenarios/fixtures/v1.10.0-takeover-gate-fixture/README.md +0 -23
- package/test-scenarios/fixtures/v1.10.0-takeover-gate-fixture/after/project-status.json +0 -7
- package/test-scenarios/fixtures/v1.10.0-takeover-gate-fixture/before/project-status.json +0 -7
- package/test-scenarios/fixtures/v1.9.0-behavior-fixture/README.md +0 -9
- package/test-scenarios/review-template.md +0 -165
- package/test-scenarios/scenarios.md +0 -1273
- package/test-scenarios/v1.10.0-behavior-evidence.json +0 -31
- package/test-scenarios/v1.10.0-scenarios.md +0 -96
- package/test-scenarios/v1.10.2-scenarios.md +0 -38
- package/test-scenarios/v1.11.0-scenarios.md +0 -30
- package/test-scenarios/v1.12.0-behavior-evidence.json +0 -29
- package/test-scenarios/v1.12.0-scenarios.md +0 -51
- package/test-scenarios/v1.12.1-behavior-evidence.json +0 -27
- package/test-scenarios/v1.12.1-scenarios.md +0 -42
- package/test-scenarios/v1.13.0-behavior-evidence.json +0 -650
- package/test-scenarios/v1.13.0-scenarios.md +0 -94
- package/test-scenarios/v1.14.0-behavior-evidence.json +0 -780
- package/test-scenarios/v1.14.0-scenarios.md +0 -63
- package/test-scenarios/v1.15.0-behavior-evidence.json +0 -476
- package/test-scenarios/v1.15.0-scenarios.md +0 -57
- package/test-scenarios/v1.16.0-behavior-evidence.json +0 -46
- package/test-scenarios/v1.16.0-real-host-probe.json +0 -132
- package/test-scenarios/v1.16.0-scenarios.md +0 -19
- package/test-scenarios/v1.7.0-scenarios.md +0 -91
- package/test-scenarios/v1.7.1-scenarios.md +0 -130
- package/test-scenarios/v1.7.2-scenarios.md +0 -51
- package/test-scenarios/v1.7.3-scenarios.md +0 -79
- package/test-scenarios/v1.8.0-scenarios.md +0 -39
- package/test-scenarios/v1.9.0-behavior-evidence.json +0 -269
- package/test-scenarios/v1.9.0-scenarios.md +0 -55
package/README.md
CHANGED
|
@@ -1,73 +1,58 @@
|
|
|
1
|
-
|
|
1
|
+
# @pm_zhousl/xinyun-ai-init v3
|
|
2
2
|
|
|
3
|
-
欣芸技术开发
|
|
3
|
+
欣芸技术开发 Skill Runtime 初始化包。v3 将项目接管、任务分析、技术设计、实现、验证、安全审查、发布审查和交接同步封装为可直接路由的 Skills。
|
|
4
4
|
|
|
5
|
-
##
|
|
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
|
-
|
|
11
|
+
默认只 materialize `xinyun-ai-init/` 中的核心 Runtime,不创建 `xinyun-ai/`,不修改业务代码,也不读取整套历史兼容材料。
|
|
20
12
|
|
|
21
|
-
|
|
13
|
+
初始化后,宿主 AI 使用:
|
|
22
14
|
|
|
23
15
|
```bash
|
|
24
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
## 注意
|
|
19
|
+
## 参数
|
|
33
20
|
|
|
34
|
-
|
|
21
|
+
- `--offline`:跳过 npm registry 版本检查。
|
|
22
|
+
- `--no-version-check`:跳过版本检查。
|
|
23
|
+
- `--strict-version`:发现本地版本落后时退出。
|
|
24
|
+
- `--force`:覆盖脚本托管文件;用户自行修改的文件仍会记录冲突。
|
|
35
25
|
|
|
36
|
-
|
|
26
|
+
## 维护者校验
|
|
37
27
|
|
|
38
|
-
|
|
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
|
-
|
|
36
|
+
## v3 升级
|
|
41
37
|
|
|
42
|
-
|
|
38
|
+
旧项目升级先预览:
|
|
43
39
|
|
|
44
40
|
```bash
|
|
45
|
-
--
|
|
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
|
-
```
|
|
54
|
-
|
|
46
|
+
```bash
|
|
47
|
+
node xinyun-ai-init/runtime/migrate-v3.js --project-root <project-root> --apply
|
|
55
48
|
```
|
|
56
49
|
|
|
57
|
-
|
|
58
|
-
发布脚本会先执行 `npm run verify:release`;v1.16 要求可审计的真实宿主运行证据,且只有证据完整时才允许进入发布流程。未验证范围必须继续保留在证据记录中。
|
|
50
|
+
升级会合并必要的项目事实和任务结果,然后移除已知的 team、角色任务和角色会话运行目录。v3 不再提供旧兼容运行路径。
|
|
59
51
|
|
|
60
|
-
|
|
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 仍需用户针对目标版本单独授权。
|
package/bin/xinyun-ai-init.js
CHANGED
|
@@ -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
|
|
24
|
-
fs.
|
|
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
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64
|
+
// A cache is an optimization. Failure must not block initialization.
|
|
125
65
|
}
|
|
126
66
|
}
|
|
127
67
|
|
|
128
|
-
function
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return
|
|
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
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
173
|
-
|
|
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
|
|
179
|
-
const nowIso = new Date(nowMs).toISOString();
|
|
180
|
-
|
|
118
|
+
const nowIso = new Date().toISOString();
|
|
181
119
|
if (!latestVersion) {
|
|
182
|
-
console.log("[xinyun-ai-init]
|
|
183
|
-
writeUpdateCache(
|
|
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
|
-
|
|
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
|
-
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
177
|
+
next.files[relative] = { sourceHash, targetHash: hashFile(targetPath), status: "managed" };
|
|
236
178
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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:
|
|
247
|
-
console.log("");
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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) => {
|