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,927 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { spawnSync } from "node:child_process";
5
+ import { parse as parseYaml, stringify as stringifyYaml } from "./yaml-runtime.mjs";
6
+ import { executeDeliveryStateCommand } from "./delivery-state.mjs";
7
+ import { inspectGitRepository, readWorkspaceConfiguration } from "./workspace.mjs";
8
+
9
+ const IGNORED_DIRECTORIES = new Set([
10
+ ".git",
11
+ ".agents",
12
+ ".codex",
13
+ ".ai-delivery",
14
+ ".workflow",
15
+ ".idea",
16
+ ".vscode",
17
+ "node_modules",
18
+ "vendor",
19
+ "dist",
20
+ "build",
21
+ "coverage",
22
+ ".next",
23
+ ".nuxt",
24
+ ".tmp",
25
+ ".tools",
26
+ ".venv",
27
+ "venv",
28
+ "__pycache__",
29
+ "target",
30
+ ]);
31
+ const VALID_NEXT_ACTION_OWNERS = new Set(["agent", "user", "external"]);
32
+
33
+ const SOURCE_EXTENSIONS = new Map([
34
+ [".c", "C"],
35
+ [".cc", "C++"],
36
+ [".cpp", "C++"],
37
+ [".cs", "C#"],
38
+ [".css", "CSS"],
39
+ [".go", "Go"],
40
+ [".h", "C/C++"],
41
+ [".html", "HTML"],
42
+ [".java", "Java"],
43
+ [".js", "JavaScript"],
44
+ [".jsx", "React"],
45
+ [".kt", "Kotlin"],
46
+ [".kts", "Kotlin"],
47
+ [".mjs", "JavaScript"],
48
+ [".php", "PHP"],
49
+ [".py", "Python"],
50
+ [".rb", "Ruby"],
51
+ [".rs", "Rust"],
52
+ [".scss", "SCSS"],
53
+ [".sql", "SQL"],
54
+ [".svelte", "Svelte"],
55
+ [".swift", "Swift"],
56
+ [".tf", "Terraform"],
57
+ [".ts", "TypeScript"],
58
+ [".tsx", "React"],
59
+ [".vue", "Vue"],
60
+ ]);
61
+
62
+ const MANIFESTS = new Map([
63
+ ["package.json", "Node.js"],
64
+ ["pyproject.toml", "Python"],
65
+ ["requirements.txt", "Python"],
66
+ ["poetry.lock", "Python"],
67
+ ["go.mod", "Go"],
68
+ ["cargo.toml", "Rust"],
69
+ ["gemfile", "Ruby"],
70
+ ["composer.json", "PHP"],
71
+ ["pom.xml", "Java"],
72
+ ["build.gradle", "Gradle"],
73
+ ["build.gradle.kts", "Gradle"],
74
+ ]);
75
+
76
+ const WORKFLOW_REQUIRED_FILES = [
77
+ ".workflow/config/workspace.yaml",
78
+ ".workflow/config/install-manifest.json",
79
+ ".codex/config.toml",
80
+ "AGENTS.md",
81
+ ".workflow/tools/state/state.mjs",
82
+ ".workflow/tools/evolution/evolve.mjs",
83
+ ".workflow/tools/hooks/hook-event.mjs",
84
+ ".workflow/delivery/project.yaml",
85
+ ".workflow/delivery/workflow-state.yaml",
86
+ ".workflow/delivery/artifact-registry.yaml",
87
+ ".workflow/delivery/standards-baseline.yaml",
88
+ ".workflow/standards/index.yaml",
89
+ ".gitattributes",
90
+ ];
91
+ const LEGACY_WORKFLOW_REQUIRED_FILES = [
92
+ ".ai-delivery/workspace.yaml",
93
+ ".ai-delivery/install-manifest.json",
94
+ ".codex/config.toml",
95
+ "AGENTS.md",
96
+ ".ai-delivery/state/state.mjs",
97
+ ".ai-delivery/hooks/hook-event.mjs",
98
+ "delivery/project.yaml",
99
+ "delivery/workflow-state.yaml",
100
+ "delivery/artifact-registry.yaml",
101
+ "delivery/standards-baseline.yaml",
102
+ "standards/index.yaml",
103
+ ];
104
+ const ESSENTIAL_STARTUP_SKILLS = [
105
+ "ai-delivery-bootstrap",
106
+ "ai-delivery-checkpoint-task",
107
+ "ai-delivery-evolve-workflow",
108
+ "ai-delivery-orchestrate",
109
+ ];
110
+
111
+ const ACTIVE_TASK_STATUSES = new Set([
112
+ "preparing",
113
+ "running",
114
+ "checkpointed",
115
+ "pending-review",
116
+ "blocked",
117
+ "interrupted",
118
+ ]);
119
+ const CLOSED_TASK_STATUSES = new Set(["completed", "failed", "abandoned"]);
120
+
121
+ function toPosix(value) {
122
+ return value.replaceAll("\\", "/");
123
+ }
124
+
125
+ function exists(root, relativePath) {
126
+ return fs.existsSync(path.join(root, relativePath));
127
+ }
128
+
129
+ function atomicWrite(file, content) {
130
+ fs.mkdirSync(path.dirname(file), { recursive: true });
131
+ const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
132
+ fs.writeFileSync(temporary, content, "utf8");
133
+ fs.renameSync(temporary, file);
134
+ }
135
+
136
+ function sha256File(file) {
137
+ return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
138
+ }
139
+
140
+ function isSensitiveFile(name) {
141
+ const lower = name.toLowerCase();
142
+ if (lower === ".env" || (lower.startsWith(".env.") && !/\.(example|sample|template)$/.test(lower))) {
143
+ return true;
144
+ }
145
+ return /\.(pem|p12|pfx|key|keystore|jks)$/.test(lower)
146
+ || /(^|[-_.])(credential|credentials|secret|secrets)([-_.]|$)/.test(lower);
147
+ }
148
+
149
+ function classifyFile(relativePath) {
150
+ const normalized = toPosix(relativePath);
151
+ const name = path.basename(relativePath).toLowerCase();
152
+ const extension = path.extname(name);
153
+ if (MANIFESTS.has(name)) {
154
+ return { category: "manifest", technology: MANIFESTS.get(name), purpose: "依赖与项目清单", priority: "high" };
155
+ }
156
+ if (/^(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|uv\.lock|go\.sum|cargo\.lock)$/.test(name)) {
157
+ return { category: "lockfile", technology: "dependency-management", purpose: "依赖锁定", priority: "high" };
158
+ }
159
+ if (name === "dockerfile" || name.startsWith("dockerfile.")) {
160
+ return { category: "deployment", technology: "Docker", purpose: "容器构建", priority: "high" };
161
+ }
162
+ if (/^(compose|docker-compose).*\.(ya?ml)$/.test(name)) {
163
+ return { category: "deployment", technology: "Docker Compose", purpose: "服务编排", priority: "high" };
164
+ }
165
+ if (extension === ".tf") {
166
+ return { category: "infrastructure", technology: "Terraform", purpose: "基础设施定义", priority: "high" };
167
+ }
168
+ if (/(^|\/)(test|tests|spec|specs)(\/|$)/i.test(normalized)
169
+ || /\.(test|spec)\.[^.]+$/i.test(name)) {
170
+ return { category: "test", technology: SOURCE_EXTENSIONS.get(extension) || extension.slice(1), purpose: "自动化测试", priority: "medium" };
171
+ }
172
+ if (SOURCE_EXTENSIONS.has(extension)) {
173
+ return { category: "source", technology: SOURCE_EXTENSIONS.get(extension), purpose: "源代码", priority: "medium" };
174
+ }
175
+ if (/^(makefile|cmakelists\.txt|justfile)$/.test(name)
176
+ || /^(webpack|vite|rollup|eslint|prettier|tsconfig|babel)\..+/.test(name)) {
177
+ return { category: "build", technology: "build-tooling", purpose: "构建或工程配置", priority: "high" };
178
+ }
179
+ if (/\.(ya?ml|json|toml|ini|conf|config|properties)$/.test(name)) {
180
+ return { category: "configuration", technology: extension.slice(1).toUpperCase(), purpose: "配置", priority: "medium" };
181
+ }
182
+ if (/\.(md|mdx|rst|txt)$/.test(name)) {
183
+ return { category: "documentation", technology: "documentation", purpose: "文档", priority: "low" };
184
+ }
185
+ return { category: "other", technology: extension ? extension.slice(1) : "unknown", purpose: "其他项目文件", priority: "low" };
186
+ }
187
+
188
+ function scanProjectFiles(root, maxFiles = 10_000) {
189
+ const files = [];
190
+ const warnings = [];
191
+ let sensitiveFileCount = 0;
192
+ let truncated = false;
193
+
194
+ function visit(directory) {
195
+ if (truncated) return;
196
+ let entries;
197
+ try {
198
+ entries = fs.readdirSync(directory, { withFileTypes: true });
199
+ } catch (error) {
200
+ warnings.push(`无法读取目录:${toPosix(path.relative(root, directory)) || "."} (${error.code || error.message})`);
201
+ return;
202
+ }
203
+ for (const entry of entries) {
204
+ if (files.length >= maxFiles) {
205
+ truncated = true;
206
+ break;
207
+ }
208
+ const absolute = path.join(directory, entry.name);
209
+ if (entry.isSymbolicLink()) continue;
210
+ if (entry.isDirectory()) {
211
+ if (!IGNORED_DIRECTORIES.has(entry.name.toLowerCase())) visit(absolute);
212
+ continue;
213
+ }
214
+ if (!entry.isFile()) continue;
215
+ if (isSensitiveFile(entry.name)) {
216
+ sensitiveFileCount += 1;
217
+ continue;
218
+ }
219
+ const relative = toPosix(path.relative(root, absolute));
220
+ const stat = fs.statSync(absolute);
221
+ files.push({
222
+ path: relative,
223
+ absolute,
224
+ size_bytes: stat.size,
225
+ ...classifyFile(relative),
226
+ });
227
+ }
228
+ }
229
+
230
+ visit(root);
231
+ const codeFiles = files.filter((file) =>
232
+ new Set(["manifest", "source", "test", "build", "deployment", "infrastructure"]).has(file.category));
233
+ const technologyCategories = new Set([
234
+ "manifest",
235
+ "source",
236
+ "test",
237
+ "build",
238
+ "deployment",
239
+ "infrastructure",
240
+ ]);
241
+ const technologies = [...new Set(files
242
+ .filter((file) => technologyCategories.has(file.category)
243
+ && file.technology
244
+ && file.technology !== "unknown")
245
+ .map((file) => file.technology))].sort();
246
+ const categories = Object.fromEntries(
247
+ [...new Set(files.map((file) => file.category))]
248
+ .sort()
249
+ .map((category) => [category, files.filter((file) => file.category === category).length]),
250
+ );
251
+ return {
252
+ files,
253
+ summary: {
254
+ has_code: codeFiles.length > 0,
255
+ scanned_file_count: files.length,
256
+ code_evidence_count: codeFiles.length,
257
+ sensitive_file_count: sensitiveFileCount,
258
+ truncated,
259
+ technologies,
260
+ categories,
261
+ },
262
+ warnings,
263
+ };
264
+ }
265
+
266
+ function gitState(root) {
267
+ const run = (args) => spawnSync("git", args, { cwd: root, encoding: "utf8", windowsHide: true });
268
+ const topLevel = run(["rev-parse", "--show-toplevel"]);
269
+ if (topLevel.status !== 0) return { repository: false };
270
+ const branch = run(["branch", "--show-current"]);
271
+ const head = run(["rev-parse", "HEAD"]);
272
+ const status = run(["status", "--porcelain=v1"]);
273
+ return {
274
+ repository: true,
275
+ root: toPosix(topLevel.stdout.trim()),
276
+ branch: branch.stdout.trim() || null,
277
+ head: head.status === 0 ? head.stdout.trim() : null,
278
+ dirty_paths: status.stdout.split(/\r?\n/).filter(Boolean).map((line) => toPosix(line.slice(3))),
279
+ };
280
+ }
281
+
282
+ function readStructuredFile(file, format, errors) {
283
+ try {
284
+ const text = fs.readFileSync(file, "utf8");
285
+ return format === "json" ? JSON.parse(text) : parseYaml(text);
286
+ } catch (error) {
287
+ errors.push(`${toPosix(file)}: ${error.message}`);
288
+ return null;
289
+ }
290
+ }
291
+
292
+ function compareTasks(left, right) {
293
+ const leftSequence = Number.isInteger(left.sequence) ? left.sequence : Number.MAX_SAFE_INTEGER;
294
+ const rightSequence = Number.isInteger(right.sequence) ? right.sequence : Number.MAX_SAFE_INTEGER;
295
+ return leftSequence - rightSequence || String(left.task_id).localeCompare(String(right.task_id));
296
+ }
297
+
298
+ function stableValue(value) {
299
+ if (Array.isArray(value)) return value.map(stableValue);
300
+ if (!value || typeof value !== "object") return value;
301
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableValue(value[key])]));
302
+ }
303
+
304
+ function taskStateChecksum(task, root) {
305
+ const comparable = { ...task };
306
+ const stateRoot = path.resolve(root, comparable.state_root || ".");
307
+ comparable.state_root = process.platform === "win32" ? stateRoot.toLowerCase() : stateRoot;
308
+ if (comparable.worktree) {
309
+ const worktree = path.resolve(stateRoot, comparable.worktree);
310
+ comparable.worktree = process.platform === "win32" ? worktree.toLowerCase() : worktree;
311
+ }
312
+ for (const key of ["code_branch", "code_commit"]) delete comparable[key];
313
+ return crypto.createHash("sha256").update(JSON.stringify(stableValue(comparable))).digest("hex");
314
+ }
315
+
316
+ function inspectRuntime(root, errors, workflowRoot = ".workflow") {
317
+ const localDirectory = path.join(root, workflowRoot, "delivery", "runtime", "tasks");
318
+ const sharedDirectory = path.join(root, workflowRoot, "control", "tasks");
319
+ const normalizeTaskOwner = (task) => {
320
+ const owner = task.next_action_owner === undefined ? "agent" : task.next_action_owner;
321
+ if (!VALID_NEXT_ACTION_OWNERS.has(owner)) {
322
+ errors.push(`任务 ${task.task_id || "unknown"} 的 next_action_owner 无效: ${owner}`);
323
+ return { ...task, next_action_owner: null };
324
+ }
325
+ return { ...task, next_action_owner: owner };
326
+ };
327
+ const readTasks = (directory) => fs.existsSync(directory)
328
+ ? fs.readdirSync(directory)
329
+ .filter((name) => /\.(json|ya?ml)$/i.test(name))
330
+ .map((name) => readStructuredFile(
331
+ path.join(directory, name),
332
+ name.endsWith(".json") ? "json" : "yaml",
333
+ errors,
334
+ ))
335
+ .filter(Boolean)
336
+ .map(normalizeTaskOwner)
337
+ : [];
338
+ const localTasks = readTasks(localDirectory);
339
+ const sharedTasks = readTasks(sharedDirectory);
340
+ const localById = new Map(localTasks.map((task) => [task.task_id, task]));
341
+ const sharedById = new Map(sharedTasks.map((task) => [task.task_id, task]));
342
+ for (const [taskId, local] of localById) {
343
+ const shared = sharedById.get(taskId);
344
+ if (shared && Number(local.shared_revision || 0) !== Number(shared.shared_revision || 0)) {
345
+ errors.push(`任务 ${taskId} 的本机 revision ${local.shared_revision || 0} 与共享 revision ${shared.shared_revision || 0} 冲突`);
346
+ } else if (shared && taskStateChecksum(local, root) !== taskStateChecksum(shared, root)) {
347
+ errors.push(`任务 ${taskId} 的本机与共享内容在 revision ${local.shared_revision || 0} 发生分叉`);
348
+ }
349
+ }
350
+ const tasks = [...new Map([
351
+ ...sharedTasks.map((task) => [task.task_id, task]),
352
+ ...localTasks.map((task) => [task.task_id, task]),
353
+ ]).values()].sort(compareTasks);
354
+ const tasksById = new Map(tasks.map((task) => [task.task_id, task]));
355
+ const dependencyErrors = [];
356
+ const queued = tasks.filter((task) => task.status === "queued");
357
+ const ready = queued.filter((task) => {
358
+ for (const dependencyId of task.depends_on || []) {
359
+ const dependency = tasksById.get(dependencyId);
360
+ if (!dependency) {
361
+ dependencyErrors.push(`${task.task_id} 缺少依赖任务 ${dependencyId}`);
362
+ return false;
363
+ }
364
+ if (dependency.status !== "completed") return false;
365
+ }
366
+ return true;
367
+ });
368
+ const active = tasks.filter((task) => ACTIVE_TASK_STATUSES.has(task.status));
369
+ const recommended = [...active, ...ready].sort(compareTasks)[0] || null;
370
+ const checkpointRoot = path.join(root, workflowRoot, "control", "checkpoints");
371
+ let sharedCheckpointCount = 0;
372
+ const countCheckpoints = (directory) => {
373
+ if (!fs.existsSync(directory)) return;
374
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
375
+ const absolute = path.join(directory, entry.name);
376
+ if (entry.isDirectory()) countCheckpoints(absolute);
377
+ else if (entry.isFile() && /\.(json|ya?ml)$/i.test(entry.name)) sharedCheckpointCount += 1;
378
+ }
379
+ };
380
+ countCheckpoints(checkpointRoot);
381
+ return {
382
+ task_count: tasks.length,
383
+ closed_task_count: tasks.filter((task) => CLOSED_TASK_STATUSES.has(task.status)).length,
384
+ active_task_ids: active.map((task) => task.task_id),
385
+ queued_task_ids: queued.map((task) => task.task_id),
386
+ ready_task_ids: ready.map((task) => task.task_id),
387
+ recommended_task_id: recommended?.task_id || null,
388
+ recommended_iteration_id: recommended?.iteration_id || null,
389
+ recommended_status: recommended?.status || null,
390
+ next_action: recommended?.next_action || null,
391
+ next_action_owner: recommended?.next_action_owner || null,
392
+ source: localTasks.length ? "local-and-shared" : sharedTasks.length ? "shared" : "none",
393
+ local_task_count: localTasks.length,
394
+ shared_task_count: sharedTasks.length,
395
+ shared_checkpoint_count: sharedCheckpointCount,
396
+ dependency_errors: [...new Set(dependencyErrors)],
397
+ };
398
+ }
399
+
400
+ function recommendationFor(state, workflow, runtime) {
401
+ if (state === "empty-uninitialized") {
402
+ return {
403
+ skill: "ai-delivery-bootstrap",
404
+ action: "初始化项目级工作流,然后调用 ai-delivery-define-product 逐项收集首个产品需求。",
405
+ requires_user_input: true,
406
+ };
407
+ }
408
+ if (state === "codebase-uninitialized") {
409
+ return {
410
+ skill: "ai-delivery-bootstrap",
411
+ action: "初始化项目级工作流,生成存量代码上下文,再调用 ai-delivery-define-product 核对业务意图。",
412
+ requires_user_input: true,
413
+ };
414
+ }
415
+ if (state === "workflow-inconsistent") {
416
+ return {
417
+ skill: "ai-delivery-bootstrap",
418
+ action: "停止自动流转,审阅缺失或冲突项并形成修复决定;不得自动使用 force 覆盖。",
419
+ requires_user_input: true,
420
+ };
421
+ }
422
+ if (state === "workflow-interrupted" || runtime.recommended_task_id) {
423
+ const awaitingUser = runtime.next_action_owner === "user";
424
+ const awaitingExternal = runtime.next_action_owner === "external";
425
+ return {
426
+ skill: "ai-delivery-checkpoint-task",
427
+ action: awaitingUser || awaitingExternal
428
+ ? runtime.next_action
429
+ : `恢复并核对任务 ${runtime.recommended_task_id || "unknown"},只继续其幂等 next_action。`,
430
+ requires_user_input: awaitingUser,
431
+ mutation_allowed: !awaitingUser && !awaitingExternal,
432
+ };
433
+ }
434
+ const initial = workflow.delivery_artifact_count === 0
435
+ && !(workflow.active_nodes?.length)
436
+ && !(workflow.pending_gates?.length);
437
+ if (initial) {
438
+ return {
439
+ skill: "ai-delivery-define-product",
440
+ action: "开始产品发现,一次询问一个业务决策并形成 Discovery Gate A 草稿。",
441
+ requires_user_input: true,
442
+ };
443
+ }
444
+ return {
445
+ skill: "ai-delivery-orchestrate",
446
+ action: "依据 workflow-state、artifact-registry 和 Gate 状态推导下一节点。",
447
+ requires_user_input: Boolean(workflow.pending_gates?.length),
448
+ };
449
+ }
450
+
451
+ export function inspectProjectState(target) {
452
+ const root = path.resolve(target);
453
+ if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) {
454
+ throw new Error(`Project directory does not exist: ${root}`);
455
+ }
456
+ const workspaceRecord = readWorkspaceConfiguration(root, { optional: true });
457
+ const inferredCodeRoot = fs.existsSync(path.join(root, "code")) ? path.join(root, "code") : root;
458
+ const codeRoot = workspaceRecord?.codeRoot || inferredCodeRoot;
459
+ const codebase = scanProjectFiles(codeRoot);
460
+ const errors = [];
461
+ const currentIndicators = WORKFLOW_REQUIRED_FILES.filter((file) => exists(root, file));
462
+ const legacyIndicators = LEGACY_WORKFLOW_REQUIRED_FILES.filter((file) => exists(root, file));
463
+ const currentStateIndicators = WORKFLOW_REQUIRED_FILES.filter((file) =>
464
+ file.startsWith(".workflow/delivery/") || file === ".workflow/standards/index.yaml")
465
+ .filter((file) => exists(root, file));
466
+ const legacyStateIndicators = LEGACY_WORKFLOW_REQUIRED_FILES.filter((file) =>
467
+ file.startsWith("delivery/") || file === "standards/index.yaml")
468
+ .filter((file) => exists(root, file));
469
+ const legacyLayout = currentStateIndicators.length === 0 && legacyStateIndicators.length > 0;
470
+ const requiredFiles = legacyLayout ? LEGACY_WORKFLOW_REQUIRED_FILES : WORKFLOW_REQUIRED_FILES;
471
+ const workflowRoot = legacyLayout ? "." : ".workflow";
472
+ const workflowIndicators = requiredFiles.filter((file) => exists(root, file));
473
+ const missingFiles = requiredFiles.filter((file) => !exists(root, file));
474
+ const installation = {
475
+ detected: currentIndicators.length > 0 || legacyIndicators.length > 0,
476
+ complete: workflowIndicators.length === requiredFiles.length,
477
+ workflow_root: workflowRoot,
478
+ legacy_layout: legacyLayout,
479
+ present_files: workflowIndicators,
480
+ missing_files: missingFiles,
481
+ installed_skill_count: 0,
482
+ missing_skills: [],
483
+ };
484
+ const workflow = {
485
+ project_id: null,
486
+ iteration_id: null,
487
+ artifact_count: 0,
488
+ delivery_artifact_count: 0,
489
+ active_nodes: [],
490
+ blocked_nodes: [],
491
+ pending_gates: [],
492
+ };
493
+ let runtime = {
494
+ task_count: 0,
495
+ active_task_ids: [],
496
+ queued_task_ids: [],
497
+ ready_task_ids: [],
498
+ recommended_task_id: null,
499
+ dependency_errors: [],
500
+ };
501
+
502
+ if (legacyLayout) {
503
+ errors.push("检测到旧版 delivery/ 与 standards/ 布局;迁移到 .workflow/ 后才能继续执行。");
504
+ }
505
+ if (currentStateIndicators.length > 0 && legacyStateIndicators.length > 0) {
506
+ errors.push("同时检测到 .workflow 和旧版 delivery/standards 状态;必须人工确定唯一权威根。");
507
+ }
508
+
509
+ if (installation.complete) {
510
+ if (legacyLayout) {
511
+ errors.push("检测到旧版 delivery/ 与 standards/ 布局;迁移到 .workflow/ 后才能继续执行。");
512
+ }
513
+ if (currentStateIndicators.length > 0 && legacyStateIndicators.length > 0) {
514
+ errors.push("同时检测到 .workflow 和旧版 delivery/standards 状态;必须人工确定唯一权威根。");
515
+ }
516
+ const manifest = readStructuredFile(path.join(
517
+ root,
518
+ legacyLayout ? ".ai-delivery/install-manifest.json" : ".workflow/config/install-manifest.json",
519
+ ), "json", errors);
520
+ const stateRoot = path.join(root, workflowRoot);
521
+ const project = readStructuredFile(path.join(stateRoot, "delivery", "project.yaml"), "yaml", errors);
522
+ const workflowState = readStructuredFile(path.join(stateRoot, "delivery", "workflow-state.yaml"), "yaml", errors);
523
+ const registry = readStructuredFile(path.join(stateRoot, "delivery", "artifact-registry.yaml"), "yaml", errors);
524
+ readStructuredFile(path.join(stateRoot, "delivery", "standards-baseline.yaml"), "yaml", errors);
525
+ readStructuredFile(path.join(stateRoot, "standards", "index.yaml"), "yaml", errors);
526
+ if (manifest) {
527
+ const installedSkills = Array.isArray(manifest.installed_skills) ? manifest.installed_skills : [];
528
+ installation.installed_skill_count = installedSkills.length;
529
+ installation.missing_skills = [...new Set([...installedSkills, ...ESSENTIAL_STARTUP_SKILLS])]
530
+ .filter((name) => !exists(root, `.agents/skills/${name}/SKILL.md`));
531
+ if (manifest.scope !== "workspace") errors.push("安装清单 scope 必须为 workspace");
532
+ if (!installedSkills.length) errors.push("安装清单未登记任何 skill");
533
+ }
534
+ if (project && workflowState) {
535
+ workflow.project_id = project.project_id || null;
536
+ workflow.iteration_id = workflowState.iteration_id || project.current_iteration_id || null;
537
+ workflow.active_nodes = Array.isArray(workflowState.active_nodes) ? workflowState.active_nodes : [];
538
+ workflow.blocked_nodes = Array.isArray(workflowState.blocked_nodes) ? workflowState.blocked_nodes : [];
539
+ workflow.pending_gates = Array.isArray(workflowState.pending_gates) ? workflowState.pending_gates : [];
540
+ if (project.project_id !== workflowState.project_id) {
541
+ errors.push(`${workflowRoot}/delivery/project.yaml 与 workflow-state.yaml 的 project_id 不一致`);
542
+ }
543
+ }
544
+ if (!registry || !Array.isArray(registry.artifacts)) {
545
+ errors.push(`${workflowRoot}/delivery/artifact-registry.yaml 必须包含 artifacts 数组`);
546
+ } else {
547
+ workflow.artifact_count = registry.artifacts.length;
548
+ workflow.delivery_artifact_count = registry.artifacts.filter((artifact) =>
549
+ !new Set(["project-state-report", "bootstrap-plan", "repository-observation-baseline"])
550
+ .has(artifact?.artifact_type)).length;
551
+ }
552
+ runtime = inspectRuntime(root, errors, workflowRoot);
553
+ errors.push(...runtime.dependency_errors);
554
+ if (installation.missing_skills.length) {
555
+ errors.push(`缺少项目级 skills:${installation.missing_skills.join(", ")}`);
556
+ }
557
+ }
558
+
559
+ let state;
560
+ if (!installation.detected) {
561
+ state = codebase.summary.has_code ? "codebase-uninitialized" : "empty-uninitialized";
562
+ } else if (!installation.complete || errors.length) {
563
+ state = "workflow-inconsistent";
564
+ } else if (runtime.active_task_ids.length) {
565
+ state = "workflow-interrupted";
566
+ } else {
567
+ state = "workflow-ready";
568
+ }
569
+ const recommendation = recommendationFor(state, workflow, runtime);
570
+ const codeGit = inspectGitRepository(codeRoot);
571
+ const workflowGit = inspectGitRepository(root);
572
+ const syncWarnings = [];
573
+ if (workflowGit.sync_status === "uncommitted") {
574
+ syncWarnings.push("工作流仓库存在未提交的共享状态或物料,其他协作者尚不可见。可以按任务范围 publish 后再交接。");
575
+ } else if (workflowGit.sync_status === "unpushed") {
576
+ syncWarnings.push("工作流仓库存在未推送提交,其他工作区尚不可见。");
577
+ }
578
+ return {
579
+ schema_version: 2,
580
+ project_root: toPosix(root),
581
+ workspace: workspaceRecord?.configuration || {
582
+ workflow_repository: { path: "." },
583
+ code_repository: { repo_id: "app", path: toPosix(path.relative(root, codeRoot)) || "." },
584
+ },
585
+ detected_at: new Date().toISOString(),
586
+ state,
587
+ installation,
588
+ workflow,
589
+ runtime,
590
+ codebase: codebase.summary,
591
+ git: codeGit,
592
+ workflow_git: workflowGit,
593
+ recommendation,
594
+ errors,
595
+ warnings: [...codebase.warnings, ...syncWarnings],
596
+ };
597
+ }
598
+
599
+ function csvCell(value) {
600
+ const text = value === null || value === undefined ? "" : String(value);
601
+ return /[",\r\n]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text;
602
+ }
603
+
604
+ function toCsv(headers, rows) {
605
+ return `${[
606
+ headers.join(","),
607
+ ...rows.map((row) => headers.map((header) => csvCell(row[header])).join(",")),
608
+ ].join("\n")}\n`;
609
+ }
610
+
611
+ function packageCommands(root, files) {
612
+ const commands = [];
613
+ const manager = files.some((file) => file.path === "pnpm-lock.yaml")
614
+ ? "pnpm"
615
+ : files.some((file) => file.path === "yarn.lock") ? "yarn" : "npm";
616
+ for (const file of files.filter((item) => path.basename(item.path).toLowerCase() === "package.json")) {
617
+ try {
618
+ const packageJson = JSON.parse(fs.readFileSync(file.absolute, "utf8"));
619
+ for (const script of Object.keys(packageJson.scripts || {}).sort()) {
620
+ commands.push({
621
+ working_directory: toPosix(path.dirname(file.path)) === "." ? "." : toPosix(path.dirname(file.path)),
622
+ command: manager === "yarn" ? `yarn ${script}` : `${manager} run ${script}`,
623
+ source: file.path,
624
+ });
625
+ }
626
+ } catch {
627
+ // The project state report already records malformed structured files when they are workflow-owned.
628
+ }
629
+ }
630
+ return commands;
631
+ }
632
+
633
+ function taskPlanFor(initialState, result) {
634
+ if (initialState === "empty-uninitialized") {
635
+ return [
636
+ { task_id: "BOOTSTRAP-NEXT-01", sequence: 1, skill: "ai-delivery-define-product", objective: "向用户收集初始产品问题、目标用户和成功标准", status: "queued", depends_on: [] },
637
+ { task_id: "BOOTSTRAP-NEXT-02", sequence: 2, skill: "ai-delivery-define-product", objective: "形成 Discovery Gate A 草稿并提交人工确认", status: "queued", depends_on: ["BOOTSTRAP-NEXT-01"] },
638
+ ];
639
+ }
640
+ if (initialState === "codebase-uninitialized") {
641
+ return [
642
+ { task_id: "BOOTSTRAP-NEXT-01", sequence: 1, skill: "ai-delivery-bootstrap", objective: "人工核对存量代码清单、技术栈和可执行命令", status: "queued", depends_on: [] },
643
+ { task_id: "BOOTSTRAP-NEXT-02", sequence: 2, skill: "ai-delivery-define-product", objective: "核对代码当前行为与真实产品意图,形成产品发现草稿", status: "queued", depends_on: ["BOOTSTRAP-NEXT-01"] },
644
+ ];
645
+ }
646
+ return [{
647
+ task_id: "BOOTSTRAP-NEXT-01",
648
+ sequence: 1,
649
+ skill: result.recommendation.skill,
650
+ objective: result.recommendation.action,
651
+ status: "queued",
652
+ depends_on: [],
653
+ }];
654
+ }
655
+
656
+ function adoptionMarkdown(initialState, scan, result) {
657
+ const gaps = [];
658
+ let artifactTypes = [];
659
+ try {
660
+ const registry = parseYaml(fs.readFileSync(
661
+ path.join(result.project_root, ".workflow", "delivery", "artifact-registry.yaml"),
662
+ "utf8",
663
+ ));
664
+ artifactTypes = (registry?.artifacts || []).map((artifact) => artifact.artifact_type || "");
665
+ } catch {
666
+ artifactTypes = [];
667
+ }
668
+ if (!artifactTypes.some((type) => /product.*baseline/.test(type))) gaps.push("缺少经确认的产品基线。");
669
+ if (!artifactTypes.some((type) => /architecture.*baseline/.test(type))) gaps.push("缺少经确认的架构基线。");
670
+ if (scan.summary.has_code && !scan.summary.categories.test) gaps.push("未发现可识别的自动化测试文件,需人工核对测试入口。");
671
+ if (!result.git.repository) gaps.push("当前目录不是有效 Git 仓库,分支和提交身份不可用。");
672
+ return `# 存量项目接入评估
673
+
674
+ ## 探测结论
675
+
676
+ - 初始状态:\`${initialState}\`
677
+ - 当前状态:\`${result.state}\`
678
+ - 扫描文件:${scan.summary.scanned_file_count}
679
+ - 代码证据:${scan.summary.code_evidence_count}
680
+ - 检测技术:${scan.summary.technologies.join("、") || "未识别"}
681
+ - 排除的敏感文件:${scan.summary.sensitive_file_count}
682
+
683
+ ## 上下文边界
684
+
685
+ 本报告只描述代码和仓库的可观察现状。代码行为不是已批准产品需求,自动归纳内容均保持 \`draft\`,必须通过产品和架构流程核对后才能成为正式基线。
686
+
687
+ ## 当前缺口
688
+
689
+ ${gaps.length ? gaps.map((gap) => `- ${gap}`).join("\n") : "- 未发现基础接入缺口;仍需按正式 Gate 验证产物。"}
690
+
691
+ ## 推荐动作
692
+
693
+ ${result.recommendation.action}
694
+ `;
695
+ }
696
+
697
+ function registerBootstrapReport(root, entry) {
698
+ const yaml = {
699
+ parse: parseYaml,
700
+ stringify: (value) => stringifyYaml(value, { lineWidth: 0 }),
701
+ };
702
+ const inspected = executeDeliveryStateCommand(["inspect", "--project", root], { yaml });
703
+ executeDeliveryStateCommand([
704
+ "artifact", "register",
705
+ "--project", root,
706
+ "--expected-revision", String(inspected.artifact_registry.revision),
707
+ "--id", entry.artifact_id,
708
+ "--artifact-type", entry.artifact_type,
709
+ "--path", entry.path,
710
+ "--node", "00-bootstrap",
711
+ "--status", entry.status,
712
+ "--version", String(entry.version),
713
+ "--producer-skill", entry.producer_skill,
714
+ ], { yaml });
715
+ }
716
+
717
+ function writeBootstrapArtifacts(root, initialState, result, sourceScan = null) {
718
+ const configuredCodeRoot = path.resolve(root, result.workspace?.code_repository?.path || "code");
719
+ const scan = sourceScan || scanProjectFiles(configuredCodeRoot);
720
+ const stamp = new Date().toISOString().replace(/[-:.Z]/g, "");
721
+ const runId = `BOOTSTRAP-${stamp}`;
722
+ const artifactSuffix = stamp;
723
+ const relativeRunDirectory = `.workflow/delivery/bootstrap/runs/${runId}`;
724
+ const runDirectory = path.join(root, relativeRunDirectory);
725
+ fs.mkdirSync(runDirectory, { recursive: true });
726
+
727
+ const contextHeaders = ["path", "category", "technology", "purpose", "size_bytes", "sha256", "read_priority", "scan_status"];
728
+ const contextRows = scan.files.map((file) => ({
729
+ path: file.path,
730
+ category: file.category,
731
+ technology: file.technology,
732
+ purpose: file.purpose,
733
+ size_bytes: file.size_bytes,
734
+ sha256: file.size_bytes <= 10 * 1024 * 1024 ? sha256File(file.absolute) : "",
735
+ read_priority: file.priority,
736
+ scan_status: file.size_bytes <= 10 * 1024 * 1024 ? "indexed" : "large-file-not-hashed",
737
+ }));
738
+ const inventoryHeaders = ["inventory_id", "item_type", "name", "path", "technology", "role", "evidence", "status"];
739
+ const inventoryRows = scan.files
740
+ .filter((file) => file.category !== "other")
741
+ .map((file, index) => ({
742
+ inventory_id: `INV-${String(index + 1).padStart(4, "0")}`,
743
+ item_type: file.category,
744
+ name: path.basename(file.path),
745
+ path: file.path,
746
+ technology: file.technology,
747
+ role: file.purpose,
748
+ evidence: file.path,
749
+ status: "observed",
750
+ }));
751
+
752
+ const contextFile = path.join(runDirectory, "context-index.csv");
753
+ const inventoryFile = path.join(runDirectory, "codebase-inventory.csv");
754
+ const repositoryFile = path.join(runDirectory, "repository-baseline.yaml");
755
+ const assessmentFile = path.join(runDirectory, "adoption-assessment.md");
756
+ const planFile = path.join(runDirectory, "bootstrap-plan.yaml");
757
+ atomicWrite(contextFile, toCsv(contextHeaders, contextRows));
758
+ atomicWrite(inventoryFile, toCsv(inventoryHeaders, inventoryRows));
759
+ atomicWrite(repositoryFile, stringifyYaml({
760
+ artifact_id: `REPOSITORY-BASELINE-${artifactSuffix}`,
761
+ artifact_type: "repository-observation-baseline",
762
+ version: 1,
763
+ status: "draft",
764
+ producer_skill: "ai-delivery-bootstrap",
765
+ detected_at: result.detected_at,
766
+ repository: result.git,
767
+ codebase: scan.summary,
768
+ command_candidates: packageCommands(root, scan.files),
769
+ constraints: [
770
+ "Observed code is not an approved product requirement.",
771
+ "Commands are candidates until executed and verified.",
772
+ "Sensitive files are excluded from generated context.",
773
+ ],
774
+ }, { lineWidth: 0 }));
775
+ atomicWrite(assessmentFile, adoptionMarkdown(initialState, scan, result));
776
+ atomicWrite(planFile, stringifyYaml({
777
+ artifact_id: `BOOTSTRAP-PLAN-${artifactSuffix}`,
778
+ artifact_type: "bootstrap-plan",
779
+ version: 1,
780
+ status: "draft",
781
+ producer_skill: "ai-delivery-bootstrap",
782
+ initial_state: initialState,
783
+ resulting_state: result.state,
784
+ recommended_skill: result.recommendation.skill,
785
+ tasks: taskPlanFor(initialState, result),
786
+ }, { lineWidth: 0 }));
787
+
788
+ const relatedArtifacts = {
789
+ repository_baseline: {
790
+ ref: `${relativeRunDirectory}/repository-baseline.yaml`,
791
+ checksum: sha256File(repositoryFile),
792
+ },
793
+ codebase_inventory: {
794
+ ref: `${relativeRunDirectory}/codebase-inventory.csv`,
795
+ checksum: sha256File(inventoryFile),
796
+ },
797
+ context_index: {
798
+ ref: `${relativeRunDirectory}/context-index.csv`,
799
+ checksum: sha256File(contextFile),
800
+ },
801
+ adoption_assessment: {
802
+ ref: `${relativeRunDirectory}/adoption-assessment.md`,
803
+ checksum: sha256File(assessmentFile),
804
+ },
805
+ bootstrap_plan: {
806
+ ref: `${relativeRunDirectory}/bootstrap-plan.yaml`,
807
+ checksum: sha256File(planFile),
808
+ },
809
+ };
810
+ const reportFile = path.join(runDirectory, "project-state-report.yaml");
811
+ const report = {
812
+ artifact_id: `PROJECT-STATE-${artifactSuffix}`,
813
+ artifact_type: "project-state-report",
814
+ version: 1,
815
+ status: "draft",
816
+ producer_skill: "ai-delivery-bootstrap",
817
+ detected_at: result.detected_at,
818
+ initial_state: initialState,
819
+ resulting_state: result.state,
820
+ installation: result.installation,
821
+ workflow: result.workflow,
822
+ runtime: result.runtime,
823
+ codebase: result.codebase,
824
+ git: result.git,
825
+ recommendation: result.recommendation,
826
+ errors: result.errors,
827
+ warnings: result.warnings,
828
+ related_artifacts: relatedArtifacts,
829
+ };
830
+ atomicWrite(reportFile, stringifyYaml(report, { lineWidth: 0 }));
831
+ const reportChecksum = sha256File(reportFile);
832
+ const reportRef = `${relativeRunDirectory}/project-state-report.yaml`;
833
+ atomicWrite(path.join(root, ".workflow", "delivery", "bootstrap", "latest.yaml"), stringifyYaml({
834
+ run_id: runId,
835
+ state: result.state,
836
+ report_ref: reportRef,
837
+ report_algorithm: "sha256",
838
+ report_checksum: reportChecksum,
839
+ recommended_skill: result.recommendation.skill,
840
+ recommended_task_id: result.runtime.recommended_task_id,
841
+ updated_at: result.detected_at,
842
+ }, { lineWidth: 0 }));
843
+ registerBootstrapReport(root, {
844
+ artifact_id: report.artifact_id,
845
+ artifact_type: report.artifact_type,
846
+ version: 1,
847
+ status: "draft",
848
+ producer_skill: "ai-delivery-bootstrap",
849
+ path: reportRef,
850
+ checksum_algorithm: "sha256",
851
+ checksum: reportChecksum,
852
+ });
853
+ return {
854
+ run_id: runId,
855
+ report_ref: reportRef,
856
+ report_checksum: reportChecksum,
857
+ related_artifacts: relatedArtifacts,
858
+ };
859
+ }
860
+
861
+ export function bootstrapProject(packageRoot, target, options = {}) {
862
+ const root = path.resolve(target);
863
+ const { dryRun = false, force = false, packageVersion = "0.0.0", initProject = null } = options;
864
+ const initial = inspectProjectState(root);
865
+ const initialCodeRoot = path.resolve(root, initial.workspace?.code_repository?.path || "code");
866
+ const initialCodebaseScan = new Set(["empty-uninitialized", "codebase-uninitialized"]).has(initial.state)
867
+ ? scanProjectFiles(fs.existsSync(initialCodeRoot) ? initialCodeRoot : root)
868
+ : null;
869
+ const actions = [];
870
+ if (initial.state === "workflow-inconsistent") {
871
+ return {
872
+ initial_state: initial.state,
873
+ resulting_state: initial.state,
874
+ initialized: false,
875
+ artifacts_written: false,
876
+ actions,
877
+ inspection: initial,
878
+ message: "Workflow state is inconsistent; review the reported conflicts before mutation.",
879
+ };
880
+ }
881
+ if (new Set(["empty-uninitialized", "codebase-uninitialized"]).has(initial.state)) {
882
+ if (typeof initProject !== "function") {
883
+ throw new Error("Project-local bootstrap cannot install missing workflow files; run init once before using the local entrypoint.");
884
+ }
885
+ const initialization = initProject(packageRoot, root, { dryRun, force, packageVersion });
886
+ actions.push(...initialization.actions);
887
+ if (dryRun) {
888
+ return {
889
+ initial_state: initial.state,
890
+ resulting_state: initial.state,
891
+ initialized: false,
892
+ artifacts_written: false,
893
+ actions,
894
+ inspection: initial,
895
+ message: "Dry run completed; no project files were written.",
896
+ };
897
+ }
898
+ }
899
+ const result = inspectProjectState(root);
900
+ const hasRecommendedTask = result.state === "workflow-interrupted"
901
+ || Boolean(result.runtime.recommended_task_id);
902
+ const shouldDeferToRecovery = hasRecommendedTask && result.runtime.next_action_owner === "agent";
903
+ const shouldDeferToUser = hasRecommendedTask && result.runtime.next_action_owner === "user";
904
+ const shouldDeferToExternal = hasRecommendedTask && result.runtime.next_action_owner === "external";
905
+ const artifacts = dryRun || hasRecommendedTask
906
+ ? null
907
+ : writeBootstrapArtifacts(root, initial.state, result, initialCodebaseScan);
908
+ return {
909
+ initial_state: initial.state,
910
+ resulting_state: result.state,
911
+ initialized: new Set(["empty-uninitialized", "codebase-uninitialized"]).has(initial.state) && !dryRun,
912
+ artifacts_written: Boolean(artifacts),
913
+ deferred_to_recovery: shouldDeferToRecovery,
914
+ deferred_to_user: shouldDeferToUser,
915
+ deferred_to_external: shouldDeferToExternal,
916
+ actions,
917
+ inspection: result,
918
+ artifacts,
919
+ message: shouldDeferToUser
920
+ ? result.runtime.next_action
921
+ : shouldDeferToExternal
922
+ ? `Wait for external input: ${result.runtime.next_action}`
923
+ : shouldDeferToRecovery
924
+ ? `Resume task ${result.runtime.recommended_task_id} before any bootstrap mutation.`
925
+ : "Project bootstrap completed.",
926
+ };
927
+ }