create-yss-spec 2.1.14 → 2.2.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 (187) hide show
  1. package/README.md +18 -2
  2. package/package.json +1 -1
  3. package/src/cli.js +367 -19
  4. package/template/.agents/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  5. package/template/.agents/skills/implementation-repo-onboarding/SKILL.md +5 -5
  6. package/template/.agents/skills/llm-wiki/SKILL.md +1 -1
  7. package/template/.agents/skills/llm-wiki/references/discover.md +1 -0
  8. package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  9. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +30 -3
  10. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +52 -0
  11. package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  12. package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +1 -1
  13. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +24 -0
  14. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +2 -0
  15. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +1 -1
  16. package/template/.agents/skills/yss-router/SKILL.md +1 -1
  17. package/template/.agents/skills/yss-router/references/router-contract.yaml +20 -0
  18. package/template/.agents/skills/yss-router/references/slice-implementation-contract.md +1 -1
  19. package/template/.claude/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  20. package/template/.claude/skills/implementation-repo-onboarding/SKILL.md +5 -5
  21. package/template/.claude/skills/llm-wiki/SKILL.md +1 -1
  22. package/template/.claude/skills/llm-wiki/references/discover.md +1 -0
  23. package/template/.claude/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  24. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +30 -3
  25. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +52 -0
  26. package/template/.claude/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  27. package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +1 -1
  28. package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +24 -0
  29. package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +2 -0
  30. package/template/.claude/skills/yss-product-lifecycle/references/state-model.md +1 -1
  31. package/template/.claude/skills/yss-router/SKILL.md +1 -1
  32. package/template/.claude/skills/yss-router/references/router-contract.yaml +20 -0
  33. package/template/.claude/skills/yss-router/references/slice-implementation-contract.md +1 -1
  34. package/template/.codex/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  35. package/template/.codex/skills/implementation-repo-onboarding/SKILL.md +5 -5
  36. package/template/.codex/skills/llm-wiki/SKILL.md +1 -1
  37. package/template/.codex/skills/llm-wiki/references/discover.md +1 -0
  38. package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  39. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +30 -3
  40. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +52 -0
  41. package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  42. package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +1 -1
  43. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +24 -0
  44. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +2 -0
  45. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +1 -1
  46. package/template/.codex/skills/yss-router/SKILL.md +1 -1
  47. package/template/.codex/skills/yss-router/references/router-contract.yaml +20 -0
  48. package/template/.codex/skills/yss-router/references/slice-implementation-contract.md +1 -1
  49. package/template/.cursor/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  50. package/template/.cursor/skills/implementation-repo-onboarding/SKILL.md +5 -5
  51. package/template/.cursor/skills/llm-wiki/SKILL.md +1 -1
  52. package/template/.cursor/skills/llm-wiki/references/discover.md +1 -0
  53. package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  54. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +30 -3
  55. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +52 -0
  56. package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  57. package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +1 -1
  58. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +24 -0
  59. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +2 -0
  60. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +1 -1
  61. package/template/.cursor/skills/yss-router/SKILL.md +1 -1
  62. package/template/.cursor/skills/yss-router/references/router-contract.yaml +20 -0
  63. package/template/.cursor/skills/yss-router/references/slice-implementation-contract.md +1 -1
  64. package/template/.hermes/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  65. package/template/.hermes/skills/implementation-repo-onboarding/SKILL.md +5 -5
  66. package/template/.hermes/skills/llm-wiki/SKILL.md +1 -1
  67. package/template/.hermes/skills/llm-wiki/references/discover.md +1 -0
  68. package/template/.hermes/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  69. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +30 -3
  70. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +52 -0
  71. package/template/.hermes/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  72. package/template/.hermes/skills/yss-product-lifecycle/references/matt-yss-adapter.md +1 -1
  73. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration-contract.yaml +24 -0
  74. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration.md +2 -0
  75. package/template/.hermes/skills/yss-product-lifecycle/references/state-model.md +1 -1
  76. package/template/.hermes/skills/yss-router/SKILL.md +1 -1
  77. package/template/.hermes/skills/yss-router/references/router-contract.yaml +20 -0
  78. package/template/.hermes/skills/yss-router/references/slice-implementation-contract.md +1 -1
  79. package/template/.pi/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  80. package/template/.pi/skills/implementation-repo-onboarding/SKILL.md +5 -5
  81. package/template/.pi/skills/llm-wiki/SKILL.md +1 -1
  82. package/template/.pi/skills/llm-wiki/references/discover.md +1 -0
  83. package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  84. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +30 -3
  85. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +52 -0
  86. package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  87. package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +1 -1
  88. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +24 -0
  89. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +2 -0
  90. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +1 -1
  91. package/template/.pi/skills/yss-router/SKILL.md +1 -1
  92. package/template/.pi/skills/yss-router/references/router-contract.yaml +20 -0
  93. package/template/.pi/skills/yss-router/references/slice-implementation-contract.md +1 -1
  94. package/template/.qoder/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  95. package/template/.qoder/skills/implementation-repo-onboarding/SKILL.md +5 -5
  96. package/template/.qoder/skills/llm-wiki/SKILL.md +1 -1
  97. package/template/.qoder/skills/llm-wiki/references/discover.md +1 -0
  98. package/template/.qoder/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  99. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +30 -3
  100. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +52 -0
  101. package/template/.qoder/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  102. package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +1 -1
  103. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +24 -0
  104. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +2 -0
  105. package/template/.qoder/skills/yss-product-lifecycle/references/state-model.md +1 -1
  106. package/template/.qoder/skills/yss-router/SKILL.md +1 -1
  107. package/template/.qoder/skills/yss-router/references/router-contract.yaml +20 -0
  108. package/template/.qoder/skills/yss-router/references/slice-implementation-contract.md +1 -1
  109. package/template/.trae/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  110. package/template/.trae/skills/implementation-repo-onboarding/SKILL.md +5 -5
  111. package/template/.trae/skills/llm-wiki/SKILL.md +1 -1
  112. package/template/.trae/skills/llm-wiki/references/discover.md +1 -0
  113. package/template/.trae/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  114. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +30 -3
  115. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +52 -0
  116. package/template/.trae/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  117. package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +1 -1
  118. package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +24 -0
  119. package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +2 -0
  120. package/template/.trae/skills/yss-product-lifecycle/references/state-model.md +1 -1
  121. package/template/.trae/skills/yss-router/SKILL.md +1 -1
  122. package/template/.trae/skills/yss-router/references/router-contract.yaml +20 -0
  123. package/template/.trae/skills/yss-router/references/slice-implementation-contract.md +1 -1
  124. package/template/AGENTS.md +5 -5
  125. package/template/CONTEXT.md +3 -2
  126. package/template/docs/process/implementation-repo-integration.md +24 -2
  127. package/template/docs/templates/cross-repo-slice-template.md +15 -0
  128. package/template/docs/templates/implementation-repo-registry-template.md +8 -3
  129. package/template/docs/templates/implementation-routing-template.md +3 -3
  130. package/template/docs/user-guide//344/272/247/345/223/201/347/224/237/345/221/275/345/221/250/346/234/237/345/267/245/344/275/234/346/265/201.md +2 -2
  131. package/template/scripts/lib/git-submodule-fixtures.mjs +58 -0
  132. package/template/scripts/lib/repository-scope-policy.mjs +456 -0
  133. package/template/scripts/lib/scenario-checks.mjs +11 -1
  134. package/template/scripts/repository-scope-policy +16 -0
  135. package/template/scripts/verify-repository-scope-scenarios +239 -0
  136. package/template/scripts/verify-scaffold-generator-scenarios +54 -0
  137. package/template/scripts/verify-template +3 -2
  138. package/template/skills-lock.json +7 -7
  139. package/template.manifest.json +6 -5
  140. package/template.snapshot.json +4 -4
  141. package/template/.cursor/environment.json +0 -5
  142. package/template/.github/workflows/template-node-tooling.yml +0 -61
  143. package/template/wiki/.wiki-manifest.json +0 -390
  144. package/template/wiki/raw/AGENTS.md +0 -122
  145. package/template/wiki/raw/CONTEXT.md +0 -80
  146. package/template/wiki/raw/README.md +0 -130
  147. package/template/wiki/raw/adr-0002-repository-mode.md +0 -3
  148. package/template/wiki/raw/create-yss-spec-repository-mode-contract.md +0 -95
  149. package/template/wiki/raw/harness-process-tailoring.md +0 -64
  150. package/template/wiki/raw/implementation-repo-integration.md +0 -62
  151. package/template/wiki/raw/issue-tracker.md +0 -135
  152. package/template/wiki/raw/lifecycle-registry.yaml +0 -369
  153. package/template/wiki/raw/maintenance-intensity.yaml +0 -25
  154. package/template/wiki/raw/skills-lock-names.md +0 -93
  155. package/template/wiki/raw/skills-maintenance.md +0 -90
  156. package/template/wiki/raw/spec-template.md +0 -102
  157. package/template/wiki/raw/triage-labels.md +0 -15
  158. package/template/wiki/raw/vertical-slice-ticket-template.md +0 -132
  159. package/template/wiki/raw/yss-project.yaml +0 -2
  160. package/template/wiki/raw/yss-skill-registry.yaml +0 -510
  161. package/template/wiki/wiki/Agent/345/205/245/345/217/243/350/247/204/345/210/231.md +0 -21
  162. package/template/wiki/wiki/CLAUDE.md +0 -28
  163. package/template/wiki/wiki/Fresh/351/252/214/350/257/201/344/270/216/347/213/254/347/253/213/345/256/241/346/237/245.md +0 -19
  164. package/template/wiki/wiki/LLM Wiki.md +0 -30
  165. package/template/wiki/wiki/Matt/346/212/200/350/203/275/344/275/223/347/263/273.md +0 -21
  166. package/template/wiki/wiki/OpenAPI/345/245/221/347/272/246.md +0 -20
  167. package/template/wiki/wiki/SpecDelta.md +0 -19
  168. package/template/wiki/wiki/Spec/345/237/272/347/272/277.md +0 -22
  169. package/template/wiki/wiki/Ticket/344/270/216/346/265/201/347/250/213/347/212/266/346/200/201.md +0 -21
  170. package/template/wiki/wiki/YSS/345/267/245/347/250/213/346/212/200/350/203/275/344/275/223/347/263/273.md +0 -23
  171. package/template/wiki/wiki/YSS/350/267/257/347/224/261/344/270/216/345/220/210/345/220/214/347/274/226/350/257/221.md +0 -20
  172. package/template/wiki/wiki/concept-table.md +0 -9
  173. package/template/wiki/wiki/index.md +0 -41
  174. package/template/wiki/wiki/log.md +0 -51
  175. package/template/wiki/wiki//344/272/247/345/223/201/347/240/224/345/217/221/347/224/237/345/221/275/345/221/250/346/234/237.md +0 -20
  176. package/template/wiki/wiki//344/272/247/345/223/201/350/256/276/350/256/241/345/275/261/345/223/215/344/270/216/345/216/237/345/236/213.md +0 -19
  177. package/template/wiki/wiki//344/273/223/345/272/223/350/272/253/344/273/275/344/270/216/350/267/257/347/224/261.md +0 -20
  178. package/template/wiki/wiki//345/210/207/347/211/207/345/256/236/347/216/260/345/220/210/345/220/214.md +0 -21
  179. package/template/wiki/wiki//345/236/202/347/233/264/345/210/207/347/211/207Ticket.md +0 -23
  180. package/template/wiki/wiki//345/244/215/347/233/230/344/270/216/346/235/203/345/250/201/350/265/204/344/272/247/344/277/256/350/256/242.md +0 -20
  181. package/template/wiki/wiki//345/256/236/347/216/260/344/273/223/345/272/223/344/270/216/350/267/250/344/273/223/345/272/223/345/245/221/347/272/246.md +0 -22
  182. package/template/wiki/wiki//345/275/261/345/223/215/351/235/242/345/210/206/350/257/212/344/270/216/346/265/201/347/250/213/350/243/201/345/211/252.md +0 -20
  183. package/template/wiki/wiki//346/212/200/350/203/275/346/212/225/345/275/261/344/270/216/351/224/201/345/256/232.md +0 -23
  184. package/template/wiki/wiki//346/235/241/344/273/266/345/274/272/345/210/266/351/227/250/347/246/201.md +0 -18
  185. package/template/wiki/wiki//346/250/241/346/235/277/345/217/221/345/270/203/351/227/250/347/246/201/344/270/216/351/252/214/350/257/201.md +0 -22
  186. package/template/wiki/wiki//346/250/241/346/235/277/346/200/273/350/247/210.md +0 -20
  187. package/template/wiki/wiki//346/250/241/346/235/277/347/273/264/346/212/244/346/265/201/347/250/213.md +0 -19
