@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,111 @@
1
+ import { normalizeRepoRelativePath } from '../core/paths.js';
2
+ import { ProdifyError } from '../core/errors.js';
3
+ import { stageToTaskId } from '../core/flow-state.js';
4
+ import { normalizeStageSkillRouting } from '../skills/schema.js';
5
+ export const CONTRACT_STAGE_NAMES = ['understand', 'diagnose', 'architecture', 'plan', 'refactor', 'validate'];
6
+ function asString(value, fieldName) {
7
+ if (typeof value !== 'string' || value.trim() === '') {
8
+ throw new ProdifyError(`Contract frontmatter field "${fieldName}" must be a non-empty string.`, {
9
+ code: 'CONTRACT_SCHEMA_INVALID'
10
+ });
11
+ }
12
+ return value.trim();
13
+ }
14
+ function asStringArray(value, fieldName) {
15
+ if (!Array.isArray(value) || value.length === 0) {
16
+ throw new ProdifyError(`Contract frontmatter field "${fieldName}" must be a non-empty list.`, {
17
+ code: 'CONTRACT_SCHEMA_INVALID'
18
+ });
19
+ }
20
+ const normalized = value.map((entry) => asString(entry, fieldName));
21
+ return [...new Set(normalized)].sort((left, right) => left.localeCompare(right));
22
+ }
23
+ function asOptionalStringArray(value, fieldName) {
24
+ if (!Array.isArray(value) || value.length === 0) {
25
+ return [];
26
+ }
27
+ return asStringArray(value, fieldName);
28
+ }
29
+ function asStage(value) {
30
+ const stage = asString(value, 'stage');
31
+ if (!CONTRACT_STAGE_NAMES.includes(stage)) {
32
+ throw new ProdifyError(`Contract frontmatter field "stage" is invalid: ${stage}.`, {
33
+ code: 'CONTRACT_SCHEMA_INVALID'
34
+ });
35
+ }
36
+ return stage;
37
+ }
38
+ function normalizeArtifactRule(rawValue) {
39
+ if (typeof rawValue !== 'object' || rawValue === null || Array.isArray(rawValue)) {
40
+ throw new ProdifyError('Each required_artifacts entry must be a mapping.', {
41
+ code: 'CONTRACT_SCHEMA_INVALID'
42
+ });
43
+ }
44
+ const record = rawValue;
45
+ const path = normalizeRepoRelativePath(asString(record.path, 'required_artifacts.path'));
46
+ const format = asString(record.format, 'required_artifacts.format');
47
+ if (format !== 'markdown' && format !== 'json') {
48
+ throw new ProdifyError(`Unsupported artifact format: ${format}.`, {
49
+ code: 'CONTRACT_SCHEMA_INVALID'
50
+ });
51
+ }
52
+ const requiredSections = asOptionalStringArray(record.required_sections, 'required_artifacts.required_sections');
53
+ const requiredJsonKeys = asOptionalStringArray(record.required_json_keys, 'required_artifacts.required_json_keys');
54
+ if (format === 'markdown' && requiredSections.length === 0) {
55
+ throw new ProdifyError(`Markdown artifact "${path}" must declare required_sections.`, {
56
+ code: 'CONTRACT_SCHEMA_INVALID'
57
+ });
58
+ }
59
+ if (format === 'json' && requiredJsonKeys.length === 0) {
60
+ throw new ProdifyError(`JSON artifact "${path}" must declare required_json_keys.`, {
61
+ code: 'CONTRACT_SCHEMA_INVALID'
62
+ });
63
+ }
64
+ return {
65
+ path,
66
+ format,
67
+ required_sections: requiredSections,
68
+ required_json_keys: requiredJsonKeys
69
+ };
70
+ }
71
+ export function normalizeSourceContractDocument(options) {
72
+ const { document, sourcePath, sourceHash } = options;
73
+ const stage = asStage(document.frontmatter.stage);
74
+ const taskId = asString(document.frontmatter.task_id, 'task_id');
75
+ if (taskId !== stageToTaskId(stage)) {
76
+ throw new ProdifyError(`Contract task_id "${taskId}" does not match stage "${stage}".`, {
77
+ code: 'CONTRACT_SCHEMA_INVALID'
78
+ });
79
+ }
80
+ if (document.body.trim().length === 0) {
81
+ throw new ProdifyError(`Contract source "${sourcePath}" must contain explanatory body content.`, {
82
+ code: 'CONTRACT_BODY_EMPTY'
83
+ });
84
+ }
85
+ const rawArtifacts = document.frontmatter.required_artifacts;
86
+ if (!Array.isArray(rawArtifacts) || rawArtifacts.length === 0) {
87
+ throw new ProdifyError('Contract frontmatter field "required_artifacts" must contain at least one artifact rule.', {
88
+ code: 'CONTRACT_SCHEMA_INVALID'
89
+ });
90
+ }
91
+ const requiredArtifacts = rawArtifacts
92
+ .map((artifact) => normalizeArtifactRule(artifact))
93
+ .sort((left, right) => left.path.localeCompare(right.path));
94
+ return {
95
+ schema_version: String(document.frontmatter.schema_version ?? '1'),
96
+ contract_version: asString(document.frontmatter.contract_version, 'contract_version'),
97
+ stage,
98
+ task_id: taskId,
99
+ source_path: normalizeRepoRelativePath(sourcePath),
100
+ source_hash: sourceHash,
101
+ required_artifacts: requiredArtifacts,
102
+ allowed_write_roots: asStringArray(document.frontmatter.allowed_write_roots, 'allowed_write_roots')
103
+ .map((entry) => normalizeRepoRelativePath(entry)),
104
+ forbidden_writes: Array.isArray(document.frontmatter.forbidden_writes)
105
+ ? asStringArray(document.frontmatter.forbidden_writes, 'forbidden_writes').map((entry) => normalizeRepoRelativePath(entry))
106
+ : [],
107
+ policy_rules: asStringArray(document.frontmatter.policy_rules, 'policy_rules'),
108
+ success_criteria: asStringArray(document.frontmatter.success_criteria, 'success_criteria'),
109
+ skill_routing: normalizeStageSkillRouting(document.frontmatter.skill_routing)
110
+ };
111
+ }
@@ -0,0 +1,36 @@
1
+ import { ProdifyError } from './errors.js';
2
+ import { readGlobalAgentSetupState, listConfiguredAgents } from './agent-setup.js';
3
+ import { getRuntimeProfile } from './targets.js';
4
+ export function detectRuntimeAgentFromEnv(env = process.env) {
5
+ const candidate = env.PRODIFY_ACTIVE_AGENT?.trim();
6
+ if (!candidate) {
7
+ return null;
8
+ }
9
+ return getRuntimeProfile(candidate)?.name ?? null;
10
+ }
11
+ export async function resolveRuntimeAgentBinding({ requestedAgent = null, env = process.env } = {}) {
12
+ const explicit = requestedAgent ? getRuntimeProfile(requestedAgent)?.name ?? null : null;
13
+ if (explicit) {
14
+ return explicit;
15
+ }
16
+ const envAgent = detectRuntimeAgentFromEnv(env);
17
+ if (envAgent) {
18
+ return envAgent;
19
+ }
20
+ const setupState = await readGlobalAgentSetupState({
21
+ allowMissing: true,
22
+ env
23
+ });
24
+ const configuredAgents = listConfiguredAgents(setupState);
25
+ if (configuredAgents.length === 1) {
26
+ return configuredAgents[0];
27
+ }
28
+ if (configuredAgents.length === 0) {
29
+ throw new ProdifyError('Could not resolve the active agent runtime. Run `prodify setup-agent <agent>` or set PRODIFY_ACTIVE_AGENT.', {
30
+ code: 'AGENT_RUNTIME_UNRESOLVED'
31
+ });
32
+ }
33
+ throw new ProdifyError('Multiple agents are configured globally. Set PRODIFY_ACTIVE_AGENT or pass an explicit agent for runtime binding.', {
34
+ code: 'AGENT_RUNTIME_AMBIGUOUS'
35
+ });
36
+ }
@@ -0,0 +1,111 @@
1
+ import fs from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { ProdifyError } from './errors.js';
5
+ import { pathExists, writeFileEnsuringDir } from './fs.js';
6
+ import { getRuntimeProfile } from './targets.js';
7
+ export const GLOBAL_AGENT_SETUP_SCHEMA_VERSION = '1';
8
+ export const PRODIFY_RUNTIME_COMMANDS = ['$prodify-init', '$prodify-execute', '$prodify-resume'];
9
+ function asRecord(value) {
10
+ return typeof value === 'object' && value !== null ? value : {};
11
+ }
12
+ export function resolveGlobalProdifyHome(env = process.env) {
13
+ const explicit = env.PRODIFY_HOME?.trim();
14
+ if (explicit) {
15
+ return path.resolve(explicit);
16
+ }
17
+ const xdgStateHome = env.XDG_STATE_HOME?.trim();
18
+ if (xdgStateHome) {
19
+ return path.resolve(xdgStateHome, 'prodify');
20
+ }
21
+ return path.join(os.homedir(), '.prodify');
22
+ }
23
+ export function resolveGlobalAgentSetupStatePath(env = process.env) {
24
+ return path.join(resolveGlobalProdifyHome(env), 'agent-setup.json');
25
+ }
26
+ export function createInitialGlobalAgentSetupState() {
27
+ return {
28
+ schema_version: GLOBAL_AGENT_SETUP_SCHEMA_VERSION,
29
+ configured_agents: {}
30
+ };
31
+ }
32
+ export function listConfiguredAgents(state) {
33
+ if (!state) {
34
+ return [];
35
+ }
36
+ return Object.entries(state.configured_agents)
37
+ .filter(([, value]) => Boolean(value))
38
+ .map(([agent]) => agent)
39
+ .sort((left, right) => left.localeCompare(right));
40
+ }
41
+ function normalizeGlobalAgentSetupState(raw) {
42
+ const record = asRecord(raw);
43
+ const configuredAgents = asRecord(record.configured_agents);
44
+ const normalized = createInitialGlobalAgentSetupState();
45
+ for (const [agent, value] of Object.entries(configuredAgents)) {
46
+ const profile = getRuntimeProfile(agent);
47
+ const entry = asRecord(value);
48
+ if (!profile) {
49
+ continue;
50
+ }
51
+ normalized.configured_agents[profile.name] = {
52
+ agent: profile.name,
53
+ display_name: typeof entry.display_name === 'string' ? entry.display_name : profile.displayName,
54
+ configured_at: typeof entry.configured_at === 'string' ? entry.configured_at : 'unknown',
55
+ commands: Array.isArray(entry.commands)
56
+ ? entry.commands.filter((command) => typeof command === 'string')
57
+ : [...PRODIFY_RUNTIME_COMMANDS]
58
+ };
59
+ }
60
+ return normalized;
61
+ }
62
+ export async function readGlobalAgentSetupState({ allowMissing = false, env = process.env } = {}) {
63
+ const statePath = resolveGlobalAgentSetupStatePath(env);
64
+ if (!(await pathExists(statePath))) {
65
+ if (allowMissing) {
66
+ return null;
67
+ }
68
+ throw new ProdifyError(`Global agent setup state is missing: ${statePath}`, {
69
+ code: 'GLOBAL_AGENT_SETUP_MISSING'
70
+ });
71
+ }
72
+ try {
73
+ return normalizeGlobalAgentSetupState(JSON.parse(await fs.readFile(statePath, 'utf8')));
74
+ }
75
+ catch {
76
+ throw new ProdifyError(`Global agent setup state is malformed: ${statePath}`, {
77
+ code: 'GLOBAL_AGENT_SETUP_MALFORMED'
78
+ });
79
+ }
80
+ }
81
+ export async function writeGlobalAgentSetupState(state, { env = process.env } = {}) {
82
+ const statePath = resolveGlobalAgentSetupStatePath(env);
83
+ await writeFileEnsuringDir(statePath, `${JSON.stringify(state, null, 2)}\n`);
84
+ return statePath;
85
+ }
86
+ export async function setupAgentIntegration(agent, { now = new Date().toISOString(), env = process.env } = {}) {
87
+ const profile = getRuntimeProfile(agent);
88
+ if (!profile) {
89
+ throw new ProdifyError('setup-agent requires <codex|claude|copilot|opencode>.', {
90
+ code: 'INVALID_AGENT'
91
+ });
92
+ }
93
+ const existingState = await readGlobalAgentSetupState({
94
+ allowMissing: true,
95
+ env
96
+ });
97
+ const nextState = existingState ?? createInitialGlobalAgentSetupState();
98
+ const alreadyConfigured = Boolean(nextState.configured_agents[profile.name]);
99
+ nextState.configured_agents[profile.name] = {
100
+ agent: profile.name,
101
+ display_name: profile.displayName,
102
+ configured_at: now,
103
+ commands: [...PRODIFY_RUNTIME_COMMANDS]
104
+ };
105
+ const statePath = await writeGlobalAgentSetupState(nextState, { env });
106
+ return {
107
+ statePath,
108
+ configuredAgents: listConfiguredAgents(nextState),
109
+ alreadyConfigured
110
+ };
111
+ }
@@ -0,0 +1,155 @@
1
+ import fs from 'node:fs/promises';
2
+ import { REQUIRED_CANONICAL_PATHS, resolveCanonicalPath, resolveRepoPath } from './paths.js';
3
+ import { pathExists } from './fs.js';
4
+ import { readRuntimeState } from './state.js';
5
+ import { inspectVersionStatus } from './version-checks.js';
6
+ import { loadDefaultPreset } from '../presets/loader.js';
7
+ import { parseVersionMetadata } from '../presets/version.js';
8
+ import { hasManualBootstrapGuidance } from './prompt-builder.js';
9
+ import { inspectCompiledContracts } from '../contracts/freshness.js';
10
+ function isProdifyDirectoryIgnore(pattern) {
11
+ const trimmed = pattern.trim();
12
+ return trimmed === '.prodify'
13
+ || trimmed === '.prodify/'
14
+ || trimmed === '/.prodify'
15
+ || trimmed === '/.prodify/';
16
+ }
17
+ async function inspectGitignore(repoRoot) {
18
+ const gitignorePath = resolveRepoPath(repoRoot, '.gitignore');
19
+ if (!(await pathExists(gitignorePath))) {
20
+ return {
21
+ label: 'gitignore/prodify',
22
+ ok: true,
23
+ details: '.gitignore does not hide .prodify/'
24
+ };
25
+ }
26
+ const content = await fs.readFile(gitignorePath, 'utf8');
27
+ const badPatterns = content
28
+ .split('\n')
29
+ .map((line) => line.trim())
30
+ .filter((line) => line !== '' && !line.startsWith('#'))
31
+ .filter(isProdifyDirectoryIgnore);
32
+ return {
33
+ label: 'gitignore/prodify',
34
+ ok: badPatterns.length === 0,
35
+ details: badPatterns.length === 0
36
+ ? '.gitignore does not ignore .prodify/'
37
+ : `bad patterns found: ${badPatterns.join(', ')}`
38
+ };
39
+ }
40
+ async function inspectBootstrapGuidance(repoRoot) {
41
+ const guidancePath = resolveCanonicalPath(repoRoot, '.prodify/AGENTS.md');
42
+ if (!(await pathExists(guidancePath))) {
43
+ return {
44
+ label: 'bootstrap/guidance',
45
+ ok: false,
46
+ details: '.prodify/AGENTS.md is missing'
47
+ };
48
+ }
49
+ const guidance = await fs.readFile(guidancePath, 'utf8');
50
+ return {
51
+ label: 'bootstrap/guidance',
52
+ ok: hasManualBootstrapGuidance(guidance),
53
+ details: hasManualBootstrapGuidance(guidance)
54
+ ? 'manual agent entry is documented in .prodify/AGENTS.md'
55
+ : '.prodify/AGENTS.md does not contain the manual bootstrap instruction'
56
+ };
57
+ }
58
+ export async function runDoctor(repoRoot) {
59
+ const checks = [];
60
+ const preset = await loadDefaultPreset();
61
+ const prodifyPath = resolveRepoPath(repoRoot, '.prodify');
62
+ const prodifyExists = await pathExists(prodifyPath);
63
+ if (!prodifyExists) {
64
+ checks.push({
65
+ label: 'canonical',
66
+ ok: false,
67
+ details: '.prodify/ is missing'
68
+ });
69
+ return {
70
+ ok: false,
71
+ checks
72
+ };
73
+ }
74
+ const missingCanonicalFiles = [];
75
+ for (const relativePath of REQUIRED_CANONICAL_PATHS) {
76
+ if (!(await pathExists(resolveCanonicalPath(repoRoot, relativePath)))) {
77
+ missingCanonicalFiles.push(relativePath);
78
+ }
79
+ }
80
+ checks.push({
81
+ label: 'canonical',
82
+ ok: missingCanonicalFiles.length === 0,
83
+ details: missingCanonicalFiles.length === 0
84
+ ? 'required canonical files verified'
85
+ : `missing canonical files: ${missingCanonicalFiles.join(', ')}`
86
+ });
87
+ const contractInventory = await inspectCompiledContracts(repoRoot);
88
+ checks.push({
89
+ label: 'contracts/source',
90
+ ok: contractInventory.missingSourceStages.length === 0 && contractInventory.invalidStages.length === 0,
91
+ details: contractInventory.missingSourceStages.length === 0
92
+ ? `source contracts detected: ${contractInventory.sourceCount}`
93
+ : `missing source contracts: ${contractInventory.missingSourceStages.join(', ')}`
94
+ });
95
+ checks.push({
96
+ label: 'contracts/compiled',
97
+ ok: contractInventory.ok,
98
+ details: contractInventory.ok
99
+ ? `compiled contracts synchronized: ${contractInventory.compiledCount}`
100
+ : [
101
+ contractInventory.missingCompiledStages.length > 0 ? `missing compiled: ${contractInventory.missingCompiledStages.join(', ')}` : '',
102
+ contractInventory.staleStages.length > 0 ? `stale: ${contractInventory.staleStages.join(', ')}` : '',
103
+ contractInventory.invalidStages.length > 0 ? `invalid: ${contractInventory.invalidStages.join(', ')}` : ''
104
+ ].filter(Boolean).join('; ')
105
+ });
106
+ const versionPath = resolveCanonicalPath(repoRoot, '.prodify/version.json');
107
+ if (await pathExists(versionPath)) {
108
+ try {
109
+ parseVersionMetadata(await fs.readFile(versionPath, 'utf8'));
110
+ checks.push({
111
+ label: 'canonical/version',
112
+ ok: true,
113
+ details: 'version metadata is readable'
114
+ });
115
+ }
116
+ catch {
117
+ checks.push({
118
+ label: 'canonical/version',
119
+ ok: false,
120
+ details: '.prodify/version.json is malformed'
121
+ });
122
+ }
123
+ }
124
+ const versionStatus = await inspectVersionStatus(repoRoot, preset.metadata);
125
+ checks.push({
126
+ label: 'canonical/schema',
127
+ ok: versionStatus.status === 'current',
128
+ details: versionStatus.status === 'current'
129
+ ? `preset ${preset.metadata.name}@${preset.metadata.version} matches`
130
+ : `version status is ${versionStatus.status}`
131
+ });
132
+ try {
133
+ await readRuntimeState(repoRoot, {
134
+ presetMetadata: preset.metadata
135
+ });
136
+ checks.push({
137
+ label: 'runtime/state',
138
+ ok: true,
139
+ details: '.prodify/state.json is readable'
140
+ });
141
+ }
142
+ catch {
143
+ checks.push({
144
+ label: 'runtime/state',
145
+ ok: false,
146
+ details: '.prodify/state.json is missing or malformed'
147
+ });
148
+ }
149
+ checks.push(await inspectGitignore(repoRoot));
150
+ checks.push(await inspectBootstrapGuidance(repoRoot));
151
+ return {
152
+ ok: checks.every((check) => check.ok || check.skipped === true),
153
+ checks
154
+ };
155
+ }
@@ -0,0 +1,19 @@
1
+ export class ProdifyError extends Error {
2
+ code;
3
+ exitCode;
4
+ constructor(message, options = {}) {
5
+ super(message);
6
+ this.name = 'ProdifyError';
7
+ this.code = options.code ?? 'PRODIFY_ERROR';
8
+ this.exitCode = options.exitCode ?? 1;
9
+ }
10
+ }
11
+ export function toErrorMessage(error) {
12
+ if (error instanceof ProdifyError) {
13
+ return error.message;
14
+ }
15
+ if (error instanceof Error) {
16
+ return error.message;
17
+ }
18
+ return String(error);
19
+ }