@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,29 @@
1
+ import { buildManagedMarkdownOutput, readCanonicalMarkdown, stripLeadingTitle } from './shared.js';
2
+ import { LEGACY_TARGET_PATH_DEFINITIONS } from '../core/paths.js';
3
+ export async function generateCopilotContent(repoRoot) {
4
+ const target = LEGACY_TARGET_PATH_DEFINITIONS.copilot;
5
+ const agentsMarkdown = await readCanonicalMarkdown(repoRoot, '.prodify/AGENTS.md');
6
+ const projectMarkdown = await readCanonicalMarkdown(repoRoot, '.prodify/project.md');
7
+ const runtimeMarkdown = await readCanonicalMarkdown(repoRoot, '.prodify/runtime-commands.md');
8
+ const body = [
9
+ '# Copilot Instructions',
10
+ '',
11
+ '## Project Context',
12
+ '',
13
+ stripLeadingTitle(projectMarkdown),
14
+ '',
15
+ '## Operating Guidance',
16
+ '',
17
+ stripLeadingTitle(agentsMarkdown),
18
+ '',
19
+ '## Runtime Commands',
20
+ '',
21
+ stripLeadingTitle(runtimeMarkdown)
22
+ ].join('\n').trimEnd() + '\n';
23
+ return buildManagedMarkdownOutput({
24
+ agent: target.agent,
25
+ canonicalSources: target.canonicalSources,
26
+ regenerateCommand: 'prodify update',
27
+ body
28
+ });
29
+ }
@@ -0,0 +1,13 @@
1
+ import { renderCanonicalSourceList } from '../core/managed-files.js';
2
+ export function renderManagedFileHeader({ agent, canonicalSources, regenerateCommand, bodyFingerprint }) {
3
+ return [
4
+ '<!--',
5
+ 'Generated by Prodify.',
6
+ `Target agent: ${agent}`,
7
+ `Canonical source: ${renderCanonicalSourceList(canonicalSources)}`,
8
+ `Regenerate with: ${regenerateCommand}`,
9
+ `Body fingerprint: ${bodyFingerprint}`,
10
+ 'Manual edits may be overwritten.',
11
+ '-->'
12
+ ].join('\n');
13
+ }
@@ -0,0 +1,14 @@
1
+ import { buildManagedMarkdownOutput, readCanonicalMarkdown } from './shared.js';
2
+ import { LEGACY_TARGET_PATH_DEFINITIONS } from '../core/paths.js';
3
+ export async function generateOpenCodeContent(repoRoot) {
4
+ const target = LEGACY_TARGET_PATH_DEFINITIONS.opencode;
5
+ const guidance = await readCanonicalMarkdown(repoRoot, target.canonicalSources[0]);
6
+ const runtime = await readCanonicalMarkdown(repoRoot, '.prodify/runtime-commands.md');
7
+ const body = `${guidance.trimEnd()}\n\n${runtime.trim()}\n`;
8
+ return buildManagedMarkdownOutput({
9
+ agent: target.agent,
10
+ canonicalSources: target.canonicalSources,
11
+ regenerateCommand: 'prodify update',
12
+ body
13
+ });
14
+ }
@@ -0,0 +1,37 @@
1
+ import fs from 'node:fs/promises';
2
+ import { ProdifyError } from '../core/errors.js';
3
+ import { resolveCanonicalPath } from '../core/paths.js';
4
+ import { computeBodyFingerprint } from '../core/managed-files.js';
5
+ import { renderManagedFileHeader } from './header.js';
6
+ export async function readCanonicalMarkdown(repoRoot, relativePath) {
7
+ const absolutePath = resolveCanonicalPath(repoRoot, relativePath);
8
+ try {
9
+ return await fs.readFile(absolutePath, 'utf8');
10
+ }
11
+ catch {
12
+ throw new ProdifyError(`Canonical source is missing: ${relativePath}`, {
13
+ code: 'CANONICAL_SOURCE_MISSING'
14
+ });
15
+ }
16
+ }
17
+ export function stripLeadingTitle(markdown) {
18
+ const lines = markdown.split('\n');
19
+ if (lines[0]?.startsWith('# ')) {
20
+ let index = 1;
21
+ while (index < lines.length && lines[index].trim() === '') {
22
+ index += 1;
23
+ }
24
+ return lines.slice(index).join('\n').trim();
25
+ }
26
+ return markdown.trim();
27
+ }
28
+ export function buildManagedMarkdownOutput({ agent, canonicalSources, regenerateCommand, body }) {
29
+ const bodyFingerprint = computeBodyFingerprint(body);
30
+ const header = renderManagedFileHeader({
31
+ agent,
32
+ canonicalSources,
33
+ regenerateCommand,
34
+ bodyFingerprint
35
+ });
36
+ return `${header}\n\n${body}`;
37
+ }
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from './cli.js';
3
+ const context = {
4
+ cwd: process.cwd(),
5
+ stdout: process.stdout,
6
+ stderr: process.stderr
7
+ };
8
+ const exitCode = await runCli(process.argv.slice(2), context);
9
+ process.exitCode = exitCode;
@@ -0,0 +1,55 @@
1
+ import { ProdifyError } from '../core/errors.js';
2
+ import { LEGACY_TARGET_PATH_DEFINITIONS, isRuntimeProfileName } from '../core/paths.js';
3
+ import { generateCodexContent } from '../generators/codex.js';
4
+ import { generateClaudeContent } from '../generators/claude.js';
5
+ import { generateCopilotContent } from '../generators/copilot.js';
6
+ import { generateOpenCodeContent } from '../generators/opencode.js';
7
+ export const LEGACY_TARGET_REGISTRY = {
8
+ codex: {
9
+ ...LEGACY_TARGET_PATH_DEFINITIONS.codex,
10
+ enabled: true,
11
+ doctorEligible: false,
12
+ generator: generateCodexContent
13
+ },
14
+ claude: {
15
+ ...LEGACY_TARGET_PATH_DEFINITIONS.claude,
16
+ enabled: true,
17
+ doctorEligible: false,
18
+ generator: generateClaudeContent
19
+ },
20
+ copilot: {
21
+ ...LEGACY_TARGET_PATH_DEFINITIONS.copilot,
22
+ enabled: true,
23
+ doctorEligible: false,
24
+ generator: generateCopilotContent
25
+ },
26
+ opencode: {
27
+ ...LEGACY_TARGET_PATH_DEFINITIONS.opencode,
28
+ enabled: true,
29
+ doctorEligible: false,
30
+ generator: generateOpenCodeContent
31
+ }
32
+ };
33
+ export function listLegacyTargets() {
34
+ return Object.values(LEGACY_TARGET_REGISTRY);
35
+ }
36
+ export function getLegacyTarget(agent) {
37
+ if (!agent || !isRuntimeProfileName(agent)) {
38
+ return null;
39
+ }
40
+ return LEGACY_TARGET_REGISTRY[agent];
41
+ }
42
+ export function assertSupportedInstallTarget(agent) {
43
+ const metadata = getLegacyTarget(agent);
44
+ if (!metadata) {
45
+ throw new ProdifyError(`Unknown target agent: ${agent}`, {
46
+ code: 'UNKNOWN_TARGET'
47
+ });
48
+ }
49
+ if (!metadata.enabled || !metadata.generator) {
50
+ throw new ProdifyError(`Legacy compatibility target ${agent} is not enabled.`, {
51
+ code: 'TARGET_NOT_ENABLED'
52
+ });
53
+ }
54
+ return metadata;
55
+ }
@@ -0,0 +1,3 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ export const DEFAULT_PRESET_ASSET_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../assets/presets/default');
@@ -0,0 +1,34 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { listFilesRecursive } from '../core/fs.js';
4
+ import { validatePresetEntries } from '../core/preset-validation.js';
5
+ import { DEFAULT_PRESET_ASSET_DIR } from './default.js';
6
+ import { serializeVersionMetadata } from './version.js';
7
+ export async function loadDefaultPreset() {
8
+ const metadataPath = path.join(DEFAULT_PRESET_ASSET_DIR, 'preset.json');
9
+ const canonicalDir = path.join(DEFAULT_PRESET_ASSET_DIR, 'canonical');
10
+ const rawMetadata = JSON.parse(await fs.readFile(metadataPath, 'utf8'));
11
+ const metadata = {
12
+ name: rawMetadata.name,
13
+ version: rawMetadata.version,
14
+ schemaVersion: rawMetadata.schemaVersion
15
+ };
16
+ const rawFiles = await listFilesRecursive(canonicalDir);
17
+ const entries = [];
18
+ for (const file of rawFiles) {
19
+ entries.push({
20
+ relativePath: `.prodify/${file.relativePath}`,
21
+ content: await fs.readFile(file.fullPath, 'utf8')
22
+ });
23
+ }
24
+ entries.push({
25
+ relativePath: '.prodify/version.json',
26
+ content: serializeVersionMetadata(metadata)
27
+ });
28
+ entries.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
29
+ validatePresetEntries(entries);
30
+ return {
31
+ metadata,
32
+ entries
33
+ };
34
+ }
@@ -0,0 +1,18 @@
1
+ export function createVersionMetadata(presetMetadata) {
2
+ return {
3
+ schema_version: presetMetadata.schemaVersion,
4
+ preset_name: presetMetadata.name,
5
+ preset_version: presetMetadata.version
6
+ };
7
+ }
8
+ export function serializeVersionMetadata(presetMetadata) {
9
+ return `${JSON.stringify(createVersionMetadata(presetMetadata), null, 2)}\n`;
10
+ }
11
+ export function parseVersionMetadata(content) {
12
+ const parsed = JSON.parse(content);
13
+ return {
14
+ schemaVersion: parsed.schema_version,
15
+ presetName: parsed.preset_name,
16
+ presetVersion: parsed.preset_version
17
+ };
18
+ }
@@ -0,0 +1,262 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { inspectCompiledContracts } from '../contracts/freshness.js';
4
+ import { ProdifyError } from '../core/errors.js';
5
+ import { listFilesRecursive, pathExists, writeFileEnsuringDir } from '../core/fs.js';
6
+ import { resolveRepoPath } from '../core/paths.js';
7
+ const SCORE_SCHEMA_VERSION = '1';
8
+ function roundScore(value) {
9
+ return Math.round(value * 100) / 100;
10
+ }
11
+ function createMetric(options) {
12
+ const ratio = Math.max(0, Math.min(1, options.ratio));
13
+ const points = roundScore(options.weight * ratio);
14
+ return {
15
+ id: options.id,
16
+ label: options.label,
17
+ tool: options.tool,
18
+ weight: options.weight,
19
+ max_points: options.weight,
20
+ points,
21
+ status: ratio === 1 ? 'pass' : ratio === 0 ? 'fail' : 'partial',
22
+ details: options.details
23
+ };
24
+ }
25
+ async function detectEcosystems(repoRoot) {
26
+ const ecosystems = [];
27
+ if (await pathExists(resolveRepoPath(repoRoot, 'package.json'))) {
28
+ ecosystems.push('typescript-javascript');
29
+ }
30
+ const repoFiles = await listFilesRecursive(repoRoot);
31
+ if (repoFiles.some((file) => file.relativePath.endsWith('.py'))) {
32
+ ecosystems.push('python');
33
+ }
34
+ if (repoFiles.some((file) => file.relativePath.endsWith('.cs') || file.relativePath.endsWith('.csproj') || file.relativePath.endsWith('.sln'))) {
35
+ ecosystems.push('csharp');
36
+ }
37
+ return ecosystems.sort((left, right) => left.localeCompare(right));
38
+ }
39
+ async function buildEcosystemMetrics(repoRoot, ecosystems) {
40
+ const metrics = [];
41
+ const toolOutputs = [];
42
+ if (ecosystems.includes('typescript-javascript')) {
43
+ const packageJsonPath = resolveRepoPath(repoRoot, 'package.json');
44
+ const tsconfigPath = resolveRepoPath(repoRoot, 'tsconfig.json');
45
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
46
+ const scripts = packageJson.scripts ?? {};
47
+ const hasBuild = typeof scripts.build === 'string' && scripts.build.length > 0;
48
+ const hasTest = typeof scripts.test === 'string' && scripts.test.length > 0;
49
+ const hasTsconfig = await pathExists(tsconfigPath);
50
+ const score = [hasBuild, hasTest, hasTsconfig].filter(Boolean).length / 3;
51
+ metrics.push(createMetric({
52
+ id: 'typescript-javascript',
53
+ label: 'TypeScript/JavaScript local tooling',
54
+ tool: 'package-json',
55
+ weight: 15,
56
+ ratio: score,
57
+ details: `build=${hasBuild}, test=${hasTest}, tsconfig=${hasTsconfig}`
58
+ }));
59
+ toolOutputs.push({
60
+ adapter: 'typescript-javascript',
61
+ details: {
62
+ build_script: hasBuild,
63
+ test_script: hasTest,
64
+ tsconfig: hasTsconfig
65
+ }
66
+ });
67
+ }
68
+ if (ecosystems.includes('python')) {
69
+ const hasPyproject = await pathExists(resolveRepoPath(repoRoot, 'pyproject.toml'));
70
+ const hasRequirements = await pathExists(resolveRepoPath(repoRoot, 'requirements.txt'));
71
+ const ratio = [hasPyproject, hasRequirements].filter(Boolean).length / 2;
72
+ metrics.push(createMetric({
73
+ id: 'python',
74
+ label: 'Python local tooling',
75
+ tool: 'filesystem',
76
+ weight: 7.5,
77
+ ratio,
78
+ details: `pyproject=${hasPyproject}, requirements=${hasRequirements}`
79
+ }));
80
+ toolOutputs.push({
81
+ adapter: 'python',
82
+ details: {
83
+ pyproject: hasPyproject,
84
+ requirements: hasRequirements
85
+ }
86
+ });
87
+ }
88
+ if (ecosystems.includes('csharp')) {
89
+ const repoFiles = await listFilesRecursive(repoRoot);
90
+ const hasSolution = repoFiles.some((file) => file.relativePath.endsWith('.sln'));
91
+ const hasProject = repoFiles.some((file) => file.relativePath.endsWith('.csproj'));
92
+ const ratio = [hasSolution, hasProject].filter(Boolean).length / 2;
93
+ metrics.push(createMetric({
94
+ id: 'csharp',
95
+ label: 'C# local tooling',
96
+ tool: 'filesystem',
97
+ weight: 7.5,
98
+ ratio,
99
+ details: `solution=${hasSolution}, project=${hasProject}`
100
+ }));
101
+ toolOutputs.push({
102
+ adapter: 'csharp',
103
+ details: {
104
+ solution: hasSolution,
105
+ project: hasProject
106
+ }
107
+ });
108
+ }
109
+ return {
110
+ metrics,
111
+ toolOutputs
112
+ };
113
+ }
114
+ async function buildRepoHygieneMetric(repoRoot) {
115
+ const requiredSignals = ['README.md', 'LICENSE', 'tests', '.prodify/contracts-src'];
116
+ const available = await Promise.all(requiredSignals.map((relativePath) => pathExists(resolveRepoPath(repoRoot, relativePath))));
117
+ const ratio = available.filter(Boolean).length / requiredSignals.length;
118
+ return {
119
+ metric: createMetric({
120
+ id: 'repo-hygiene',
121
+ label: 'Repository hygiene signals',
122
+ tool: 'filesystem',
123
+ weight: 20,
124
+ ratio,
125
+ details: requiredSignals.map((entry, index) => `${entry}=${available[index]}`).join(', ')
126
+ }),
127
+ toolOutput: {
128
+ adapter: 'filesystem',
129
+ details: Object.fromEntries(requiredSignals.map((entry, index) => [entry, available[index]]))
130
+ }
131
+ };
132
+ }
133
+ function buildRuntimeMetric(runtimeState) {
134
+ const healthyState = runtimeState.runtime.current_state !== 'failed' && runtimeState.runtime.current_state !== 'blocked';
135
+ const ratio = healthyState ? 1 : 0;
136
+ return {
137
+ metric: createMetric({
138
+ id: 'runtime-state',
139
+ label: 'Contract-driven runtime state',
140
+ tool: 'state-json',
141
+ weight: 25,
142
+ ratio,
143
+ details: `current_state=${runtimeState.runtime.current_state}, status=${runtimeState.runtime.status}`
144
+ }),
145
+ toolOutput: {
146
+ adapter: 'state-json',
147
+ details: {
148
+ current_state: runtimeState.runtime.current_state,
149
+ status: runtimeState.runtime.status,
150
+ last_validation_result: runtimeState.runtime.last_validation_result
151
+ }
152
+ }
153
+ };
154
+ }
155
+ function buildValidationMetric(runtimeState) {
156
+ const passed = runtimeState.runtime.last_validation?.passed === true;
157
+ const finalReady = runtimeState.runtime.current_state === 'validate_complete' || runtimeState.runtime.current_state === 'completed';
158
+ const ratio = passed ? (finalReady ? 1 : 0.5) : 0;
159
+ return {
160
+ metric: createMetric({
161
+ id: 'validation-gate',
162
+ label: 'Validated contract completion',
163
+ tool: 'state-json',
164
+ weight: 10,
165
+ ratio,
166
+ details: `passed=${passed}, final_ready=${finalReady}`
167
+ }),
168
+ toolOutput: {
169
+ adapter: 'validation-gate',
170
+ details: {
171
+ passed,
172
+ final_ready: finalReady
173
+ }
174
+ }
175
+ };
176
+ }
177
+ export async function calculateLocalScore(repoRoot, { kind, runtimeState }) {
178
+ const contractInventory = await inspectCompiledContracts(repoRoot);
179
+ const ecosystems = await detectEcosystems(repoRoot);
180
+ const metrics = [];
181
+ const toolOutputs = [];
182
+ metrics.push(createMetric({
183
+ id: 'contracts',
184
+ label: 'Compiled contract health',
185
+ tool: 'contract-compiler',
186
+ weight: 30,
187
+ ratio: contractInventory.ok ? 1 : Math.max(0, 1 - ((contractInventory.missingCompiledStages.length + contractInventory.staleStages.length) / 6)),
188
+ details: `source=${contractInventory.sourceCount}, compiled=${contractInventory.compiledCount}, stale=${contractInventory.staleStages.length}, missing=${contractInventory.missingCompiledStages.length}`
189
+ }));
190
+ toolOutputs.push({
191
+ adapter: 'contract-compiler',
192
+ details: {
193
+ ok: contractInventory.ok,
194
+ sourceCount: contractInventory.sourceCount,
195
+ compiledCount: contractInventory.compiledCount,
196
+ staleStages: contractInventory.staleStages,
197
+ missingCompiledStages: contractInventory.missingCompiledStages,
198
+ missingSourceStages: contractInventory.missingSourceStages,
199
+ invalidStages: contractInventory.invalidStages
200
+ }
201
+ });
202
+ const runtimeMetric = buildRuntimeMetric(runtimeState);
203
+ metrics.push(runtimeMetric.metric);
204
+ toolOutputs.push(runtimeMetric.toolOutput);
205
+ const validationMetric = buildValidationMetric(runtimeState);
206
+ metrics.push(validationMetric.metric);
207
+ toolOutputs.push(validationMetric.toolOutput);
208
+ const hygieneMetric = await buildRepoHygieneMetric(repoRoot);
209
+ metrics.push(hygieneMetric.metric);
210
+ toolOutputs.push(hygieneMetric.toolOutput);
211
+ const ecosystemMetrics = await buildEcosystemMetrics(repoRoot, ecosystems);
212
+ metrics.push(...ecosystemMetrics.metrics);
213
+ toolOutputs.push(...ecosystemMetrics.toolOutputs);
214
+ const totalScore = roundScore(metrics.reduce((sum, metric) => sum + metric.points, 0));
215
+ const maxScore = roundScore(metrics.reduce((sum, metric) => sum + metric.max_points, 0));
216
+ if (kind === 'final' && !(runtimeState.runtime.last_validation?.passed && (runtimeState.runtime.current_state === 'validate_complete' || runtimeState.runtime.current_state === 'completed'))) {
217
+ throw new ProdifyError('Final scoring requires a validated runtime state at validate_complete or completed.', {
218
+ code: 'SCORING_STATE_INVALID'
219
+ });
220
+ }
221
+ return {
222
+ snapshot: {
223
+ schema_version: SCORE_SCHEMA_VERSION,
224
+ kind,
225
+ ecosystems,
226
+ total_score: totalScore,
227
+ max_score: maxScore,
228
+ metrics
229
+ },
230
+ toolOutputs
231
+ };
232
+ }
233
+ function serializeJson(value) {
234
+ return `${JSON.stringify(value, null, 2)}\n`;
235
+ }
236
+ export async function writeScoreSnapshot(repoRoot, { kind, runtimeState }) {
237
+ const { snapshot, toolOutputs } = await calculateLocalScore(repoRoot, {
238
+ kind,
239
+ runtimeState
240
+ });
241
+ const metricsDir = resolveRepoPath(repoRoot, '.prodify/metrics');
242
+ await writeFileEnsuringDir(path.join(metricsDir, `${kind}.score.json`), serializeJson(snapshot));
243
+ await writeFileEnsuringDir(path.join(metricsDir, `${kind}.tools.json`), serializeJson({
244
+ schema_version: SCORE_SCHEMA_VERSION,
245
+ kind,
246
+ outputs: toolOutputs
247
+ }));
248
+ return snapshot;
249
+ }
250
+ export async function writeScoreDelta(repoRoot) {
251
+ const metricsDir = resolveRepoPath(repoRoot, '.prodify/metrics');
252
+ const baseline = JSON.parse(await fs.readFile(path.join(metricsDir, 'baseline.score.json'), 'utf8'));
253
+ const final = JSON.parse(await fs.readFile(path.join(metricsDir, 'final.score.json'), 'utf8'));
254
+ const delta = {
255
+ schema_version: SCORE_SCHEMA_VERSION,
256
+ baseline_score: baseline.total_score,
257
+ final_score: final.total_score,
258
+ delta: roundScore(final.total_score - baseline.total_score)
259
+ };
260
+ await writeFileEnsuringDir(path.join(metricsDir, 'delta.json'), serializeJson(delta));
261
+ return delta;
262
+ }
@@ -0,0 +1,40 @@
1
+ import fs from 'node:fs/promises';
2
+ import { pathExists } from '../core/fs.js';
3
+ import { resolveCanonicalPath } from '../core/paths.js';
4
+ import { ProdifyError } from '../core/errors.js';
5
+ import { validateSkillDefinitionShape, validateSkillRegistryManifest } from './schema.js';
6
+ const SKILL_REGISTRY_PATH = '.prodify/skills/registry.json';
7
+ async function readJsonFile(filePath, missingCode, invalidCode) {
8
+ if (!(await pathExists(filePath))) {
9
+ throw new ProdifyError(`Required skill file is missing: ${filePath}.`, {
10
+ code: missingCode
11
+ });
12
+ }
13
+ try {
14
+ return JSON.parse(await fs.readFile(filePath, 'utf8'));
15
+ }
16
+ catch {
17
+ throw new ProdifyError(`Skill file is malformed JSON: ${filePath}.`, {
18
+ code: invalidCode
19
+ });
20
+ }
21
+ }
22
+ export async function readSkillRegistryManifest(repoRoot) {
23
+ const registryPath = resolveCanonicalPath(repoRoot, SKILL_REGISTRY_PATH);
24
+ return validateSkillRegistryManifest(await readJsonFile(registryPath, 'SKILL_REGISTRY_MISSING', 'SKILL_REGISTRY_INVALID'));
25
+ }
26
+ export async function loadSkillRegistry(repoRoot) {
27
+ const manifest = await readSkillRegistryManifest(repoRoot);
28
+ const registry = new Map();
29
+ for (const relativePath of manifest.skills) {
30
+ const fullPath = resolveCanonicalPath(repoRoot, `.prodify/skills/${relativePath}`);
31
+ const definition = validateSkillDefinitionShape(await readJsonFile(fullPath, 'SKILL_DEFINITION_MISSING', 'SKILL_DEFINITION_INVALID'));
32
+ if (registry.has(definition.id)) {
33
+ throw new ProdifyError(`Duplicate skill id detected: ${definition.id}.`, {
34
+ code: 'SKILL_REGISTRY_INVALID'
35
+ });
36
+ }
37
+ registry.set(definition.id, definition);
38
+ }
39
+ return registry;
40
+ }