package/README.md CHANGED
@@ -14,6 +14,18 @@ npm create yss-spec@latest
14
14
  npx create-yss-spec@latest
15
15
  ```
16
16
 
17
+ 查看用法、命令、参数和样例:
18
+
19
+ ```bash
20
+ npx create-yss-spec@latest --help
21
+ ```
22
+
23
+ 查看 CLI 版本:
24
+
25
+ ```bash
26
+ npx create-yss-spec@latest --version
27
+ ```
28
+
17
29
  ## 当前支持
18
30
 
19
31
  - 交互式收集 `projectName`、`businessDomain`、`targetDir`
@@ -32,8 +44,10 @@ npx create-yss-spec@latest
32
44
  - 接管 / 升级时迁移 Spec / Ticket 路径并删除 `to-prd`、`to-issues` 旧 skill
33
45
  - 旧、新资产内容冲突或清单 schema / mode 非法时 fail closed
34
46
  - 保留模板的共享 skill 投影;生成实例可在尚未 `git init` 时运行模板校验
47
+ - init / sync 不把模板源治理笔记、wiki、审查证据、源仓 CI / Cloud 环境和公开发布清单写入项目实例;attach 会带上 `yss-public-skills.json` 供 `verify-template` 使用
48
+ - 空 gitlink / detached HEAD / git-submodule 挂载点 fail closed,`--force` 也不能覆盖
35
49
 
36
- 本次模板适配按 `2.1.14` 发布。`templateCommit` 会写入实例 metadata;“最新模板”指用户执行的 `npx create-yss-spec@latest` 所携带的最新已发布快照,CLI 运行时不会拉取模板仓库。
50
+ 本次模板适配按 `2.2.0` 发布,绑定 `yss-spec-project-template@04a6151`。`templateCommit` 会写入实例 metadata;“最新模板”指用户执行的 `npx create-yss-spec@latest` 所携带的最新已发布快照,CLI 运行时不会拉取模板仓库。
37
51
 
38
52
  ## 接管已有项目
39
53
 
@@ -60,6 +74,7 @@ npx create-yss-spec@latest attach \
60
74
  - 根规则文件等受管冲突默认阻断;`--force` 才覆盖,并把被覆盖文件备份到目标目录外的临时目录。
61
75
  - 合法 `template-source` 身份会转换为 `project-instance`;非法身份、迁移冲突和无法判断归属的扁平 Ticket 始终阻断。
62
76
  - Git worktree 有脏改动时只提醒,不自动 stash 或提交。
77
+ - `.gitmodules`、gitlink 和 `apps/` 挂载工作树是用户资产;空 gitlink / detached HEAD 即使 `--force` 也阻断。
63
78
 
64
79
  ## 同步已有模板实例仓库
65
80
 
@@ -84,7 +99,8 @@ npx create-yss-spec@latest sync --dry-run
84
99
  - 对模板已删除文件只报告,不自动删除
85
100
  - 对已知的 Spec / Ticket 旧路径执行一次性迁移
86
101
  - 迁移目标已存在且内容不一致时停止,不静默覆盖
87
- - 同步结束会重新执行 `scripts/sync-skills --check`、`scripts/update-skill-lock --check` 和 `scripts/verify-template`;门禁失败会回滚文件和 metadata
102
+ - `.gitmodules`、gitlink(mode `160000`)和 `apps/` 下已挂载实现仓是用户资产,不创建、不覆盖、不删除
103
+ - init / sync 保持瘦实例:不复制 `scripts/`、`wiki/`、`.github/`、`.nvmrc`、`.gitignore`、`.template-source/`;attach 结束后会执行 `scripts/sync-skills --check`、`scripts/update-skill-lock --check` 和 `scripts/verify-template`,门禁失败会回滚文件和 metadata
88
104
 
89
105
  ```bash
