create-yss-spec 2.1.1 → 2.1.3
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 +1 -1
- package/package.json +1 -1
- package/src/cli.js +1 -29
- package/src/template-hash.js +44 -0
- package/template/scripts/verify-template +11 -3
- package/template.snapshot.json +4 -4
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ npx create-yss-spec@latest
|
|
|
33
33
|
- 旧、新资产内容冲突或清单 schema / mode 非法时 fail closed
|
|
34
34
|
- 保留模板的共享 skill 投影;生成实例可在尚未 `git init` 时运行模板校验
|
|
35
35
|
|
|
36
|
-
本次新增能力按 `2.1.
|
|
36
|
+
本次新增能力按 `2.1.3` 发布。`templateCommit` 会写入实例 metadata;“最新模板”指用户执行的 `npx create-yss-spec@latest` 所携带的最新已发布快照,CLI 运行时不会拉取模板仓库。
|
|
37
37
|
|
|
38
38
|
## 接管已有项目
|
|
39
39
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ const os = require("node:os");
|
|
|
4
4
|
const path = require("node:path");
|
|
5
5
|
const readline = require("node:readline/promises");
|
|
6
6
|
const { spawnSync } = require("node:child_process");
|
|
7
|
+
const { treeHash } = require("./template-hash");
|
|
7
8
|
|
|
8
9
|
const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
9
10
|
const PACKAGE_MANIFEST = JSON.parse(
|
|
@@ -489,35 +490,6 @@ function fileHash(filePath) {
|
|
|
489
490
|
return sha256(fs.readFileSync(filePath));
|
|
490
491
|
}
|
|
491
492
|
|
|
492
|
-
function treeHash(rootPath) {
|
|
493
|
-
const digest = crypto.createHash("sha256");
|
|
494
|
-
|
|
495
|
-
function visit(currentPath, relativeDir = "") {
|
|
496
|
-
for (const entry of fs
|
|
497
|
-
.readdirSync(currentPath, { withFileTypes: true })
|
|
498
|
-
.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
499
|
-
if (entry.name === ".DS_Store") {
|
|
500
|
-
continue;
|
|
501
|
-
}
|
|
502
|
-
const relativePath = relativeDir
|
|
503
|
-
? `${relativeDir}/${entry.name}`
|
|
504
|
-
: entry.name;
|
|
505
|
-
const absolutePath = path.join(currentPath, entry.name);
|
|
506
|
-
if (entry.isDirectory()) {
|
|
507
|
-
digest.update(`dir:${relativePath}\0`);
|
|
508
|
-
visit(absolutePath, relativePath);
|
|
509
|
-
} else if (entry.isFile()) {
|
|
510
|
-
digest.update(`file:${relativePath}\0`);
|
|
511
|
-
digest.update(fs.readFileSync(absolutePath));
|
|
512
|
-
digest.update("\0");
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
visit(rootPath);
|
|
518
|
-
return digest.digest("hex");
|
|
519
|
-
}
|
|
520
|
-
|
|
521
493
|
function pathKind(absolutePath) {
|
|
522
494
|
let stat;
|
|
523
495
|
try {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const crypto = require("node:crypto");
|
|
2
|
+
const fs = require("node:fs");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
|
|
5
|
+
function compareNames(left, right) {
|
|
6
|
+
if (left < right) {
|
|
7
|
+
return -1;
|
|
8
|
+
}
|
|
9
|
+
if (left > right) {
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
return 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function treeHash(rootPath) {
|
|
16
|
+
const digest = crypto.createHash("sha256");
|
|
17
|
+
|
|
18
|
+
function visit(currentPath, relativeDir = "") {
|
|
19
|
+
for (const entry of fs
|
|
20
|
+
.readdirSync(currentPath, { withFileTypes: true })
|
|
21
|
+
.sort((left, right) => compareNames(left.name, right.name))) {
|
|
22
|
+
if (entry.name === ".DS_Store") {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const relativePath = relativeDir
|
|
26
|
+
? `${relativeDir}/${entry.name}`
|
|
27
|
+
: entry.name;
|
|
28
|
+
const absolutePath = path.join(currentPath, entry.name);
|
|
29
|
+
if (entry.isDirectory()) {
|
|
30
|
+
digest.update(`dir:${relativePath}\0`);
|
|
31
|
+
visit(absolutePath, relativePath);
|
|
32
|
+
} else if (entry.isFile()) {
|
|
33
|
+
digest.update(`file:${relativePath}\0`);
|
|
34
|
+
digest.update(fs.readFileSync(absolutePath));
|
|
35
|
+
digest.update("\0");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
visit(rootPath);
|
|
41
|
+
return digest.digest("hex");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { treeHash };
|
|
@@ -4,7 +4,13 @@ set -euo pipefail
|
|
|
4
4
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
5
|
cd "$ROOT_DIR"
|
|
6
6
|
|
|
7
|
+
# Ruby inherits US-ASCII under a C locale on some macOS installations. Keep
|
|
8
|
+
# all verification scripts UTF-8 capable because the template contains Chinese
|
|
9
|
+
# documentation and skill metadata.
|
|
10
|
+
export RUBYOPT="${RUBYOPT:+$RUBYOPT }-EUTF-8:UTF-8"
|
|
11
|
+
|
|
7
12
|
ruby <<'RUBY'
|
|
13
|
+
# encoding: UTF-8
|
|
8
14
|
require "pathname"
|
|
9
15
|
require "yaml"
|
|
10
16
|
|
|
@@ -84,6 +90,7 @@ for skill_name in "${required_skills[@]}"; do
|
|
|
84
90
|
done
|
|
85
91
|
|
|
86
92
|
ruby <<'RUBY'
|
|
93
|
+
# encoding: UTF-8
|
|
87
94
|
require "json"
|
|
88
95
|
require "pathname"
|
|
89
96
|
require "yaml"
|
|
@@ -113,7 +120,7 @@ lock.fetch("skills", {}).fetch("shared", {}).each do |skill_name, metadata|
|
|
|
113
120
|
end
|
|
114
121
|
|
|
115
122
|
openapi_path = root.join("docs/templates/openapi-spec-template.yaml")
|
|
116
|
-
openapi_text = openapi_path.
|
|
123
|
+
openapi_text = openapi_path.binread.force_encoding("UTF-8").sub(/\A---\n.*?\n---\n/m, "")
|
|
117
124
|
YAML.safe_load(openapi_text, permitted_classes: [], aliases: true)
|
|
118
125
|
RUBY
|
|
119
126
|
|
|
@@ -128,6 +135,7 @@ scripts/verify-yss-router-scenarios
|
|
|
128
135
|
scripts/verify-yss-ui-scenarios
|
|
129
136
|
|
|
130
137
|
ruby <<'RUBY'
|
|
138
|
+
# encoding: UTF-8
|
|
131
139
|
require "pathname"
|
|
132
140
|
require "json"
|
|
133
141
|
|
|
@@ -225,8 +233,8 @@ unless legacy_hits.empty?
|
|
|
225
233
|
exit 1
|
|
226
234
|
end
|
|
227
235
|
|
|
228
|
-
unless root.join("AGENTS.md").
|
|
229
|
-
root.join("CONTEXT.md").
|
|
236
|
+
unless root.join("AGENTS.md").binread.force_encoding("UTF-8").include?("正文统一使用简体中文") &&
|
|
237
|
+
root.join("CONTEXT.md").binread.force_encoding("UTF-8").include?("统一使用简体中文")
|
|
230
238
|
abort("AGENTS.md 与 CONTEXT.md 必须共同声明落地文档使用简体中文")
|
|
231
239
|
end
|
|
232
240
|
|
package/template.snapshot.json
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
"templateName": "yss-spec-project-template",
|
|
4
4
|
"templateSource": "github:iloveZzz/yss-spec-project-template",
|
|
5
5
|
"templateRepository": "https://github.com/iloveZzz/yss-spec-project-template.git",
|
|
6
|
-
"requestedRef": "
|
|
7
|
-
"templateCommit": "
|
|
6
|
+
"requestedRef": "41c68b545caaa439a999ec9d6786bf80a06c70ba",
|
|
7
|
+
"templateCommit": "41c68b545caaa439a999ec9d6786bf80a06c70ba",
|
|
8
8
|
"encodedPaths": {
|
|
9
9
|
".agents/skills/beautiful-article/assets/scaffold-template/.npmrc": ".agents/skills/beautiful-article/assets/scaffold-template/__yss_dotfile__.npmrc",
|
|
10
10
|
".agents/skills/drawio-academic-skills/.gitignore": ".agents/skills/drawio-academic-skills/__yss_dotfile__.gitignore",
|
|
@@ -32,6 +32,6 @@
|
|
|
32
32
|
".trae/skills/drawio-academic-skills/.gitignore": ".trae/skills/drawio-academic-skills/__yss_dotfile__.gitignore",
|
|
33
33
|
".trae/skills/excalidraw-diagram-generator/scripts/.gitignore": ".trae/skills/excalidraw-diagram-generator/scripts/__yss_dotfile__.gitignore"
|
|
34
34
|
},
|
|
35
|
-
"snapshotHash": "
|
|
36
|
-
"generatedAt": "2026-08-
|
|
35
|
+
"snapshotHash": "8817d7689fe6ac5fff6c94ada86f7db25394133a7094108afefcde24fffcfaa5",
|
|
36
|
+
"generatedAt": "2026-08-14T06:23:07.847Z"
|
|
37
37
|
}
|