@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,262 @@
1
+ import { ProdifyError } from './errors.js';
2
+ import { isRuntimeProfileName } from './paths.js';
3
+ import { RUNTIME_STATUS } from './state.js';
4
+ export const STAGE_ORDER = ['understand', 'diagnose', 'architecture', 'plan', 'refactor', 'validate'];
5
+ const STAGE_TASK_IDS = {
6
+ understand: '01-understand',
7
+ diagnose: '02-diagnose',
8
+ architecture: '03-architecture',
9
+ plan: '04-plan',
10
+ refactor: '05-refactor',
11
+ validate: '06-validate'
12
+ };
13
+ function cloneState(state) {
14
+ return {
15
+ ...state,
16
+ runtime: {
17
+ ...state.runtime,
18
+ completed_stages: [...state.runtime.completed_stages],
19
+ last_validated_contract_versions: {
20
+ ...state.runtime.last_validated_contract_versions
21
+ },
22
+ last_validation: state.runtime.last_validation
23
+ ? {
24
+ ...state.runtime.last_validation,
25
+ violated_rules: [...state.runtime.last_validation.violated_rules],
26
+ missing_artifacts: [...state.runtime.last_validation.missing_artifacts],
27
+ warnings: [...state.runtime.last_validation.warnings],
28
+ diagnostics: [...state.runtime.last_validation.diagnostics]
29
+ }
30
+ : null,
31
+ failure_metadata: state.runtime.failure_metadata
32
+ ? { ...state.runtime.failure_metadata }
33
+ : null,
34
+ bootstrap: {
35
+ ...state.runtime.bootstrap
36
+ },
37
+ timestamps: {
38
+ ...state.runtime.timestamps
39
+ }
40
+ }
41
+ };
42
+ }
43
+ function nextStage(currentStage) {
44
+ const index = STAGE_ORDER.indexOf(currentStage);
45
+ if (index === -1) {
46
+ return null;
47
+ }
48
+ return STAGE_ORDER[index + 1] ?? null;
49
+ }
50
+ function pendingState(stage) {
51
+ return `${stage}_pending`;
52
+ }
53
+ function completeState(stage) {
54
+ return `${stage}_complete`;
55
+ }
56
+ function assertAgent(agent) {
57
+ if (!isRuntimeProfileName(agent)) {
58
+ throw new ProdifyError(`Unknown target agent: ${agent}`, {
59
+ code: 'UNKNOWN_TARGET'
60
+ });
61
+ }
62
+ }
63
+ function assertMode(mode) {
64
+ if (!['interactive', 'auto'].includes(mode)) {
65
+ throw new ProdifyError(`Unsupported runtime mode: ${mode}`, {
66
+ code: 'INVALID_RUNTIME_MODE'
67
+ });
68
+ }
69
+ }
70
+ function ensureStageCheckpoint(state) {
71
+ if (!state.runtime.current_stage) {
72
+ throw new ProdifyError('Cannot complete a runtime stage when no stage is active.', {
73
+ code: 'RUNTIME_STAGE_MISSING'
74
+ });
75
+ }
76
+ return state.runtime.current_stage;
77
+ }
78
+ export function stageToTaskId(stage) {
79
+ return STAGE_TASK_IDS[stage];
80
+ }
81
+ export function bootstrapFlowState(state, { agent, mode = 'interactive', now = null }) {
82
+ assertAgent(agent);
83
+ assertMode(mode);
84
+ const nextState = cloneState(state);
85
+ nextState.runtime.status = RUNTIME_STATUS.READY;
86
+ nextState.runtime.current_state = 'bootstrapped';
87
+ nextState.runtime.mode = mode;
88
+ nextState.runtime.current_stage = null;
89
+ nextState.runtime.current_task_id = null;
90
+ nextState.runtime.pending_stage = STAGE_ORDER[0];
91
+ nextState.runtime.completed_stages = [];
92
+ nextState.runtime.awaiting_user_validation = false;
93
+ nextState.runtime.last_validation_result = 'unknown';
94
+ nextState.runtime.last_validation = null;
95
+ nextState.runtime.last_validated_contract_versions = {};
96
+ nextState.runtime.resumable = true;
97
+ nextState.runtime.blocked_reason = null;
98
+ nextState.runtime.failure_metadata = null;
99
+ nextState.runtime.bootstrap = {
100
+ bootstrapped: true
101
+ };
102
+ nextState.runtime.next_action = mode === 'auto' ? '$prodify-execute --auto' : '$prodify-execute';
103
+ nextState.runtime.timestamps.bootstrapped_at = nextState.runtime.timestamps.bootstrapped_at ?? now;
104
+ nextState.runtime.timestamps.last_transition_at = now;
105
+ nextState.runtime.timestamps.completed_at = null;
106
+ return nextState;
107
+ }
108
+ export function startFlowExecution(state, { mode = state.runtime.mode ?? 'interactive', now = null } = {}) {
109
+ assertMode(mode);
110
+ const nextState = cloneState(state);
111
+ nextState.runtime.mode = mode;
112
+ if (nextState.runtime.current_state === 'validate_complete') {
113
+ nextState.runtime.status = RUNTIME_STATUS.COMPLETE;
114
+ nextState.runtime.current_state = 'completed';
115
+ nextState.runtime.current_stage = null;
116
+ nextState.runtime.current_task_id = null;
117
+ nextState.runtime.pending_stage = null;
118
+ nextState.runtime.awaiting_user_validation = false;
119
+ nextState.runtime.resumable = false;
120
+ nextState.runtime.next_action = 'none';
121
+ nextState.runtime.timestamps.completed_at = now;
122
+ nextState.runtime.timestamps.last_transition_at = now;
123
+ return nextState;
124
+ }
125
+ const stage = nextState.runtime.pending_stage;
126
+ if (!stage) {
127
+ throw new ProdifyError('Cannot start flow execution without a pending stage.', {
128
+ code: 'RUNTIME_STAGE_MISSING'
129
+ });
130
+ }
131
+ nextState.runtime.status = RUNTIME_STATUS.RUNNING;
132
+ nextState.runtime.current_state = pendingState(stage);
133
+ nextState.runtime.current_stage = stage;
134
+ nextState.runtime.current_task_id = stageToTaskId(stage);
135
+ nextState.runtime.pending_stage = null;
136
+ nextState.runtime.awaiting_user_validation = false;
137
+ nextState.runtime.blocked_reason = null;
138
+ nextState.runtime.failure_metadata = null;
139
+ nextState.runtime.next_action = '$prodify-resume';
140
+ nextState.runtime.resumable = true;
141
+ nextState.runtime.timestamps.last_transition_at = now;
142
+ return nextState;
143
+ }
144
+ export function completeFlowStage(state, { validation, now = null }) {
145
+ const currentStage = ensureStageCheckpoint(state);
146
+ if (validation.stage !== currentStage) {
147
+ throw new ProdifyError(`Validation result stage ${validation.stage} does not match active stage ${currentStage}.`, {
148
+ code: 'VALIDATION_STAGE_MISMATCH'
149
+ });
150
+ }
151
+ if (!validation.passed) {
152
+ return failFlowStage(state, {
153
+ reason: validation.violated_rules.map((issue) => issue.message).join('; ') || 'stage validation failed',
154
+ validation,
155
+ now
156
+ });
157
+ }
158
+ const nextState = cloneState(state);
159
+ if (!nextState.runtime.completed_stages.includes(currentStage)) {
160
+ nextState.runtime.completed_stages.push(currentStage);
161
+ }
162
+ nextState.runtime.last_validation_result = 'pass';
163
+ nextState.runtime.last_validation = validation;
164
+ nextState.runtime.last_validated_contract_versions[currentStage] = validation.contract_version;
165
+ nextState.runtime.blocked_reason = null;
166
+ nextState.runtime.failure_metadata = null;
167
+ nextState.runtime.timestamps.last_transition_at = now;
168
+ const upcomingStage = nextStage(currentStage);
169
+ if (!upcomingStage) {
170
+ nextState.runtime.current_state = 'validate_complete';
171
+ nextState.runtime.current_stage = currentStage;
172
+ nextState.runtime.current_task_id = stageToTaskId(currentStage);
173
+ nextState.runtime.pending_stage = null;
174
+ if (nextState.runtime.mode === 'interactive') {
175
+ nextState.runtime.status = RUNTIME_STATUS.AWAITING_VALIDATION;
176
+ nextState.runtime.awaiting_user_validation = true;
177
+ nextState.runtime.resumable = true;
178
+ nextState.runtime.next_action = '$prodify-resume';
179
+ return nextState;
180
+ }
181
+ return startFlowExecution(nextState, {
182
+ mode: nextState.runtime.mode ?? 'auto',
183
+ now
184
+ });
185
+ }
186
+ if (nextState.runtime.mode === 'interactive') {
187
+ nextState.runtime.status = RUNTIME_STATUS.AWAITING_VALIDATION;
188
+ nextState.runtime.current_state = completeState(currentStage);
189
+ nextState.runtime.current_stage = currentStage;
190
+ nextState.runtime.current_task_id = stageToTaskId(currentStage);
191
+ nextState.runtime.pending_stage = upcomingStage;
192
+ nextState.runtime.awaiting_user_validation = true;
193
+ nextState.runtime.resumable = true;
194
+ nextState.runtime.next_action = '$prodify-resume';
195
+ return nextState;
196
+ }
197
+ nextState.runtime.status = RUNTIME_STATUS.READY;
198
+ nextState.runtime.current_state = pendingState(upcomingStage);
199
+ nextState.runtime.current_stage = upcomingStage;
200
+ nextState.runtime.current_task_id = stageToTaskId(upcomingStage);
201
+ nextState.runtime.pending_stage = null;
202
+ nextState.runtime.awaiting_user_validation = false;
203
+ nextState.runtime.resumable = true;
204
+ nextState.runtime.next_action = '$prodify-execute --auto';
205
+ return nextState;
206
+ }
207
+ export function failFlowStage(state, { reason, validation = null, now = null }) {
208
+ const nextState = cloneState(state);
209
+ nextState.runtime.status = RUNTIME_STATUS.FAILED;
210
+ nextState.runtime.current_state = 'failed';
211
+ nextState.runtime.blocked_reason = reason;
212
+ nextState.runtime.awaiting_user_validation = false;
213
+ nextState.runtime.resumable = false;
214
+ nextState.runtime.next_action = 'repair runtime state';
215
+ nextState.runtime.last_validation_result = validation ? 'fail' : nextState.runtime.last_validation_result;
216
+ nextState.runtime.last_validation = validation;
217
+ nextState.runtime.failure_metadata = {
218
+ stage: validation?.stage ?? nextState.runtime.current_stage,
219
+ contract_version: validation?.contract_version ?? null,
220
+ reason
221
+ };
222
+ nextState.runtime.timestamps.last_transition_at = now;
223
+ return nextState;
224
+ }
225
+ export function getResumeDecision(state) {
226
+ const runtime = state.runtime;
227
+ if (runtime.current_state === 'completed' || runtime.status === RUNTIME_STATUS.COMPLETE) {
228
+ return {
229
+ resumable: false,
230
+ command: null,
231
+ reason: 'flow complete'
232
+ };
233
+ }
234
+ if (runtime.current_state === 'failed' || runtime.current_state === 'blocked') {
235
+ return {
236
+ resumable: false,
237
+ command: null,
238
+ reason: runtime.blocked_reason ?? 'runtime is blocked'
239
+ };
240
+ }
241
+ if (!runtime.resumable) {
242
+ return {
243
+ resumable: false,
244
+ command: null,
245
+ reason: runtime.blocked_reason ?? 'runtime not bootstrapped'
246
+ };
247
+ }
248
+ if (runtime.status === RUNTIME_STATUS.READY) {
249
+ return {
250
+ resumable: true,
251
+ command: runtime.mode === 'auto' ? '$prodify-execute --auto' : '$prodify-execute',
252
+ reason: runtime.pending_stage
253
+ ? `ready for ${runtime.pending_stage}`
254
+ : runtime.current_state
255
+ };
256
+ }
257
+ return {
258
+ resumable: true,
259
+ command: '$prodify-resume',
260
+ reason: runtime.current_state
261
+ };
262
+ }
@@ -0,0 +1,50 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ export async function pathExists(targetPath) {
4
+ try {
5
+ await fs.access(targetPath);
6
+ return true;
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ }
12
+ export async function ensureDir(targetPath) {
13
+ await fs.mkdir(targetPath, { recursive: true });
14
+ }
15
+ export async function writeFileEnsuringDir(targetPath, content) {
16
+ await ensureDir(path.dirname(targetPath));
17
+ await fs.writeFile(targetPath, content, 'utf8');
18
+ }
19
+ export async function copyTree(sourceDir, destinationDir) {
20
+ const entries = await fs.readdir(sourceDir, { withFileTypes: true });
21
+ await ensureDir(destinationDir);
22
+ for (const entry of entries) {
23
+ const sourcePath = path.join(sourceDir, entry.name);
24
+ const destinationPath = path.join(destinationDir, entry.name);
25
+ if (entry.isDirectory()) {
26
+ await copyTree(sourcePath, destinationPath);
27
+ }
28
+ else if (entry.isFile()) {
29
+ await writeFileEnsuringDir(destinationPath, await fs.readFile(sourcePath, 'utf8'));
30
+ }
31
+ }
32
+ }
33
+ export async function listFilesRecursive(rootDir, prefix = '') {
34
+ const entries = await fs.readdir(rootDir, { withFileTypes: true });
35
+ const files = [];
36
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
37
+ const nextPrefix = prefix ? path.posix.join(prefix, entry.name) : entry.name;
38
+ const fullPath = path.join(rootDir, entry.name);
39
+ if (entry.isDirectory()) {
40
+ files.push(...(await listFilesRecursive(fullPath, nextPrefix)));
41
+ }
42
+ else if (entry.isFile()) {
43
+ files.push({
44
+ fullPath,
45
+ relativePath: nextPrefix
46
+ });
47
+ }
48
+ }
49
+ return files;
50
+ }
@@ -0,0 +1,44 @@
1
+ import fs from 'node:fs/promises';
2
+ import { ProdifyError } from './errors.js';
3
+ import { pathExists, writeFileEnsuringDir } from './fs.js';
4
+ import { detectManagedFileState, parseManagedFileHeader } from './managed-files.js';
5
+ import { resolveRepoPath, resolveTargetPath } from './paths.js';
6
+ import { assertSupportedInstallTarget } from '../legacy/targets.js';
7
+ export async function installTarget(repoRoot, agent, options = {}) {
8
+ const prodifyDir = resolveRepoPath(repoRoot, '.prodify');
9
+ if (!(await pathExists(prodifyDir))) {
10
+ throw new ProdifyError('Canonical .prodify/ directory is missing.', {
11
+ code: 'PRODIFY_MISSING'
12
+ });
13
+ }
14
+ const targetMetadata = assertSupportedInstallTarget(agent);
15
+ const targetPath = resolveTargetPath(repoRoot, agent);
16
+ if (!targetPath) {
17
+ throw new ProdifyError(`Unknown target agent: ${agent}`, {
18
+ code: 'UNKNOWN_TARGET'
19
+ });
20
+ }
21
+ const nextContent = await targetMetadata.generator(repoRoot);
22
+ if (await pathExists(targetPath)) {
23
+ const existingContent = await fs.readFile(targetPath, 'utf8');
24
+ const managedHeader = parseManagedFileHeader(existingContent);
25
+ if (!managedHeader) {
26
+ throw new ProdifyError(`Target file already exists and is not Prodify-managed: ${targetPath}`, {
27
+ code: 'UNMANAGED_TARGET_EXISTS'
28
+ });
29
+ }
30
+ const state = detectManagedFileState(existingContent, nextContent);
31
+ if (state.state === 'conflict' && !options.force) {
32
+ throw new ProdifyError(`Managed target has manual edits and is blocked: ${targetPath}. Re-run with --force to overwrite.`, {
33
+ code: 'MANAGED_CONFLICT'
34
+ });
35
+ }
36
+ }
37
+ await writeFileEnsuringDir(targetPath, nextContent);
38
+ return {
39
+ agent: targetMetadata.agent,
40
+ targetPath,
41
+ changed: true,
42
+ status: targetMetadata.status
43
+ };
44
+ }
@@ -0,0 +1,106 @@
1
+ import crypto from 'node:crypto';
2
+ import { isRuntimeProfileName } from './paths.js';
3
+ const HEADER_START = '<!--';
4
+ const HEADER_END = '-->';
5
+ export function renderCanonicalSourceList(sourcePaths) {
6
+ return [...sourcePaths].sort().join(', ');
7
+ }
8
+ export function parseManagedFileHeader(content) {
9
+ if (!content.startsWith(HEADER_START)) {
10
+ return null;
11
+ }
12
+ const endIndex = content.indexOf(HEADER_END);
13
+ if (endIndex === -1) {
14
+ return null;
15
+ }
16
+ const rawHeader = content.slice(HEADER_START.length, endIndex).trim();
17
+ const lines = rawHeader.split('\n').map((line) => line.trim()).filter(Boolean);
18
+ const metadata = {};
19
+ for (const line of lines) {
20
+ if (line === 'Generated by Prodify.') {
21
+ metadata.generatedByProdify = true;
22
+ continue;
23
+ }
24
+ const separatorIndex = line.indexOf(':');
25
+ if (separatorIndex === -1) {
26
+ if (line === 'Manual edits may be overwritten.') {
27
+ metadata.manualEditsWarning = true;
28
+ }
29
+ continue;
30
+ }
31
+ const key = line.slice(0, separatorIndex).trim();
32
+ const value = line.slice(separatorIndex + 1).trim();
33
+ metadata[key] = value;
34
+ }
35
+ if (metadata.generatedByProdify !== true) {
36
+ return null;
37
+ }
38
+ const targetAgent = metadata['Target agent'];
39
+ const canonicalSource = metadata['Canonical source'];
40
+ const regenerateWith = metadata['Regenerate with'];
41
+ const bodyFingerprint = metadata['Body fingerprint'];
42
+ if (typeof targetAgent !== 'string'
43
+ || !isRuntimeProfileName(targetAgent)
44
+ || typeof canonicalSource !== 'string'
45
+ || typeof regenerateWith !== 'string') {
46
+ return null;
47
+ }
48
+ return {
49
+ targetAgent,
50
+ canonicalSources: canonicalSource.split(',').map((item) => item.trim()).filter(Boolean),
51
+ regenerateWith,
52
+ bodyFingerprint: typeof bodyFingerprint === 'string' ? bodyFingerprint : null,
53
+ manualEditsWarning: metadata.manualEditsWarning === true,
54
+ headerLength: endIndex + HEADER_END.length
55
+ };
56
+ }
57
+ export function isManagedFileContent(content) {
58
+ return parseManagedFileHeader(content) !== null;
59
+ }
60
+ export function extractManagedBody(content, headerLength) {
61
+ let body = content.slice(headerLength);
62
+ if (body.startsWith('\r\n\r\n')) {
63
+ return body.slice(4);
64
+ }
65
+ if (body.startsWith('\n\n')) {
66
+ return body.slice(2);
67
+ }
68
+ if (body.startsWith('\r\n')) {
69
+ return body.slice(2);
70
+ }
71
+ if (body.startsWith('\n')) {
72
+ return body.slice(1);
73
+ }
74
+ return body;
75
+ }
76
+ export function computeBodyFingerprint(body) {
77
+ return crypto.createHash('sha256').update(body, 'utf8').digest('hex');
78
+ }
79
+ export function detectManagedFileState(existingContent, expectedContent) {
80
+ const existingHeader = parseManagedFileHeader(existingContent);
81
+ if (!existingHeader) {
82
+ return {
83
+ state: 'unmanaged',
84
+ header: null
85
+ };
86
+ }
87
+ const expectedHeader = parseManagedFileHeader(expectedContent);
88
+ const currentBody = extractManagedBody(existingContent, existingHeader.headerLength);
89
+ const currentFingerprint = computeBodyFingerprint(currentBody);
90
+ if (existingHeader.bodyFingerprint && existingHeader.bodyFingerprint !== currentFingerprint) {
91
+ return {
92
+ state: 'conflict',
93
+ header: existingHeader
94
+ };
95
+ }
96
+ if (existingContent === expectedContent) {
97
+ return {
98
+ state: 'unchanged',
99
+ header: existingHeader
100
+ };
101
+ }
102
+ return {
103
+ state: 'outdated',
104
+ header: expectedHeader ?? existingHeader
105
+ };
106
+ }
@@ -0,0 +1,56 @@
1
+ import path from 'node:path';
2
+ export const RUNTIME_PROFILE_NAMES = ['codex', 'claude', 'copilot', 'opencode'];
3
+ export const USER_OWNED_CANONICAL_PATHS = [
4
+ '.prodify/AGENTS.md',
5
+ '.prodify/project.md',
6
+ '.prodify/planning.md'
7
+ ];
8
+ export const USER_OWNED_CANONICAL_PREFIXES = [
9
+ '.prodify/contracts-src/'
10
+ ];
11
+ export const REQUIRED_CANONICAL_PATHS = [
12
+ '.prodify/AGENTS.md',
13
+ '.prodify/artifacts/README.md',
14
+ '.prodify/contracts-src/README.md',
15
+ '.prodify/contracts-src/architecture.contract.md',
16
+ '.prodify/contracts-src/diagnose.contract.md',
17
+ '.prodify/contracts-src/plan.contract.md',
18
+ '.prodify/contracts-src/refactor.contract.md',
19
+ '.prodify/contracts-src/understand.contract.md',
20
+ '.prodify/contracts-src/validate.contract.md',
21
+ '.prodify/metrics/README.md',
22
+ '.prodify/project.md',
23
+ '.prodify/planning.md',
24
+ '.prodify/runtime-commands.md',
25
+ '.prodify/skills/README.md',
26
+ '.prodify/skills/registry.json',
27
+ '.prodify/skills/domain/react-frontend.skill.json',
28
+ '.prodify/skills/domain/typescript-backend.skill.json',
29
+ '.prodify/skills/quality-policy/maintainability-review.skill.json',
30
+ '.prodify/skills/quality-policy/security-hardening.skill.json',
31
+ '.prodify/skills/quality-policy/test-hardening.skill.json',
32
+ '.prodify/skills/stage-method/architecture-method.skill.json',
33
+ '.prodify/skills/stage-method/codebase-scanning.skill.json',
34
+ '.prodify/skills/stage-method/diagnosis-method.skill.json',
35
+ '.prodify/skills/stage-method/planning-method.skill.json',
36
+ '.prodify/skills/stage-method/refactoring-method.skill.json',
37
+ '.prodify/skills/stage-method/validation-method.skill.json',
38
+ '.prodify/state.json',
39
+ '.prodify/tasks/README.md',
40
+ '.prodify/rules/README.md',
41
+ '.prodify/templates/README.md',
42
+ '.prodify/version.json'
43
+ ];
44
+ export function isRuntimeProfileName(value) {
45
+ return typeof value === 'string' && RUNTIME_PROFILE_NAMES.includes(value);
46
+ }
47
+ export function normalizeRepoRelativePath(relativePath) {
48
+ const normalized = relativePath.replaceAll('\\', '/');
49
+ return path.posix.normalize(normalized).replace(/^\/+/, '');
50
+ }
51
+ export function resolveRepoPath(repoRoot, relativePath) {
52
+ return path.join(repoRoot, ...normalizeRepoRelativePath(relativePath).split('/'));
53
+ }
54
+ export function resolveCanonicalPath(repoRoot, relativePath) {
55
+ return resolveRepoPath(repoRoot, relativePath);
56
+ }
@@ -0,0 +1,43 @@
1
+ import { ProdifyError } from './errors.js';
2
+ const REQUIRED_PRESET_ENTRIES = [
3
+ '.prodify/AGENTS.md',
4
+ '.prodify/artifacts/README.md',
5
+ '.prodify/contracts-src/README.md',
6
+ '.prodify/contracts-src/architecture.contract.md',
7
+ '.prodify/contracts-src/diagnose.contract.md',
8
+ '.prodify/contracts-src/plan.contract.md',
9
+ '.prodify/contracts-src/refactor.contract.md',
10
+ '.prodify/contracts-src/understand.contract.md',
11
+ '.prodify/contracts-src/validate.contract.md',
12
+ '.prodify/metrics/README.md',
13
+ '.prodify/project.md',
14
+ '.prodify/planning.md',
15
+ '.prodify/runtime-commands.md',
16
+ '.prodify/skills/README.md',
17
+ '.prodify/skills/registry.json',
18
+ '.prodify/skills/domain/react-frontend.skill.json',
19
+ '.prodify/skills/domain/typescript-backend.skill.json',
20
+ '.prodify/skills/quality-policy/maintainability-review.skill.json',
21
+ '.prodify/skills/quality-policy/security-hardening.skill.json',
22
+ '.prodify/skills/quality-policy/test-hardening.skill.json',
23
+ '.prodify/skills/stage-method/architecture-method.skill.json',
24
+ '.prodify/skills/stage-method/codebase-scanning.skill.json',
25
+ '.prodify/skills/stage-method/diagnosis-method.skill.json',
26
+ '.prodify/skills/stage-method/planning-method.skill.json',
27
+ '.prodify/skills/stage-method/refactoring-method.skill.json',
28
+ '.prodify/skills/stage-method/validation-method.skill.json',
29
+ '.prodify/state.json',
30
+ '.prodify/version.json',
31
+ '.prodify/tasks/README.md',
32
+ '.prodify/rules/README.md',
33
+ '.prodify/templates/README.md'
34
+ ];
35
+ export function validatePresetEntries(entries) {
36
+ const entryPaths = new Set(entries.map((entry) => entry.relativePath));
37
+ const missing = REQUIRED_PRESET_ENTRIES.filter((requiredPath) => !entryPaths.has(requiredPath));
38
+ if (missing.length > 0) {
39
+ throw new ProdifyError(`Default preset is missing required canonical files: ${missing.join(', ')}`, {
40
+ code: 'PRESET_INVALID'
41
+ });
42
+ }
43
+ }
@@ -0,0 +1,63 @@
1
+ import { STAGE_ORDER, stageToTaskId } from './flow-state.js';
2
+ import { getRuntimeProfile } from './targets.js';
3
+ export function buildBootstrapPrompt(profileName = 'codex') {
4
+ return (getRuntimeProfile(profileName ?? 'codex') ?? getRuntimeProfile('codex')).bootstrapPrompt;
5
+ }
6
+ export function hasManualBootstrapGuidance(markdown) {
7
+ return markdown.includes('.prodify/AGENTS.md') && markdown.includes('$prodify-init');
8
+ }
9
+ export function buildRuntimeCommandReference(options = {}) {
10
+ const concise = options.concise ?? false;
11
+ const profile = getRuntimeProfile(options.profileName ?? 'codex') ?? getRuntimeProfile('codex');
12
+ const bootstrapPrompt = profile?.bootstrapPrompt ?? buildBootstrapPrompt('codex');
13
+ const lines = [
14
+ '# Runtime Commands',
15
+ '',
16
+ 'Manual bootstrap:',
17
+ `- First tell the agent: "${bootstrapPrompt}"`,
18
+ '- Before using `$prodify-*` in a new agent environment, run `prodify setup-agent <agent>` once outside the repo.',
19
+ '- Keep the workflow anchored to `.prodify/` files only.',
20
+ '- Humans edit `.prodify/contracts-src/`; runtime reads only `.prodify/contracts/*.contract.json`.',
21
+ '- Stage skill definitions live under `.prodify/skills/` and can assist a stage without overriding its contract or validator.',
22
+ '',
23
+ 'Run these commands inside your coding agent after `prodify init` has created the repo scaffolding.',
24
+ '',
25
+ '- `$prodify-init`: inspect `.prodify/`, detect or resolve the active agent runtime, initialize `.prodify/state.json`, and prepare the bootstrapped state without locking the repo to one agent.',
26
+ '- `$prodify-execute`: run one stage, write stage artifacts, validate them against compiled contracts, then pause in interactive mode.',
27
+ '- `$prodify-execute --auto`: continue through the full workflow without pausing unless there is a hard failure, policy block, required approval threshold, or invalid state.',
28
+ '- `$prodify-resume`: continue from `.prodify/state.json` after a pause, interruption, or validation checkpoint.',
29
+ '',
30
+ 'Stage order:',
31
+ `- ${STAGE_ORDER.map((stage) => `${stage} (${stageToTaskId(stage)})`).join(' -> ')}`,
32
+ '',
33
+ 'Resume rules:',
34
+ '- interactive mode pauses between stages and expects validation before resume',
35
+ '- auto mode keeps advancing while state remains valid',
36
+ '- failed or corrupt state must stop with a clear reason'
37
+ ];
38
+ if (concise) {
39
+ return [
40
+ '# Runtime Commands',
41
+ '',
42
+ `- First prompt: "${bootstrapPrompt}"`,
43
+ '- Run `prodify setup-agent <agent>` once per machine before using agent runtime commands.',
44
+ '- `$prodify-init`: bootstrap `.prodify/state.json` for the active agent runtime without repo-level locking.',
45
+ '- `$prodify-execute`: run one stage, then pause.',
46
+ '- `$prodify-execute --auto`: continue until a hard stop.',
47
+ '- `$prodify-resume`: continue from saved state.'
48
+ ].join('\n') + '\n';
49
+ }
50
+ return `${lines.join('\n')}\n`;
51
+ }
52
+ export function buildExecutionPrompt(state) {
53
+ const stage = state.runtime.current_stage ?? state.runtime.pending_stage ?? 'none';
54
+ const task = state.runtime.current_task_id ?? (state.runtime.pending_stage ? stageToTaskId(state.runtime.pending_stage) : 'none');
55
+ return [
56
+ `Current stage: ${stage}`,
57
+ `Current task: ${task}`,
58
+ `Current state: ${state.runtime.current_state}`,
59
+ `Mode: ${state.runtime.mode ?? 'unset'}`,
60
+ `Status: ${state.runtime.status}`,
61
+ `Next action: ${state.runtime.next_action}`
62
+ ].join('\n');
63
+ }