90
106
  npx create-yss-spec@latest sync --target-dir . --dry-run
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-yss-spec",
3
- "version": "2.1.14",
3
+ "version": "2.2.0",
4
4
  "description": "Initialize a YSS spec project template repository",
5
5
  "repository": {
6
6
  "type": "git",
package/src/cli.js CHANGED
@@ -33,15 +33,27 @@ const TEMPLATE_METADATA_FILENAME = ".yss-template.json";
33
33
  const TEMPLATE_MANIFEST_VERSION = sha256(TEMPLATE_MANIFEST_TEXT);
34
34
  const TEMPLATE_SOURCE = "github:iloveZzz/yss-spec-project-template";
35
35
  const METADATA_SCHEMA_VERSION = 2;
36
+ const HELP_FLAGS = new Set(["--help", "-h", "-help"]);
37
+ const VERSION_FLAGS = new Set(["--version", "-v", "-version"]);
36
38
  const AGENT_SKILL_ROOTS = [
37
39
  ".agents/skills",
38
40
  ".claude/skills",
39
41
  ".codex/skills",
42
+ ".cursor/skills",
40
43
  ".hermes/skills",
41
44
  ".pi/skills",
42
45
  ".qoder/skills",
43
46
  ".trae/skills",
44
47
  ];
48
+ const GITLINK_MODE = "160000";
49
+ const UNMANAGED_USER_PATHS = new Set([".gitmodules"]);
50
+ const INSTANCE_FORBIDDEN_PATHS = [
51
+ ".template-source",
52
+ ".github",
53
+ ".cursor/environment.json",
54
+ "wiki",
55
+ "docs/reviews",
56
+ ];
45
57
  const LEGACY_SKILL_MAPPINGS = [
46
58
  ["to-prd", "to-spec"],
47
59
  ["to-issues", "to-tickets"],
@@ -184,8 +196,10 @@ function parseArgs(argv) {
184
196
  options.includeExampleDocs = true;
185
197
  } else if (current === "--no-example-docs") {
186
198
  options.includeExampleDocs = false;
187
- } else if (current === "--help" || current === "-h") {
199
+ } else if (HELP_FLAGS.has(current)) {
188
200
  options.help = true;
201
+ } else if (VERSION_FLAGS.has(current)) {
202
+ options.version = true;
189
203
  } else {
190
204
  throw new Error(`不支持的参数:${current}`);
191
205
  }
@@ -297,6 +311,235 @@ function isInsideTemplateRoot(targetDir) {
297
311
  );
298
312
  }
299
313
 
314
+ function posixRelative(from, to) {
315
+ return path.relative(from, to).split(path.sep).join("/");
316
+ }
317
+
318
+ function collectGitRoots(start) {
319
+ const roots = [];
320
+ if (!start) {
321
+ return roots;
322
+ }
323
+ let current = path.resolve(start);
324
+ while (true) {
325
+ if (
326
+ fs.existsSync(path.join(current, ".git")) ||
327
+ fs.existsSync(path.join(current, ".gitmodules"))
328
+ ) {
329
+ roots.push(current);
330
+ }
331
+ const parent = path.dirname(current);
332
+ if (parent === current) {
333
+ break;
334
+ }
335
+ current = parent;
336
+ }
337
+ return roots;
338
+ }
339
+
340
+ function parseGitmodules(content) {
341
+ const modules = [];
342
+ if (typeof content !== "string" || content.length === 0) {
343
+ return modules;
344
+ }
345
+ let current = null;
346
+ for (const rawLine of content.split(/\r?\n/)) {
347
+ const line = rawLine.trim();
348
+ const name = line.match(/^\[submodule "(.+)"\]$/);
349
+ if (name) {
350
+ current = { name: name[1], path: "", url: "" };
351
+ modules.push(current);
352
+ continue;
353
+ }
354
+ if (!current) {
355
+ continue;
356
+ }
357
+ const pathMatch = line.match(/^path\s*=\s*(.+)$/);
358
+ if (pathMatch) {
359
+ current.path = pathMatch[1].trim().replace(/\\/g, "/").replace(/\/+$/, "");
360
+ }
361
+ const urlMatch = line.match(/^url\s*=\s*(.+)$/);
362
+ if (urlMatch) {
363
+ current.url = urlMatch[1].trim();
364
+ }
365
+ }
366
+ return modules;
367
+ }
368
+
369
+ function readGitmodules(repoRoot) {
370
+ const file = path.join(repoRoot, ".gitmodules");
371
+ if (!fs.existsSync(file) || pathKind(file) !== "file") {
372
+ return [];
373
+ }
374
+ return parseGitmodules(fs.readFileSync(file, "utf8"));
375
+ }
376
+
377
+ function gitLsFilesStage(repoRoot, relativePath) {
378
+ const result = spawnSync(
379
+ "git",
380
+ ["-C", repoRoot, "ls-files", "--stage", "--", relativePath],
381
+ { encoding: "utf8" },
382
+ );
383
+ if (result.status !== 0) {
384
+ return null;
385
+ }
386
+ const line = (result.stdout || "").trim().split(/\r?\n/).filter(Boolean)[0];
387
+ if (!line) {
388
+ return null;
389
+ }
390
+ const match = line.match(/^(\d+)\s+([0-9a-f]+)\s+(\d+)\s+(.+)$/i);
391
+ if (!match) {
392
+ return null;
393
+ }
394
+ return { mode: match[1], sha: match[2], stage: match[3], path: match[4] };
395
+ }
396
+
397
+ function gitShowSuperproject(cwd) {
398
+ const result = spawnSync(
399
+ "git",
400
+ ["-C", cwd, "rev-parse", "--show-superproject-working-tree"],
401
+ { encoding: "utf8" },
402
+ );
403
+ if (result.status !== 0) {
404
+ return "";
405
+ }
406
+ return (result.stdout || "").trim();
407
+ }
408
+
409
+ function gitAbbrevRef(cwd) {
410
+ const result = spawnSync(
411
+ "git",
412
+ ["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"],
413
+ { encoding: "utf8" },
414
+ );
415
+ if (result.status !== 0) {
416
+ return null;
417
+ }
418
+ return (result.stdout || "").trim();
419
+ }
420
+
421
+ function isEmptyDir(dir) {
422
+ if (!fs.existsSync(dir)) {
423
+ return true;
424
+ }
425
+ try {
426
+ return fs.readdirSync(dir).filter((name) => name !== "." && name !== "..")
427
+ .length === 0;
428
+ } catch {
429
+ return false;
430
+ }
431
+ }
432
+
433
+ function isGitSubmoduleMount(repoRoot, targetPathValue) {
434
+ const relative = posixRelative(repoRoot, path.resolve(targetPathValue));
435
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
436
+ return false;
437
+ }
438
+ if (readGitmodules(repoRoot).some((item) => item.path === relative)) {
439
+ return true;
440
+ }
441
+ const staged = gitLsFilesStage(repoRoot, relative);
442
+ return staged?.mode === GITLINK_MODE;
443
+ }
444
+
445
+ function inspectCheckoutState(repoRoot, targetPathValue) {
446
+ const resolved = path.resolve(targetPathValue);
447
+ const relative = posixRelative(path.resolve(repoRoot), resolved);
448
+ const inRepo = Boolean(relative) && !relative.startsWith("..");
449
+ const staged = inRepo ? gitLsFilesStage(repoRoot, relative) : null;
450
+ const listed = inRepo
451
+ ? readGitmodules(repoRoot).some((item) => item.path === relative)
452
+ : false;
453
+ const superproject = fs.existsSync(path.join(resolved, ".git"))
454
+ ? gitShowSuperproject(resolved)
455
+ : "";
456
+ const isMount = staged?.mode === GITLINK_MODE || listed || Boolean(superproject);
457
+ const hasGit = fs.existsSync(path.join(resolved, ".git"));
458
+ if (isMount && !hasGit) {
459
+ return isEmptyDir(resolved) ? "empty-gitlink" : "uninitialized";
460
+ }
461
+ if (isMount && hasGit) {
462
+ const ref = gitAbbrevRef(resolved);
463
+ if (ref === "HEAD") {
464
+ return "detached-head";
465
+ }
466
+ return "attached-branch";
467
+ }
468
+ return null;
469
+ }
470
+
471
+ function gitlinkWriteViolation(targetDir, { force = false } = {}) {
472
+ const resolved = path.resolve(targetDir);
473
+ const roots = collectGitRoots(resolved);
474
+ for (const root of roots) {
475
+ if (path.resolve(root) === PACKAGE_ROOT) {
476
+ continue;
477
+ }
478
+ let current = resolved;
479
+ while (true) {
480
+ if (isGitSubmoduleMount(root, current)) {
481
+ const checkout = inspectCheckoutState(root, current);
482
+ if (checkout === "empty-gitlink" || checkout === "uninitialized") {
483
+ return "空 gitlink 不得当成普通目录写入;gitlink 不得由 CLI 覆盖";
484
+ }
485
+ if (checkout === "detached-head") {
486
+ return "detached HEAD 不得当成普通目录写入;gitlink 不得由 CLI 覆盖";
487
+ }
488
+ if (path.resolve(current) === resolved) {
489
+ return force
490
+ ? "--force 不得把 git-submodule 挂载点当成普通目录覆盖"
491
+ : "git-submodule gitlink 不得由 CLI 覆盖;先 git submodule update --init,在子仓附加分支的工作树内操作,或改用独立目录";
492
+ }
493
+ }
494
+ if (path.resolve(current) === path.resolve(root)) {
495
+ break;
496
+ }
497
+ const parent = path.dirname(current);
498
+ if (parent === current) {
499
+ break;
500
+ }
501
+ current = parent;
502
+ }
503
+ }
504
+ return null;
505
+ }
506
+
507
+ function assertTargetWorkingTreeWritable(targetDir, { force = false } = {}) {
508
+ const violation = gitlinkWriteViolation(targetDir, { force });
509
+ if (violation) {
510
+ throw new Error(violation);
511
+ }
512
+ }
513
+
514
+ function unmanagedPathReason(targetDir, relativePath) {
515
+ const normalized = normalizeRelativePath(relativePath);
516
+ if (UNMANAGED_USER_PATHS.has(normalized) || normalized.split("/")[0] === ".gitmodules") {
517
+ return ".gitmodules 是用户资产,不属于受管模板文件";
518
+ }
519
+ const absolutePath = path.resolve(targetDir, normalized);
520
+ const roots = collectGitRoots(targetDir);
521
+ for (const root of roots) {
522
+ if (path.resolve(root) === PACKAGE_ROOT) {
523
+ continue;
524
+ }
525
+ let current = absolutePath;
526
+ while (true) {
527
+ if (isGitSubmoduleMount(root, current)) {
528
+ return "gitlink / apps 挂载工作树是用户资产,不属于受管模板文件";
529
+ }
530
+ if (path.resolve(current) === path.resolve(root)) {
531
+ break;
532
+ }
533
+ const parent = path.dirname(current);
534
+ if (parent === current) {
535
+ break;
536
+ }
537
+ current = parent;
538
+ }
539
+ }
540
+ return null;
541
+ }
542
+
300
543
  function normalizeRelativePath(relativePath) {
301
544
  return relativePath.split(path.sep).join("/");
302
545
  }
