create-yss-spec 3.4.9 → 3.5.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 (287) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/src/api/_sync-service.js +111 -18
  4. package/src/cli/args.js +1 -0
  5. package/src/cli/help.js +10 -1
  6. package/src/cli/index.js +3 -0
  7. package/src/cli/prompts.js +5 -1
  8. package/src/cli/router.js +2 -0
  9. package/src/commands/attach.js +12 -2
  10. package/src/commands/init.js +2 -0
  11. package/src/commands/skills.js +253 -0
  12. package/src/commands/sync.js +3 -0
  13. package/src/family-identity.js +1 -1
  14. package/src/template/asset-runtime.js +220 -0
  15. package/src/template/attach-planner.js +5 -0
  16. package/src/template/distribution-runtime.js +165 -0
  17. package/src/template/instance-runtime.js +76 -29
  18. package/src/template/ownership-policy.js +17 -1
  19. package/src/template/prune-planner.js +6 -1
  20. package/src/template/sync-planner.js +8 -0
  21. package/src/template/verification-runtime.js +56 -1
  22. package/src/validation/metadata.js +24 -1
  23. package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
  24. package/template/.agents/skills/yss-cache/SKILL.md +6 -5
  25. package/template/.agents/skills/yss-cache/references/annotations.md +3 -1
  26. package/template/.agents/skills/yss-cache/references/architecture.md +4 -2
  27. package/template/.agents/skills/yss-cache/references/configuration.md +6 -3
  28. package/template/.agents/skills/yss-cache/references/jetcache.md +21 -0
  29. package/template/.agents/skills/yss-cache/references/redis-fallback.md +5 -3
  30. package/template/.agents/skills/yss-cache/references/verification.md +10 -4
  31. package/template/.agents/skills/yss-cache/scripts/verify-cache-component.sh +26 -20
  32. package/template/.agents/skills/yss-distributed-id/SKILL.md +14 -58
  33. package/template/.agents/skills/yss-distributed-id/agents/openai.yaml +2 -2
  34. package/template/.agents/skills/yss-distributed-id/references/README.md +11 -47
  35. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  36. package/template/.agents/skills/yss-mybatis/SKILL.md +14 -2
  37. package/template/.agents/skills/yss-openapi-draft-review/SKILL.md +2 -0
  38. package/template/.agents/skills/yss-openapi-governance/SKILL.md +4 -0
  39. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +11 -5
  40. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +68 -5
  41. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  42. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +8 -8
  43. package/template/.agents/skills/yss-prototype-stage/SKILL.md +4 -2
  44. package/template/.agents/skills/yss-prototype-stage/scripts/visual-baseline-contract.mjs +2 -2
  45. package/template/.agents/skills/yss-repository/SKILL.md +1 -0
  46. package/template/.agents/skills/yss-stage-decision/SKILL.md +2 -0
  47. package/template/.agents/skills/yss-tactical-design/SKILL.md +2 -0
  48. package/template/.agents/skills/yss-technical-design/SKILL.md +2 -0
  49. package/template/.agents/skills/yss-technical-design/scripts/engineering-design.mjs +1 -1
  50. package/template/.agents/skills/yss-technical-design/scripts/validate-technical-design.mjs +11 -8
  51. package/template/.codex/skills/yss-cache/SKILL.md +6 -5
  52. package/template/.codex/skills/yss-cache/references/annotations.md +3 -1
  53. package/template/.codex/skills/yss-cache/references/architecture.md +4 -2
  54. package/template/.codex/skills/yss-cache/references/configuration.md +6 -3
  55. package/template/.codex/skills/yss-cache/references/jetcache.md +21 -0
  56. package/template/.codex/skills/yss-cache/references/redis-fallback.md +5 -3
  57. package/template/.codex/skills/yss-cache/references/verification.md +10 -4
  58. package/template/.codex/skills/yss-cache/scripts/verify-cache-component.sh +26 -20
  59. package/template/.codex/skills/yss-distributed-id/SKILL.md +14 -58
  60. package/template/.codex/skills/yss-distributed-id/agents/openai.yaml +2 -2
  61. package/template/.codex/skills/yss-distributed-id/references/README.md +11 -47
  62. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  63. package/template/.codex/skills/yss-mybatis/SKILL.md +14 -2
  64. package/template/.codex/skills/yss-openapi-draft-review/SKILL.md +2 -0
  65. package/template/.codex/skills/yss-openapi-governance/SKILL.md +4 -0
  66. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +11 -5
  67. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +68 -5
  68. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  69. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +8 -8
  70. package/template/.codex/skills/yss-prototype-stage/SKILL.md +4 -2
  71. package/template/.codex/skills/yss-prototype-stage/scripts/visual-baseline-contract.mjs +2 -2
  72. package/template/.codex/skills/yss-repository/SKILL.md +1 -0
  73. package/template/.codex/skills/yss-stage-decision/SKILL.md +2 -0
  74. package/template/.codex/skills/yss-tactical-design/SKILL.md +2 -0
  75. package/template/.codex/skills/yss-technical-design/SKILL.md +2 -0
  76. package/template/.codex/skills/yss-technical-design/scripts/engineering-design.mjs +1 -1
  77. package/template/.codex/skills/yss-technical-design/scripts/validate-technical-design.mjs +11 -8
  78. package/template/.cursor/skills/yss-cache/SKILL.md +6 -5
  79. package/template/.cursor/skills/yss-cache/references/annotations.md +3 -1
  80. package/template/.cursor/skills/yss-cache/references/architecture.md +4 -2
  81. package/template/.cursor/skills/yss-cache/references/configuration.md +6 -3
  82. package/template/.cursor/skills/yss-cache/references/jetcache.md +21 -0
  83. package/template/.cursor/skills/yss-cache/references/redis-fallback.md +5 -3
  84. package/template/.cursor/skills/yss-cache/references/verification.md +10 -4
  85. package/template/.cursor/skills/yss-cache/scripts/verify-cache-component.sh +26 -20
  86. package/template/.cursor/skills/yss-distributed-id/SKILL.md +14 -58
  87. package/template/.cursor/skills/yss-distributed-id/agents/openai.yaml +2 -2
  88. package/template/.cursor/skills/yss-distributed-id/references/README.md +11 -47
  89. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  90. package/template/.cursor/skills/yss-mybatis/SKILL.md +14 -2
  91. package/template/.cursor/skills/yss-openapi-draft-review/SKILL.md +2 -0
  92. package/template/.cursor/skills/yss-openapi-governance/SKILL.md +4 -0
  93. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +11 -5
  94. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +68 -5
  95. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  96. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +8 -8
  97. package/template/.cursor/skills/yss-prototype-stage/SKILL.md +4 -2
  98. package/template/.cursor/skills/yss-prototype-stage/scripts/visual-baseline-contract.mjs +2 -2
  99. package/template/.cursor/skills/yss-repository/SKILL.md +1 -0
  100. package/template/.cursor/skills/yss-stage-decision/SKILL.md +2 -0
  101. package/template/.cursor/skills/yss-tactical-design/SKILL.md +2 -0
  102. package/template/.cursor/skills/yss-technical-design/SKILL.md +2 -0
  103. package/template/.cursor/skills/yss-technical-design/scripts/engineering-design.mjs +1 -1
  104. package/template/.cursor/skills/yss-technical-design/scripts/validate-technical-design.mjs +11 -8
  105. package/template/.pi/skills/yss-cache/SKILL.md +6 -5
  106. package/template/.pi/skills/yss-cache/references/annotations.md +3 -1
  107. package/template/.pi/skills/yss-cache/references/architecture.md +4 -2
  108. package/template/.pi/skills/yss-cache/references/configuration.md +6 -3
  109. package/template/.pi/skills/yss-cache/references/jetcache.md +21 -0
  110. package/template/.pi/skills/yss-cache/references/redis-fallback.md +5 -3
  111. package/template/.pi/skills/yss-cache/references/verification.md +10 -4
  112. package/template/.pi/skills/yss-cache/scripts/verify-cache-component.sh +26 -20
  113. package/template/.pi/skills/yss-distributed-id/SKILL.md +14 -58
  114. package/template/.pi/skills/yss-distributed-id/agents/openai.yaml +2 -2
  115. package/template/.pi/skills/yss-distributed-id/references/README.md +11 -47
  116. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  117. package/template/.pi/skills/yss-mybatis/SKILL.md +14 -2
  118. package/template/.pi/skills/yss-openapi-draft-review/SKILL.md +2 -0
  119. package/template/.pi/skills/yss-openapi-governance/SKILL.md +4 -0
  120. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +11 -5
  121. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +68 -5
  122. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  123. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +8 -8
  124. package/template/.pi/skills/yss-prototype-stage/SKILL.md +4 -2
  125. package/template/.pi/skills/yss-prototype-stage/scripts/visual-baseline-contract.mjs +2 -2
  126. package/template/.pi/skills/yss-repository/SKILL.md +1 -0
  127. package/template/.pi/skills/yss-stage-decision/SKILL.md +2 -0
  128. package/template/.pi/skills/yss-tactical-design/SKILL.md +2 -0
  129. package/template/.pi/skills/yss-technical-design/SKILL.md +2 -0
  130. package/template/.pi/skills/yss-technical-design/scripts/engineering-design.mjs +1 -1
  131. package/template/.pi/skills/yss-technical-design/scripts/validate-technical-design.mjs +11 -8
  132. package/template/AGENTS.md +2 -0
  133. package/template/CONTEXT.md +2 -1
  134. package/template/README.md +3 -5
  135. package/template/docs/agents/issue-tracker.md +7 -0
  136. package/template/docs/agents/skill-migrations.md +4 -0
  137. package/template/docs/agents/yss-skill-registry.yaml +7 -6
  138. package/template/docs/design/README.md +0 -1
  139. package/template/docs/design/design.md +2 -2
  140. package/template/docs/design/templates/interaction-spec-template.md +1 -1
  141. package/template/docs/engineering/evidence/aliyun-artifact-resolution.json +446 -0
  142. package/template/docs/process/contract-reading.md +46 -0
  143. package/template/docs/process/lifecycle-artifact-map.md +2 -2
  144. package/template/docs/process/lifecycle-registry-baseline.json +2 -1
  145. package/template/docs/process/lifecycle-registry.yaml +10 -4
  146. package/template/docs/process/schemas/api-contract-decision-v2.schema.json +198 -0
  147. package/template/docs/process/schemas/digital-human-task-package.schema.json +8 -0
  148. package/template/docs/process/schemas/lifecycle-checkpoint.schema.json +259 -0
  149. package/template/docs/process/schemas/stage-tracking.schema.json +259 -0
  150. package/template/docs/process/stage-tracking.md +62 -0
  151. package/template/docs/process/strategic-handoff-package.md +8 -0
  152. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +2 -0
  153. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214/347/264/242/345/274/225.md +1 -0
  154. package/template/docs/user-guide//351/230/266/346/256/265/346/216/245/345/205/245/344/270/216/344/272/247/347/211/251/345/244/215/347/224/250/344/275/277/347/224/250/346/211/213/345/206/214.md +243 -0
  155. package/template/scripts/api-contract-decision +16 -0
  156. package/template/scripts/complete-backend-delivery +17 -0
  157. package/template/scripts/contract +13 -0
  158. package/template/scripts/design-md +2 -0
  159. package/template/scripts/dispatch-slice-task +13 -0
  160. package/template/scripts/lib/api-contract-decision.mjs +45 -4
  161. package/template/scripts/lib/approval-record.mjs +1 -1
  162. package/template/scripts/lib/approved-execution-context.mjs +35 -7
  163. package/template/scripts/lib/backend-architecture.mjs +2 -2
  164. package/template/scripts/lib/backend-delivery-terminal.mjs +51 -0
  165. package/template/scripts/lib/backend-platform-provenance.mjs +1 -1
  166. package/template/scripts/lib/backend-platform.mjs +1 -1
  167. package/template/scripts/lib/backend-scaffold-prerequisites.mjs +1 -1
  168. package/template/scripts/lib/context-contract.mjs +1 -1
  169. package/template/scripts/lib/context-reconciliation.mjs +1 -1
  170. package/template/scripts/lib/contract-source.mjs +22 -0
  171. package/template/scripts/lib/contract-views.mjs +109 -0
  172. package/template/scripts/lib/delivery-preflight.mjs +3 -2
  173. package/template/scripts/lib/design-md.mjs +230 -0
  174. package/template/scripts/lib/digital-human-roles.mjs +1 -1
  175. package/template/scripts/lib/existing-backend-architecture.mjs +4 -3
  176. package/template/scripts/lib/existing-ui-baseline.mjs +4 -2
  177. package/template/scripts/lib/frontend-delivery-boundary.mjs +1 -1
  178. package/template/scripts/lib/frontend-delivery.mjs +1 -1
  179. package/template/scripts/lib/harness-execution-scope.mjs +15 -1
  180. package/template/scripts/lib/implementation-contract-compiler.mjs +4 -4
  181. package/template/scripts/lib/json-schema.mjs +19 -4
  182. package/template/scripts/lib/lifecycle-context-query.mjs +5 -1
  183. package/template/scripts/lib/lifecycle-controls.mjs +1 -1
  184. package/template/scripts/lib/lifecycle-execution-scope.mjs +92 -0
  185. package/template/scripts/lib/lifecycle-registry.mjs +2 -1
  186. package/template/scripts/lib/lifecycle-transition.mjs +21 -3
  187. package/template/scripts/lib/openapi-draft-validation.mjs +2 -1
  188. package/template/scripts/lib/skill-registry.mjs +10 -3
  189. package/template/scripts/lib/skill-supply-chain.mjs +33 -14
  190. package/template/scripts/lib/slice-contract-preparation.mjs +16 -14
  191. package/template/scripts/lib/slice-contract-views.mjs +1 -1
  192. package/template/scripts/lib/slice-contract.mjs +18 -3
  193. package/template/scripts/lib/slice-execution-preflight.mjs +7 -3
  194. package/template/scripts/lib/slice-repositories.mjs +1 -1
  195. package/template/scripts/lib/slice-task-package.mjs +3 -2
  196. package/template/scripts/lib/stage-tracking-migration.mjs +176 -0
  197. package/template/scripts/lib/stage-tracking.mjs +184 -0
  198. package/template/scripts/lib/strategic-handoff-consumption.mjs +5 -4
  199. package/template/scripts/lib/strategic-handoff-io.mjs +5 -2
  200. package/template/scripts/lib/strategic-handoff-routing.mjs +31 -0
  201. package/template/scripts/lib/strategic-handoff.mjs +4 -2
  202. package/template/scripts/lib/task-package.mjs +2 -0
  203. package/template/scripts/lib/technical-design-boundary.mjs +4 -2
  204. package/template/scripts/lib/ui-baseline.mjs +1 -1
  205. package/template/scripts/lib/user-decision.mjs +1 -1
  206. package/template/scripts/lib/validation-phase.mjs +72 -0
  207. package/template/scripts/stage-tracking +25 -0
  208. package/template/scripts/strategic-consumer-entry +47 -0
  209. package/template/scripts/verify-context-reconciliation +4 -3
  210. package/template/scripts/verify-lifecycle-checkpoint +10 -0
  211. package/template/scripts/verify-project-instance +11 -10
  212. package/template/skills-lock.json +17 -32
  213. package/template.manifest.json +18 -3
  214. package/template.snapshot.json +5 -5
  215. package/template/.agents/skills/grill-me/SKILL.md +0 -7
  216. package/template/.agents/skills/grill-me/agents/openai.yaml +0 -5
  217. package/template/.agents/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
  218. package/template/.agents/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
  219. package/template/.agents/skills/yss-distributed-id/assets/LeafConf.java +0 -36
  220. package/template/.codex/skills/grill-me/SKILL.md +0 -7
  221. package/template/.codex/skills/grill-me/agents/openai.yaml +0 -5
  222. package/template/.codex/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
  223. package/template/.codex/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
  224. package/template/.codex/skills/yss-distributed-id/assets/LeafConf.java +0 -36
  225. package/template/.cursor/skills/grill-me/SKILL.md +0 -7
  226. package/template/.cursor/skills/grill-me/agents/openai.yaml +0 -5
  227. package/template/.cursor/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
  228. package/template/.cursor/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
  229. package/template/.cursor/skills/yss-distributed-id/assets/LeafConf.java +0 -36
  230. package/template/.pi/skills/grill-me/SKILL.md +0 -7
  231. package/template/.pi/skills/grill-me/agents/openai.yaml +0 -5
  232. package/template/.pi/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
  233. package/template/.pi/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
  234. package/template/.pi/skills/yss-distributed-id/assets/LeafConf.java +0 -36
  235. package/template/docs/agents/yss-plugin-dependency-contract.md +0 -45
  236. package/template/docs/api/.gitkeep +0 -0
  237. package/template/docs/api/specs/.gitkeep +0 -0
  238. package/template/docs/architecture/.gitkeep +0 -0
  239. package/template/docs/design/facts/antdv-next/1.5.2/cli-help.txt +0 -38
  240. package/template/docs/design/facts/antdv-next/1.5.2/component-list.json +0 -434
  241. package/template/docs/design/facts/antdv-next/1.5.2/components/Alert/demo-basic.json +0 -7
  242. package/template/docs/design/facts/antdv-next/1.5.2/components/Alert/info.json +0 -246
  243. package/template/docs/design/facts/antdv-next/1.5.2/components/Alert/semantic.json +0 -40
  244. package/template/docs/design/facts/antdv-next/1.5.2/components/Alert/token.json +0 -32
  245. package/template/docs/design/facts/antdv-next/1.5.2/components/Button/demo-basic.json +0 -7
  246. package/template/docs/design/facts/antdv-next/1.5.2/components/Button/info.json +0 -190
  247. package/template/docs/design/facts/antdv-next/1.5.2/components/Button/semantic.json +0 -20
  248. package/template/docs/design/facts/antdv-next/1.5.2/components/Button/token.json +0 -256
  249. package/template/docs/design/facts/antdv-next/1.5.2/components/Input/demo-basic.json +0 -7
  250. package/template/docs/design/facts/antdv-next/1.5.2/components/Input/info.json +0 -460
  251. package/template/docs/design/facts/antdv-next/1.5.2/components/Input/semantic.json +0 -70
  252. package/template/docs/design/facts/antdv-next/1.5.2/components/Input/token.json +0 -123
  253. package/template/docs/design/facts/antdv-next/1.5.2/components/Modal/demo-basic.json +0 -7
  254. package/template/docs/design/facts/antdv-next/1.5.2/components/Modal/info.json +0 -646
  255. package/template/docs/design/facts/antdv-next/1.5.2/components/Modal/semantic.json +0 -50
  256. package/template/docs/design/facts/antdv-next/1.5.2/components/Modal/token.json +0 -130
  257. package/template/docs/design/facts/antdv-next/1.5.2/components/Select/demo-basic.json +0 -7
  258. package/template/docs/design/facts/antdv-next/1.5.2/components/Select/info.json +0 -640
  259. package/template/docs/design/facts/antdv-next/1.5.2/components/Select/semantic.json +0 -70
  260. package/template/docs/design/facts/antdv-next/1.5.2/components/Select/token.json +0 -172
  261. package/template/docs/design/facts/antdv-next/1.5.2/components/Table/demo-basic.json +0 -7
  262. package/template/docs/design/facts/antdv-next/1.5.2/components/Table/info.json +0 -1010
  263. package/template/docs/design/facts/antdv-next/1.5.2/components/Table/semantic.json +0 -70
  264. package/template/docs/design/facts/antdv-next/1.5.2/components/Table/token.json +0 -263
  265. package/template/docs/design/facts/antdv-next/1.5.2/components/Tag/demo-basic.json +0 -7
  266. package/template/docs/design/facts/antdv-next/1.5.2/components/Tag/info.json +0 -321
  267. package/template/docs/design/facts/antdv-next/1.5.2/components/Tag/semantic.json +0 -25
  268. package/template/docs/design/facts/antdv-next/1.5.2/components/Tag/token.json +0 -25
  269. package/template/docs/design/facts/antdv-next/1.5.2/design-md.json +0 -3
  270. package/template/docs/design/facts/antdv-next/1.5.2/manifest.json +0 -215
  271. package/template/docs/design/facts/antdv-next/1.5.2/resolution-probe.json +0 -5
  272. package/template/docs/design/prototypes/.gitkeep +0 -1
  273. package/template/docs/implementation/.gitkeep +0 -1
  274. package/template/docs/plan/IDEATION.md +0 -96
  275. package/template/docs/process/PDCA-SCRUM.md +0 -10
  276. package/template/docs/process/harness-executive-blueprint.md +0 -10
  277. package/template/docs/releases/.gitkeep +0 -1
  278. package/template/docs/requirements/README.md +0 -85
  279. package/template/docs/requirements/tickets/.gitkeep +0 -1
  280. package/template/docs/templates/agent-brief-template.md +0 -46
  281. package/template/docs/templates/architecture-proposal-template.md +0 -18
  282. package/template/docs/templates/implementation-plan-template.md +0 -16
  283. package/template/docs/templates/implementation-routing-template.md +0 -350
  284. package/template/docs/templates/requirement-freeze-template.md +0 -63
  285. package/template/docs/templates/risk-register-template.md +0 -11
  286. package/template/docs/templates/user-story-template.md +0 -20
  287. package/template/docs/testing/README.md +0 -110
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+ const { ASSET_PROFILE, INITIAL_STAGES } = require("./asset-runtime");
6
+
7
+ const RUNTIMES = Object.freeze({ codex: ".codex/skills", cursor: ".cursor/skills", pi: ".pi/skills" });
8
+ const OMIT_INSTANCE_PATHS = [
9
+ "docs/process/github-workflows.md",
10
+ "docs/process/harness-executive-blueprint.md",
11
+ "docs/process/PDCA-SCRUM.md",
12
+ "docs/design/preview.html", "docs/design/preview.css", "docs/design/preview.js", "docs/design/preview-dark.html",
13
+ "docs/design/facts/antdv-next", "docs/user-guide/设备借用贯穿案例.md",
14
+ "docs/design/design-system-sync.yaml",
15
+ ];
16
+
17
+ function readDistributionRegistry(templateRoot) {
18
+ const registry = fs.readFileSync(path.join(templateRoot, "docs/agents/yss-skill-registry.yaml"), "utf8");
19
+ const match = registry.match(/^instance_distribution:\n([\s\S]*?)(?=^[^\s#][^\n]*:\s*$)/m);
20
+ if (!match) throw new Error("Skill 注册表缺少 instance_distribution");
21
+ const initial = match[1].match(/^ initial_skills: \[([^\]]+)\]/m);
22
+ if (!initial) throw new Error("Skill 注册表缺少 initial_skills");
23
+ return {
24
+ initialSkills: initial[1].split(",").map((item) => item.trim()),
25
+ text: registry,
26
+ };
27
+ }
28
+
29
+ function assertRuntime(runtime) {
30
+ if (!Object.hasOwn(RUNTIMES, runtime)) throw new Error(`--agent-runtime 必须是 ${Object.keys(RUNTIMES).join("| ")}`);
31
+ return runtime;
32
+ }
33
+
34
+ function distributionForVariables(variables, templateRoot) {
35
+ if (variables.distribution?.mode === "legacy-all") return { mode: "legacy-all" };
36
+ if (variables.distribution?.mode === "selected") return variables.distribution;
37
+ const runtime = assertRuntime(variables.agentRuntime);
38
+ return {
39
+ mode: "selected",
40
+ runtimes: [runtime],
41
+ installedSkills: [...readDistributionRegistry(templateRoot).initialSkills],
42
+ assetProfile: ASSET_PROFILE,
43
+ installedStages: [...INITIAL_STAGES],
44
+ resourceSkills: [],
45
+ };
46
+ }
47
+
48
+ function isIncludedInstancePath(relativePath, distribution) {
49
+ if (distribution.mode === "legacy-all") return true;
50
+ if (relativePath === ".cursorrules" && !distribution.runtimes.includes("cursor")) return false;
51
+ if (OMIT_INSTANCE_PATHS.some((excluded) => relativePath === excluded || relativePath.startsWith(`${excluded}/`))) return false;
52
+ if (distribution.assetProfile === ASSET_PROFILE &&
53
+ (relativePath.startsWith("docs/") || relativePath.startsWith("scripts/") || relativePath.startsWith(".vscode/"))) {
54
+ return distribution.assetPaths.has(relativePath) ||
55
+ [...distribution.assetPaths].some((ref) => ref.startsWith(`${relativePath}/`));
56
+ }
57
+ for (const [runtime, root] of Object.entries(RUNTIMES)) {
58
+ const runtimeRoot = root.split("/")[0];
59
+ if (relativePath === runtimeRoot || relativePath.startsWith(`${runtimeRoot}/`)) {
60
+ if (!distribution.runtimes.includes(runtime)) return false;
61
+ if (relativePath.startsWith(`${root}/`)) {
62
+ const name = relativePath.slice(root.length + 1).split("/")[0];
63
+ if (name && !name.startsWith(".") && !distribution.installedSkills.includes(name)) return false;
64
+ }
65
+ }
66
+ }
67
+ if (relativePath.startsWith(".agents/skills/")) {
68
+ const name = relativePath.slice(".agents/skills/".length).split("/")[0];
69
+ if (name && !name.startsWith(".") && !distribution.installedSkills.includes(name)) return false;
70
+ }
71
+ return true;
72
+ }
73
+
74
+ function selectedSkillLock(sourceText, distribution) {
75
+ if (distribution.mode === "legacy-all") return sourceText;
76
+ const source = JSON.parse(sourceText);
77
+ const roots = distribution.runtimes.map((runtime) => RUNTIMES[assertRuntime(runtime)]);
78
+ source.projectionRoots = roots;
79
+ source.skills.shared = Object.fromEntries(Object.entries(source.skills.shared).filter(([name]) => distribution.installedSkills.includes(name)).map(([name, record]) => [name, { ...record, targets: [".agents/skills", ...roots] }]));
80
+ source.skills.platform = Object.fromEntries(roots.map((root) => [root, Object.fromEntries(Object.entries(source.skills.platform?.[root] || {}).filter(([name]) => distribution.installedSkills.includes(name)))]).filter(([, entries]) => Object.keys(entries).length));
81
+ return `${JSON.stringify(source, null, 2)}\n`;
82
+ }
83
+
84
+ function renderInstanceSkillSupplyChain(sourceText) {
85
+ const additions = `\nfunction projectionRootsFor(lock) {
86
+ const identityPath = path.join(ROOT, "yss-project.yaml");
87
+ const isInstance = existsSync(identityPath) && /^repository_mode:\\s*project-instance\\s*$/m.test(readFileSync(identityPath, "utf8"));
88
+ if (!isInstance) return PROJECTION_ROOTS;
89
+ const roots = lock?.projectionRoots;
90
+ if (!Array.isArray(roots) || !roots.length || roots.some((root) => !PROJECTION_ROOTS.includes(root))) throw new TypeError("project-instance skills-lock.json 缺少有效 projectionRoots");
91
+ return [...new Set(roots)];
92
+ }\n`;
93
+ const marker = 'export const PROJECTION_ROOTS = [".codex/skills", ".cursor/skills", ".pi/skills"];';
94
+ if (!sourceText.includes(marker)) throw new Error("Skill 投影脚本分发基线已变化");
95
+ return sourceText.replace(marker, marker + additions)
96
+ .replace(" const shared = sharedFromLock(lock);", " const shared = sharedFromLock(lock);\n const projectionRoots = projectionRootsFor(lock);")
97
+ .replace(" const oldLock = parseLock(); const previous = priorMetadata(oldLock);", " const oldLock = parseLock(); const projectionRoots = projectionRootsFor(oldLock); const previous = priorMetadata(oldLock);")
98
+ .replaceAll("for (const root of PROJECTION_ROOTS) {", "for (const root of projectionRoots) {")
99
+ .replace("const targets = [\".agents/skills\", ...PROJECTION_ROOTS];", "const targets = [\".agents/skills\", ...projectionRoots];")
100
+ .replace("projectionRoots: PROJECTION_ROOTS, sources, skills", "projectionRoots, sources, skills");
101
+ }
102
+
103
+ function renderInstanceDesignSkillFile(sourceText) {
104
+ return sourceText
105
+ .replaceAll("node .template-source/tooling/node/scripts/design-md.mjs", "scripts/design-md")
106
+ .replace("运行前先更新 `docs/design/design-system-sync.yaml` 的规范源摘要。", "项目实例无需模板间的 design-system-sync 摘要。")
107
+ .replace("修改根 DESIGN.md → 更新 design-system-sync.yaml 摘要 →", "修改根 DESIGN.md →");
108
+ }
109
+
110
+ function linkedSkillDependencies(templateRoot, name) {
111
+ const skillRoot = path.join(templateRoot, ".agents/skills");
112
+ const current = path.join(skillRoot, name);
113
+ const result = new Set();
114
+ function visitDir(directory) {
115
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
116
+ const file = path.join(directory, entry.name);
117
+ if (entry.isDirectory()) visitDir(file);
118
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
119
+ const content = fs.readFileSync(file, "utf8");
120
+ for (const match of content.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)) {
121
+ const ref = match[1].split("#")[0];
122
+ if (!ref || ref.startsWith("/") || ref.includes("://")) continue;
123
+ const resolved = path.resolve(path.dirname(file), ref);
124
+ const relative = path.relative(skillRoot, resolved).split(path.sep);
125
+ if (relative.length > 1 && relative[0] !== name && !relative[0].startsWith(".") && !relative[0].includes("..") && fs.existsSync(resolved)) result.add(relative[0]);
126
+ }
127
+ }
128
+ }
129
+ }
130
+ visitDir(current);
131
+ return [...result];
132
+ }
133
+
134
+ function requiredSkills(registryText, requested, triggers = [], templateRoot = null) {
135
+ const skillBlock = registryText.split(/^skills:\s*$/m)[1]?.split(/^platform_skills:\s*$/m)[0] || "";
136
+ const available = new Set([...skillBlock.matchAll(/^ - (?:\{ )?id: ([a-z][a-z0-9-]+)/gm)].map((match) => match[1]));
137
+ const platformBlock = registryText.split(/^platform_skills:\s*$/m)[1]?.split(/^external_skills:\s*$/m)[0] || "";
138
+ const platformSkills = new Set([...platformBlock.matchAll(/^ - id: ([a-z][a-z0-9-]+)/gm)].map((match) => match[1]));
139
+ const block = registryText.split(/^skill_dependencies:\s*$/m)[1]?.split(/^[^\s#][^\n]*:\s*$/m)[0] || "";
140
+ const dependencies = new Map();
141
+ let current = null;
142
+ for (const line of block.split("\n")) {
143
+ const header = line.match(/^ ([a-z][a-z0-9-]+):\s*$/);
144
+ if (header) { current = header[1]; continue; }
145
+ const item = line.match(/^ - \{ skill: ([a-z][a-z0-9-]+), type: ([a-z-]+)(?:, when: ([a-z0-9-]+))? \}/);
146
+ if (current && item) {
147
+ if (!dependencies.has(current)) dependencies.set(current, []);
148
+ dependencies.get(current).push({ name: item[1], type: item[2], when: item[3] });
149
+ }
150
+ }
151
+ const result = new Set();
152
+ function visit(name) {
153
+ if (!available.has(name)) throw new Error(platformSkills.has(name) ? `平台专属 Skill 不可作为共享 Skill 补装:${name}` : `Skill 未在注册表登记:${name}`);
154
+ if (result.has(name)) return;
155
+ result.add(name);
156
+ for (const dep of dependencies.get(name) || []) {
157
+ if (dep.type === "context-required" || (dep.type === "context-conditional" && triggers.includes(dep.when))) visit(dep.name);
158
+ }
159
+ if (templateRoot) for (const linked of linkedSkillDependencies(templateRoot, name)) visit(linked);
160
+ }
161
+ requested.forEach(visit);
162
+ return [...result].sort();
163
+ }
164
+
165
+ module.exports = { RUNTIMES, assertRuntime, readDistributionRegistry, distributionForVariables, isIncludedInstancePath, selectedSkillLock, renderInstanceSkillSupplyChain, renderInstanceDesignSkillFile, requiredSkills };
@@ -8,6 +8,8 @@ const { treeHash } = require("../template-hash");
8
8
  const { targetPath, pathKind, normalizeRelativePath } = require("../filesystem/path-utils");
9
9
  const { validateTemplateSnapshot } = require("../validation/snapshot");
10
10
  const { validateTemplateMetadata } = require("../validation/metadata");
11
+ const { distributionForVariables, isIncludedInstancePath, selectedSkillLock, renderInstanceSkillSupplyChain, renderInstanceDesignSkillFile } = require("./distribution-runtime");
12
+ const { ASSET_PROFILE, assetPaths } = require("./asset-runtime");
11
13
  const {
12
14
  extractManagedGitignoreBlock,
13
15
  mergeManagedGitignoreBlock,
@@ -29,7 +31,7 @@ const TEMPLATE_MANIFEST_TEXT = fs.readFileSync(BUNDLED_MANIFEST_PATH, "utf8");
29
31
  const TEMPLATE_MANIFEST = JSON.parse(TEMPLATE_MANIFEST_TEXT);
30
32
  const TEMPLATE_METADATA_FILENAME = ".yss-template.json";
31
33
  const TEMPLATE_SOURCE = "github:iloveZzz/yss-spec-project-template";
32
- const METADATA_SCHEMA_VERSION = 2;
34
+ const METADATA_SCHEMA_VERSION = 3;
33
35
 
34
36
  const ROOT_EXCLUDED_ENTRIES = new Set(TEMPLATE_MANIFEST.excludeRootEntries);
35
37
  const ROOT_EXCLUDED_FILES = new Set(TEMPLATE_MANIFEST.excludeRootFiles);
@@ -47,8 +49,6 @@ const INIT_EXCLUDED_RELATIVE_PATHS = new Set(
47
49
  const RENDERED_RELATIVE_PATHS = new Set(TEMPLATE_MANIFEST.renderPaths);
48
50
  const EXAMPLE_DOC_PATHS = new Set(TEMPLATE_MANIFEST.exampleDocPaths);
49
51
 
50
- let bundledPathToLogicalPath = null;
51
-
52
52
  function sha256(value) {
53
53
  return crypto.createHash("sha256").update(value).digest("hex");
54
54
  }
@@ -79,16 +79,16 @@ function readTemplateSnapshot() {
79
79
  });
80
80
  }
81
81
 
82
- function logicalTemplatePath(bundledPath) {
83
- if (bundledPathToLogicalPath === null) {
84
- const snapshot = readTemplateSnapshot();
85
- bundledPathToLogicalPath = new Map(
86
- Object.entries(snapshot.encodedPaths || {}).map(([logicalPath, encodedPath]) => [
87
- normalizeRelativePath(encodedPath),
88
- normalizeRelativePath(logicalPath),
89
- ]),
90
- );
91
- }
82
+ function logicalPathMap(snapshot) {
83
+ return new Map(
84
+ Object.entries(snapshot.encodedPaths || {}).map(([logicalPath, encodedPath]) => [
85
+ normalizeRelativePath(encodedPath),
86
+ normalizeRelativePath(logicalPath),
87
+ ]),
88
+ );
89
+ }
90
+
91
+ function logicalTemplatePath(bundledPath, bundledPathToLogicalPath) {
92
92
  return (
93
93
  bundledPathToLogicalPath.get(normalizeRelativePath(bundledPath)) ||
94
94
  normalizeRelativePath(bundledPath)
@@ -143,12 +143,35 @@ function readTargetIdentity(targetDir) {
143
143
  }
144
144
 
145
145
  function renderTemplateFile(relativePath, content, variables) {
146
+ const distribution = variables.distribution || distributionForVariables(variables, BUNDLED_TEMPLATE_ROOT);
147
+ if (relativePath === "skills-lock.json") return selectedSkillLock(content, distribution);
148
+ if (relativePath === "scripts/lib/skill-supply-chain.mjs" && distribution.mode === "selected") return renderInstanceSkillSupplyChain(content);
149
+ if (distribution.mode === "selected" && /^\.(agents|codex|cursor|pi)\/skills\/yss-design-system\/(SKILL\.md|references\/data-quality-theme\.md)$/.test(relativePath)) return renderInstanceDesignSkillFile(content);
150
+ if (relativePath === "docs/engineering/backend-platforms.json" && distribution.mode === "selected") {
151
+ return content.replaceAll(".template-source/evidence/maintenance/2026-09-18-yss-backend-components/aliyun-artifact-resolution.json", "docs/engineering/evidence/aliyun-artifact-resolution.json");
152
+ }
153
+ if (relativePath === "docs/agents/backend-architecture-profiles.md" && distribution.mode === "selected") {
154
+ return content.replace(/;依据见 `\.template-source\/evidence\/maintenance\/2026-09-12-existing-project-delivery\/maven-adapters-04\.json`/, ";适配验证证据保留在模板源,项目实例须对自身工程重新验证");
155
+ }
156
+ if (relativePath === "docs/user-guide/用户手册.md" && distribution.mode === "selected") {
157
+ if (distribution.assetProfile !== ASSET_PROFILE) {
158
+ return `# ${variables.projectName} 用户手册\n\n本仓是 \`project-instance\`,用于 ${variables.businessDomain} 的研发资产。先阅读根 [AGENTS.md](../../AGENTS.md)、[CONTEXT.md](../../CONTEXT.md) 与 [生命周期资产索引](../process/lifecycle-artifact-map.md)。\n\n阶段派发前,由 Agent 运行 \`create-yss-spec skills ensure <skill-id...> --plan\` 核对依赖,再运行 \`--apply\` 安装。旧实例继续沿用既有文件分发范围;增加平台使用 \`create-yss-spec skills runtime add <codex|cursor|pi> --plan/--apply\`。CLI 快照必须与实例记录的模板提交一致;升级 CLI 后先运行 \`create-yss-spec sync\`。\n\n项目校验运行 \`scripts/verify-project-instance\`。\n`;
159
+ }
160
+ return `# ${variables.projectName} 用户手册\n\n本仓是 \`project-instance\`,用于 ${variables.businessDomain} 的研发资产。先阅读根 [AGENTS.md](../../AGENTS.md)、[CONTEXT.md](../../CONTEXT.md) 与 [生命周期资产索引](../process/lifecycle-artifact-map.md)。\n\n初始化安装分诊与 Plan 入口资产、三项入口 Skill 与所选 Agent 平台。进入后续阶段前,运行 \`create-yss-spec assets ensure <stage-id> --plan\` 核对文件,再运行 \`--apply\` 原子安装;专项 Skill 使用 \`create-yss-spec skills ensure <skill-id...> --plan/--apply\`,会补齐该 Skill 引用的实例文件。增加平台使用 \`create-yss-spec skills runtime add <codex|cursor|pi> --plan/--apply\`。CLI 快照必须与实例记录的模板提交一致;升级 CLI 后先运行 \`create-yss-spec sync\`。\n\n项目校验运行 \`scripts/verify-project-instance\`;实例 CI 应执行该命令和项目实际的构建、测试。\n`;
161
+ }
162
+ if (distribution.mode === "selected" && relativePath === "docs/design/README.md") {
163
+ return content.replace(/^.*design-system-sync\.yaml.*\n/m, "");
164
+ }
165
+ if (distribution.mode === "selected" && relativePath.startsWith("docs/user-guide/") && relativePath.endsWith(".md")) {
166
+ return content.replaceAll("[设备借用贯穿案例](设备借用贯穿案例.md)", "[项目用户手册](用户手册.md)")
167
+ .replaceAll("本仓是 `template-source`", "模板源是 `template-source`");
168
+ }
146
169
  if (relativePath === "yss-project.yaml") {
147
170
  return convertTemplateSourceToInstance(content);
148
171
  }
149
172
 
150
173
  if (relativePath === "AGENTS.md") {
151
- return content
174
+ const rendered = content
152
175
  .replace(
153
176
  /(\*\*项目名称:\*\*\s*)\[填写\]/,
154
177
  (_, prefix) => `${prefix}${variables.projectName}`,
@@ -161,10 +184,20 @@ function renderTemplateFile(relativePath, content, variables) {
161
184
  /(\*\*团队规模:\*\*\s*)\[填写\]/,
162
185
  (_, prefix) => `${prefix}${variables.teamSize}`,
163
186
  );
187
+ if (distribution.mode !== "selected") return rendered;
188
+ const base = rendered.replace(/## 4\. \`template-source\` 模板维护路由[\s\S]*?(?=## 5\.)/, "")
189
+ .replace(/\| 影响面、\`not-applicable\`、模板维护强度 \|[^\n]*\n/, "| 影响面与 `not-applicable` | `docs/process/harness-process-tailoring.md` |\n");
190
+ const guidance = distribution.assetProfile === ASSET_PROFILE
191
+ ? "## 按需阶段资产与 Skill\n\n进入后续生命周期阶段前,运行 `create-yss-spec assets ensure <stage-id> --plan` 查看完整依赖,核对后运行 `--apply`。专项任务根据 docs/agents/yss-skill-registry.yaml 选定 Skill,运行 `create-yss-spec skills ensure <skill-id...> --plan`,核对后运行 `--apply`。缺少阶段资产时先补装,不以缺文件推定门禁不适用。若 CLI 快照与实例模板提交不一致,先运行 `create-yss-spec sync`。\n"
192
+ : "## 按需 Skill\n\n此实例继续沿用原文件分发范围。阶段派发或专项任务开始前,根据 docs/agents/yss-skill-registry.yaml 选定 Skill,运行 `create-yss-spec skills ensure <skill-id...> --plan`,核对后运行 `--apply`。若 CLI 快照与实例模板提交不一致,先运行 `create-yss-spec sync`。\n";
193
+ return `${base}\n${guidance}`;
164
194
  }
165
195
 
166
196
  if (relativePath === "README.md") {
167
- return `# ${variables.projectName}\n\n本仓库用于管理 ${variables.businessDomain} 的研发资产。\n\n- 默认 Issue Tracker:${variables.issueTracker}\n- 协作入口:[AGENTS.md](./AGENTS.md)\n- 业务词汇:[CONTEXT.md](./CONTEXT.md)\n- 用户指南:[docs/user-guide/用户手册.md](./docs/user-guide/用户手册.md)\n`;
197
+ const assetGuidance = distribution.assetProfile === ASSET_PROFILE
198
+ ? "后续阶段先运行 `create-yss-spec assets ensure <stage-id> --plan` 核对依赖,再运行 `--apply`;专项 Skill 使用 `create-yss-spec skills ensure <skill-id> --plan/--apply`。"
199
+ : "专项 Skill 使用 `create-yss-spec skills ensure <skill-id> --plan/--apply`。";
200
+ return `# ${variables.projectName}\n\n本仓库用于管理 ${variables.businessDomain} 的研发资产。\n\n- 默认 Issue Tracker:${variables.issueTracker}\n- Agent 平台:${distribution.mode === "selected" ? distribution.runtimes.join(", ") : "legacy-all"}\n- 协作入口:[AGENTS.md](./AGENTS.md)\n- 业务词汇:[CONTEXT.md](./CONTEXT.md)\n- 用户指南:[docs/user-guide/用户手册.md](./docs/user-guide/用户手册.md)\n\n项目校验:\`scripts/verify-project-instance\`。${assetGuidance}\n`;
168
201
  }
169
202
 
170
203
  if (relativePath === ".gitignore") return extractManagedGitignoreBlock(content);
@@ -178,6 +211,8 @@ function buildCopyPlan(
178
211
  variables,
179
212
  relativeDir = "",
180
213
  mode = "managed",
214
+ bundledPathToLogicalPath,
215
+ distribution = { mode: "legacy-all" },
181
216
  ) {
182
217
  const operations = [];
183
218
  const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
@@ -188,9 +223,10 @@ function buildCopyPlan(
188
223
  const bundledRelativePath = relativeDir
189
224
  ? path.posix.join(relativeDir, entry.name)
190
225
  : entry.name;
191
- const relativePath = logicalTemplatePath(bundledRelativePath);
226
+ const relativePath = logicalTemplatePath(bundledRelativePath, bundledPathToLogicalPath);
192
227
 
193
228
  if (shouldExcludeRelativePath(relativePath, mode)) continue;
229
+ if (!isIncludedInstancePath(relativePath, distribution)) continue;
194
230
  if (!variables.includeExampleDocs && EXAMPLE_DOC_PATHS.has(relativePath)) continue;
195
231
 
196
232
  const sourcePath = path.join(sourceDir, entry.name);
@@ -205,6 +241,8 @@ function buildCopyPlan(
205
241
  variables,
206
242
  bundledRelativePath,
207
243
  mode,
244
+ bundledPathToLogicalPath,
245
+ distribution,
208
246
  ),
209
247
  );
210
248
  continue;
@@ -235,8 +273,9 @@ function buildSyncVariables(metadata) {
235
273
  issueTracker: variables.issueTracker || "github",
236
274
  includeExampleDocs:
237
275
  variables.includeExampleDocs === undefined
238
- ? true
276
+ ? !(metadata.metadataSchemaVersion >= 3)
239
277
  : Boolean(variables.includeExampleDocs),
278
+ distribution: metadata.metadataSchemaVersion >= 3 ? metadata.distribution : { mode: "legacy-all" },
240
279
  };
241
280
  }
242
281
 
@@ -254,15 +293,18 @@ function buildDesiredManagedFile(operation, variables) {
254
293
  };
255
294
  }
256
295
 
296
+ const desiredContent = fs.readFileSync(operation.sourcePath);
257
297
  return {
258
298
  ...operation,
259
- desiredContent: fs.readFileSync(operation.sourcePath),
260
- desiredHash: fileHash(operation.sourcePath),
299
+ desiredContent,
300
+ desiredHash: sha256(desiredContent),
261
301
  };
262
302
  }
263
303
 
264
- function buildDesiredManagedOperations(targetDir, variables, mode = "managed") {
265
- return buildCopyPlan(BUNDLED_TEMPLATE_ROOT, targetDir, variables, "", mode)
304
+ function buildDesiredManagedOperations(targetDir, variables, mode = "managed", snapshot = readTemplateSnapshot()) {
305
+ const distribution = variables.distribution || distributionForVariables(variables, BUNDLED_TEMPLATE_ROOT);
306
+ const effectiveDistribution = { ...distribution, assetPaths: assetPaths(BUNDLED_TEMPLATE_ROOT, distribution) };
307
+ return buildCopyPlan(BUNDLED_TEMPLATE_ROOT, targetDir, { ...variables, distribution }, "", mode, logicalPathMap(snapshot), effectiveDistribution)
266
308
  .filter((operation) => operation.type === "copy" || operation.type === "render")
267
309
  .map((operation) => buildDesiredManagedFile(operation, variables));
268
310
  }
@@ -297,9 +339,9 @@ function adaptGitignoreOperation(
297
339
  };
298
340
  }
299
341
 
300
- function buildSyncDesiredOperations(targetDir, metadata, identity) {
342
+ function buildSyncDesiredOperations(targetDir, metadata, identity, snapshot = readTemplateSnapshot()) {
301
343
  const variables = buildSyncVariables(metadata);
302
- return buildDesiredManagedOperations(targetDir, variables, "init")
344
+ return buildDesiredManagedOperations(targetDir, variables, "init", snapshot)
303
345
  .filter((operation) => operation.relativePath !== "README.md")
304
346
  .map((operation) =>
305
347
  adaptGitignoreOperation(operation, targetDir, {
@@ -327,7 +369,7 @@ function buildSyncDesiredOperations(targetDir, metadata, identity) {
327
369
  }
328
370
 
329
371
  function buildAttachDesiredOperations(targetDir, variables, identity) {
330
- return buildDesiredManagedOperations(targetDir, variables, "managed")
372
+ return buildDesiredManagedOperations(targetDir, variables, "init")
331
373
  .filter((operation) => operation.relativePath !== "README.md")
332
374
  .map((operation) => adaptGitignoreOperation(operation, targetDir, { attach: true }))
333
375
  .map((operation) => {
@@ -396,6 +438,7 @@ function collectManagedFiles(desiredOperations) {
396
438
 
397
439
  function buildMetadata(variables, desiredOperations, timestamp = nowIsoString()) {
398
440
  const snapshot = readTemplateSnapshot();
441
+ const distribution = variables.distribution || distributionForVariables(variables, BUNDLED_TEMPLATE_ROOT);
399
442
  return {
400
443
  metadataSchemaVersion: METADATA_SCHEMA_VERSION,
401
444
  templateName: PACKAGE_MANIFEST.name,
@@ -408,6 +451,7 @@ function buildMetadata(variables, desiredOperations, timestamp = nowIsoString())
408
451
  initializedAt: timestamp,
409
452
  lastSyncedAt: timestamp,
410
453
  managedFilesManifestVersion: TEMPLATE_MANIFEST_VERSION,
454
+ distribution,
411
455
  variables: {
412
456
  projectName: variables.projectName,
413
457
  businessDomain: variables.businessDomain,
@@ -429,19 +473,21 @@ function writeTemplateMetadata(targetDir, metadata, transaction = null) {
429
473
  fs.writeFileSync(metadataPath, content, "utf8");
430
474
  }
431
475
 
432
- function buildNextSyncMetadata(metadata, syncPlan) {
433
- const snapshot = readTemplateSnapshot();
476
+ function buildNextSyncMetadata(metadata, syncPlan, {
477
+ snapshot = readTemplateSnapshot(),
478
+ currentHashes = null,
479
+ } = {}) {
434
480
  const nextManagedFiles = { ...(metadata.managedFiles || {}) };
435
481
  delete nextManagedFiles["README.md"];
436
482
  for (const relativePath of syncPlan.alreadyMissing || []) delete nextManagedFiles[relativePath];
437
483
  for (const relativePath of syncPlan.pruned || []) delete nextManagedFiles[relativePath];
438
484
  for (const operation of syncPlan.desiredOperations) {
439
485
  if (pathKind(operation.targetPath) !== "file") continue;
440
- const currentHash = fileHash(operation.targetPath);
441
- if (currentHash === operation.desiredHash) {
486
+ const currentHash = currentHashes?.[operation.relativePath] ?? fileHash(operation.targetPath);
487
+ if (currentHash === operation.desiredHash || operation.relativePath === "skills-lock.json") {
442
488
  nextManagedFiles[operation.relativePath] = {
443
489
  type: operation.type,
444
- contentHash: operation.desiredHash,
490
+ contentHash: currentHash,
445
491
  };
446
492
  }
447
493
  }
@@ -449,6 +495,7 @@ function buildNextSyncMetadata(metadata, syncPlan) {
449
495
  return {
450
496
  ...metadata,
451
497
  metadataSchemaVersion: METADATA_SCHEMA_VERSION,
498
+ distribution: metadata.metadataSchemaVersion >= 3 ? metadata.distribution : { mode: "legacy-all" },
452
499
  templateName: PACKAGE_MANIFEST.name,
453
500
  cliVersion: PACKAGE_MANIFEST.version,
454
501
  templateVersion: PACKAGE_MANIFEST.version,
@@ -124,10 +124,25 @@ function isWriteProtectedOwnership(ownership) {
124
124
  return ownership === "user-owned" || ownership === "protected";
125
125
  }
126
126
 
127
+ function projectAssetWriteViolation(relativePath) {
128
+ const path = normalizePolicyPath(relativePath);
129
+ if (path === "docs/implementation/.gitkeep" || path === "docs/requirements/tickets/.gitkeep") return null;
130
+ if (
131
+ path === "scaffold-architecture-decisions.yaml" ||
132
+ path.startsWith("docs/implementation/") ||
133
+ path.startsWith("docs/requirements/tickets/")
134
+ ) {
135
+ return `${path} 是项目合同或决策资产,CLI 同步不得写入或清理`;
136
+ }
137
+ return null;
138
+ }
139
+
127
140
  function ownershipWriteViolation(operation) {
141
+ const path = operation?.relativePath || operation?.path || "unknown";
142
+ const projectAssetViolation = projectAssetWriteViolation(path);
143
+ if (projectAssetViolation) return projectAssetViolation;
128
144
  const ownership = operation?.ownership || "managed";
129
145
  if (!isWriteProtectedOwnership(ownership)) return null;
130
- const path = operation?.relativePath || operation?.path || "unknown";
131
146
  return ownership === "protected"
132
147
  ? `${path} 被 ownership policy 标记为 protected,CLI 不得写入或覆盖`
133
148
  : `${path} 被 ownership policy 标记为 user-owned,不属于受管模板文件`;
@@ -145,5 +160,6 @@ module.exports = {
145
160
  resolveOwnership,
146
161
  isTemplateManagedOwnership,
147
162
  isWriteProtectedOwnership,
163
+ projectAssetWriteViolation,
148
164
  ownershipWriteViolation,
149
165
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- const { isTemplateManagedOwnership } = require("./ownership-policy");
3
+ const { isTemplateManagedOwnership, projectAssetWriteViolation } = require("./ownership-policy");
4
4
  const { resolveTemplateOwnership } = require("./ownership-runtime");
5
5
 
6
6
  function classifyRemovedFiles({
@@ -29,6 +29,11 @@ function classifyRemovedFiles({
29
29
  retainedRemoved.push({ path: relativePath, reason: "缺少可信受管基线" });
30
30
  continue;
31
31
  }
32
+ const projectAssetViolation = projectAssetWriteViolation(relativePath);
33
+ if (projectAssetViolation) {
34
+ retainedRemoved.push({ path: relativePath, reason: projectAssetViolation });
35
+ continue;
36
+ }
32
37
  const recordedOwnership = record.ownership || getCurrentOwnership(relativePath);
33
38
  const currentOwnership = getCurrentOwnership(relativePath);
34
39
  if (
@@ -23,6 +23,7 @@ function classifySyncOperations({
23
23
  const conflicts = [];
24
24
  const forceableConflicts = [];
25
25
  const unsafe = [];
26
+ const currentHashes = Object.create(null);
26
27
 
27
28
  for (const operation of desiredOperations) {
28
29
  const unmanagedReason = getUnmanagedReason(operation);
@@ -47,7 +48,13 @@ function classifySyncOperations({
47
48
  continue;
48
49
  }
49
50
 
51
+ if (operation.relativePath === "docs/agents/issue-tracker.md") {
52
+ unchanged.push({ ...operation, reason: "project-owned-tracker-configuration" });
53
+ continue;
54
+ }
55
+
50
56
  const currentHash = getFileHash(operation);
57
+ currentHashes[operation.relativePath] = currentHash;
51
58
 
52
59
  if (operation.safeSectionMerge && currentHash !== operation.desiredHash) {
53
60
  updated.push(operation);
@@ -112,6 +119,7 @@ function classifySyncOperations({
112
119
  unsafe,
113
120
  removed,
114
121
  desiredOperations,
122
+ currentHashes,
115
123
  };
116
124
  }
117
125
 
@@ -134,7 +134,61 @@ function verifyGeneratedAttach(targetDir) {
134
134
  runTemplateVerification(targetDir, "scripts/verify-project-instance", []);
135
135
  }
136
136
 
137
- function refreshGeneratedProjectInstance(targetDir) {
137
+ function readProjectSkillLock(targetDir) {
138
+ const lockPath = targetPath(targetDir, "skills-lock.json");
139
+ if (pathKind(lockPath) === "missing") return null;
140
+ const lock = JSON.parse(fs.readFileSync(lockPath, "utf8"));
141
+ const isRecord = (value) => value && typeof value === "object" && !Array.isArray(value);
142
+ if (
143
+ lock.version !== 3 || !isRecord(lock.skills?.shared) ||
144
+ !isRecord(lock.skills?.platform) || !isRecord(lock.sources) ||
145
+ !Array.isArray(lock.projectionRoots)
146
+ ) {
147
+ throw new Error("现有 skills-lock.json 结构非法,无法安全保留项目技能登记");
148
+ }
149
+ return lock;
150
+ }
151
+
152
+ function preserveProjectSkillRegistrations(targetDir, previousLock, previousManagedFiles, transaction) {
153
+ if (!previousLock) return;
154
+ const lockPath = targetPath(targetDir, "skills-lock.json");
155
+ const generated = JSON.parse(fs.readFileSync(lockPath, "utf8"));
156
+ const managedPaths = Object.keys(previousManagedFiles || {});
157
+ const wasManaged = (skillPath) => managedPaths.some((ref) => ref.startsWith(`${skillPath}/`));
158
+ let changed = false;
159
+
160
+ for (const [name, record] of Object.entries(previousLock.skills.shared)) {
161
+ const skillPath = `.agents/skills/${name}`;
162
+ if (generated.skills.shared[name] || wasManaged(skillPath)) continue;
163
+ if (pathKind(targetPath(targetDir, skillPath)) !== "directory") continue;
164
+ generated.skills.shared[name] = record;
165
+ changed = true;
166
+ }
167
+ for (const [root, entries] of Object.entries(previousLock.skills.platform || {})) {
168
+ if (!generated.projectionRoots.includes(root)) continue;
169
+ for (const [name, record] of Object.entries(entries)) {
170
+ const skillPath = `${root}/${name}`;
171
+ if (generated.skills.platform?.[root]?.[name] || wasManaged(skillPath)) continue;
172
+ if (pathKind(targetPath(targetDir, skillPath)) !== "directory") continue;
173
+ generated.skills.platform ||= {};
174
+ generated.skills.platform[root] ||= {};
175
+ generated.skills.platform[root][name] = record;
176
+ changed = true;
177
+ }
178
+ }
179
+ if (changed) {
180
+ generated.sources = { ...previousLock.sources, ...generated.sources };
181
+ transaction.writeFile(lockPath, `${JSON.stringify(generated, null, 2)}\n`);
182
+ }
183
+ }
184
+
185
+ function refreshGeneratedProjectInstance(targetDir, options = {}) {
186
+ preserveProjectSkillRegistrations(
187
+ targetDir,
188
+ options.previousSkillLock,
189
+ options.previousManagedFiles,
190
+ options.transaction,
191
+ );
138
192
  runTemplateVerification(targetDir, "scripts/update-skill-lock", []);
139
193
  }
140
194
 
@@ -146,6 +200,7 @@ module.exports = {
146
200
  verifyGeneratedInstance,
147
201
  verifyGeneratedInit,
148
202
  verifyGeneratedAttach,
203
+ readProjectSkillLock,
149
204
  refreshGeneratedProjectInstance,
150
205
  verifyGeneratedProjectInstance: verifyGeneratedAttach,
151
206
  };
@@ -8,6 +8,7 @@ const OWNERSHIP_TYPES = new Set([
8
8
  "protected",
9
9
  ]);
10
10
  const MERGE_STRATEGIES = new Set(["replace-with-force", "manual"]);
11
+ const { ASSET_PROFILE, INITIAL_STAGES, STAGES } = require("../template/asset-runtime");
11
12
 
12
13
  function assertPositiveInteger(value, name) {
13
14
  if (value !== undefined && (!Number.isInteger(value) || value < 1)) {
@@ -22,7 +23,7 @@ function assertSha256(value, name) {
22
23
  }
23
24
 
24
25
  function validateTemplateMetadata(metadata, {
25
- currentSchemaVersion = 2,
26
+ currentSchemaVersion = 3,
26
27
  templateName,
27
28
  templateSource,
28
29
  } = {}) {
@@ -35,6 +36,28 @@ function validateTemplateMetadata(metadata, {
35
36
  if (metadata.metadataSchemaVersion > currentSchemaVersion) {
36
37
  throw new Error(`不支持的模板元数据版本:${metadata.metadataSchemaVersion}`);
37
38
  }
39
+ if (metadata.metadataSchemaVersion === 3) {
40
+ const selection = metadata.distribution;
41
+ if (!selection || !["selected", "legacy-all"].includes(selection.mode)) throw new Error("模板元数据 distribution 非法");
42
+ if (selection.mode === "selected" && (
43
+ !Array.isArray(selection.runtimes) || !selection.runtimes.length ||
44
+ new Set(selection.runtimes).size !== selection.runtimes.length ||
45
+ selection.runtimes.some((runtime) => !["codex", "cursor", "pi"].includes(runtime)) ||
46
+ !Array.isArray(selection.installedSkills) ||
47
+ new Set(selection.installedSkills).size !== selection.installedSkills.length ||
48
+ selection.installedSkills.some((skill) => !/^[a-z][a-z0-9-]+$/.test(skill))
49
+ )) throw new Error("模板元数据 distribution 选择非法");
50
+ if (selection.mode === "selected" && selection.assetProfile !== undefined && (
51
+ selection.assetProfile !== ASSET_PROFILE ||
52
+ !Array.isArray(selection.installedStages) ||
53
+ INITIAL_STAGES.some((stage) => !selection.installedStages.includes(stage)) ||
54
+ new Set(selection.installedStages).size !== selection.installedStages.length ||
55
+ selection.installedStages.some((stage) => !Object.hasOwn(STAGES, stage)) ||
56
+ !Array.isArray(selection.resourceSkills) ||
57
+ new Set(selection.resourceSkills).size !== selection.resourceSkills.length ||
58
+ selection.resourceSkills.some((skill) => !selection.installedSkills.includes(skill))
59
+ )) throw new Error("模板元数据阶段资产选择非法");
60
+ }
38
61
  if (metadata.managedFiles !== undefined && (typeof metadata.managedFiles !== "object" || metadata.managedFiles === null || Array.isArray(metadata.managedFiles))) {
39
62
  throw new Error("模板元数据 managedFiles 必须是 JSON 对象");
40
63
  }
@@ -3,7 +3,7 @@
3
3
  "source": "iloveZzz/yss-harness-design-agent",
4
4
  "source_type": "git-submodule",
5
5
  "source_root": ".agents/skills",
6
- "source_revision": "83d7bce7eab1adf16e02a419717c94fa8ba84f43",
6
+ "source_revision": "9b579c7b954c153d2347696f53123aebaccde48f",
7
7
  "adaptation_ref": "docs/agents/strategic-design-skills-integration.md",
8
8
  "skills": [
9
9
  {