@urielsh/prodify 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/.prodify/AGENTS.md +56 -0
  2. package/.prodify/README.md +10 -0
  3. package/.prodify/artifacts/01-understand.md +28 -0
  4. package/.prodify/artifacts/02-diagnose.md +26 -0
  5. package/.prodify/artifacts/03-architecture.md +30 -0
  6. package/.prodify/artifacts/04-plan.md +35 -0
  7. package/.prodify/artifacts/05-refactor.md +24 -0
  8. package/.prodify/artifacts/06-validate.md +17 -0
  9. package/.prodify/artifacts/README.md +6 -0
  10. package/.prodify/artifacts/architecture_spec.md +29 -0
  11. package/.prodify/artifacts/artifact-validation-design.md +276 -0
  12. package/.prodify/artifacts/cli-command-design.md +299 -0
  13. package/.prodify/artifacts/completed-steps-tracking.md +201 -0
  14. package/.prodify/artifacts/diagnostic_report.md +45 -0
  15. package/.prodify/artifacts/hardening-patch-summary.md +57 -0
  16. package/.prodify/artifacts/hardening-verification-report.md +48 -0
  17. package/.prodify/artifacts/implementation_summary.md +19 -0
  18. package/.prodify/artifacts/improvement-evaluation-spec.md +148 -0
  19. package/.prodify/artifacts/next-step-resolver-design.md +570 -0
  20. package/.prodify/artifacts/orientation_map.md +32 -0
  21. package/.prodify/artifacts/persona-removal-audit.md +106 -0
  22. package/.prodify/artifacts/planning-alignment-report.md +189 -0
  23. package/.prodify/artifacts/refactor-plan-template-hardening.md +83 -0
  24. package/.prodify/artifacts/refactor-validate-loop-design.md +231 -0
  25. package/.prodify/artifacts/refactor_plan.md +21 -0
  26. package/.prodify/artifacts/refactor_plan.strict-example.md +31 -0
  27. package/.prodify/artifacts/run-state-design.md +292 -0
  28. package/.prodify/artifacts/run-summary-spec.md +149 -0
  29. package/.prodify/artifacts/run_state.json +14 -0
  30. package/.prodify/artifacts/sample-repo-test-plan.md +129 -0
  31. package/.prodify/artifacts/status-output-spec.md +349 -0
  32. package/.prodify/artifacts/step-selection-design.md +251 -0
  33. package/.prodify/artifacts/task-dispatcher-design.md +266 -0
  34. package/.prodify/artifacts/task-header-audit.md +42 -0
  35. package/.prodify/artifacts/task-log-enhancement-spec.md +264 -0
  36. package/.prodify/artifacts/task-protocol-hardening-plan.md +68 -0
  37. package/.prodify/artifacts/task-self-validation-spec.md +171 -0
  38. package/.prodify/artifacts/task_log.json +160 -0
  39. package/.prodify/artifacts/template-usage-audit.md +20 -0
  40. package/.prodify/artifacts/validation_report.md +22 -0
  41. package/.prodify/contracts/README.md +3 -0
  42. package/.prodify/contracts/architecture.contract.json +42 -0
  43. package/.prodify/contracts/diagnose.contract.json +42 -0
  44. package/.prodify/contracts/plan.contract.json +42 -0
  45. package/.prodify/contracts/refactor.contract.json +45 -0
  46. package/.prodify/contracts/understand.contract.json +42 -0
  47. package/.prodify/contracts/validate.contract.json +52 -0
  48. package/.prodify/contracts-src/README.md +4 -0
  49. package/.prodify/contracts-src/architecture.contract.md +29 -0
  50. package/.prodify/contracts-src/diagnose.contract.md +29 -0
  51. package/.prodify/contracts-src/plan.contract.md +29 -0
  52. package/.prodify/contracts-src/refactor.contract.md +32 -0
  53. package/.prodify/contracts-src/understand.contract.md +29 -0
  54. package/.prodify/contracts-src/validate.contract.md +35 -0
  55. package/.prodify/metrics/README.md +4 -0
  56. package/.prodify/planning.md +13 -0
  57. package/.prodify/project.md +15 -0
  58. package/.prodify/rules/01-global-operating-rules.md +21 -0
  59. package/.prodify/rules/02-analysis-discipline.md +17 -0
  60. package/.prodify/rules/03-diagnosis-severity-rules.md +42 -0
  61. package/.prodify/rules/04-architecture-rules.md +27 -0
  62. package/.prodify/rules/05-planning-rules.md +23 -0
  63. package/.prodify/rules/06-refactor-rules.md +20 -0
  64. package/.prodify/rules/07-validation-rules.md +25 -0
  65. package/.prodify/rules/08-output-format-rules.md +20 -0
  66. package/.prodify/rules/09-template-usage-rules.md +11 -0
  67. package/.prodify/rules/10-artifact-flow-and-orchestration-rules.md +40 -0
  68. package/.prodify/rules/README.md +3 -0
  69. package/.prodify/rules/example-rule.md +4 -0
  70. package/.prodify/runtime-commands.md +57 -0
  71. package/.prodify/skills/README.md +8 -0
  72. package/.prodify/skills/domain/react-frontend.skill.json +34 -0
  73. package/.prodify/skills/domain/typescript-backend.skill.json +34 -0
  74. package/.prodify/skills/quality-policy/maintainability-review.skill.json +25 -0
  75. package/.prodify/skills/quality-policy/security-hardening.skill.json +32 -0
  76. package/.prodify/skills/quality-policy/test-hardening.skill.json +23 -0
  77. package/.prodify/skills/registry.json +16 -0
  78. package/.prodify/skills/stage-method/architecture-method.skill.json +22 -0
  79. package/.prodify/skills/stage-method/codebase-scanning.skill.json +22 -0
  80. package/.prodify/skills/stage-method/diagnosis-method.skill.json +22 -0
  81. package/.prodify/skills/stage-method/planning-method.skill.json +22 -0
  82. package/.prodify/skills/stage-method/refactoring-method.skill.json +22 -0
  83. package/.prodify/skills/stage-method/validation-method.skill.json +22 -0
  84. package/.prodify/state.json +30 -0
  85. package/.prodify/tasks/01-understand.md +83 -0
  86. package/.prodify/tasks/02-diagnose.md +67 -0
  87. package/.prodify/tasks/03-architecture.md +70 -0
  88. package/.prodify/tasks/04-plan.md +71 -0
  89. package/.prodify/tasks/05-refactor.md +61 -0
  90. package/.prodify/tasks/06-validate.md +69 -0
  91. package/.prodify/tasks/README.md +3 -0
  92. package/.prodify/tasks/example-task.md +13 -0
  93. package/.prodify/templates/01-understand.template.md +18 -0
  94. package/.prodify/templates/02-diagnose.template.md +18 -0
  95. package/.prodify/templates/03-architecture.template.md +18 -0
  96. package/.prodify/templates/04-plan.template.md +22 -0
  97. package/.prodify/templates/05-refactor.template.md +19 -0
  98. package/.prodify/templates/06-validate.template.md +16 -0
  99. package/.prodify/templates/README.md +3 -0
  100. package/.prodify/templates/architecture_spec.template.md +29 -0
  101. package/.prodify/templates/diagnostic_report.template.md +45 -0
  102. package/.prodify/templates/example.template.md +5 -0
  103. package/.prodify/templates/implementation_summary.template.md +19 -0
  104. package/.prodify/templates/orientation_map.template.md +32 -0
  105. package/.prodify/templates/refactor_plan.template.md +24 -0
  106. package/.prodify/templates/validation_report.template.md +22 -0
  107. package/.prodify/version.json +5 -0
  108. package/AGENTS.md +305 -0
  109. package/LICENSE +201 -0
  110. package/README.md +118 -0
  111. package/assets/presets/default/canonical/AGENTS.md +54 -0
  112. package/assets/presets/default/canonical/artifacts/README.md +6 -0
  113. package/assets/presets/default/canonical/contracts-src/README.md +4 -0
  114. package/assets/presets/default/canonical/contracts-src/architecture.contract.md +56 -0
  115. package/assets/presets/default/canonical/contracts-src/diagnose.contract.md +49 -0
  116. package/assets/presets/default/canonical/contracts-src/plan.contract.md +42 -0
  117. package/assets/presets/default/canonical/contracts-src/refactor.contract.md +54 -0
  118. package/assets/presets/default/canonical/contracts-src/understand.contract.md +56 -0
  119. package/assets/presets/default/canonical/contracts-src/validate.contract.md +64 -0
  120. package/assets/presets/default/canonical/metrics/README.md +4 -0
  121. package/assets/presets/default/canonical/planning.md +13 -0
  122. package/assets/presets/default/canonical/project.md +15 -0
  123. package/assets/presets/default/canonical/rules/README.md +3 -0
  124. package/assets/presets/default/canonical/rules/example-rule.md +4 -0
  125. package/assets/presets/default/canonical/runtime-commands.md +57 -0
  126. package/assets/presets/default/canonical/skills/README.md +8 -0
  127. package/assets/presets/default/canonical/skills/domain/react-frontend.skill.json +34 -0
  128. package/assets/presets/default/canonical/skills/domain/typescript-backend.skill.json +34 -0
  129. package/assets/presets/default/canonical/skills/quality-policy/maintainability-review.skill.json +25 -0
  130. package/assets/presets/default/canonical/skills/quality-policy/security-hardening.skill.json +32 -0
  131. package/assets/presets/default/canonical/skills/quality-policy/test-hardening.skill.json +23 -0
  132. package/assets/presets/default/canonical/skills/registry.json +16 -0
  133. package/assets/presets/default/canonical/skills/stage-method/architecture-method.skill.json +22 -0
  134. package/assets/presets/default/canonical/skills/stage-method/codebase-scanning.skill.json +22 -0
  135. package/assets/presets/default/canonical/skills/stage-method/diagnosis-method.skill.json +22 -0
  136. package/assets/presets/default/canonical/skills/stage-method/planning-method.skill.json +22 -0
  137. package/assets/presets/default/canonical/skills/stage-method/refactoring-method.skill.json +22 -0
  138. package/assets/presets/default/canonical/skills/stage-method/validation-method.skill.json +22 -0
  139. package/assets/presets/default/canonical/state.json +30 -0
  140. package/assets/presets/default/canonical/tasks/README.md +3 -0
  141. package/assets/presets/default/canonical/tasks/example-task.md +13 -0
  142. package/assets/presets/default/canonical/templates/README.md +3 -0
  143. package/assets/presets/default/canonical/templates/example.template.md +5 -0
  144. package/assets/presets/default/preset.json +5 -0
  145. package/dist/cli.js +53 -0
  146. package/dist/commands/doctor.js +16 -0
  147. package/dist/commands/init.js +30 -0
  148. package/dist/commands/install.js +27 -0
  149. package/dist/commands/setup-agent.js +23 -0
  150. package/dist/commands/status.js +28 -0
  151. package/dist/commands/sync.js +29 -0
  152. package/dist/commands/update.js +18 -0
  153. package/dist/contracts/compiled-schema.js +37 -0
  154. package/dist/contracts/compiler.js +83 -0
  155. package/dist/contracts/freshness.js +52 -0
  156. package/dist/contracts/index.js +5 -0
  157. package/dist/contracts/parser.js +201 -0
  158. package/dist/contracts/schema.js +138 -0
  159. package/dist/contracts/source-schema.js +111 -0
  160. package/dist/core/agent-runtime.js +36 -0
  161. package/dist/core/agent-setup.js +111 -0
  162. package/dist/core/doctor.js +155 -0
  163. package/dist/core/errors.js +19 -0
  164. package/dist/core/flow-state.js +262 -0
  165. package/dist/core/fs.js +50 -0
  166. package/dist/core/install.js +44 -0
  167. package/dist/core/managed-files.js +106 -0
  168. package/dist/core/paths.js +56 -0
  169. package/dist/core/preset-validation.js +43 -0
  170. package/dist/core/prompt-builder.js +63 -0
  171. package/dist/core/repo-context.js +77 -0
  172. package/dist/core/repo-root.js +55 -0
  173. package/dist/core/skill-resolution.js +123 -0
  174. package/dist/core/state.js +220 -0
  175. package/dist/core/status.js +293 -0
  176. package/dist/core/sync.js +63 -0
  177. package/dist/core/targets.js +48 -0
  178. package/dist/core/upgrade.js +57 -0
  179. package/dist/core/validation.js +138 -0
  180. package/dist/core/version-checks.js +35 -0
  181. package/dist/generators/claude.js +14 -0
  182. package/dist/generators/codex.js +14 -0
  183. package/dist/generators/copilot.js +29 -0
  184. package/dist/generators/header.js +13 -0
  185. package/dist/generators/opencode.js +14 -0
  186. package/dist/generators/shared.js +37 -0
  187. package/dist/index.js +9 -0
  188. package/dist/legacy/targets.js +55 -0
  189. package/dist/presets/default.js +3 -0
  190. package/dist/presets/loader.js +34 -0
  191. package/dist/presets/version.js +18 -0
  192. package/dist/scoring/model.js +262 -0
  193. package/dist/skills/loader.js +40 -0
  194. package/dist/skills/schema.js +159 -0
  195. package/dist/types.js +1 -0
  196. package/docs/canonical-prodify-layout.md +87 -0
  197. package/docs/claude-support.md +22 -0
  198. package/docs/cli-doctor-spec.md +52 -0
  199. package/docs/cli-init-spec.md +70 -0
  200. package/docs/cli-install-agent-spec.md +48 -0
  201. package/docs/cli-sync-spec.md +40 -0
  202. package/docs/codex-support.md +24 -0
  203. package/docs/compatibility-targets.md +59 -0
  204. package/docs/copilot-support.md +30 -0
  205. package/docs/default-preset.md +47 -0
  206. package/docs/generated-file-headers.md +45 -0
  207. package/docs/generation-rules.md +94 -0
  208. package/docs/idempotency-guarantees.md +31 -0
  209. package/docs/managed-file-detection.md +45 -0
  210. package/docs/manual-edit-conflicts.md +37 -0
  211. package/docs/opencode-support.md +27 -0
  212. package/docs/ownership-rules.md +39 -0
  213. package/docs/path-resolution-rules.md +40 -0
  214. package/docs/preset-structure.md +49 -0
  215. package/docs/skill-system.md +67 -0
  216. package/docs/versioning-and-upgrade-strategy.md +40 -0
  217. package/package.json +22 -0
  218. package/src/README.md +11 -0
  219. package/src/cli.ts +61 -0
  220. package/src/commands/doctor.ts +20 -0
  221. package/src/commands/init.ts +37 -0
  222. package/src/commands/setup-agent.ts +28 -0
  223. package/src/commands/status.ts +34 -0
  224. package/src/commands/update.ts +23 -0
  225. package/src/contracts/README.md +10 -0
  226. package/src/contracts/compiled-schema.ts +42 -0
  227. package/src/contracts/compiler.ts +111 -0
  228. package/src/contracts/freshness.ts +58 -0
  229. package/src/contracts/index.ts +11 -0
  230. package/src/contracts/parser.ts +253 -0
  231. package/src/contracts/source-schema.ts +141 -0
  232. package/src/core/agent-runtime.ts +53 -0
  233. package/src/core/agent-setup.ts +147 -0
  234. package/src/core/doctor.ts +171 -0
  235. package/src/core/errors.ts +28 -0
  236. package/src/core/flow-state.ts +333 -0
  237. package/src/core/fs.ts +59 -0
  238. package/src/core/paths.ts +63 -0
  239. package/src/core/preset-validation.ts +47 -0
  240. package/src/core/prompt-builder.ts +73 -0
  241. package/src/core/repo-context.ts +93 -0
  242. package/src/core/repo-root.ts +74 -0
  243. package/src/core/skill-resolution.ts +151 -0
  244. package/src/core/state.ts +264 -0
  245. package/src/core/status.ts +372 -0
  246. package/src/core/targets.ts +53 -0
  247. package/src/core/upgrade.ts +66 -0
  248. package/src/core/validation.ts +233 -0
  249. package/src/core/version-checks.ts +40 -0
  250. package/src/index.ts +13 -0
  251. package/src/presets/default.ts +7 -0
  252. package/src/presets/loader.ts +46 -0
  253. package/src/presets/version.ts +31 -0
  254. package/src/scoring/model.ts +332 -0
  255. package/src/skills/loader.ts +58 -0
  256. package/src/skills/schema.ts +197 -0
  257. package/src/types.ts +329 -0
  258. package/tests/integration/cli-flows.test.js +347 -0
  259. package/tests/unit/agent-setup.test.js +81 -0
  260. package/tests/unit/cli.test.js +28 -0
  261. package/tests/unit/contracts.test.js +61 -0
  262. package/tests/unit/helpers.js +28 -0
  263. package/tests/unit/paths.test.js +52 -0
  264. package/tests/unit/preset-loader.test.js +37 -0
  265. package/tests/unit/scoring.test.js +65 -0
  266. package/tests/unit/self-hosted-workspace.test.js +22 -0
  267. package/tests/unit/skills.test.js +115 -0
  268. package/tests/unit/state-and-flow.test.js +120 -0
  269. package/tests/unit/targets.test.js +13 -0
  270. package/tests/unit/validation.test.js +73 -0
  271. package/tests/unit/version.test.js +24 -0
  272. package/tsconfig.json +23 -0
  273. package/urielsh-prodify-0.1.0.tgz +0 -0
