ai-delivery-workflow 0.2.2

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 (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/bin/ai-delivery.mjs +138 -0
  4. package/bin/validate-skills.mjs +12 -0
  5. package/docs/DUAL-REPOSITORY-WORKSPACE.zh-CN.md +125 -0
  6. package/docs/EVOLUTION-MAINTENANCE.zh-CN.md +75 -0
  7. package/docs/EVOLUTION-USER-GUIDE.zh-CN.md +160 -0
  8. package/docs/FILE-REFERENCE.zh-CN.md +503 -0
  9. package/docs/PROJECT-MANUAL.zh-CN.md +931 -0
  10. package/docs/STATE-CLI-MAINTENANCE.zh-CN.md +135 -0
  11. package/docs/STATE-CLI-USER-GUIDE.zh-CN.md +186 -0
  12. package/docs/VIEWER-MAINTENANCE.zh-CN.md +373 -0
  13. package/docs/VIEWER-USER-GUIDE.zh-CN.md +235 -0
  14. package/lib/delivery-state.mjs +1220 -0
  15. package/lib/evolution.mjs +971 -0
  16. package/lib/project-bootstrap.mjs +927 -0
  17. package/lib/project-installer.mjs +1184 -0
  18. package/lib/skill-validator.mjs +154 -0
  19. package/lib/toml-hooks.mjs +97 -0
  20. package/lib/workspace.mjs +302 -0
  21. package/lib/yaml-runtime.mjs +18 -0
  22. package/package.json +39 -0
  23. package/skills/ai-delivery-assemble-release/SKILL.md +27 -0
  24. package/skills/ai-delivery-assemble-release/agents/openai.yaml +4 -0
  25. package/skills/ai-delivery-assemble-release/assets/release-candidate-manifest.yaml +22 -0
  26. package/skills/ai-delivery-assemble-release/assets/release-git-plan.yaml +9 -0
  27. package/skills/ai-delivery-assemble-release/assets/release-material-index.csv +2 -0
  28. package/skills/ai-delivery-assemble-release/assets/version-inclusion.csv +2 -0
  29. package/skills/ai-delivery-assemble-release/references/release-assembly-contract.md +7 -0
  30. package/skills/ai-delivery-assemble-release/scripts/plan-release.mjs +298 -0
  31. package/skills/ai-delivery-bootstrap/SKILL.md +59 -0
  32. package/skills/ai-delivery-bootstrap/agents/openai.yaml +4 -0
  33. package/skills/ai-delivery-bootstrap/references/bootstrap-contract.md +70 -0
  34. package/skills/ai-delivery-checkpoint-task/SKILL.md +63 -0
  35. package/skills/ai-delivery-checkpoint-task/agents/openai.yaml +4 -0
  36. package/skills/ai-delivery-checkpoint-task/assets/codex-hook-config.toml +92 -0
  37. package/skills/ai-delivery-checkpoint-task/assets/runtime-template/resume.md +15 -0
  38. package/skills/ai-delivery-checkpoint-task/assets/runtime-template/task.json +30 -0
  39. package/skills/ai-delivery-checkpoint-task/assets/runtime-template/version-archive.json +10 -0
  40. package/skills/ai-delivery-checkpoint-task/references/checkpoint-contract.md +95 -0
  41. package/skills/ai-delivery-checkpoint-task/references/checkpoint-recovery.zh-CN.md +111 -0
  42. package/skills/ai-delivery-checkpoint-task/scripts/hook-event.mjs +131 -0
  43. package/skills/ai-delivery-checkpoint-task/scripts/task-state.mjs +1073 -0
  44. package/skills/ai-delivery-close-version/SKILL.md +33 -0
  45. package/skills/ai-delivery-close-version/agents/openai.yaml +4 -0
  46. package/skills/ai-delivery-close-version/assets/line-advance-record.yaml +12 -0
  47. package/skills/ai-delivery-close-version/assets/version-lineage.yaml +12 -0
  48. package/skills/ai-delivery-close-version/assets/version-manifest.yaml +20 -0
  49. package/skills/ai-delivery-close-version/references/version-closeout-contract.md +20 -0
  50. package/skills/ai-delivery-define-product/SKILL.md +76 -0
  51. package/skills/ai-delivery-define-product/agents/openai.yaml +4 -0
  52. package/skills/ai-delivery-define-product/assets/product-template/acceptance-criteria.csv +1 -0
  53. package/skills/ai-delivery-define-product/assets/product-template/discovery-baseline.md +19 -0
  54. package/skills/ai-delivery-define-product/assets/product-template/feature-detail.md +19 -0
  55. package/skills/ai-delivery-define-product/assets/product-template/feature-terminals.csv +1 -0
  56. package/skills/ai-delivery-define-product/assets/product-template/features.csv +1 -0
  57. package/skills/ai-delivery-define-product/assets/product-template/product-baseline.yaml +8 -0
  58. package/skills/ai-delivery-define-product/assets/product-template/product-manual.md +24 -0
  59. package/skills/ai-delivery-define-product/assets/product-template/terminals.csv +1 -0
  60. package/skills/ai-delivery-define-product/assets/product-template/user-stories.csv +1 -0
  61. package/skills/ai-delivery-define-product/references/product-contract.md +57 -0
  62. package/skills/ai-delivery-deploy-production/SKILL.md +45 -0
  63. package/skills/ai-delivery-deploy-production/agents/openai.yaml +4 -0
  64. package/skills/ai-delivery-deploy-production/references/deployment-contract.md +7 -0
  65. package/skills/ai-delivery-design-architecture/SKILL.md +67 -0
  66. package/skills/ai-delivery-design-architecture/agents/openai.yaml +4 -0
  67. package/skills/ai-delivery-design-architecture/assets/architecture-template/architecture-baseline.md +25 -0
  68. package/skills/ai-delivery-design-architecture/assets/architecture-template/architecture-gate.yaml +15 -0
  69. package/skills/ai-delivery-design-architecture/assets/architecture-template/prototype-architecture-validation.yaml +58 -0
  70. package/skills/ai-delivery-design-architecture/references/architecture-contract.md +32 -0
  71. package/skills/ai-delivery-design-experience/SKILL.md +71 -0
  72. package/skills/ai-delivery-design-experience/agents/openai.yaml +4 -0
  73. package/skills/ai-delivery-design-experience/assets/experience-template/canvas-catalog.csv +1 -0
  74. package/skills/ai-delivery-design-experience/assets/experience-template/canvas-pages.csv +1 -0
  75. package/skills/ai-delivery-design-experience/assets/experience-template/component-state-matrix.csv +1 -0
  76. package/skills/ai-delivery-design-experience/assets/experience-template/design-tokens.json +19 -0
  77. package/skills/ai-delivery-design-experience/assets/experience-template/experience-baseline.yaml +17 -0
  78. package/skills/ai-delivery-design-experience/assets/experience-template/experience-change-domains.csv +1 -0
  79. package/skills/ai-delivery-design-experience/assets/experience-template/experience-change-set.csv +1 -0
  80. package/skills/ai-delivery-design-experience/assets/experience-template/feature-screen-coverage.csv +1 -0
  81. package/skills/ai-delivery-design-experience/assets/experience-template/formal-ui-confirmation.yaml +33 -0
  82. package/skills/ai-delivery-design-experience/assets/experience-template/interaction-contract.csv +1 -0
  83. package/skills/ai-delivery-design-experience/assets/experience-template/low-fidelity-confirmation.yaml +32 -0
  84. package/skills/ai-delivery-design-experience/assets/experience-template/page-catalog.csv +1 -0
  85. package/skills/ai-delivery-design-experience/assets/experience-template/page-component-map.csv +1 -0
  86. package/skills/ai-delivery-design-experience/assets/experience-template/product-prototype-reconciliation.yaml +28 -0
  87. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-adjustment-log.csv +1 -0
  88. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-confirmation.yaml +99 -0
  89. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-file-terminals.csv +1 -0
  90. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-files.csv +1 -0
  91. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-impact-assessment.yaml +25 -0
  92. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-manifest.yaml +74 -0
  93. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-product-coverage.csv +1 -0
  94. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-review-comments.csv +1 -0
  95. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-review-decision.yaml +29 -0
  96. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-review-sessions.csv +1 -0
  97. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-set.yaml +22 -0
  98. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-tool-candidates.csv +1 -0
  99. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-tool-selection.yaml +22 -0
  100. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-traceability.csv +1 -0
  101. package/skills/ai-delivery-design-experience/assets/experience-template/requirement-change-set.csv +1 -0
  102. package/skills/ai-delivery-design-experience/assets/experience-template/screen-states.csv +1 -0
  103. package/skills/ai-delivery-design-experience/assets/experience-template/ui-framework-candidates.csv +1 -0
  104. package/skills/ai-delivery-design-experience/assets/experience-template/ui-framework-selection.yaml +18 -0
  105. package/skills/ai-delivery-design-experience/assets/experience-template/ui-handoff.yaml +44 -0
  106. package/skills/ai-delivery-design-experience/assets/experience-template/visual-direction.yaml +16 -0
  107. package/skills/ai-delivery-design-experience/assets/experience-template/visual-system-confirmation.yaml +31 -0
  108. package/skills/ai-delivery-design-experience/references/experience-contract.md +82 -0
  109. package/skills/ai-delivery-design-experience/references/prototype-management-contract.md +68 -0
  110. package/skills/ai-delivery-design-tests/SKILL.md +56 -0
  111. package/skills/ai-delivery-design-tests/agents/openai.yaml +4 -0
  112. package/skills/ai-delivery-design-tests/assets/test-design-template/iteration-test-contract.md +19 -0
  113. package/skills/ai-delivery-design-tests/assets/test-design-template/prototype-test-scope.csv +1 -0
  114. package/skills/ai-delivery-design-tests/assets/test-design-template/prototype-test-validation.yaml +73 -0
  115. package/skills/ai-delivery-design-tests/references/test-design-contract.md +31 -0
  116. package/skills/ai-delivery-develop-iteration/SKILL.md +66 -0
  117. package/skills/ai-delivery-develop-iteration/agents/openai.yaml +4 -0
  118. package/skills/ai-delivery-develop-iteration/assets/development-template/task-experience-change-traceability.csv +1 -0
  119. package/skills/ai-delivery-develop-iteration/assets/development-template/task-prototype-traceability.csv +1 -0
  120. package/skills/ai-delivery-develop-iteration/references/development-contract.md +39 -0
  121. package/skills/ai-delivery-evolve-workflow/SKILL.md +57 -0
  122. package/skills/ai-delivery-evolve-workflow/agents/openai.yaml +4 -0
  123. package/skills/ai-delivery-evolve-workflow/references/evolution-contract.md +42 -0
  124. package/skills/ai-delivery-execute-test/SKILL.md +34 -0
  125. package/skills/ai-delivery-execute-test/agents/openai.yaml +4 -0
  126. package/skills/ai-delivery-execute-test/references/test-execution-contract.md +7 -0
  127. package/skills/ai-delivery-execute-work-package/SKILL.md +53 -0
  128. package/skills/ai-delivery-execute-work-package/agents/openai.yaml +4 -0
  129. package/skills/ai-delivery-execute-work-package/references/work-package-contract.md +20 -0
  130. package/skills/ai-delivery-manage-git/SKILL.md +60 -0
  131. package/skills/ai-delivery-manage-git/agents/openai.yaml +4 -0
  132. package/skills/ai-delivery-manage-git/references/git-policy.md +42 -0
  133. package/skills/ai-delivery-manage-standards/SKILL.md +44 -0
  134. package/skills/ai-delivery-manage-standards/agents/openai.yaml +4 -0
  135. package/skills/ai-delivery-manage-standards/assets/standards-template/standard.md +25 -0
  136. package/skills/ai-delivery-manage-standards/references/standards-contract.md +20 -0
  137. package/skills/ai-delivery-orchestrate/SKILL.md +105 -0
  138. package/skills/ai-delivery-orchestrate/agents/openai.yaml +4 -0
  139. package/skills/ai-delivery-orchestrate/assets/project-template/.codex/config.toml +92 -0
  140. package/skills/ai-delivery-orchestrate/assets/project-template/.gitattributes +6 -0
  141. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/delivery/artifact-registry.yaml +3 -0
  142. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/delivery/project.yaml +6 -0
  143. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/delivery/standards-baseline.yaml +8 -0
  144. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/delivery/workflow-state.yaml +34 -0
  145. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/evolution/decisions/.gitkeep +0 -0
  146. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/evolution/experiments/.gitkeep +0 -0
  147. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/evolution/feedback/.gitkeep +0 -0
  148. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/evolution/index.yaml +7 -0
  149. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/evolution/promotion/.gitkeep +0 -0
  150. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/evolution/proposals/.gitkeep +0 -0
  151. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/extensions/.gitkeep +0 -0
  152. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/standards/STD-ARTIFACT-PATH-001.md +47 -0
  153. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/standards/STD-DOC-LANG-001.md +62 -0
  154. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/standards/STD-TEST-LOCATION-001.md +23 -0
  155. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/standards/index.yaml +36 -0
  156. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/bootstrap/bootstrap.mjs +33 -0
  157. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/evolution/evolve.mjs +15 -0
  158. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/hooks/hook-event.mjs +131 -0
  159. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/state/state.mjs +21 -0
  160. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/public/app.js +546 -0
  161. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/public/index.html +199 -0
  162. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/public/styles.css +529 -0
  163. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/server.mjs +780 -0
  164. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/start.cmd +4 -0
  165. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/start.ps1 +2 -0
  166. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/start.sh +4 -0
  167. package/skills/ai-delivery-orchestrate/assets/project-template/AGENTS.md +44 -0
  168. package/skills/ai-delivery-orchestrate/references/formal-state-contract.md +64 -0
  169. package/skills/ai-delivery-orchestrate/references/workflow-model.md +29 -0
  170. package/skills/ai-delivery-orchestrate-release/SKILL.md +33 -0
  171. package/skills/ai-delivery-orchestrate-release/agents/openai.yaml +4 -0
  172. package/skills/ai-delivery-orchestrate-release/assets/release-request.yaml +9 -0
  173. package/skills/ai-delivery-orchestrate-release/assets/version-selection.yaml +16 -0
  174. package/skills/ai-delivery-orchestrate-release/references/release-workflow-contract.md +11 -0
  175. package/skills/ai-delivery-plan-iteration/SKILL.md +57 -0
  176. package/skills/ai-delivery-plan-iteration/agents/openai.yaml +4 -0
  177. package/skills/ai-delivery-plan-iteration/assets/planning-template/backlog.csv +1 -0
  178. package/skills/ai-delivery-plan-iteration/assets/planning-template/slice-dependencies.csv +1 -0
  179. package/skills/ai-delivery-plan-iteration/assets/planning-template/slice-experience-change-traceability.csv +1 -0
  180. package/skills/ai-delivery-plan-iteration/assets/planning-template/slice-prototype-traceability.csv +1 -0
  181. package/skills/ai-delivery-plan-iteration/assets/planning-template/slice-sources.csv +1 -0
  182. package/skills/ai-delivery-plan-iteration/assets/planning-template/vertical-slices.csv +1 -0
  183. package/skills/ai-delivery-plan-iteration/references/planning-contract.md +40 -0
  184. package/skills/ai-delivery-prepare-platform/SKILL.md +49 -0
  185. package/skills/ai-delivery-prepare-platform/agents/openai.yaml +4 -0
  186. package/skills/ai-delivery-prepare-platform/assets/platform-template/environment-ledger.csv +1 -0
  187. package/skills/ai-delivery-prepare-platform/references/platform-contract.md +18 -0
  188. package/skills/ai-delivery-prepare-release/SKILL.md +47 -0
  189. package/skills/ai-delivery-prepare-release/agents/openai.yaml +4 -0
  190. package/skills/ai-delivery-prepare-release/assets/release-template/delivery-items.csv +1 -0
  191. package/skills/ai-delivery-prepare-release/assets/release-template/release-manifest.yaml +18 -0
  192. package/skills/ai-delivery-prepare-release/references/release-contract.md +21 -0
  193. package/skills/ai-delivery-review-change/SKILL.md +40 -0
  194. package/skills/ai-delivery-review-change/agents/openai.yaml +4 -0
  195. package/skills/ai-delivery-review-change/references/review-contract.md +20 -0
  196. package/skills/ai-delivery-triage-failure/SKILL.md +45 -0
  197. package/skills/ai-delivery-triage-failure/agents/openai.yaml +4 -0
  198. package/skills/ai-delivery-triage-failure/references/failure-routing-contract.md +13 -0
  199. package/skills/ai-delivery-validate-artifacts/SKILL.md +37 -0
  200. package/skills/ai-delivery-validate-artifacts/agents/openai.yaml +4 -0
  201. package/skills/ai-delivery-validate-artifacts/references/artifact-contract.md +44 -0
  202. package/skills/ai-delivery-verify-candidate/SKILL.md +56 -0
  203. package/skills/ai-delivery-verify-candidate/agents/openai.yaml +4 -0
  204. package/skills/ai-delivery-verify-candidate/assets/verification-template/defect-artifacts.csv +1 -0
  205. package/skills/ai-delivery-verify-candidate/assets/verification-template/defects.csv +1 -0
  206. package/skills/ai-delivery-verify-candidate/assets/verification-template/test-work-packages.csv +1 -0
  207. package/skills/ai-delivery-verify-candidate/references/candidate-verification-contract.md +23 -0
  208. package/skills/ai-delivery-verify-production/SKILL.md +50 -0
  209. package/skills/ai-delivery-verify-production/agents/openai.yaml +4 -0
  210. package/skills/ai-delivery-verify-production/assets/production-verification-template/production-verification.md +15 -0
  211. package/skills/ai-delivery-verify-production/references/production-verification-contract.md +14 -0
@@ -0,0 +1,154 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import { parse as parseYaml } from "yaml";
5
+
6
+ const ALLOWED_FRONTMATTER_KEYS = new Set([
7
+ "name",
8
+ "description",
9
+ "license",
10
+ "allowed-tools",
11
+ "metadata",
12
+ ]);
13
+ const REQUIRED_INTERFACE_FIELDS = ["display_name", "short_description", "default_prompt"];
14
+
15
+ function readText(file, errors, relative) {
16
+ if (!fs.existsSync(file)) {
17
+ errors.push(`${relative} is required`);
18
+ return null;
19
+ }
20
+ return fs.readFileSync(file, "utf8");
21
+ }
22
+
23
+ function parseMapping(text, errors, relative) {
24
+ try {
25
+ const value = parseYaml(text);
26
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
27
+ errors.push(`${relative} must contain a YAML mapping`);
28
+ return null;
29
+ }
30
+ return value;
31
+ } catch (error) {
32
+ errors.push(`${relative} contains invalid YAML: ${error.message}`);
33
+ return null;
34
+ }
35
+ }
36
+
37
+ function validateSkill(skillDirectory, repositoryRoot, documents) {
38
+ const errors = [];
39
+ const directoryName = path.basename(skillDirectory);
40
+ const relativeSkill = path.relative(repositoryRoot, skillDirectory).replaceAll("\\", "/");
41
+ const skillFile = path.join(skillDirectory, "SKILL.md");
42
+ const skillText = readText(skillFile, errors, `${relativeSkill}/SKILL.md`);
43
+
44
+ if (skillText !== null) {
45
+ const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(skillText);
46
+ if (!match) {
47
+ errors.push(`${relativeSkill}/SKILL.md has invalid YAML frontmatter delimiters`);
48
+ } else {
49
+ const frontmatter = parseMapping(match[1], errors, `${relativeSkill}/SKILL.md frontmatter`);
50
+ if (frontmatter) {
51
+ for (const key of Object.keys(frontmatter)) {
52
+ if (!ALLOWED_FRONTMATTER_KEYS.has(key)) {
53
+ errors.push(`${relativeSkill}/SKILL.md has unexpected frontmatter key: ${key}`);
54
+ }
55
+ }
56
+
57
+ const name = typeof frontmatter.name === "string" ? frontmatter.name.trim() : "";
58
+ const description = typeof frontmatter.description === "string"
59
+ ? frontmatter.description.trim()
60
+ : "";
61
+
62
+ if (!name) errors.push(`${relativeSkill}/SKILL.md is missing non-empty name`);
63
+ if (!description) errors.push(`${relativeSkill}/SKILL.md is missing non-empty description`);
64
+ if (name && !/^[a-z0-9-]+$/.test(name)) {
65
+ errors.push(`${relativeSkill}/SKILL.md name must use lowercase hyphen-case`);
66
+ }
67
+ if (name && (name.startsWith("-") || name.endsWith("-"))) {
68
+ errors.push(`${relativeSkill}/SKILL.md name must not start or end with a hyphen`);
69
+ }
70
+ if ((name && name.includes("--")) || directoryName.includes("--")) {
71
+ errors.push(`${relativeSkill}/SKILL.md name must not contain consecutive hyphens`);
72
+ }
73
+ if (name && name.length > 64) {
74
+ errors.push(`${relativeSkill}/SKILL.md name must not exceed 64 characters`);
75
+ }
76
+ if (name && name !== directoryName) {
77
+ errors.push(`${relativeSkill}/SKILL.md frontmatter name must match directory name`);
78
+ }
79
+ if (description.includes("<") || description.includes(">")) {
80
+ errors.push(`${relativeSkill}/SKILL.md description must not contain angle brackets`);
81
+ }
82
+ if (description.length > 1024) {
83
+ errors.push(`${relativeSkill}/SKILL.md description must not exceed 1024 characters`);
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ const metadataRelative = `${relativeSkill}/agents/openai.yaml`;
90
+ const metadataText = readText(path.join(skillDirectory, "agents", "openai.yaml"), errors, metadataRelative);
91
+ if (metadataText !== null) {
92
+ const metadata = parseMapping(metadataText, errors, metadataRelative);
93
+ if (metadata) {
94
+ const interfaceMetadata = metadata.interface;
95
+ if (!interfaceMetadata || typeof interfaceMetadata !== "object" || Array.isArray(interfaceMetadata)) {
96
+ errors.push(`${metadataRelative} must contain an interface mapping`);
97
+ } else {
98
+ for (const field of REQUIRED_INTERFACE_FIELDS) {
99
+ if (typeof interfaceMetadata[field] !== "string" || !interfaceMetadata[field].trim()) {
100
+ errors.push(`${metadataRelative} interface.${field} must be a non-empty string`);
101
+ }
102
+ }
103
+ if (typeof interfaceMetadata.default_prompt === "string"
104
+ && !interfaceMetadata.default_prompt.includes(`$${directoryName}`)) {
105
+ errors.push(`${metadataRelative} default_prompt must reference $${directoryName}`);
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ if (!documents.manual.includes(`$${directoryName}`)) {
112
+ errors.push(`docs/PROJECT-MANUAL.zh-CN.md must reference $${directoryName}`);
113
+ }
114
+ if (!documents.fileReference.includes(directoryName)) {
115
+ errors.push(`docs/FILE-REFERENCE.zh-CN.md must reference ${directoryName}`);
116
+ }
117
+
118
+ return errors;
119
+ }
120
+
121
+ export function validateSkillRepository(repositoryRoot, { expectedSkillCount = 25 } = {}) {
122
+ const root = path.resolve(repositoryRoot);
123
+ const errors = [];
124
+ const skillsRoot = path.join(root, "skills");
125
+ const manualPath = path.join(root, "docs", "PROJECT-MANUAL.zh-CN.md");
126
+ const fileReferencePath = path.join(root, "docs", "FILE-REFERENCE.zh-CN.md");
127
+ const documents = {
128
+ manual: readText(manualPath, errors, "docs/PROJECT-MANUAL.zh-CN.md") ?? "",
129
+ fileReference: readText(fileReferencePath, errors, "docs/FILE-REFERENCE.zh-CN.md") ?? "",
130
+ };
131
+
132
+ if (!fs.existsSync(skillsRoot)) {
133
+ errors.push("skills/ is required");
134
+ return { valid: false, skillCount: 0, errors };
135
+ }
136
+
137
+ const skillDirectories = fs.readdirSync(skillsRoot, { withFileTypes: true })
138
+ .filter((entry) => entry.isDirectory())
139
+ .map((entry) => path.join(skillsRoot, entry.name))
140
+ .sort((left, right) => left.localeCompare(right));
141
+
142
+ if (skillDirectories.length !== expectedSkillCount) {
143
+ errors.push(`skills/ must contain exactly ${expectedSkillCount} directories; found ${skillDirectories.length}`);
144
+ }
145
+ for (const skillDirectory of skillDirectories) {
146
+ errors.push(...validateSkill(skillDirectory, root, documents));
147
+ }
148
+
149
+ return {
150
+ valid: errors.length === 0,
151
+ skillCount: skillDirectories.length,
152
+ errors,
153
+ };
154
+ }
@@ -0,0 +1,97 @@
1
+ import { parse } from "smol-toml";
2
+
3
+ export const HOOK_EVENTS = [
4
+ "SessionStart",
5
+ "UserPromptSubmit",
6
+ "PreToolUse",
7
+ "PermissionRequest",
8
+ "PostToolUse",
9
+ "PreCompact",
10
+ "PostCompact",
11
+ "SubagentStart",
12
+ "SubagentStop",
13
+ "Stop",
14
+ ];
15
+
16
+ const HOOK_COMMAND = "node .workflow/tools/hooks/hook-event.mjs";
17
+ const LEGACY_HOOK_COMMAND = "node .ai-delivery/hooks/hook-event.mjs";
18
+
19
+ function hookBlock(event, newline) {
20
+ return [
21
+ `[[hooks.${event}]]`,
22
+ 'matcher = ""',
23
+ `[[hooks.${event}.hooks]]`,
24
+ 'type = "command"',
25
+ `command = "${HOOK_COMMAND}"`,
26
+ `commandWindows = "${HOOK_COMMAND}"`,
27
+ "timeout = 5",
28
+ "async = false",
29
+ "",
30
+ ].join(newline);
31
+ }
32
+
33
+ function hasDeliveryHandler(config, event) {
34
+ const matchers = config.hooks?.[event];
35
+ if (!Array.isArray(matchers)) return false;
36
+ return matchers.some((matcher) => Array.isArray(matcher.hooks) && matcher.hooks.some((handler) =>
37
+ handler?.command === HOOK_COMMAND || handler?.commandWindows === HOOK_COMMAND));
38
+ }
39
+
40
+ function replaceFeatureHooksFalse(text) {
41
+ const header = /^\s*\[features\]\s*$/m.exec(text);
42
+ if (!header) throw new Error("Could not locate [features] for forced hooks replacement.");
43
+ const sectionStart = header.index + header[0].length;
44
+ const nextHeader = /^\s*\[/m.exec(text.slice(sectionStart));
45
+ const sectionEnd = nextHeader ? sectionStart + nextHeader.index : text.length;
46
+ const section = text.slice(sectionStart, sectionEnd);
47
+ const assignment = /^(\s*hooks\s*=\s*)false(\s*(?:#.*)?)$/m;
48
+ if (!assignment.test(section)) throw new Error("Could not locate features.hooks = false for forced replacement.");
49
+ return `${text.slice(0, sectionStart)}${section.replace(assignment, "$1true$2")}${text.slice(sectionEnd)}`;
50
+ }
51
+
52
+ export function mergeHookConfig(existing, { force = false } = {}) {
53
+ const newline = existing.includes("\r\n") ? "\r\n" : "\n";
54
+ let text = existing.trimEnd();
55
+ const actions = [];
56
+ if (text.includes(LEGACY_HOOK_COMMAND)) {
57
+ text = text.replaceAll(LEGACY_HOOK_COMMAND, HOOK_COMMAND);
58
+ actions.push("migrate AI delivery Hook command into .workflow/tools");
59
+ }
60
+ let config = text ? parse(text) : {};
61
+
62
+ if (config.features?.hooks === false && !force) {
63
+ throw new Error('Existing .codex/config.toml sets features.hooks = false; enable it or rerun with --force.');
64
+ }
65
+
66
+ if (!config.features) {
67
+ text += `${text ? newline.repeat(2) : ""}[features]${newline}hooks = true`;
68
+ actions.push("enable Codex hooks feature");
69
+ } else if (config.features.hooks === undefined) {
70
+ const section = /^\s*\[features\]\s*$/m;
71
+ if (!section.test(text)) throw new Error("Could not locate the parsed [features] section.");
72
+ text = text.replace(section, (line) => `${line}${newline}hooks = true`);
73
+ actions.push("enable Codex hooks feature");
74
+ } else if (config.features.hooks === false) {
75
+ text = replaceFeatureHooksFalse(text);
76
+ actions.push("replace features.hooks = false");
77
+ }
78
+
79
+ config = parse(text);
80
+ for (const event of HOOK_EVENTS) {
81
+ if (hasDeliveryHandler(config, event)) continue;
82
+ text += `${newline.repeat(2)}${hookBlock(event, newline).trimEnd()}`;
83
+ actions.push(`register ${event} Hook`);
84
+ config = parse(text);
85
+ }
86
+
87
+ parse(`${text}${newline}`);
88
+ return { text: `${text}${newline}`, actions };
89
+ }
90
+
91
+ export function inspectHookConfig(text) {
92
+ const config = parse(text);
93
+ return {
94
+ hooksEnabled: config.features?.hooks === true,
95
+ registeredEvents: HOOK_EVENTS.filter((event) => hasDeliveryHandler(config, event)),
96
+ };
97
+ }
@@ -0,0 +1,302 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+ import { parse as parseYaml, stringify as stringifyYaml } from "./yaml-runtime.mjs";
5
+
6
+ export const WORKSPACE_CONFIG = path.join(".workflow", "config", "workspace.yaml");
7
+ export const LEGACY_WORKSPACE_CONFIG = path.join(".ai-delivery", "workspace.yaml");
8
+
9
+ function toPosix(value) {
10
+ return value.replaceAll("\\", "/");
11
+ }
12
+
13
+ function samePath(left, right) {
14
+ return path.resolve(left).toLowerCase() === path.resolve(right).toLowerCase();
15
+ }
16
+
17
+ function canonicalRemote(value) {
18
+ if (!value) return null;
19
+ const text = String(value).trim();
20
+ if (path.isAbsolute(text) || text.startsWith(".") || fs.existsSync(text)) {
21
+ return toPosix(path.resolve(text)).replace(/\/$/, "").toLowerCase();
22
+ }
23
+ return text.replaceAll("\\", "/").replace(/\/$/, "");
24
+ }
25
+
26
+ function runGit(cwd, args) {
27
+ const result = spawnSync("git", args, { cwd, encoding: "utf8", windowsHide: true });
28
+ if (result.status !== 0) {
29
+ throw new Error(`Git command failed in ${cwd}: git ${args.join(" ")}: ${result.stderr.trim()}`);
30
+ }
31
+ return result.stdout.trim();
32
+ }
33
+
34
+ export function gitTopLevel(directory) {
35
+ const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {
36
+ cwd: directory,
37
+ encoding: "utf8",
38
+ windowsHide: true,
39
+ });
40
+ return result.status === 0 ? path.resolve(result.stdout.trim()) : null;
41
+ }
42
+
43
+ export function gitCommonDirectory(directory) {
44
+ const result = spawnSync("git", ["rev-parse", "--git-common-dir"], {
45
+ cwd: directory,
46
+ encoding: "utf8",
47
+ windowsHide: true,
48
+ });
49
+ return result.status === 0 ? path.resolve(directory, result.stdout.trim()) : null;
50
+ }
51
+
52
+ export function normalizeWorkspaceId(value, workspaceRoot) {
53
+ const explicit = value !== undefined && value !== null && String(value).trim() !== "";
54
+ const candidate = explicit
55
+ ? String(value).trim()
56
+ : path.basename(path.resolve(workspaceRoot)).toLowerCase()
57
+ .replace(/[^a-z0-9._-]+/g, "-")
58
+ .replace(/^-+|-+$/g, "");
59
+ if (!/^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/.test(candidate)) {
60
+ throw new Error("Workspace ID must be 1-64 lowercase ASCII letters, digits, dots, underscores, or hyphens.");
61
+ }
62
+ return candidate;
63
+ }
64
+
65
+ function gitBranch(directory) {
66
+ const result = spawnSync("git", ["branch", "--show-current"], {
67
+ cwd: directory, encoding: "utf8", windowsHide: true,
68
+ });
69
+ return result.status === 0 ? result.stdout.trim() : null;
70
+ }
71
+
72
+ function gitHasCommit(directory) {
73
+ return spawnSync("git", ["rev-parse", "--verify", "HEAD"], {
74
+ cwd: directory, encoding: "utf8", windowsHide: true,
75
+ }).status === 0;
76
+ }
77
+
78
+ function remoteHeads(remote) {
79
+ if (!remote) return new Set();
80
+ const result = spawnSync("git", ["ls-remote", "--heads", remote], {
81
+ encoding: "utf8", windowsHide: true,
82
+ });
83
+ if (result.status !== 0) throw new Error(`Cannot inspect remote ${remote}: ${result.stderr.trim()}`);
84
+ return new Set(result.stdout.split(/\r?\n/).filter(Boolean).map((line) => line.split(/\s+/)[1]?.replace("refs/heads/", "")));
85
+ }
86
+
87
+ function originUrl(directory) {
88
+ const result = spawnSync("git", ["remote", "get-url", "origin"], {
89
+ cwd: directory, encoding: "utf8", windowsHide: true,
90
+ });
91
+ return result.status === 0 ? result.stdout.trim() || null : null;
92
+ }
93
+
94
+ function ensureOrigin(directory, remote) {
95
+ if (!remote) return originUrl(directory);
96
+ const existing = originUrl(directory);
97
+ if (existing && canonicalRemote(existing) !== canonicalRemote(remote)) {
98
+ throw new Error(`Repository origin conflicts with configured remote: ${existing} != ${remote}`);
99
+ }
100
+ if (!existing) runGit(directory, ["remote", "add", "origin", remote]);
101
+ return remote;
102
+ }
103
+
104
+ function initializeWorkflowRepository(workspaceRoot, branch, remote, heads, actions, dryRun) {
105
+ const gitEntry = path.join(workspaceRoot, ".git");
106
+ if (!fs.existsSync(gitEntry)) {
107
+ actions.push(`initialize workflow Git repository on ${branch}`);
108
+ if (dryRun) return;
109
+ runGit(workspaceRoot, ["init", "-b", branch]);
110
+ } else {
111
+ if (!fs.statSync(gitEntry).isDirectory()) throw new Error("Workflow repository must use an independent .git directory, not a linked worktree.");
112
+ const root = gitTopLevel(workspaceRoot);
113
+ if (!root || !samePath(root, workspaceRoot)) throw new Error("Workspace root must be its own Git repository.");
114
+ const current = gitBranch(workspaceRoot);
115
+ if (current !== branch) {
116
+ if (gitHasCommit(workspaceRoot)) {
117
+ throw new Error(`Existing workflow repository must already use ${branch}; automatic migration is not supported.`);
118
+ }
119
+ if (!dryRun) runGit(workspaceRoot, ["symbolic-ref", "HEAD", `refs/heads/${branch}`]);
120
+ }
121
+ }
122
+ if (dryRun || !remote) return;
123
+ ensureOrigin(workspaceRoot, remote);
124
+ if (heads.has(branch)) {
125
+ runGit(workspaceRoot, ["fetch", "origin", `refs/heads/${branch}:refs/remotes/origin/${branch}`]);
126
+ if (!gitHasCommit(workspaceRoot)) {
127
+ runGit(workspaceRoot, ["checkout", "-B", branch, "--track", `origin/${branch}`]);
128
+ } else {
129
+ runGit(workspaceRoot, ["branch", "--set-upstream-to", `origin/${branch}`, branch]);
130
+ }
131
+ }
132
+ }
133
+
134
+ function initializeCodeRepository(workspaceRoot, code, remote, heads, actions, dryRun) {
135
+ if (!fs.existsSync(code.absolute)) {
136
+ if (remote && heads.size) {
137
+ actions.push(`clone code repository main into ${code.relative}/`);
138
+ if (!dryRun) runGit(workspaceRoot, ["clone", "--branch", "main", remote, code.relative]);
139
+ } else {
140
+ actions.push(`create code repository ${code.relative}/ on main`);
141
+ if (!dryRun) {
142
+ fs.mkdirSync(code.absolute, { recursive: true });
143
+ runGit(code.absolute, ["init", "-b", "main"]);
144
+ ensureOrigin(code.absolute, remote);
145
+ }
146
+ }
147
+ return;
148
+ }
149
+ if (!fs.statSync(code.absolute).isDirectory()) throw new Error(`Code path is not a directory: ${code.absolute}`);
150
+ const entries = fs.readdirSync(code.absolute);
151
+ const nestedRoot = gitTopLevel(code.absolute);
152
+ if (!nestedRoot || !samePath(nestedRoot, code.absolute)) {
153
+ if (entries.length) throw new Error(`Code directory is non-empty and not a Git repository: ${code.absolute}`);
154
+ actions.push(`initialize existing empty code directory ${code.relative}/ on main`);
155
+ if (!dryRun) {
156
+ runGit(code.absolute, ["init", "-b", "main"]);
157
+ ensureOrigin(code.absolute, remote);
158
+ }
159
+ return;
160
+ }
161
+ if (!dryRun) {
162
+ if (!fs.statSync(path.join(code.absolute, ".git")).isDirectory()) {
163
+ throw new Error("Code repository must use an independent .git directory, not a linked worktree.");
164
+ }
165
+ ensureOrigin(code.absolute, remote);
166
+ }
167
+ }
168
+
169
+ export function validateCodePath(workspaceRoot, codePath = "code") {
170
+ if (!codePath || path.isAbsolute(codePath)) throw new Error("Code path must be a relative direct child directory.");
171
+ const normalized = toPosix(path.normalize(codePath)).replace(/^\.\//, "").replace(/\/$/, "");
172
+ if (!normalized || normalized.includes("/") || normalized === "." || normalized === "..") {
173
+ throw new Error("Code path must be a relative direct child directory.");
174
+ }
175
+ if (new Set([".git", ".workflow", ".ai-delivery", ".agents", ".codex"]).has(normalized)) {
176
+ throw new Error(`Reserved code path: ${normalized}`);
177
+ }
178
+ const absolute = path.resolve(workspaceRoot, normalized);
179
+ const relative = path.relative(path.resolve(workspaceRoot), absolute);
180
+ if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error("Code path escapes the workspace.");
181
+ if (fs.existsSync(absolute) && fs.lstatSync(absolute).isSymbolicLink()) {
182
+ throw new Error(`Code path must not be a symbolic link: ${absolute}`);
183
+ }
184
+ return { relative: normalized, absolute };
185
+ }
186
+
187
+ export function initializeGitWorkspace(workspaceRoot, options = {}) {
188
+ const {
189
+ codePath = "code", codeId = "app", codeRemote = null, workflowRemote = null,
190
+ sharedRemote = null, workspaceId: requestedWorkspaceId = null, dryRun = false,
191
+ } = options;
192
+ if (sharedRemote && (codeRemote || workflowRemote)) {
193
+ throw new Error("--shared-remote cannot be combined with --code-remote or --workflow-remote.");
194
+ }
195
+ const workspaceId = normalizeWorkspaceId(requestedWorkspaceId, workspaceRoot);
196
+ const resolvedWorkflowRemote = sharedRemote || workflowRemote || null;
197
+ const resolvedCodeRemote = sharedRemote || codeRemote || null;
198
+ const workflowBranch = `workflow/${workspaceId}`;
199
+ const code = validateCodePath(workspaceRoot, codePath);
200
+ const actions = [];
201
+ const workflowHeads = remoteHeads(resolvedWorkflowRemote);
202
+ const codeHeads = canonicalRemote(resolvedWorkflowRemote) === canonicalRemote(resolvedCodeRemote)
203
+ ? workflowHeads : remoteHeads(resolvedCodeRemote);
204
+ if (resolvedCodeRemote && codeHeads.size && !codeHeads.has("main")) {
205
+ throw new Error(`Code remote is non-empty but does not contain required main branch: ${resolvedCodeRemote}`);
206
+ }
207
+
208
+ initializeWorkflowRepository(workspaceRoot, workflowBranch, resolvedWorkflowRemote, workflowHeads, actions, dryRun);
209
+ initializeCodeRepository(workspaceRoot, code, resolvedCodeRemote, codeHeads, actions, dryRun);
210
+
211
+ if (!dryRun) {
212
+ const workflowGit = gitTopLevel(workspaceRoot);
213
+ const codeGit = gitTopLevel(code.absolute);
214
+ if (!workflowGit || !codeGit || samePath(workflowGit, codeGit)) {
215
+ throw new Error("Workflow and code repositories must have different Git roots.");
216
+ }
217
+ const workflowCommon = gitCommonDirectory(workspaceRoot);
218
+ const codeCommon = gitCommonDirectory(code.absolute);
219
+ if (!workflowCommon || !codeCommon || samePath(workflowCommon, codeCommon)) {
220
+ throw new Error("Workflow and code repositories must use independent Git common directories.");
221
+ }
222
+ }
223
+ const actualWorkflowRemote = dryRun ? resolvedWorkflowRemote : originUrl(workspaceRoot);
224
+ const actualCodeRemote = dryRun ? resolvedCodeRemote : originUrl(code.absolute);
225
+
226
+ return {
227
+ actions,
228
+ configuration: {
229
+ schema_version: 2,
230
+ workspace_id: workspaceId,
231
+ repository_layout: "independent-git-roots",
232
+ shared_remote: Boolean(actualWorkflowRemote && actualCodeRemote
233
+ && canonicalRemote(actualWorkflowRemote) === canonicalRemote(actualCodeRemote)),
234
+ workflow_repository: { path: ".", remote: actualWorkflowRemote, branch: workflowBranch },
235
+ code_repository: {
236
+ repo_id: codeId,
237
+ path: code.relative,
238
+ remote: actualCodeRemote,
239
+ default_branch: "main",
240
+ },
241
+ },
242
+ };
243
+ }
244
+
245
+ export function readWorkspaceConfiguration(workspaceRoot, options = {}) {
246
+ const file = path.join(workspaceRoot, options.configurationPath || WORKSPACE_CONFIG);
247
+ if (!fs.existsSync(file)) {
248
+ if (options.optional) return null;
249
+ throw new Error(`Workspace configuration is missing: ${file}`);
250
+ }
251
+ const configuration = parseYaml(fs.readFileSync(file, "utf8"));
252
+ if (configuration?.schema_version !== 2
253
+ || configuration?.repository_layout !== "independent-git-roots"
254
+ || !configuration?.workspace_id
255
+ || configuration?.workflow_repository?.branch !== `workflow/${configuration.workspace_id}`
256
+ || !configuration?.code_repository?.repo_id
257
+ || !configuration?.code_repository?.path) {
258
+ throw new Error(`Workspace configuration is invalid: ${file}`);
259
+ }
260
+ const code = validateCodePath(workspaceRoot, configuration.code_repository.path);
261
+ return { file, configuration, codeRoot: code.absolute };
262
+ }
263
+
264
+ export function workspaceConfigurationText(configuration) {
265
+ return stringifyYaml(configuration, { lineWidth: 0 });
266
+ }
267
+
268
+ export function inspectGitRepository(repositoryRoot) {
269
+ const topLevel = gitTopLevel(repositoryRoot);
270
+ if (!topLevel || !samePath(topLevel, repositoryRoot)) return { repository: false };
271
+ const run = (args) => spawnSync("git", args, { cwd: repositoryRoot, encoding: "utf8", windowsHide: true });
272
+ const branch = run(["branch", "--show-current"]);
273
+ const head = run(["rev-parse", "HEAD"]);
274
+ const status = run(["status", "--porcelain=v1"]);
275
+ const upstream = run(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]);
276
+ let ahead = null;
277
+ let behind = null;
278
+ if (upstream.status === 0) {
279
+ const divergence = run(["rev-list", "--left-right", "--count", "HEAD...@{upstream}"]);
280
+ if (divergence.status === 0) {
281
+ const [aheadText, behindText] = divergence.stdout.trim().split(/\s+/);
282
+ ahead = Number(aheadText);
283
+ behind = Number(behindText);
284
+ }
285
+ }
286
+ const dirtyPaths = status.stdout.split(/\r?\n/).filter(Boolean).map((line) => toPosix(line.slice(3)));
287
+ return {
288
+ repository: true,
289
+ root: toPosix(topLevel),
290
+ branch: branch.stdout.trim() || "main",
291
+ head: head.status === 0 ? head.stdout.trim() : null,
292
+ dirty_paths: dirtyPaths,
293
+ upstream: upstream.status === 0 ? upstream.stdout.trim() : null,
294
+ ahead,
295
+ behind,
296
+ sync_status: dirtyPaths.length
297
+ ? "uncommitted"
298
+ : ahead > 0 ? "unpushed"
299
+ : behind > 0 ? "behind"
300
+ : upstream.status === 0 ? "synchronized" : "no-upstream",
301
+ };
302
+ }
@@ -0,0 +1,18 @@
1
+ let yamlModule;
2
+
3
+ try {
4
+ yamlModule = await import("yaml");
5
+ } catch (packageError) {
6
+ try {
7
+ yamlModule = await import(new URL("../state/vendor/yaml/dist/index.js", import.meta.url));
8
+ } catch {
9
+ throw packageError;
10
+ }
11
+ }
12
+
13
+ const yaml = yamlModule.default || yamlModule;
14
+
15
+ export const parse = yaml.parse;
16
+ export const parseDocument = yaml.parseDocument;
17
+ export const isSeq = yaml.isSeq;
18
+ export const stringify = yaml.stringify;
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "ai-delivery-workflow",
3
+ "version": "0.2.2",
4
+ "description": "Project-scoped Codex skills and hooks for artifact-driven AI software delivery",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://gitee.com/hzr1348/work-flow.git"
9
+ },
10
+ "homepage": "https://gitee.com/hzr1348/work-flow",
11
+ "publishConfig": {
12
+ "access": "public",
13
+ "registry": "https://registry.npmjs.org/"
14
+ },
15
+ "bin": {
16
+ "ai-delivery": "bin/ai-delivery.mjs"
17
+ },
18
+ "files": [
19
+ "bin",
20
+ "docs",
21
+ "lib",
22
+ "skills"
23
+ ],
24
+ "scripts": {
25
+ "test": "node --test verification/*.test.mjs",
26
+ "skills:validate": "node bin/validate-skills.mjs",
27
+ "audit:generate": "node audit/generate-all.mjs",
28
+ "audit:verify": "node verification/audit/verify-audit.mjs",
29
+ "pack:check": "npm pack --dry-run"
30
+ },
31
+ "dependencies": {
32
+ "smol-toml": "1.7.0",
33
+ "yaml": "2.8.1"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "license": "MIT"
39
+ }
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: ai-delivery-assemble-release
3
+ description: Calculate a selected release target's continuous cumulative version closure, validate immutable version-ready tags and Git ancestry, and aggregate checksummed cross-version delivery materials into a release candidate. Use during R01-R04 after an explicit release request, including multi-version releases and same-version redeploys.
4
+ ---
5
+
6
+ # Assemble Release
7
+
8
+ Produce a deterministic release selection and auditable material package. Do not approve or deploy it.
9
+
10
+ ## Select The Version Closure
11
+
12
+ 1. Build a JSON lineage input from registered version-closeout manifests. Include exactly one predecessor, status, candidate ID/commit, code Tag, workflow commit, workflow Tag, manifest path, and checksum per version.
13
+ 2. Run `node scripts/plan-release.mjs --lineage <file> --release-id <id> --target <version> --requested-by <actor> --rationale <text> --workspace <workspace-root>`.
14
+ 3. Preserve the JSON result as the source for `R01-version-selection`. Do not use `--skip-git` outside isolated contract tests.
15
+ 4. Block on missing predecessors, branching, cycles, disconnected lineage, stale/ineligible versions, wrong `repo_id`, missing code or workflow Tags, workflow manifest checksum drift, backward targets, or any failed adjacent code/workflow ancestry edge. Checking only production-to-target reachability is insufficient.
16
+
17
+ ## Aggregate Materials
18
+
19
+ 1. Copy the assets into `.workflow/releases/<release-id>/R03-material-aggregation/` and `R04-release-candidate/`.
20
+ 2. Add every actual requirement, product, architecture, prototype, source, image, configuration, migration, infrastructure, test, security, risk, operations, rollback, and observability item to `release-material-index.csv`. Code entries require `repo_id`, `code_commit`, optional `code_tag`, `source_path`, source version, original workflow path, and SHA-256.
21
+ 3. Record every selected version and immutable identity in `version-inclusion.csv`. Intermediate versions provide traceability; build the release branch from only the exact target tag.
22
+ 4. Create `release/<release-id>-<slug>` at the target tag. Apply release-only fixes through `release-fix/<release-id>/<defect-id>`, create a new candidate, and repeat affected verification.
23
+ 5. Freeze `release-candidate-manifest.yaml` with the branch commit, image digest, material index checksum, environment/configuration snapshot requirements, and rollback image.
24
+
25
+ Same-version redeploy reuses immutable application identities but regenerates the release-scoped indexes, snapshots, verification, approval, deployment, and archive evidence.
26
+
27
+ Read [release-assembly-contract.md](references/release-assembly-contract.md) before aggregation.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Assemble Release"
3
+ short_description: "Validate version lineage and aggregate release materials"
4
+ default_prompt: "Use $ai-delivery-assemble-release to validate both repositories, calculate the version closure, and assemble its candidate."
@@ -0,0 +1,22 @@
1
+ schema_version: 2
2
+ release_id: "REL-0001"
3
+ release_candidate_id: "RC-0001"
4
+ mode: "incremental"
5
+ target_version: "VER-0005"
6
+ source_tag: "version-ready/VER-0005/r1"
7
+ repository:
8
+ repo_id: "app"
9
+ source_path: "code"
10
+ code_commit: "<git-sha>"
11
+ code_tag: "version-ready/VER-0005/r1"
12
+ workflow_identity:
13
+ workflow_commit: "<workflow-git-sha>"
14
+ workflow_tag: "workflow-ready/VER-0005/r1"
15
+ version_manifest_sha256: "<sha256>"
16
+ commit: "<git-sha>"
17
+ image_digest: "sha256:<digest>"
18
+ material_index_sha256: "<sha256>"
19
+ configuration_baseline_sha256: "<sha256>"
20
+ migration_set_sha256: "<sha256>"
21
+ rollback_image_digest: "sha256:<previous-digest>"
22
+ status: "immutable"
@@ -0,0 +1,9 @@
1
+ schema_version: 1
2
+ release_id: "REL-0001"
3
+ source_tag: "version-ready/VER-0005/r1"
4
+ source_commit: "<git-sha>"
5
+ release_branch: "release/REL-0001-short-slug"
6
+ production_branch: "main"
7
+ line_branch: "line/PRODUCT-001"
8
+ merge_intermediate_tags: false
9
+ rollback_branch_allowed: false