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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/api/_sync-service.js +111 -18
- package/src/cli/args.js +1 -0
- package/src/cli/help.js +10 -1
- package/src/cli/index.js +3 -0
- package/src/cli/prompts.js +5 -1
- package/src/cli/router.js +2 -0
- package/src/commands/attach.js +12 -2
- package/src/commands/init.js +2 -0
- package/src/commands/skills.js +253 -0
- package/src/commands/sync.js +3 -0
- package/src/family-identity.js +1 -1
- package/src/template/asset-runtime.js +220 -0
- package/src/template/attach-planner.js +5 -0
- package/src/template/distribution-runtime.js +165 -0
- package/src/template/instance-runtime.js +76 -29
- package/src/template/ownership-policy.js +17 -1
- package/src/template/prune-planner.js +6 -1
- package/src/template/sync-planner.js +8 -0
- package/src/template/verification-runtime.js +56 -1
- package/src/validation/metadata.js +24 -1
- package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
- package/template/.agents/skills/yss-cache/SKILL.md +6 -5
- package/template/.agents/skills/yss-cache/references/annotations.md +3 -1
- package/template/.agents/skills/yss-cache/references/architecture.md +4 -2
- package/template/.agents/skills/yss-cache/references/configuration.md +6 -3
- package/template/.agents/skills/yss-cache/references/jetcache.md +21 -0
- package/template/.agents/skills/yss-cache/references/redis-fallback.md +5 -3
- package/template/.agents/skills/yss-cache/references/verification.md +10 -4
- package/template/.agents/skills/yss-cache/scripts/verify-cache-component.sh +26 -20
- package/template/.agents/skills/yss-distributed-id/SKILL.md +14 -58
- package/template/.agents/skills/yss-distributed-id/agents/openai.yaml +2 -2
- package/template/.agents/skills/yss-distributed-id/references/README.md +11 -47
- package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.agents/skills/yss-mybatis/SKILL.md +14 -2
- package/template/.agents/skills/yss-openapi-draft-review/SKILL.md +2 -0
- package/template/.agents/skills/yss-openapi-governance/SKILL.md +4 -0
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +11 -5
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +68 -5
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +8 -8
- package/template/.agents/skills/yss-prototype-stage/SKILL.md +4 -2
- package/template/.agents/skills/yss-prototype-stage/scripts/visual-baseline-contract.mjs +2 -2
- package/template/.agents/skills/yss-repository/SKILL.md +1 -0
- package/template/.agents/skills/yss-stage-decision/SKILL.md +2 -0
- package/template/.agents/skills/yss-tactical-design/SKILL.md +2 -0
- package/template/.agents/skills/yss-technical-design/SKILL.md +2 -0
- package/template/.agents/skills/yss-technical-design/scripts/engineering-design.mjs +1 -1
- package/template/.agents/skills/yss-technical-design/scripts/validate-technical-design.mjs +11 -8
- package/template/.codex/skills/yss-cache/SKILL.md +6 -5
- package/template/.codex/skills/yss-cache/references/annotations.md +3 -1
- package/template/.codex/skills/yss-cache/references/architecture.md +4 -2
- package/template/.codex/skills/yss-cache/references/configuration.md +6 -3
- package/template/.codex/skills/yss-cache/references/jetcache.md +21 -0
- package/template/.codex/skills/yss-cache/references/redis-fallback.md +5 -3
- package/template/.codex/skills/yss-cache/references/verification.md +10 -4
- package/template/.codex/skills/yss-cache/scripts/verify-cache-component.sh +26 -20
- package/template/.codex/skills/yss-distributed-id/SKILL.md +14 -58
- package/template/.codex/skills/yss-distributed-id/agents/openai.yaml +2 -2
- package/template/.codex/skills/yss-distributed-id/references/README.md +11 -47
- package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.codex/skills/yss-mybatis/SKILL.md +14 -2
- package/template/.codex/skills/yss-openapi-draft-review/SKILL.md +2 -0
- package/template/.codex/skills/yss-openapi-governance/SKILL.md +4 -0
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +11 -5
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +68 -5
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +8 -8
- package/template/.codex/skills/yss-prototype-stage/SKILL.md +4 -2
- package/template/.codex/skills/yss-prototype-stage/scripts/visual-baseline-contract.mjs +2 -2
- package/template/.codex/skills/yss-repository/SKILL.md +1 -0
- package/template/.codex/skills/yss-stage-decision/SKILL.md +2 -0
- package/template/.codex/skills/yss-tactical-design/SKILL.md +2 -0
- package/template/.codex/skills/yss-technical-design/SKILL.md +2 -0
- package/template/.codex/skills/yss-technical-design/scripts/engineering-design.mjs +1 -1
- package/template/.codex/skills/yss-technical-design/scripts/validate-technical-design.mjs +11 -8
- package/template/.cursor/skills/yss-cache/SKILL.md +6 -5
- package/template/.cursor/skills/yss-cache/references/annotations.md +3 -1
- package/template/.cursor/skills/yss-cache/references/architecture.md +4 -2
- package/template/.cursor/skills/yss-cache/references/configuration.md +6 -3
- package/template/.cursor/skills/yss-cache/references/jetcache.md +21 -0
- package/template/.cursor/skills/yss-cache/references/redis-fallback.md +5 -3
- package/template/.cursor/skills/yss-cache/references/verification.md +10 -4
- package/template/.cursor/skills/yss-cache/scripts/verify-cache-component.sh +26 -20
- package/template/.cursor/skills/yss-distributed-id/SKILL.md +14 -58
- package/template/.cursor/skills/yss-distributed-id/agents/openai.yaml +2 -2
- package/template/.cursor/skills/yss-distributed-id/references/README.md +11 -47
- package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.cursor/skills/yss-mybatis/SKILL.md +14 -2
- package/template/.cursor/skills/yss-openapi-draft-review/SKILL.md +2 -0
- package/template/.cursor/skills/yss-openapi-governance/SKILL.md +4 -0
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +11 -5
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +68 -5
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +8 -8
- package/template/.cursor/skills/yss-prototype-stage/SKILL.md +4 -2
- package/template/.cursor/skills/yss-prototype-stage/scripts/visual-baseline-contract.mjs +2 -2
- package/template/.cursor/skills/yss-repository/SKILL.md +1 -0
- package/template/.cursor/skills/yss-stage-decision/SKILL.md +2 -0
- package/template/.cursor/skills/yss-tactical-design/SKILL.md +2 -0
- package/template/.cursor/skills/yss-technical-design/SKILL.md +2 -0
- package/template/.cursor/skills/yss-technical-design/scripts/engineering-design.mjs +1 -1
- package/template/.cursor/skills/yss-technical-design/scripts/validate-technical-design.mjs +11 -8
- package/template/.pi/skills/yss-cache/SKILL.md +6 -5
- package/template/.pi/skills/yss-cache/references/annotations.md +3 -1
- package/template/.pi/skills/yss-cache/references/architecture.md +4 -2
- package/template/.pi/skills/yss-cache/references/configuration.md +6 -3
- package/template/.pi/skills/yss-cache/references/jetcache.md +21 -0
- package/template/.pi/skills/yss-cache/references/redis-fallback.md +5 -3
- package/template/.pi/skills/yss-cache/references/verification.md +10 -4
- package/template/.pi/skills/yss-cache/scripts/verify-cache-component.sh +26 -20
- package/template/.pi/skills/yss-distributed-id/SKILL.md +14 -58
- package/template/.pi/skills/yss-distributed-id/agents/openai.yaml +2 -2
- package/template/.pi/skills/yss-distributed-id/references/README.md +11 -47
- package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.pi/skills/yss-mybatis/SKILL.md +14 -2
- package/template/.pi/skills/yss-openapi-draft-review/SKILL.md +2 -0
- package/template/.pi/skills/yss-openapi-governance/SKILL.md +4 -0
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +11 -5
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +68 -5
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +8 -8
- package/template/.pi/skills/yss-prototype-stage/SKILL.md +4 -2
- package/template/.pi/skills/yss-prototype-stage/scripts/visual-baseline-contract.mjs +2 -2
- package/template/.pi/skills/yss-repository/SKILL.md +1 -0
- package/template/.pi/skills/yss-stage-decision/SKILL.md +2 -0
- package/template/.pi/skills/yss-tactical-design/SKILL.md +2 -0
- package/template/.pi/skills/yss-technical-design/SKILL.md +2 -0
- package/template/.pi/skills/yss-technical-design/scripts/engineering-design.mjs +1 -1
- package/template/.pi/skills/yss-technical-design/scripts/validate-technical-design.mjs +11 -8
- package/template/AGENTS.md +2 -0
- package/template/CONTEXT.md +2 -1
- package/template/README.md +3 -5
- package/template/docs/agents/issue-tracker.md +7 -0
- package/template/docs/agents/skill-migrations.md +4 -0
- package/template/docs/agents/yss-skill-registry.yaml +7 -6
- package/template/docs/design/README.md +0 -1
- package/template/docs/design/design.md +2 -2
- package/template/docs/design/templates/interaction-spec-template.md +1 -1
- package/template/docs/engineering/evidence/aliyun-artifact-resolution.json +446 -0
- package/template/docs/process/contract-reading.md +46 -0
- package/template/docs/process/lifecycle-artifact-map.md +2 -2
- package/template/docs/process/lifecycle-registry-baseline.json +2 -1
- package/template/docs/process/lifecycle-registry.yaml +10 -4
- package/template/docs/process/schemas/api-contract-decision-v2.schema.json +198 -0
- package/template/docs/process/schemas/digital-human-task-package.schema.json +8 -0
- package/template/docs/process/schemas/lifecycle-checkpoint.schema.json +259 -0
- package/template/docs/process/schemas/stage-tracking.schema.json +259 -0
- package/template/docs/process/stage-tracking.md +62 -0
- package/template/docs/process/strategic-handoff-package.md +8 -0
- package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +2 -0
- 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
- 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
- package/template/scripts/api-contract-decision +16 -0
- package/template/scripts/complete-backend-delivery +17 -0
- package/template/scripts/contract +13 -0
- package/template/scripts/design-md +2 -0
- package/template/scripts/dispatch-slice-task +13 -0
- package/template/scripts/lib/api-contract-decision.mjs +45 -4
- package/template/scripts/lib/approval-record.mjs +1 -1
- package/template/scripts/lib/approved-execution-context.mjs +35 -7
- package/template/scripts/lib/backend-architecture.mjs +2 -2
- package/template/scripts/lib/backend-delivery-terminal.mjs +51 -0
- package/template/scripts/lib/backend-platform-provenance.mjs +1 -1
- package/template/scripts/lib/backend-platform.mjs +1 -1
- package/template/scripts/lib/backend-scaffold-prerequisites.mjs +1 -1
- package/template/scripts/lib/context-contract.mjs +1 -1
- package/template/scripts/lib/context-reconciliation.mjs +1 -1
- package/template/scripts/lib/contract-source.mjs +22 -0
- package/template/scripts/lib/contract-views.mjs +109 -0
- package/template/scripts/lib/delivery-preflight.mjs +3 -2
- package/template/scripts/lib/design-md.mjs +230 -0
- package/template/scripts/lib/digital-human-roles.mjs +1 -1
- package/template/scripts/lib/existing-backend-architecture.mjs +4 -3
- package/template/scripts/lib/existing-ui-baseline.mjs +4 -2
- package/template/scripts/lib/frontend-delivery-boundary.mjs +1 -1
- package/template/scripts/lib/frontend-delivery.mjs +1 -1
- package/template/scripts/lib/harness-execution-scope.mjs +15 -1
- package/template/scripts/lib/implementation-contract-compiler.mjs +4 -4
- package/template/scripts/lib/json-schema.mjs +19 -4
- package/template/scripts/lib/lifecycle-context-query.mjs +5 -1
- package/template/scripts/lib/lifecycle-controls.mjs +1 -1
- package/template/scripts/lib/lifecycle-execution-scope.mjs +92 -0
- package/template/scripts/lib/lifecycle-registry.mjs +2 -1
- package/template/scripts/lib/lifecycle-transition.mjs +21 -3
- package/template/scripts/lib/openapi-draft-validation.mjs +2 -1
- package/template/scripts/lib/skill-registry.mjs +10 -3
- package/template/scripts/lib/skill-supply-chain.mjs +33 -14
- package/template/scripts/lib/slice-contract-preparation.mjs +16 -14
- package/template/scripts/lib/slice-contract-views.mjs +1 -1
- package/template/scripts/lib/slice-contract.mjs +18 -3
- package/template/scripts/lib/slice-execution-preflight.mjs +7 -3
- package/template/scripts/lib/slice-repositories.mjs +1 -1
- package/template/scripts/lib/slice-task-package.mjs +3 -2
- package/template/scripts/lib/stage-tracking-migration.mjs +176 -0
- package/template/scripts/lib/stage-tracking.mjs +184 -0
- package/template/scripts/lib/strategic-handoff-consumption.mjs +5 -4
- package/template/scripts/lib/strategic-handoff-io.mjs +5 -2
- package/template/scripts/lib/strategic-handoff-routing.mjs +31 -0
- package/template/scripts/lib/strategic-handoff.mjs +4 -2
- package/template/scripts/lib/task-package.mjs +2 -0
- package/template/scripts/lib/technical-design-boundary.mjs +4 -2
- package/template/scripts/lib/ui-baseline.mjs +1 -1
- package/template/scripts/lib/user-decision.mjs +1 -1
- package/template/scripts/lib/validation-phase.mjs +72 -0
- package/template/scripts/stage-tracking +25 -0
- package/template/scripts/strategic-consumer-entry +47 -0
- package/template/scripts/verify-context-reconciliation +4 -3
- package/template/scripts/verify-lifecycle-checkpoint +10 -0
- package/template/scripts/verify-project-instance +11 -10
- package/template/skills-lock.json +17 -32
- package/template.manifest.json +18 -3
- package/template.snapshot.json +5 -5
- package/template/.agents/skills/grill-me/SKILL.md +0 -7
- package/template/.agents/skills/grill-me/agents/openai.yaml +0 -5
- package/template/.agents/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
- package/template/.agents/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
- package/template/.agents/skills/yss-distributed-id/assets/LeafConf.java +0 -36
- package/template/.codex/skills/grill-me/SKILL.md +0 -7
- package/template/.codex/skills/grill-me/agents/openai.yaml +0 -5
- package/template/.codex/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
- package/template/.codex/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
- package/template/.codex/skills/yss-distributed-id/assets/LeafConf.java +0 -36
- package/template/.cursor/skills/grill-me/SKILL.md +0 -7
- package/template/.cursor/skills/grill-me/agents/openai.yaml +0 -5
- package/template/.cursor/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
- package/template/.cursor/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
- package/template/.cursor/skills/yss-distributed-id/assets/LeafConf.java +0 -36
- package/template/.pi/skills/grill-me/SKILL.md +0 -7
- package/template/.pi/skills/grill-me/agents/openai.yaml +0 -5
- package/template/.pi/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
- package/template/.pi/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
- package/template/.pi/skills/yss-distributed-id/assets/LeafConf.java +0 -36
- package/template/docs/agents/yss-plugin-dependency-contract.md +0 -45
- package/template/docs/api/.gitkeep +0 -0
- package/template/docs/api/specs/.gitkeep +0 -0
- package/template/docs/architecture/.gitkeep +0 -0
- package/template/docs/design/facts/antdv-next/1.5.2/cli-help.txt +0 -38
- package/template/docs/design/facts/antdv-next/1.5.2/component-list.json +0 -434
- package/template/docs/design/facts/antdv-next/1.5.2/components/Alert/demo-basic.json +0 -7
- package/template/docs/design/facts/antdv-next/1.5.2/components/Alert/info.json +0 -246
- package/template/docs/design/facts/antdv-next/1.5.2/components/Alert/semantic.json +0 -40
- package/template/docs/design/facts/antdv-next/1.5.2/components/Alert/token.json +0 -32
- package/template/docs/design/facts/antdv-next/1.5.2/components/Button/demo-basic.json +0 -7
- package/template/docs/design/facts/antdv-next/1.5.2/components/Button/info.json +0 -190
- package/template/docs/design/facts/antdv-next/1.5.2/components/Button/semantic.json +0 -20
- package/template/docs/design/facts/antdv-next/1.5.2/components/Button/token.json +0 -256
- package/template/docs/design/facts/antdv-next/1.5.2/components/Input/demo-basic.json +0 -7
- package/template/docs/design/facts/antdv-next/1.5.2/components/Input/info.json +0 -460
- package/template/docs/design/facts/antdv-next/1.5.2/components/Input/semantic.json +0 -70
- package/template/docs/design/facts/antdv-next/1.5.2/components/Input/token.json +0 -123
- package/template/docs/design/facts/antdv-next/1.5.2/components/Modal/demo-basic.json +0 -7
- package/template/docs/design/facts/antdv-next/1.5.2/components/Modal/info.json +0 -646
- package/template/docs/design/facts/antdv-next/1.5.2/components/Modal/semantic.json +0 -50
- package/template/docs/design/facts/antdv-next/1.5.2/components/Modal/token.json +0 -130
- package/template/docs/design/facts/antdv-next/1.5.2/components/Select/demo-basic.json +0 -7
- package/template/docs/design/facts/antdv-next/1.5.2/components/Select/info.json +0 -640
- package/template/docs/design/facts/antdv-next/1.5.2/components/Select/semantic.json +0 -70
- package/template/docs/design/facts/antdv-next/1.5.2/components/Select/token.json +0 -172
- package/template/docs/design/facts/antdv-next/1.5.2/components/Table/demo-basic.json +0 -7
- package/template/docs/design/facts/antdv-next/1.5.2/components/Table/info.json +0 -1010
- package/template/docs/design/facts/antdv-next/1.5.2/components/Table/semantic.json +0 -70
- package/template/docs/design/facts/antdv-next/1.5.2/components/Table/token.json +0 -263
- package/template/docs/design/facts/antdv-next/1.5.2/components/Tag/demo-basic.json +0 -7
- package/template/docs/design/facts/antdv-next/1.5.2/components/Tag/info.json +0 -321
- package/template/docs/design/facts/antdv-next/1.5.2/components/Tag/semantic.json +0 -25
- package/template/docs/design/facts/antdv-next/1.5.2/components/Tag/token.json +0 -25
- package/template/docs/design/facts/antdv-next/1.5.2/design-md.json +0 -3
- package/template/docs/design/facts/antdv-next/1.5.2/manifest.json +0 -215
- package/template/docs/design/facts/antdv-next/1.5.2/resolution-probe.json +0 -5
- package/template/docs/design/prototypes/.gitkeep +0 -1
- package/template/docs/implementation/.gitkeep +0 -1
- package/template/docs/plan/IDEATION.md +0 -96
- package/template/docs/process/PDCA-SCRUM.md +0 -10
- package/template/docs/process/harness-executive-blueprint.md +0 -10
- package/template/docs/releases/.gitkeep +0 -1
- package/template/docs/requirements/README.md +0 -85
- package/template/docs/requirements/tickets/.gitkeep +0 -1
- package/template/docs/templates/agent-brief-template.md +0 -46
- package/template/docs/templates/architecture-proposal-template.md +0 -18
- package/template/docs/templates/implementation-plan-template.md +0 -16
- package/template/docs/templates/implementation-routing-template.md +0 -350
- package/template/docs/templates/requirement-freeze-template.md +0 -63
- package/template/docs/templates/risk-register-template.md +0 -11
- package/template/docs/templates/user-story-template.md +0 -20
- 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 =
|
|
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
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
?
|
|
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
|
|
260
|
-
desiredHash:
|
|
299
|
+
desiredContent,
|
|
300
|
+
desiredHash: sha256(desiredContent),
|
|
261
301
|
};
|
|
262
302
|
}
|
|
263
303
|
|
|
264
|
-
function buildDesiredManagedOperations(targetDir, variables, mode = "managed") {
|
|
265
|
-
|
|
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, "
|
|
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
|
-
|
|
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:
|
|
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
|
|
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 =
|
|
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": "
|
|
6
|
+
"source_revision": "9b579c7b954c153d2347696f53123aebaccde48f",
|
|
7
7
|
"adaptation_ref": "docs/agents/strategic-design-skills-integration.md",
|
|
8
8
|
"skills": [
|
|
9
9
|
{
|