@@ -857,6 +1100,14 @@ function classifyAttachPlan(targetDir, variables) {
857
1100
  };
858
1101
 
859
1102
  for (const operation of desiredOperations) {
1103
+ const unmanagedReason = unmanagedPathReason(targetDir, operation.relativePath);
1104
+ if (unmanagedReason) {
1105
+ plan.unsafe.push({
1106
+ ...operation,
1107
+ reason: unmanagedReason,
1108
+ });
1109
+ continue;
1110
+ }
860
1111
  const kind = pathKind(operation.targetPath);
861
1112
  if (kind === "missing") {
862
1113
  plan.missing.push(operation);
@@ -900,6 +1151,14 @@ function classifySyncPlan(targetDir, metadata, identity = readTargetIdentity(tar
900
1151
  const unsafe = [];
901
1152
 
902
1153
  for (const operation of desiredOperations) {
1154
+ const unmanagedReason = unmanagedPathReason(targetDir, operation.relativePath);
1155
+ if (unmanagedReason) {
1156
+ unsafe.push({
1157
+ ...operation,
1158
+ reason: unmanagedReason,
1159
+ });
1160
+ continue;
1161
+ }
903
1162
  const existingRecord = managedFiles[operation.relativePath];
904
1163
  const existingKind = pathKind(operation.targetPath);
905
1164
 
@@ -1024,6 +1283,8 @@ function inspectTargetDir(targetDir, force) {
1024
1283
  throw new Error("目标目录不能位于模板源仓库内部");
1025
1284
  }
1026
1285
 
1286
+ assertTargetWorkingTreeWritable(targetDir, { force });
1287
+
1027
1288
  if (!fs.existsSync(targetDir)) {
1028
1289
  return { exists: false, clearEntries: false };
1029
1290
  }
@@ -1040,13 +1301,14 @@ function inspectTargetDir(targetDir, force) {
1040
1301
  return { exists: true, clearEntries: entries.length > 0 && force };
1041
1302
  }
1042
1303
 
1043
- function inspectExistingTargetDir(targetDir) {
1304
+ function inspectExistingTargetDir(targetDir, { force = false } = {}) {
1044
1305
  if (isInsideTemplateRoot(targetDir)) {
1045
1306
  throw new Error("目标目录不能位于模板源仓库内部");
1046
1307
  }
1047
1308
  if (!fs.existsSync(targetDir) || pathKind(targetDir) !== "directory") {
1048
1309
  throw new Error("attach 目标目录必须是已经存在的项目目录");
1049
1310
  }
1311
+ assertTargetWorkingTreeWritable(targetDir, { force });
1050
1312
  }
1051
1313
 
1052
1314
  function prepareTargetDir(targetDir, targetState) {
@@ -1126,8 +1388,8 @@ function runTemplateVerification(targetDir, scriptPath) {
1126
1388
  }
1127
1389
 
1128
1390
  function verifyGeneratedTemplate(targetDir, mode = "managed") {
1129
- if (mode === "init") {
1130
- verifyGeneratedInstance(targetDir);
1391
+ if (mode === "init" || mode === "sync") {
1392
+ verifyGeneratedInstance(targetDir, { checkForbiddenPaths: mode === "init" });
1131
1393
  return;
1132
1394
  }
1133
1395
 
@@ -1140,15 +1402,18 @@ function verifyGeneratedTemplate(targetDir, mode = "managed") {
1140
1402
  }
1141
1403
  }
1142
1404
 
1143
- function verifyGeneratedInstance(targetDir) {
1144
- const forbiddenPaths = [
1145
- ...[...INIT_EXCLUDED_ROOT_ENTRIES],
1146
- ...[...INIT_EXCLUDED_ROOT_FILES],
1147
- ...[...INIT_EXCLUDED_RELATIVE_PATHS],
1148
- ];
1149
- for (const relativePath of forbiddenPaths) {
1150
- if (pathKind(targetPath(targetDir, relativePath)) !== "missing") {
1151
- throw new Error(`初始化结果包含禁止分发的模板源资产:${relativePath}`);
1405
+ function verifyGeneratedInstance(targetDir, { checkForbiddenPaths = true } = {}) {
1406
+ if (checkForbiddenPaths) {
1407
+ const forbiddenPaths = [
1408
+ ...INSTANCE_FORBIDDEN_PATHS,
1409
+ ...[...INIT_EXCLUDED_ROOT_ENTRIES],
1410
+ ...[...INIT_EXCLUDED_ROOT_FILES],
1411
+ ...[...INIT_EXCLUDED_RELATIVE_PATHS],
1412
+ ];
1413
+ for (const relativePath of [...new Set(forbiddenPaths)]) {
1414
+ if (pathKind(targetPath(targetDir, relativePath)) !== "missing") {
1415
+ throw new Error(`初始化结果包含禁止分发的模板源资产:${relativePath}`);
1416
+ }
1152
1417
  }
1153
1418
  }
1154
1419
 
@@ -1539,6 +1804,10 @@ function runInit(argv = []) {
1539
1804
  printHelp();
1540
1805
  return;
1541
1806
  }
1807
+ if (options.version) {
1808
+ printVersion();
1809
+ return;
1810
+ }
1542
1811
  return promptForMissingOptions(options).then((promptedOptions) => {
1543
1812
  assertRequiredOptions(promptedOptions, "init");
1544
1813
  readTemplateSnapshot();
@@ -1592,6 +1861,10 @@ function runAttach(argv = []) {
1592
1861
  printHelp();
1593
1862
  return;
1594
1863
  }
1864
+ if (options.version) {
1865
+ printVersion();
1866
+ return;
1867
+ }
1595
1868
  assertRequiredOptions(options, "attach");
1596
1869
  if (options.dryRun && options.apply) {
1597
1870
  throw new Error("attach 的 --dry-run 与 --apply 互斥");
@@ -1602,7 +1875,7 @@ function runAttach(argv = []) {
1602
1875
 
1603
1876
  readTemplateSnapshot();
1604
1877
  const targetDir = normalizeTargetDir(options.targetDir);
1605
- inspectExistingTargetDir(targetDir);
1878
+ inspectExistingTargetDir(targetDir, { force: Boolean(options.force) });
1606
1879
  if (pathKind(targetPath(targetDir, TEMPLATE_METADATA_FILENAME)) !== "missing") {
1607
1880
  throw new Error("当前项目已有模板元数据,请使用 sync,不要重复 attach");
1608
1881
  }
@@ -1692,9 +1965,13 @@ function runSync(argv = []) {
1692
1965
  printHelp();
1693
1966
  return;
1694
1967
  }
1968
+ if (options.version) {
1969
+ printVersion();
1970
+ return;
1971
+ }
1695
1972
  readTemplateSnapshot();
1696
1973
  const targetDir = normalizeTargetDir(options.targetDir || ".");
1697
- inspectExistingTargetDir(targetDir);
1974
+ inspectExistingTargetDir(targetDir, { force: Boolean(options.force) });
1698
1975
  const { metadata } = loadTemplateMetadata(targetDir);
1699
1976
  const identity = readTargetIdentity(targetDir);
1700
1977
  const syncPlan = classifySyncPlan(targetDir, metadata, identity);
@@ -1738,7 +2015,7 @@ function runSync(argv = []) {
1738
2015
  applyManagedOperation(operation, transaction);
1739
2016
  }
1740
2017
  applyMigrationPlan(migrationPlan, transaction);
1741
- verifyGeneratedTemplate(targetDir, "init");
2018
+ verifyGeneratedTemplate(targetDir, "sync");
1742
2019
  writeTemplateMetadata(
1743
2020
  targetDir,
1744
2021
  buildNextSyncMetadata(metadata, syncPlan, targetDir),
@@ -1782,16 +2059,87 @@ function runSync(argv = []) {
1782
2059
  console.log("3. 确认无误后提交本次模板同步结果");
1783
2060
  }
1784
2061
 
2062
+ function argvIncludesFlag(argv, flags) {
2063
+ return argv.some((arg) => flags.has(arg));
2064
+ }
2065
+
2066
+ function printVersion() {
2067
+ console.log(`create-yss-spec ${PACKAGE_MANIFEST.version}`);
2068
+ }
2069
+
1785
2070
  function printHelp() {
1786
2071
  console.log(`create-yss-spec ${PACKAGE_MANIFEST.version}
1787
2072
 
1788
- 初始化:create-yss-spec --project-name <name> --business-domain <domain> --target-dir <dir>
1789
- 接管已有项目:create-yss-spec attach --target-dir <dir> --project-name <name> --business-domain <domain> --dry-run|--apply [--force]
1790
- 同步模板:create-yss-spec sync [--target-dir <dir>] [--dry-run] [--force]
2073
+ USAGE
2074
+ $ create-yss-spec [COMMAND] [OPTIONS]
2075
+
2076
+ COMMANDS
2077
+ (default) 初始化新的模板实例仓库
2078
+ attach 向已有项目补齐受管研发管理资产
2079
+ sync 同步已有模板实例的受管资产
2080
+
2081
+ OPTIONS
2082
+ --project-name <name> 项目名称;init 不传则进入交互输入
2083
+ --business-domain <domain> 业务领域;init 不传则进入交互输入
2084
+ --team-size <size> 团队规模;init 不传则可留空,默认「待补充」
2085
+ --target-dir <dir> 目标目录;init 不传则进入交互输入,sync 默认为当前目录
2086
+ --issue-tracker github|gitlab 默认 issue tracker 偏好(默认 github)
2087
+ --dry-run 只预览计划,不写入文件
2088
+ --apply attach 确认执行写入;不能与 --dry-run 同时使用
2089
+ --force init:允许清空非空目录后重新生成
2090
+ attach / sync:覆盖受管冲突文件;unsafe 路径始终阻断
2091
+ --git-init 初始化完成后执行 git init
2092
+ --include-example-docs 显式保留示例文档(默认开启)
2093
+ --no-example-docs 不生成示例文档
2094
+ -h, --help 显示本帮助信息
2095
+ -v, --version 显示 CLI 版本
2096
+
2097
+ EXAMPLES
2098
+ $ npm create yss-spec@latest
2099
+ $ npx create-yss-spec@latest --help
2100
+ $ npx create-yss-spec@latest --version
2101
+ $ npx create-yss-spec@latest \\
2102
+ --project-name "Acme Spec Repo" \\
2103
+ --business-domain "Investment Research" \\
2104
+ --team-size "12" \\
2105
+ --target-dir "./acme-spec-repo" \\
2106
+ --issue-tracker github \\
2107
+ --git-init
2108
+ $ npx create-yss-spec@latest \\
2109
+ --project-name "Preview Repo" \\
2110
+ --business-domain "Data Platform" \\
2111
+ --target-dir "./preview-repo" \\
2112
+ --dry-run
2113
+ $ npx create-yss-spec@latest attach \\
2114
+ --target-dir . \\
2115
+ --project-name "Acme Application" \\
2116
+ --business-domain "Data Platform" \\
2117
+ --dry-run
2118
+ $ npx create-yss-spec@latest attach \\
2119
+ --target-dir . \\
2120
+ --project-name "Acme Application" \\
2121
+ --business-domain "Data Platform" \\
2122
+ --apply
2123
+ $ npx create-yss-spec@latest sync
2124
+ $ npx create-yss-spec@latest sync --dry-run
2125
+ $ npx create-yss-spec@latest sync --target-dir . --force
2126
+
2127
+ LEARN MORE
2128
+ 仓库 README:https://github.com/iloveZzz/create-yss-spec#readme
2129
+ 使用手册:https://github.com/iloveZzz/create-yss-spec/blob/main/docs/user-guide/create-yss-spec-cli-guide.md
2130
+ 模板仓库:https://github.com/iloveZzz/yss-spec-project-template
1791
2131
  `);
1792
2132
  }
1793
2133
 
1794
2134
  async function runCli(argv = []) {
2135
+ if (argvIncludesFlag(argv, HELP_FLAGS)) {
2136
+ printHelp();
2137
+ return;
2138
+ }
2139
+ if (argvIncludesFlag(argv, VERSION_FLAGS)) {
2140
+ printVersion();
2141
+ return;
2142
+ }
1795
2143
  if (argv[0] === "sync") {
1796
2144
  runSync(argv.slice(1));
1797
2145
  return;
@@ -19,8 +19,8 @@ description: Use when a Harness change or vertical slice may require work across
19
19
  1. 读取 `docs/process/implementation-repo-integration.md` 和当前 change 资产。
20
20
  2. 判断影响面:Harness-only、backend-only、frontend-only、backend+frontend、contract-only、release-only。
21
21
  3. 确认每个受影响实现仓库已有登记记录;缺失则阻断并要求 onboarding。
22
- 4. 核对项目根路径:Harness 内项目必须是 `apps/backend/<project>/` 或 `apps/frontend/<project>/`;`apps/backend/`、`apps/frontend/` 只能作为容器,`app/backend/`、`app/frontend/` 及其子路径必须阻断。外部仓库记录各自的真实项目根。
23
- 5. 输出最小任务分配:Harness 文档 / OpenAPI / 后端 MR / 前端 MR / 验证 / 发布。
22
+ 4. 核对项目根路径:Harness 内 `harness-apps` 与 `git-submodule` 项目必须是 `apps/backend/<project>/` 或 `apps/frontend/<project>/`;`apps/backend/`、`apps/frontend/` 只能作为容器,`app/backend/`、`app/frontend/` 及其子路径必须阻断。`git-submodule` 另核 gitlink、`.gitmodules` 和独立 `git_url`。外部 `external-repository` 记录各自的真实项目根。
23
+ 5. 输出最小任务分配:Harness 文档 / OpenAPI / 后端 MR / 前端 MR / 验证 / 发布。`git-submodule` 必须追加 `superproject-gitlink-update`。
24
24
  6. 按 `docs/templates/cross-repo-slice-template.md` 生成切片记录草案。
25
25
  7. 给出 fresh verification 命令和阶段 checkpoint 回写字段。
26
26
 
@@ -18,11 +18,11 @@ description: Use when connecting an existing frontend, backend, fullstack, or ot
18
18
 
19
19
  1. 确认当前仓库是 Harness 仓库,读取 `docs/process/implementation-repo-integration.md`。
20
20
  2. 如果输入是本地路径,只读检查 Git remote、当前分支、目录结构、构建文件和测试脚本。
21
- 3. 如果输入是远端 URL,优先使用只读 Git 查询;需要 clone 时只能 clone 到临时目录,不能 clone 到 Harness 仓库内。
21
+ 3. 如果输入是远端 URL,优先使用只读 Git 查询。需要完整工作树时:`external-repository` 只能 clone 到临时目录;`git-submodule` 只能通过 `git submodule add` / `git submodule update --init` 在已批准的 `apps/backend/<project>/` 或 `apps/frontend/<project>/` gitlink 挂载点检出,禁止普通 clone 或复制源码进 Harness。
22
22
  4. 识别技术栈、包管理器、CI、测试命令、构建命令、OpenAPI 接入和设计 token 接入。
23
- 5. 判定实现位置:Harness 内只能使用 `apps/backend/<project>/` 或 `apps/frontend/<project>/`;`apps/backend/`、`apps/frontend/` 仅为容器,`app/backend/`、`app/frontend/` 及其子路径必须标记为阻断。外部实现仓库记录真实项目根路径,不套用 Harness 占位路径。
24
- 6. 按 `docs/templates/implementation-repo-registry-template.md` 输出实现仓库登记内容。
25
- 7. 列出 `known_gaps`、人审点、fresh verification 命令和需要回写到 Harness change / Issue / checkpoint 的信息。
23
+ 5. 判定实现位置与 `repository_scope`:`external-repository` 记录真实项目根;`harness-apps` 与 `git-submodule` 只能使用 `apps/backend/<project>/` 或 `apps/frontend/<project>/`。`apps/backend/`、`apps/frontend/` 仅为容器,`app/backend/`、`app/frontend/` 及其子路径必须标记为阻断。gitlink(mode `160000`)必须登记为 `git-submodule` 和 `layout_policy: git-submodule-harness-apps`,不得写成 `harness-apps`。必须对照工作树:`git ls-files --stage`、`.gitmodules` 与声明 scope 不一致时阻断;缺少 `git_entry_mode: 160000` 不得把挂载点当普通目录。
24
+ 6. 按 `docs/templates/implementation-repo-registry-template.md` 输出实现仓库登记内容。`git-submodule` 必填 `gitmodules_name`、`gitlink_path`、`git_entry_mode`、`superproject_git_url` 和 `checkout_state`。`harness-apps` / `external-repository` 这些字段填 `不适用`。
25
+ 7. 列出 `known_gaps`、人审点、fresh verification 命令和需要回写到 Harness change / Issue / checkpoint 的信息。空 gitlink、detached HEAD、`--force` 覆盖挂载点或缺少递归检出凭据时标记阻断。写入前必须看 `inspectWorkingTreeScope` / `implementationWriteViolation`:只接受对象结果且 `.writable === true` 才可写;字符串、`null` 或 `.writable !== true` 一律不可写。即使已正确登记为 `git-submodule`,空 gitlink 或 detached HEAD 也必须 `.writable === false`,不得当普通目录写文件或脚手架。
26
26
 
27
27
  ## Baseline Checks
28
28
 
@@ -36,7 +36,7 @@ description: Use when connecting an existing frontend, backend, fullstack, or ot
36
36
  ## Boundaries
37
37
 
38
38
  - 不直接提交、推送、创建 MR / PR 或修改实现仓库。
39
- - 不把实现仓库源码复制进 Harness 仓库。
39
+ - 不把实现仓库源码复制进 Harness 仓库;`git-submodule` 只能形成 gitlink,不能 copy / subtree 冒充。
40
40
  - 不把缺失命令编造为已存在;找不到时标记 `unknown` 或 `需人工确认`。
41
41
  - 不得把 `app/backend/`、`app/frontend/` 或 `apps/backend/`、`apps/frontend/` 容器根登记为可生成项目根。
42
42
  - 触碰认证、授权、SQL、迁移、加密、公共基础库 API 时标记 `TODO-HUMAN-REVIEW`。
@@ -51,7 +51,7 @@ node <skill-root>/scripts/extract.mjs skill-names --in <live-lock.json> --out <w
51
51
 
52
52
  ## Steps
53
53
 
54
- 1. Detect: `wiki/index.md` + `CLAUDE.md` (or `AGENTS.md`) means a wiki exists.
54
+ 1. Detect: `<wiki-root>/wiki/index.md` + `CLAUDE.md` (or `AGENTS.md`) means a wiki exists. Default wiki-root is repo-root `wiki/`. If `yss-project.yaml` is `repository_mode: template-source`, wiki-root is `.template-source/wiki`.
55
55
  2. If the user is asking a repository question, load [query.md](references/query.md) and stop. Do not lint, ingest, or append `log.md`.
56
56
  3. Load the mode algorithm: [compile.md](references/compile.md) for init/refresh/rebuild, [ingest.md](references/ingest.md) for ingest. For init/rebuild corpus choice, load [discover.md](discover.md). For writing, load [writing.md](references/writing.md). For checks, load [lint.md](references/lint.md).
57
57
  4. **Fact order:** called code > table comments / config defaults > stale raw copies. After writing, re-read live sources for a sample of claims (`N = min(5, changed pages)`).
@@ -27,6 +27,7 @@ Unless the user replaces this list with the host's single source-of-truth table:
27
27
  ## Default exclude
28
28
 
29
29
  - `docs/reviews/`
30
+ - `.template-source/evidence/`
30
31
  - `docs/.scratch/`
31
32
  - Agent projection directories (copies or symlinks of shared skills)
32
33
  - `node_modules`
@@ -64,7 +64,7 @@ node scripts/generate_scaffold.mjs \
64
64
  - 永不生成 `User CRUD` 示例;`--with-example` 已禁用,业务代码必须按批准的 Slice Implementation Contract 逐切片实现。
65
65
  - 若仓库已经存在相同工程,先核对覆盖范围。
66
66
  - 输出目录必须显式指定;非空目录默认拒绝写入,确认覆盖时才使用 `--force`。
67
- - Harness 内多项目布局必须使用 `apps/backend/<project>/`;`apps/backend/` 只能是生成器的父容器,`app/backend/`、`app/frontend/` 及其子路径一律拒绝。
67
+ - Harness 内多项目布局必须使用 `apps/backend/<project>/`;`apps/backend/` 只能是生成器的父容器,`app/backend/`、`app/frontend/` 及其子路径一律拒绝。`git-submodule` gitlink、空挂载点、detached HEAD 工作树不得覆盖生成,即使传入 `--force` 也不得当成普通目录,且不得走「请显式传入 `--force`」普通目录覆盖 / rename 路径。`--output-dir` 指向 detached HEAD 子仓时不得 mkdir、staging 或生成工程。先 `git submodule update --init` 并在子仓附加分支工作树内生成。生成器必须先调用 `gitSubmoduleScaffoldViolation`,再在 exists / `--force` / rename 之前调用 `refuseGitlinkAsRegularDirectory`。
68
68
  - 不要在 skill 里硬编码用户业务字段或真实连接信息。
69
69
  - 生成后要检查依赖关系是否仍符合分层约束。
70
70
  - 生成后必须继续使用 `yss-backend-scaffold-parent` 校验工程基线,再按垂直切片加载 `yss-domain`、`yss-application`、`yss-repository`、`yss-web-controller` 等行为 skill;各 skill 的 `references/*-layer-guide.md` 承载分层开发规范。