@@ -0,0 +1,233 @@
1
+ import fs from 'node:fs/promises';
2
+
3
+ import { loadCompiledContract } from '../contracts/compiler.js';
4
+ import { normalizeRepoRelativePath, resolveRepoPath } from './paths.js';
5
+ import type { CompiledStageContract, FlowStage, ProdifyState, StageValidationResult, ValidationIssue } from '../types.js';
6
+
7
+ function pathIsWithin(pathToCheck: string, root: string): boolean {
8
+ const normalizedPath = normalizeRepoRelativePath(pathToCheck);
9
+ const normalizedRoot = normalizeRepoRelativePath(root);
10
+ return normalizedPath === normalizedRoot || normalizedPath.startsWith(`${normalizedRoot.replace(/\/$/, '')}/`);
11
+ }
12
+
13
+ function collectMarkdownSections(markdown: string): Map<string, string> {
14
+ const sections = new Map<string, string>();
15
+ const lines = markdown.replace(/\r\n/g, '\n').split('\n');
16
+ let currentHeading: string | null = null;
17
+ let buffer: string[] = [];
18
+
19
+ const flush = (): void => {
20
+ if (currentHeading) {
21
+ sections.set(currentHeading, buffer.join('\n').trim());
22
+ }
23
+ };
24
+
25
+ for (const line of lines) {
26
+ const headingMatch = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
27
+ if (headingMatch) {
28
+ flush();
29
+ currentHeading = headingMatch[2].trim();
30
+ buffer = [];
31
+ continue;
32
+ }
33
+
34
+ buffer.push(line);
35
+ }
36
+
37
+ flush();
38
+ return sections;
39
+ }
40
+
41
+ function containsAllStatements(sectionContent: string, statements: string[]): boolean {
42
+ return statements.every((statement) => sectionContent.includes(statement));
43
+ }
44
+
45
+ function buildRule(rule: string, message: string, path?: string): ValidationIssue {
46
+ return { rule, message, path };
47
+ }
48
+
49
+ async function validateArtifact(
50
+ repoRoot: string,
51
+ contract: CompiledStageContract,
52
+ artifact: CompiledStageContract['required_artifacts'][number]
53
+ ): Promise<{ issues: ValidationIssue[]; missing: string[]; diagnostics: string[] }> {
54
+ const artifactPath = resolveRepoPath(repoRoot, artifact.path);
55
+ const issues: ValidationIssue[] = [];
56
+ const missing: string[] = [];
57
+ const diagnostics: string[] = [];
58
+
59
+ try {
60
+ const content = await fs.readFile(artifactPath, 'utf8');
61
+ diagnostics.push(`validated artifact ${artifact.path}`);
62
+
63
+ if (!contract.allowed_write_roots.some((root) => pathIsWithin(artifact.path, root))) {
64
+ issues.push(buildRule(
65
+ 'artifact/outside-allowed-roots',
66
+ `Required artifact ${artifact.path} is outside allowed write roots.`,
67
+ artifact.path
68
+ ));
69
+ }
70
+
71
+ if (artifact.format === 'markdown') {
72
+ const sections = collectMarkdownSections(content);
73
+ for (const section of artifact.required_sections) {
74
+ if (!sections.has(section)) {
75
+ issues.push(buildRule(
76
+ 'artifact/markdown-section-missing',
77
+ `Markdown artifact ${artifact.path} is missing section "${section}".`,
78
+ artifact.path
79
+ ));
80
+ }
81
+ }
82
+
83
+ const successCriteriaSection = sections.get('Success Criteria') ?? '';
84
+ if (contract.success_criteria.length > 0 && !containsAllStatements(successCriteriaSection, contract.success_criteria)) {
85
+ issues.push(buildRule(
86
+ 'artifact/success-criteria-missing',
87
+ `Markdown artifact ${artifact.path} does not document all success criteria.`,
88
+ artifact.path
89
+ ));
90
+ }
91
+
92
+ const policyChecksSection = sections.get('Policy Checks') ?? '';
93
+ if (contract.policy_rules.length > 0 && !containsAllStatements(policyChecksSection, contract.policy_rules)) {
94
+ issues.push(buildRule(
95
+ 'artifact/policy-rules-missing',
96
+ `Markdown artifact ${artifact.path} does not document all policy checks.`,
97
+ artifact.path
98
+ ));
99
+ }
100
+ } else {
101
+ const parsed = JSON.parse(content) as Record<string, unknown>;
102
+ for (const key of artifact.required_json_keys) {
103
+ if (!(key in parsed)) {
104
+ issues.push(buildRule(
105
+ 'artifact/json-key-missing',
106
+ `JSON artifact ${artifact.path} is missing key "${key}".`,
107
+ artifact.path
108
+ ));
109
+ }
110
+ }
111
+ }
112
+ } catch {
113
+ missing.push(artifact.path);
114
+ issues.push(buildRule(
115
+ 'artifact/missing',
116
+ `Required artifact ${artifact.path} is missing.`,
117
+ artifact.path
118
+ ));
119
+ }
120
+
121
+ return {
122
+ issues,
123
+ missing,
124
+ diagnostics
125
+ };
126
+ }
127
+
128
+ export async function validateStageOutputs(
129
+ repoRoot: string,
130
+ {
131
+ contract,
132
+ runtimeState,
133
+ touchedPaths = []
134
+ }: {
135
+ contract: CompiledStageContract;
136
+ runtimeState: ProdifyState;
137
+ touchedPaths?: string[];
138
+ }
139
+ ): Promise<StageValidationResult> {
140
+ const violatedRules: ValidationIssue[] = [];
141
+ const missingArtifacts: string[] = [];
142
+ const diagnostics: string[] = [];
143
+ const warnings: string[] = [];
144
+
145
+ if (runtimeState.runtime.current_stage !== contract.stage) {
146
+ violatedRules.push(buildRule(
147
+ 'runtime/stage-mismatch',
148
+ `Runtime stage ${runtimeState.runtime.current_stage ?? 'none'} does not match contract stage ${contract.stage}.`
149
+ ));
150
+ }
151
+
152
+ for (const artifact of contract.required_artifacts) {
153
+ const artifactValidation = await validateArtifact(repoRoot, contract, artifact);
154
+ violatedRules.push(...artifactValidation.issues);
155
+ missingArtifacts.push(...artifactValidation.missing);
156
+ diagnostics.push(...artifactValidation.diagnostics);
157
+ }
158
+
159
+ for (const rawTouchedPath of touchedPaths.map((entry) => normalizeRepoRelativePath(entry))) {
160
+ if (!contract.allowed_write_roots.some((root) => pathIsWithin(rawTouchedPath, root))) {
161
+ violatedRules.push(buildRule(
162
+ 'writes/outside-allowed-roots',
163
+ `Touched path ${rawTouchedPath} is outside the contract write boundary.`,
164
+ rawTouchedPath
165
+ ));
166
+ }
167
+
168
+ if (contract.forbidden_writes.some((entry) => pathIsWithin(rawTouchedPath, entry))) {
169
+ violatedRules.push(buildRule(
170
+ 'writes/forbidden',
171
+ `Touched path ${rawTouchedPath} is forbidden by the contract.`,
172
+ rawTouchedPath
173
+ ));
174
+ }
175
+ }
176
+
177
+ if (touchedPaths.length === 0) {
178
+ warnings.push('No touched paths were provided; forbidden-write checks are limited to required artifacts.');
179
+ }
180
+
181
+ return {
182
+ stage: contract.stage,
183
+ contract_version: contract.contract_version,
184
+ passed: violatedRules.length === 0,
185
+ violated_rules: violatedRules,
186
+ missing_artifacts: [...new Set(missingArtifacts)].sort((left, right) => left.localeCompare(right)),
187
+ warnings,
188
+ diagnostics
189
+ };
190
+ }
191
+
192
+ export async function validateStageOutputsForCurrentState(
193
+ repoRoot: string,
194
+ {
195
+ runtimeState,
196
+ touchedPaths = []
197
+ }: {
198
+ runtimeState: ProdifyState;
199
+ touchedPaths?: string[];
200
+ }
201
+ ): Promise<StageValidationResult> {
202
+ const stage = runtimeState.runtime.current_stage;
203
+ if (!stage) {
204
+ throw new Error('Cannot validate stage outputs when no stage is active.');
205
+ }
206
+
207
+ const contract = await loadCompiledContract(repoRoot, stage);
208
+ return validateStageOutputs(repoRoot, {
209
+ contract,
210
+ runtimeState,
211
+ touchedPaths
212
+ });
213
+ }
214
+
215
+ export async function validateStageOutputsForStage(
216
+ repoRoot: string,
217
+ {
218
+ stage,
219
+ runtimeState,
220
+ touchedPaths = []
221
+ }: {
222
+ stage: FlowStage;
223
+ runtimeState: ProdifyState;
224
+ touchedPaths?: string[];
225
+ }
226
+ ): Promise<StageValidationResult> {
227
+ const contract = await loadCompiledContract(repoRoot, stage);
228
+ return validateStageOutputs(repoRoot, {
229
+ contract,
230
+ runtimeState,
231
+ touchedPaths
232
+ });
233
+ }
@@ -0,0 +1,40 @@
1
+ import fs from 'node:fs/promises';
2
+
3
+ import { pathExists } from './fs.js';
4
+ import { resolveCanonicalPath } from './paths.js';
5
+ import { parseVersionMetadata } from '../presets/version.js';
6
+ import type { VersionInspection, VersionMetadata } from '../types.js';
7
+
8
+ export async function inspectVersionStatus(repoRoot: string, presetMetadata: VersionMetadata): Promise<VersionInspection> {
9
+ const versionPath = resolveCanonicalPath(repoRoot, '.prodify/version.json');
10
+
11
+ if (!(await pathExists(versionPath))) {
12
+ return {
13
+ status: 'missing',
14
+ current: null,
15
+ expected: presetMetadata,
16
+ schemaMigrationRequired: false
17
+ };
18
+ }
19
+
20
+ try {
21
+ const current = parseVersionMetadata(await fs.readFile(versionPath, 'utf8'));
22
+ const versionMatches = current.presetName === presetMetadata.name
23
+ && current.presetVersion === presetMetadata.version;
24
+ const schemaMatches = current.schemaVersion === presetMetadata.schemaVersion;
25
+
26
+ return {
27
+ status: versionMatches && schemaMatches ? 'current' : 'outdated',
28
+ current,
29
+ expected: presetMetadata,
30
+ schemaMigrationRequired: !schemaMatches
31
+ };
32
+ } catch {
33
+ return {
34
+ status: 'malformed',
35
+ current: null,
36
+ expected: presetMetadata,
37
+ schemaMigrationRequired: true
38
+ };
39
+ }
40
+ }
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from './cli.js';
3
+ import type { CommandContext } from './types.js';
4
+
5
+ const context: CommandContext = {
6
+ cwd: process.cwd(),
7
+ stdout: process.stdout,
8
+ stderr: process.stderr
9
+ };
10
+
11
+ const exitCode = await runCli(process.argv.slice(2), context);
12
+
13
+ process.exitCode = exitCode;
@@ -0,0 +1,7 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ export const DEFAULT_PRESET_ASSET_DIR = path.join(
5
+ path.dirname(fileURLToPath(import.meta.url)),
6
+ '../../assets/presets/default'
7
+ );
@@ -0,0 +1,46 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ import { listFilesRecursive } from '../core/fs.js';
5
+ import { validatePresetEntries } from '../core/preset-validation.js';
6
+ import { DEFAULT_PRESET_ASSET_DIR } from './default.js';
7
+ import { serializeVersionMetadata } from './version.js';
8
+ import type { LoadedPreset, PresetEntry, VersionMetadata } from '../types.js';
9
+
10
+ export async function loadDefaultPreset(): Promise<LoadedPreset> {
11
+ const metadataPath = path.join(DEFAULT_PRESET_ASSET_DIR, 'preset.json');
12
+ const canonicalDir = path.join(DEFAULT_PRESET_ASSET_DIR, 'canonical');
13
+ const rawMetadata = JSON.parse(await fs.readFile(metadataPath, 'utf8')) as {
14
+ name: string;
15
+ version: string;
16
+ schemaVersion: string;
17
+ };
18
+ const metadata: VersionMetadata = {
19
+ name: rawMetadata.name,
20
+ version: rawMetadata.version,
21
+ schemaVersion: rawMetadata.schemaVersion
22
+ };
23
+ const rawFiles = await listFilesRecursive(canonicalDir);
24
+
25
+ const entries: PresetEntry[] = [];
26
+
27
+ for (const file of rawFiles) {
28
+ entries.push({
29
+ relativePath: `.prodify/${file.relativePath}`,
30
+ content: await fs.readFile(file.fullPath, 'utf8')
31
+ });
32
+ }
33
+
34
+ entries.push({
35
+ relativePath: '.prodify/version.json',
36
+ content: serializeVersionMetadata(metadata)
37
+ });
38
+
39
+ entries.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
40
+ validatePresetEntries(entries);
41
+
42
+ return {
43
+ metadata,
44
+ entries
45
+ };
46
+ }
@@ -0,0 +1,31 @@
1
+ import type { ParsedVersionMetadata, VersionMetadata } from '../types.js';
2
+
3
+ export function createVersionMetadata(presetMetadata: VersionMetadata): {
4
+ schema_version: string;
5
+ preset_name: string;
6
+ preset_version: string;
7
+ } {
8
+ return {
9
+ schema_version: presetMetadata.schemaVersion,
10
+ preset_name: presetMetadata.name,
11
+ preset_version: presetMetadata.version
12
+ };
13
+ }
14
+
15
+ export function serializeVersionMetadata(presetMetadata: VersionMetadata): string {
16
+ return `${JSON.stringify(createVersionMetadata(presetMetadata), null, 2)}\n`;
17
+ }
18
+
19
+ export function parseVersionMetadata(content: string): ParsedVersionMetadata {
20
+ const parsed = JSON.parse(content) as {
21
+ schema_version: string;
22
+ preset_name: string;
23
+ preset_version: string;
24
+ };
25
+
26
+ return {
27
+ schemaVersion: parsed.schema_version,
28
+ presetName: parsed.preset_name,
29
+ presetVersion: parsed.preset_version
30
+ };
31
+ }