@shahmarasy/prodo 0.1.4 → 0.1.6

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 (205) hide show
  1. package/README.md +201 -97
  2. package/bin/prodo.cjs +6 -6
  3. package/dist/agents/agent-registry.d.ts +13 -0
  4. package/dist/agents/agent-registry.js +79 -0
  5. package/dist/agents/anthropic/index.d.ts +9 -0
  6. package/dist/agents/anthropic/index.js +55 -0
  7. package/dist/agents/base.d.ts +25 -0
  8. package/dist/agents/base.js +71 -0
  9. package/dist/agents/google/index.d.ts +9 -0
  10. package/dist/agents/google/index.js +53 -0
  11. package/dist/agents/mock/index.d.ts +11 -0
  12. package/dist/agents/mock/index.js +26 -0
  13. package/dist/agents/openai/index.d.ts +9 -0
  14. package/dist/agents/openai/index.js +57 -0
  15. package/dist/agents/system-prompts.d.ts +3 -0
  16. package/dist/agents/system-prompts.js +32 -0
  17. package/dist/cli/agent-command-installer.d.ts +4 -0
  18. package/dist/cli/agent-command-installer.js +148 -0
  19. package/dist/cli/agent-ids.d.ts +15 -0
  20. package/dist/cli/agent-ids.js +49 -0
  21. package/dist/cli/doctor.d.ts +1 -0
  22. package/dist/cli/doctor.js +144 -0
  23. package/dist/cli/fix-tui.d.ts +4 -0
  24. package/dist/cli/fix-tui.js +79 -0
  25. package/dist/cli/index.d.ts +9 -0
  26. package/dist/cli/index.js +467 -0
  27. package/dist/cli/init-tui.d.ts +23 -0
  28. package/dist/cli/init-tui.js +183 -0
  29. package/dist/cli/init.d.ts +12 -0
  30. package/dist/cli/init.js +335 -0
  31. package/dist/cli/normalize-interactive.d.ts +8 -0
  32. package/dist/cli/normalize-interactive.js +167 -0
  33. package/dist/cli/preset-loader.d.ts +4 -0
  34. package/dist/cli/preset-loader.js +210 -0
  35. package/dist/core/artifact-registry.d.ts +11 -0
  36. package/dist/core/artifact-registry.js +49 -0
  37. package/dist/core/artifacts.d.ts +10 -0
  38. package/dist/core/artifacts.js +892 -0
  39. package/dist/core/clean.d.ts +10 -0
  40. package/dist/core/clean.js +74 -0
  41. package/dist/core/consistency.d.ts +8 -0
  42. package/dist/core/consistency.js +328 -0
  43. package/dist/core/constants.d.ts +7 -0
  44. package/dist/core/constants.js +64 -0
  45. package/dist/core/errors.d.ts +3 -0
  46. package/dist/core/errors.js +10 -0
  47. package/dist/core/fix.d.ts +31 -0
  48. package/dist/core/fix.js +188 -0
  49. package/dist/core/hook-executor.d.ts +1 -0
  50. package/dist/core/hook-executor.js +175 -0
  51. package/dist/core/markdown.d.ts +16 -0
  52. package/dist/core/markdown.js +81 -0
  53. package/dist/core/normalize.d.ts +8 -0
  54. package/dist/core/normalize.js +125 -0
  55. package/dist/core/normalized-brief.d.ts +48 -0
  56. package/dist/core/normalized-brief.js +182 -0
  57. package/dist/core/output-index.d.ts +13 -0
  58. package/dist/core/output-index.js +55 -0
  59. package/dist/core/paths.d.ts +17 -0
  60. package/dist/core/paths.js +80 -0
  61. package/dist/core/project-config.d.ts +14 -0
  62. package/dist/core/project-config.js +69 -0
  63. package/dist/core/registry.d.ts +13 -0
  64. package/dist/core/registry.js +115 -0
  65. package/dist/core/settings.d.ts +8 -0
  66. package/dist/core/settings.js +43 -0
  67. package/dist/core/template-engine.d.ts +3 -0
  68. package/dist/core/template-engine.js +43 -0
  69. package/dist/core/template-resolver.d.ts +15 -0
  70. package/dist/core/template-resolver.js +46 -0
  71. package/dist/core/templates.d.ts +33 -0
  72. package/dist/core/templates.js +440 -0
  73. package/dist/core/terminology.d.ts +21 -0
  74. package/dist/core/terminology.js +143 -0
  75. package/dist/core/tracing.d.ts +21 -0
  76. package/dist/core/tracing.js +74 -0
  77. package/dist/core/types.d.ts +35 -0
  78. package/dist/core/types.js +5 -0
  79. package/dist/core/utils.d.ts +7 -0
  80. package/dist/core/utils.js +66 -0
  81. package/dist/core/validate.d.ts +10 -0
  82. package/dist/core/validate.js +226 -0
  83. package/dist/core/validator.d.ts +5 -0
  84. package/dist/core/validator.js +76 -0
  85. package/dist/core/version.d.ts +1 -0
  86. package/dist/core/version.js +30 -0
  87. package/dist/core/workflow-commands.d.ts +7 -0
  88. package/dist/core/workflow-commands.js +29 -0
  89. package/dist/i18n/en.json +45 -0
  90. package/dist/i18n/index.d.ts +5 -0
  91. package/dist/i18n/index.js +63 -0
  92. package/dist/i18n/tr.json +45 -0
  93. package/dist/providers/index.d.ts +2 -1
  94. package/dist/providers/index.js +20 -6
  95. package/dist/providers/mock-provider.d.ts +1 -1
  96. package/dist/providers/mock-provider.js +12 -11
  97. package/dist/providers/openai-provider.d.ts +1 -1
  98. package/dist/providers/openai-provider.js +13 -13
  99. package/dist/skill-engine/context.d.ts +7 -0
  100. package/dist/skill-engine/context.js +76 -0
  101. package/dist/skill-engine/discovery.d.ts +2 -0
  102. package/dist/skill-engine/discovery.js +52 -0
  103. package/dist/skill-engine/graph.d.ts +4 -0
  104. package/dist/skill-engine/graph.js +114 -0
  105. package/dist/skill-engine/index.d.ts +11 -0
  106. package/dist/skill-engine/index.js +49 -0
  107. package/dist/skill-engine/pipeline.d.ts +9 -0
  108. package/dist/skill-engine/pipeline.js +84 -0
  109. package/dist/skill-engine/registry.d.ts +12 -0
  110. package/dist/skill-engine/registry.js +74 -0
  111. package/dist/skill-engine/types.d.ts +66 -0
  112. package/dist/skill-engine/types.js +2 -0
  113. package/dist/skill-engine/validator.d.ts +4 -0
  114. package/dist/skill-engine/validator.js +90 -0
  115. package/dist/skills/engine.d.ts +10 -0
  116. package/dist/skills/engine.js +75 -0
  117. package/dist/skills/fix-skill.d.ts +2 -0
  118. package/dist/skills/fix-skill.js +38 -0
  119. package/dist/skills/fix.d.ts +2 -0
  120. package/dist/skills/fix.js +41 -0
  121. package/dist/skills/generate-artifact-skill.d.ts +2 -0
  122. package/dist/skills/generate-artifact-skill.js +32 -0
  123. package/dist/skills/generate-artifact.d.ts +2 -0
  124. package/dist/skills/generate-artifact.js +42 -0
  125. package/dist/skills/generate-pipeline-skill.d.ts +2 -0
  126. package/dist/skills/generate-pipeline-skill.js +45 -0
  127. package/dist/skills/normalize-skill.d.ts +2 -0
  128. package/dist/skills/normalize-skill.js +29 -0
  129. package/dist/skills/normalize.d.ts +2 -0
  130. package/dist/skills/normalize.js +29 -0
  131. package/dist/skills/register-core.d.ts +2 -0
  132. package/dist/skills/register-core.js +21 -0
  133. package/dist/skills/types.d.ts +28 -0
  134. package/dist/skills/types.js +2 -0
  135. package/dist/skills/validate-skill.d.ts +2 -0
  136. package/dist/skills/validate-skill.js +29 -0
  137. package/dist/skills/validate.d.ts +2 -0
  138. package/dist/skills/validate.js +37 -0
  139. package/package.json +72 -45
  140. package/src/agents/agent-registry.ts +93 -0
  141. package/src/agents/anthropic/index.ts +86 -0
  142. package/src/agents/anthropic/manifest.json +7 -0
  143. package/src/agents/base.ts +77 -0
  144. package/src/agents/google/index.ts +79 -0
  145. package/src/agents/google/manifest.json +7 -0
  146. package/src/agents/mock/index.ts +32 -0
  147. package/src/agents/mock/manifest.json +7 -0
  148. package/src/agents/openai/index.ts +83 -0
  149. package/src/agents/openai/manifest.json +7 -0
  150. package/src/agents/system-prompts.ts +35 -0
  151. package/src/{agent-command-installer.ts → cli/agent-command-installer.ts} +164 -164
  152. package/src/{agents.ts → cli/agent-ids.ts} +58 -58
  153. package/src/{doctor.ts → cli/doctor.ts} +157 -137
  154. package/src/cli/fix-tui.ts +111 -0
  155. package/src/{cli.ts → cli/index.ts} +463 -410
  156. package/src/{init-tui.ts → cli/init-tui.ts} +49 -37
  157. package/src/{init.ts → cli/init.ts} +399 -398
  158. package/src/cli/normalize-interactive.ts +241 -0
  159. package/src/{preset-loader.ts → cli/preset-loader.ts} +237 -237
  160. package/src/{artifact-registry.ts → core/artifact-registry.ts} +69 -69
  161. package/src/{artifacts.ts → core/artifacts.ts} +1081 -1072
  162. package/src/core/clean.ts +88 -0
  163. package/src/{consistency.ts → core/consistency.ts} +374 -303
  164. package/src/{constants.ts → core/constants.ts} +72 -72
  165. package/src/{errors.ts → core/errors.ts} +7 -7
  166. package/src/core/fix.ts +253 -0
  167. package/src/{hook-executor.ts → core/hook-executor.ts} +196 -196
  168. package/src/{markdown.ts → core/markdown.ts} +93 -73
  169. package/src/{normalize.ts → core/normalize.ts} +145 -137
  170. package/src/{normalized-brief.ts → core/normalized-brief.ts} +227 -206
  171. package/src/{output-index.ts → core/output-index.ts} +59 -59
  172. package/src/{paths.ts → core/paths.ts} +75 -71
  173. package/src/{project-config.ts → core/project-config.ts} +78 -78
  174. package/src/{registry.ts → core/registry.ts} +119 -119
  175. package/src/{settings.ts → core/settings.ts} +8 -2
  176. package/src/core/template-engine.ts +45 -0
  177. package/src/{template-resolver.ts → core/template-resolver.ts} +54 -54
  178. package/src/{templates.ts → core/templates.ts} +452 -452
  179. package/src/core/terminology.ts +177 -0
  180. package/src/core/tracing.ts +110 -0
  181. package/src/{types.ts → core/types.ts} +46 -46
  182. package/src/{utils.ts → core/utils.ts} +64 -64
  183. package/src/{validate.ts → core/validate.ts} +252 -246
  184. package/src/{validator.ts → core/validator.ts} +92 -92
  185. package/src/{version.ts → core/version.ts} +24 -24
  186. package/src/{workflow-commands.ts → core/workflow-commands.ts} +32 -32
  187. package/src/i18n/en.json +45 -0
  188. package/src/i18n/index.ts +58 -0
  189. package/src/i18n/tr.json +45 -0
  190. package/src/providers/index.ts +29 -12
  191. package/src/providers/mock-provider.ts +200 -199
  192. package/src/providers/openai-provider.ts +88 -88
  193. package/src/skill-engine/context.ts +90 -0
  194. package/src/skill-engine/discovery.ts +57 -0
  195. package/src/skill-engine/graph.ts +136 -0
  196. package/src/skill-engine/index.ts +55 -0
  197. package/src/skill-engine/pipeline.ts +112 -0
  198. package/src/skill-engine/registry.ts +75 -0
  199. package/src/skill-engine/types.ts +81 -0
  200. package/src/skill-engine/validator.ts +135 -0
  201. package/src/skills/fix.ts +45 -0
  202. package/src/skills/generate-artifact.ts +48 -0
  203. package/src/skills/normalize.ts +32 -0
  204. package/src/skills/register-core.ts +27 -0
  205. package/src/skills/validate.ts +40 -0
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generatePipelineSkill = void 0;
4
+ const artifact_registry_1 = require("../core/artifact-registry");
5
+ const artifacts_1 = require("../core/artifacts");
6
+ const normalize_1 = require("../core/normalize");
7
+ const validate_1 = require("../core/validate");
8
+ exports.generatePipelineSkill = {
9
+ manifest: {
10
+ name: "generate-pipeline",
11
+ description: "Run end-to-end pipeline: normalize → generate all artifacts → validate",
12
+ category: "core",
13
+ inputs: [
14
+ { name: "cwd", type: "path", required: true, description: "Project working directory" },
15
+ { name: "strict", type: "boolean", required: false, description: "Treat warnings as errors" }
16
+ ],
17
+ outputs: [
18
+ { name: "pass", type: "string", description: "Whether validation passed" },
19
+ { name: "reportPath", type: "path", description: "Path to validation report" },
20
+ { name: "artifactCount", type: "string", description: "Number of artifacts generated" }
21
+ ]
22
+ },
23
+ async execute(context, inputs) {
24
+ const cwd = inputs.cwd ?? context.cwd;
25
+ const normalizedPath = await (0, normalize_1.runNormalize)({ cwd });
26
+ context.log(`Normalized brief: ${normalizedPath}`);
27
+ const artifactTypes = await (0, artifact_registry_1.listArtifactTypes)(cwd);
28
+ for (const type of artifactTypes) {
29
+ const file = await (0, artifacts_1.generateArtifact)({
30
+ artifactType: type,
31
+ cwd,
32
+ normalizedBriefOverride: normalizedPath,
33
+ agent: context.agent
34
+ });
35
+ context.log(`${type.toUpperCase()} generated: ${file}`);
36
+ }
37
+ const result = await (0, validate_1.runValidate)(cwd, { strict: Boolean(inputs.strict) });
38
+ context.log(`Validation ${result.pass ? "passed" : "failed"}: ${result.reportPath}`);
39
+ return {
40
+ pass: result.pass,
41
+ reportPath: result.reportPath,
42
+ artifactCount: artifactTypes.length
43
+ };
44
+ }
45
+ };
@@ -0,0 +1,2 @@
1
+ import type { Skill } from "./types";
2
+ export declare const normalizeSkill: Skill;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeSkill = void 0;
4
+ const normalize_1 = require("../core/normalize");
5
+ exports.normalizeSkill = {
6
+ manifest: {
7
+ name: "normalize",
8
+ description: "Normalize a product brief into structured JSON with contracts and confidence scores",
9
+ category: "core",
10
+ inputs: [
11
+ { name: "cwd", type: "path", required: true, description: "Project working directory" },
12
+ { name: "brief", type: "path", required: false, description: "Override path to brief.md" },
13
+ { name: "out", type: "path", required: false, description: "Override output path" }
14
+ ],
15
+ outputs: [
16
+ { name: "normalizedBriefPath", type: "path", description: "Path to normalized-brief.json" }
17
+ ]
18
+ },
19
+ async execute(context, inputs) {
20
+ const cwd = inputs.cwd ?? context.cwd;
21
+ const outPath = await (0, normalize_1.runNormalize)({
22
+ cwd,
23
+ brief: inputs.brief,
24
+ out: inputs.out
25
+ });
26
+ context.log(`Normalized brief written to: ${outPath}`);
27
+ return { normalizedBriefPath: outPath };
28
+ }
29
+ };
@@ -0,0 +1,2 @@
1
+ import type { Skill } from "../skill-engine/types";
2
+ export declare const normalizeSkill: Skill;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeSkill = void 0;
4
+ const normalize_1 = require("../core/normalize");
5
+ exports.normalizeSkill = {
6
+ manifest: {
7
+ name: "normalize",
8
+ version: "2.0.0",
9
+ description: "Normalize a product brief into structured JSON with contracts and confidence scores",
10
+ category: "core",
11
+ depends_on: [],
12
+ inputs: [
13
+ { name: "cwd", type: "path", required: true, description: "Project working directory" }
14
+ ],
15
+ outputs: [
16
+ { name: "normalizedBriefPath", type: "path", description: "Path to normalized-brief.json" }
17
+ ],
18
+ tags: ["brief", "normalization"]
19
+ },
20
+ async execute(context, inputs) {
21
+ const cwd = inputs.cwd ?? context.state.cwd;
22
+ context.progress(1, 2, "Normalizing brief...");
23
+ const outPath = await (0, normalize_1.runNormalize)({ cwd });
24
+ context.progress(2, 2, "Brief normalized.");
25
+ context.log(`Normalized brief written to: ${outPath}`);
26
+ context.state.normalizedBriefPath = outPath;
27
+ return { normalizedBriefPath: outPath };
28
+ }
29
+ };
@@ -0,0 +1,2 @@
1
+ import type { SkillRegistry } from "../skill-engine/registry";
2
+ export declare function registerCoreSkills(registry: SkillRegistry, cwd: string): Promise<void>;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerCoreSkills = registerCoreSkills;
4
+ const artifact_registry_1 = require("../core/artifact-registry");
5
+ const generate_artifact_1 = require("./generate-artifact");
6
+ const fix_1 = require("./fix");
7
+ const normalize_1 = require("./normalize");
8
+ const validate_1 = require("./validate");
9
+ async function registerCoreSkills(registry, cwd) {
10
+ registry.register(normalize_1.normalizeSkill);
11
+ const definitions = await (0, artifact_registry_1.listArtifactDefinitions)(cwd);
12
+ const artifactNames = [];
13
+ for (const def of definitions) {
14
+ const skill = (0, generate_artifact_1.createArtifactSkill)(def.name, def.upstream);
15
+ registry.register(skill);
16
+ artifactNames.push(def.name);
17
+ }
18
+ const validateSkill = (0, validate_1.createValidateSkill)(artifactNames);
19
+ registry.register(validateSkill);
20
+ registry.register(fix_1.fixSkill);
21
+ }
@@ -0,0 +1,28 @@
1
+ export type SkillInput = {
2
+ name: string;
3
+ type: "path" | "string" | "boolean" | "json";
4
+ required: boolean;
5
+ description: string;
6
+ };
7
+ export type SkillOutput = {
8
+ name: string;
9
+ type: "path" | "string" | "json";
10
+ description: string;
11
+ };
12
+ export type SkillManifest = {
13
+ name: string;
14
+ description: string;
15
+ category: "core" | "artifact" | "validation" | "custom";
16
+ inputs: SkillInput[];
17
+ outputs: SkillOutput[];
18
+ };
19
+ export type SkillContext = {
20
+ cwd: string;
21
+ log: (message: string) => void;
22
+ agent?: string;
23
+ };
24
+ export type SkillFunction = (context: SkillContext, inputs: Record<string, unknown>) => Promise<Record<string, unknown>>;
25
+ export type Skill = {
26
+ manifest: SkillManifest;
27
+ execute: SkillFunction;
28
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ import type { Skill } from "./types";
2
+ export declare const validateSkill: Skill;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateSkill = void 0;
4
+ const validate_1 = require("../core/validate");
5
+ exports.validateSkill = {
6
+ manifest: {
7
+ name: "validate",
8
+ description: "Run cross-artifact validation and generate a report",
9
+ category: "validation",
10
+ inputs: [
11
+ { name: "cwd", type: "path", required: true, description: "Project working directory" },
12
+ { name: "strict", type: "boolean", required: false, description: "Treat warnings as errors" },
13
+ { name: "report", type: "path", required: false, description: "Override report output path" }
14
+ ],
15
+ outputs: [
16
+ { name: "pass", type: "string", description: "Whether validation passed" },
17
+ { name: "reportPath", type: "path", description: "Path to validation report" }
18
+ ]
19
+ },
20
+ async execute(context, inputs) {
21
+ const cwd = inputs.cwd ?? context.cwd;
22
+ const result = await (0, validate_1.runValidate)(cwd, {
23
+ strict: Boolean(inputs.strict),
24
+ report: inputs.report
25
+ });
26
+ context.log(`Validation ${result.pass ? "passed" : "failed"}: ${result.reportPath}`);
27
+ return { pass: result.pass, reportPath: result.reportPath, issueCount: result.issues.length };
28
+ }
29
+ };
@@ -0,0 +1,2 @@
1
+ import type { Skill } from "../skill-engine/types";
2
+ export declare function createValidateSkill(artifactTypes: string[]): Skill;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createValidateSkill = createValidateSkill;
4
+ const validate_1 = require("../core/validate");
5
+ function createValidateSkill(artifactTypes) {
6
+ return {
7
+ manifest: {
8
+ name: "validate",
9
+ version: "2.0.0",
10
+ description: "Run 7-gate cross-artifact validation and generate report",
11
+ category: "validation",
12
+ depends_on: [...artifactTypes],
13
+ inputs: [
14
+ { name: "cwd", type: "path", required: true, description: "Project working directory" }
15
+ ],
16
+ outputs: [
17
+ { name: "validationResult", type: "json", description: "Validation result with pass/fail and issues" }
18
+ ],
19
+ tags: ["validation", "consistency"]
20
+ },
21
+ async execute(context, inputs) {
22
+ const cwd = inputs.cwd ?? context.state.cwd;
23
+ context.progress(1, 2, "Validating artifacts...");
24
+ const result = await (0, validate_1.runValidate)(cwd, {});
25
+ context.progress(2, 2, `Validation ${result.pass ? "passed" : "failed"}.`);
26
+ context.log(`Validation ${result.pass ? "passed" : "failed"}: ${result.reportPath}`);
27
+ context.state.validationResult = result;
28
+ return {
29
+ validationResult: {
30
+ pass: result.pass,
31
+ reportPath: result.reportPath,
32
+ issueCount: result.issues.length
33
+ }
34
+ };
35
+ }
36
+ };
37
+ }
package/package.json CHANGED
@@ -1,45 +1,72 @@
1
- {
2
- "name": "@shahmarasy/prodo",
3
- "version": "0.1.4",
4
- "description": "CLI-first, prompt-powered product artifact kit",
5
- "main": "dist/cli.js",
6
- "types": "dist/cli.d.ts",
7
- "bin": {
8
- "prodo": "bin/prodo.cjs"
9
- },
10
- "files": [
11
- "bin/",
12
- "dist/",
13
- "templates/",
14
- "presets/",
15
- "src/",
16
- "README.md"
17
- ],
18
- "publishConfig": {
19
- "access": "public"
20
- },
21
- "engines": {
22
- "node": ">=20"
23
- },
24
- "scripts": {
25
- "build": "tsc -p tsconfig.json",
26
- "test": "npm run build && node --test tests/*.test.cjs",
27
- "verify:release": "npm run build && node scripts/verify-release-build.cjs"
28
- },
29
- "keywords": [],
30
- "author": "",
31
- "license": "ISC",
32
- "dependencies": {
33
- "@clack/prompts": "^1.1.0",
34
- "ajv": "^8.18.0",
35
- "ajv-formats": "^3.0.1",
36
- "commander": "^14.0.3",
37
- "gray-matter": "^4.0.3",
38
- "js-yaml": "^4.1.1"
39
- },
40
- "devDependencies": {
41
- "@types/js-yaml": "^4.0.9",
42
- "@types/node": "^25.5.0",
43
- "typescript": "^5.9.3"
44
- }
45
- }
1
+ {
2
+ "name": "@shahmarasy/prodo",
3
+ "version": "0.1.6",
4
+ "description": "CLI-first, prompt-powered product artifact kit",
5
+ "main": "dist/cli/index.js",
6
+ "types": "dist/cli/index.d.ts",
7
+ "bin": {
8
+ "prodo": "bin/prodo.cjs"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "dist/",
13
+ "templates/",
14
+ "presets/",
15
+ "src/",
16
+ "README.md"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "scripts": {
25
+ "build": "node scripts/build.cjs",
26
+ "test": "node scripts/test.cjs",
27
+ "verify:release": "node scripts/build.cjs"
28
+ },
29
+ "keywords": [
30
+ "product-management",
31
+ "prd",
32
+ "ai-agents",
33
+ "codex",
34
+ "claude",
35
+ "gemini",
36
+ "document-generation",
37
+ "cli"
38
+ ],
39
+ "author": "Shahmarasy",
40
+ "license": "MIT",
41
+ "dependencies": {
42
+ "@clack/prompts": "^1.1.0",
43
+ "ajv": "^8.18.0",
44
+ "ajv-formats": "^3.0.1",
45
+ "commander": "^14.0.3",
46
+ "gray-matter": "^4.0.3",
47
+ "js-yaml": "^4.1.1",
48
+ "nunjucks": "^3.2.4"
49
+ },
50
+ "peerDependencies": {
51
+ "openai": ">=4.0.0",
52
+ "@anthropic-ai/sdk": ">=0.20.0",
53
+ "@google/generative-ai": ">=0.1.0"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "openai": {
57
+ "optional": true
58
+ },
59
+ "@anthropic-ai/sdk": {
60
+ "optional": true
61
+ },
62
+ "@google/generative-ai": {
63
+ "optional": true
64
+ }
65
+ },
66
+ "devDependencies": {
67
+ "@types/js-yaml": "^4.0.9",
68
+ "@types/node": "^25.5.0",
69
+ "@types/nunjucks": "^3.2.6",
70
+ "typescript": "^5.9.3"
71
+ }
72
+ }
@@ -0,0 +1,93 @@
1
+ import type { AgentPlugin } from "./base";
2
+ import { UserError } from "../core/errors";
3
+ import type { LLMProvider } from "../core/types";
4
+ import { MockAgent } from "./mock";
5
+ import { OpenAIAgent } from "./openai";
6
+ import { AnthropicAgent } from "./anthropic";
7
+ import { GoogleAgent } from "./google";
8
+
9
+ const AGENT_ALIASES: Record<string, string> = {
10
+ mock: "mock",
11
+ openai: "openai",
12
+ anthropic: "anthropic",
13
+ claude: "anthropic",
14
+ google: "google",
15
+ gemini: "google"
16
+ };
17
+
18
+ export class AgentRegistry {
19
+ private agents = new Map<string, AgentPlugin>();
20
+
21
+ register(agent: AgentPlugin): void {
22
+ this.agents.set(agent.name, agent);
23
+ }
24
+
25
+ list(): AgentPlugin[] {
26
+ return Array.from(this.agents.values());
27
+ }
28
+
29
+ get(name: string): AgentPlugin | undefined {
30
+ const normalized = AGENT_ALIASES[name.toLowerCase().trim()] ?? name.toLowerCase().trim();
31
+ return this.agents.get(normalized);
32
+ }
33
+
34
+ async resolve(name?: string): Promise<AgentPlugin> {
35
+ const agentName = this.resolveAgentName(name);
36
+ const agent = this.get(agentName);
37
+
38
+ if (!agent) {
39
+ const available = this.list().map((a) => a.name).join(", ");
40
+ throw new UserError(
41
+ `Unknown agent: "${agentName}". Available agents: ${available}`
42
+ );
43
+ }
44
+
45
+ return agent;
46
+ }
47
+
48
+ private resolveAgentName(override?: string): string {
49
+ if (override) return override;
50
+
51
+ const fromEnv = process.env.PRODO_AGENT;
52
+ if (fromEnv) return fromEnv;
53
+
54
+ const fromLegacy = process.env.PRODO_LLM_PROVIDER;
55
+ if (fromLegacy) return fromLegacy;
56
+
57
+ const isTest =
58
+ process.env.NODE_ENV === "test" ||
59
+ process.env.PRODO_TEST === "1";
60
+ if (isTest) return "mock";
61
+
62
+ return "mock";
63
+ }
64
+
65
+ toProvider(agent: AgentPlugin): LLMProvider {
66
+ return {
67
+ generate: (prompt, inputContext, schemaHint) =>
68
+ agent.generate(prompt, inputContext, schemaHint)
69
+ };
70
+ }
71
+ }
72
+
73
+ let globalRegistry: AgentRegistry | null = null;
74
+
75
+ function createDefaultRegistry(): AgentRegistry {
76
+ const registry = new AgentRegistry();
77
+ registry.register(new MockAgent());
78
+ registry.register(new OpenAIAgent());
79
+ registry.register(new AnthropicAgent());
80
+ registry.register(new GoogleAgent());
81
+ return registry;
82
+ }
83
+
84
+ export function getGlobalRegistry(): AgentRegistry {
85
+ if (!globalRegistry) {
86
+ globalRegistry = createDefaultRegistry();
87
+ }
88
+ return globalRegistry;
89
+ }
90
+
91
+ export function resetGlobalRegistry(): void {
92
+ globalRegistry = null;
93
+ }
@@ -0,0 +1,86 @@
1
+ import { BaseAgent, type AgentConfig } from "../base";
2
+ import { buildSystemPrompt, buildUserMessage } from "../system-prompts";
3
+ import { UserError } from "../../core/errors";
4
+ import type { GenerateResult, ProviderSchemaHint } from "../../core/types";
5
+
6
+ const dynamicImport = new Function("specifier", "return import(specifier)") as (
7
+ specifier: string
8
+ ) => Promise<unknown>;
9
+
10
+ type ContentBlock = { type: string; text?: string };
11
+
12
+ export class AnthropicAgent extends BaseAgent {
13
+ readonly name = "anthropic";
14
+ readonly displayName = "Anthropic Claude";
15
+ readonly sdkRequired = "@anthropic-ai/sdk";
16
+
17
+ getConfig(): AgentConfig {
18
+ return {
19
+ name: this.name,
20
+ displayName: this.displayName,
21
+ sdkRequired: this.sdkRequired,
22
+ envVars: ["ANTHROPIC_API_KEY"],
23
+ defaultModel: "claude-sonnet-4-20250514"
24
+ };
25
+ }
26
+
27
+ async generate(
28
+ prompt: string,
29
+ inputContext: Record<string, unknown>,
30
+ schemaHint: ProviderSchemaHint
31
+ ): Promise<GenerateResult> {
32
+ const apiKey = process.env.ANTHROPIC_API_KEY;
33
+ if (!apiKey) {
34
+ throw new UserError("ANTHROPIC_API_KEY is not set. Set it to use the Anthropic agent.");
35
+ }
36
+
37
+ const model = process.env.PRODO_ANTHROPIC_MODEL ?? "claude-sonnet-4-20250514";
38
+
39
+ const outputLanguage =
40
+ typeof inputContext.outputLanguage === "string" && inputContext.outputLanguage.trim()
41
+ ? inputContext.outputLanguage.trim()
42
+ : "en";
43
+
44
+ const system = buildSystemPrompt(schemaHint, outputLanguage);
45
+ const user = buildUserMessage(prompt, inputContext);
46
+
47
+ let AnthropicConstructor: new (opts: { apiKey: string }) => {
48
+ messages: {
49
+ create: (params: Record<string, unknown>) => Promise<{
50
+ content: ContentBlock[];
51
+ }>;
52
+ };
53
+ };
54
+
55
+ try {
56
+ const mod = (await dynamicImport("@anthropic-ai/sdk")) as {
57
+ default: typeof AnthropicConstructor;
58
+ };
59
+ AnthropicConstructor = mod.default;
60
+ } catch {
61
+ throw new UserError(
62
+ "Anthropic SDK is not installed. Run: npm install @anthropic-ai/sdk"
63
+ );
64
+ }
65
+
66
+ const client = new AnthropicConstructor({ apiKey });
67
+
68
+ const response = await client.messages.create({
69
+ model,
70
+ max_tokens: 8192,
71
+ system,
72
+ messages: [{ role: "user", content: user }]
73
+ });
74
+
75
+ const textBlock = response.content.find(
76
+ (block: ContentBlock) => block.type === "text"
77
+ );
78
+ const content = textBlock?.text?.trim() ?? "";
79
+
80
+ if (!content) {
81
+ throw new UserError("Anthropic agent returned an empty response.");
82
+ }
83
+
84
+ return { body: content };
85
+ }
86
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "anthropic",
3
+ "displayName": "Anthropic Claude",
4
+ "sdkRequired": "@anthropic-ai/sdk",
5
+ "envVars": ["ANTHROPIC_API_KEY"],
6
+ "defaultModel": "claude-sonnet-4-20250514"
7
+ }
@@ -0,0 +1,77 @@
1
+ import type { GenerateResult, ProviderSchemaHint } from "../core/types";
2
+
3
+ export type AgentConfig = {
4
+ name: string;
5
+ displayName: string;
6
+ sdkRequired: string | null;
7
+ envVars: string[];
8
+ defaultModel?: string;
9
+ };
10
+
11
+ export interface AgentPlugin {
12
+ readonly name: string;
13
+ readonly displayName: string;
14
+ readonly sdkRequired: string | null;
15
+
16
+ generate(
17
+ prompt: string,
18
+ inputContext: Record<string, unknown>,
19
+ schemaHint: ProviderSchemaHint
20
+ ): Promise<GenerateResult>;
21
+
22
+ isAvailable(): Promise<boolean>;
23
+ getConfig(): AgentConfig;
24
+ }
25
+
26
+ export abstract class BaseAgent implements AgentPlugin {
27
+ abstract readonly name: string;
28
+ abstract readonly displayName: string;
29
+ abstract readonly sdkRequired: string | null;
30
+
31
+ abstract generate(
32
+ prompt: string,
33
+ inputContext: Record<string, unknown>,
34
+ schemaHint: ProviderSchemaHint
35
+ ): Promise<GenerateResult>;
36
+
37
+ abstract getConfig(): AgentConfig;
38
+
39
+ async isAvailable(): Promise<boolean> {
40
+ const config = this.getConfig();
41
+
42
+ if (config.sdkRequired) {
43
+ try {
44
+ await import(config.sdkRequired);
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+
50
+ for (const envVar of config.envVars) {
51
+ if (!process.env[envVar]) {
52
+ return false;
53
+ }
54
+ }
55
+
56
+ return true;
57
+ }
58
+
59
+ availabilityHint(): string {
60
+ const config = this.getConfig();
61
+ const hints: string[] = [];
62
+
63
+ if (config.sdkRequired) {
64
+ hints.push(`SDK required: npm install ${config.sdkRequired}`);
65
+ }
66
+
67
+ for (const envVar of config.envVars) {
68
+ if (!process.env[envVar]) {
69
+ hints.push(`Set environment variable: ${envVar}`);
70
+ }
71
+ }
72
+
73
+ return hints.length > 0
74
+ ? `Agent "${config.displayName}" is not available.\n ${hints.join("\n ")}`
75
+ : `Agent "${config.displayName}" is available.`;
76
+ }
77
+ }