@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.
- package/README.md +201 -97
- package/bin/prodo.cjs +6 -6
- package/dist/agents/agent-registry.d.ts +13 -0
- package/dist/agents/agent-registry.js +79 -0
- package/dist/agents/anthropic/index.d.ts +9 -0
- package/dist/agents/anthropic/index.js +55 -0
- package/dist/agents/base.d.ts +25 -0
- package/dist/agents/base.js +71 -0
- package/dist/agents/google/index.d.ts +9 -0
- package/dist/agents/google/index.js +53 -0
- package/dist/agents/mock/index.d.ts +11 -0
- package/dist/agents/mock/index.js +26 -0
- package/dist/agents/openai/index.d.ts +9 -0
- package/dist/agents/openai/index.js +57 -0
- package/dist/agents/system-prompts.d.ts +3 -0
- package/dist/agents/system-prompts.js +32 -0
- package/dist/cli/agent-command-installer.d.ts +4 -0
- package/dist/cli/agent-command-installer.js +148 -0
- package/dist/cli/agent-ids.d.ts +15 -0
- package/dist/cli/agent-ids.js +49 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +144 -0
- package/dist/cli/fix-tui.d.ts +4 -0
- package/dist/cli/fix-tui.js +79 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +467 -0
- package/dist/cli/init-tui.d.ts +23 -0
- package/dist/cli/init-tui.js +183 -0
- package/dist/cli/init.d.ts +12 -0
- package/dist/cli/init.js +335 -0
- package/dist/cli/normalize-interactive.d.ts +8 -0
- package/dist/cli/normalize-interactive.js +167 -0
- package/dist/cli/preset-loader.d.ts +4 -0
- package/dist/cli/preset-loader.js +210 -0
- package/dist/core/artifact-registry.d.ts +11 -0
- package/dist/core/artifact-registry.js +49 -0
- package/dist/core/artifacts.d.ts +10 -0
- package/dist/core/artifacts.js +892 -0
- package/dist/core/clean.d.ts +10 -0
- package/dist/core/clean.js +74 -0
- package/dist/core/consistency.d.ts +8 -0
- package/dist/core/consistency.js +328 -0
- package/dist/core/constants.d.ts +7 -0
- package/dist/core/constants.js +64 -0
- package/dist/core/errors.d.ts +3 -0
- package/dist/core/errors.js +10 -0
- package/dist/core/fix.d.ts +31 -0
- package/dist/core/fix.js +188 -0
- package/dist/core/hook-executor.d.ts +1 -0
- package/dist/core/hook-executor.js +175 -0
- package/dist/core/markdown.d.ts +16 -0
- package/dist/core/markdown.js +81 -0
- package/dist/core/normalize.d.ts +8 -0
- package/dist/core/normalize.js +125 -0
- package/dist/core/normalized-brief.d.ts +48 -0
- package/dist/core/normalized-brief.js +182 -0
- package/dist/core/output-index.d.ts +13 -0
- package/dist/core/output-index.js +55 -0
- package/dist/core/paths.d.ts +17 -0
- package/dist/core/paths.js +80 -0
- package/dist/core/project-config.d.ts +14 -0
- package/dist/core/project-config.js +69 -0
- package/dist/core/registry.d.ts +13 -0
- package/dist/core/registry.js +115 -0
- package/dist/core/settings.d.ts +8 -0
- package/dist/core/settings.js +43 -0
- package/dist/core/template-engine.d.ts +3 -0
- package/dist/core/template-engine.js +43 -0
- package/dist/core/template-resolver.d.ts +15 -0
- package/dist/core/template-resolver.js +46 -0
- package/dist/core/templates.d.ts +33 -0
- package/dist/core/templates.js +440 -0
- package/dist/core/terminology.d.ts +21 -0
- package/dist/core/terminology.js +143 -0
- package/dist/core/tracing.d.ts +21 -0
- package/dist/core/tracing.js +74 -0
- package/dist/core/types.d.ts +35 -0
- package/dist/core/types.js +5 -0
- package/dist/core/utils.d.ts +7 -0
- package/dist/core/utils.js +66 -0
- package/dist/core/validate.d.ts +10 -0
- package/dist/core/validate.js +226 -0
- package/dist/core/validator.d.ts +5 -0
- package/dist/core/validator.js +76 -0
- package/dist/core/version.d.ts +1 -0
- package/dist/core/version.js +30 -0
- package/dist/core/workflow-commands.d.ts +7 -0
- package/dist/core/workflow-commands.js +29 -0
- package/dist/i18n/en.json +45 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.js +63 -0
- package/dist/i18n/tr.json +45 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.js +20 -6
- package/dist/providers/mock-provider.d.ts +1 -1
- package/dist/providers/mock-provider.js +12 -11
- package/dist/providers/openai-provider.d.ts +1 -1
- package/dist/providers/openai-provider.js +13 -13
- package/dist/skill-engine/context.d.ts +7 -0
- package/dist/skill-engine/context.js +76 -0
- package/dist/skill-engine/discovery.d.ts +2 -0
- package/dist/skill-engine/discovery.js +52 -0
- package/dist/skill-engine/graph.d.ts +4 -0
- package/dist/skill-engine/graph.js +114 -0
- package/dist/skill-engine/index.d.ts +11 -0
- package/dist/skill-engine/index.js +49 -0
- package/dist/skill-engine/pipeline.d.ts +9 -0
- package/dist/skill-engine/pipeline.js +84 -0
- package/dist/skill-engine/registry.d.ts +12 -0
- package/dist/skill-engine/registry.js +74 -0
- package/dist/skill-engine/types.d.ts +66 -0
- package/dist/skill-engine/types.js +2 -0
- package/dist/skill-engine/validator.d.ts +4 -0
- package/dist/skill-engine/validator.js +90 -0
- package/dist/skills/engine.d.ts +10 -0
- package/dist/skills/engine.js +75 -0
- package/dist/skills/fix-skill.d.ts +2 -0
- package/dist/skills/fix-skill.js +38 -0
- package/dist/skills/fix.d.ts +2 -0
- package/dist/skills/fix.js +41 -0
- package/dist/skills/generate-artifact-skill.d.ts +2 -0
- package/dist/skills/generate-artifact-skill.js +32 -0
- package/dist/skills/generate-artifact.d.ts +2 -0
- package/dist/skills/generate-artifact.js +42 -0
- package/dist/skills/generate-pipeline-skill.d.ts +2 -0
- package/dist/skills/generate-pipeline-skill.js +45 -0
- package/dist/skills/normalize-skill.d.ts +2 -0
- package/dist/skills/normalize-skill.js +29 -0
- package/dist/skills/normalize.d.ts +2 -0
- package/dist/skills/normalize.js +29 -0
- package/dist/skills/register-core.d.ts +2 -0
- package/dist/skills/register-core.js +21 -0
- package/dist/skills/types.d.ts +28 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/validate-skill.d.ts +2 -0
- package/dist/skills/validate-skill.js +29 -0
- package/dist/skills/validate.d.ts +2 -0
- package/dist/skills/validate.js +37 -0
- package/package.json +72 -45
- package/src/agents/agent-registry.ts +93 -0
- package/src/agents/anthropic/index.ts +86 -0
- package/src/agents/anthropic/manifest.json +7 -0
- package/src/agents/base.ts +77 -0
- package/src/agents/google/index.ts +79 -0
- package/src/agents/google/manifest.json +7 -0
- package/src/agents/mock/index.ts +32 -0
- package/src/agents/mock/manifest.json +7 -0
- package/src/agents/openai/index.ts +83 -0
- package/src/agents/openai/manifest.json +7 -0
- package/src/agents/system-prompts.ts +35 -0
- package/src/{agent-command-installer.ts → cli/agent-command-installer.ts} +164 -164
- package/src/{agents.ts → cli/agent-ids.ts} +58 -58
- package/src/{doctor.ts → cli/doctor.ts} +157 -137
- package/src/cli/fix-tui.ts +111 -0
- package/src/{cli.ts → cli/index.ts} +463 -410
- package/src/{init-tui.ts → cli/init-tui.ts} +49 -37
- package/src/{init.ts → cli/init.ts} +399 -398
- package/src/cli/normalize-interactive.ts +241 -0
- package/src/{preset-loader.ts → cli/preset-loader.ts} +237 -237
- package/src/{artifact-registry.ts → core/artifact-registry.ts} +69 -69
- package/src/{artifacts.ts → core/artifacts.ts} +1081 -1072
- package/src/core/clean.ts +88 -0
- package/src/{consistency.ts → core/consistency.ts} +374 -303
- package/src/{constants.ts → core/constants.ts} +72 -72
- package/src/{errors.ts → core/errors.ts} +7 -7
- package/src/core/fix.ts +253 -0
- package/src/{hook-executor.ts → core/hook-executor.ts} +196 -196
- package/src/{markdown.ts → core/markdown.ts} +93 -73
- package/src/{normalize.ts → core/normalize.ts} +145 -137
- package/src/{normalized-brief.ts → core/normalized-brief.ts} +227 -206
- package/src/{output-index.ts → core/output-index.ts} +59 -59
- package/src/{paths.ts → core/paths.ts} +75 -71
- package/src/{project-config.ts → core/project-config.ts} +78 -78
- package/src/{registry.ts → core/registry.ts} +119 -119
- package/src/{settings.ts → core/settings.ts} +8 -2
- package/src/core/template-engine.ts +45 -0
- package/src/{template-resolver.ts → core/template-resolver.ts} +54 -54
- package/src/{templates.ts → core/templates.ts} +452 -452
- package/src/core/terminology.ts +177 -0
- package/src/core/tracing.ts +110 -0
- package/src/{types.ts → core/types.ts} +46 -46
- package/src/{utils.ts → core/utils.ts} +64 -64
- package/src/{validate.ts → core/validate.ts} +252 -246
- package/src/{validator.ts → core/validator.ts} +92 -92
- package/src/{version.ts → core/version.ts} +24 -24
- package/src/{workflow-commands.ts → core/workflow-commands.ts} +32 -32
- package/src/i18n/en.json +45 -0
- package/src/i18n/index.ts +58 -0
- package/src/i18n/tr.json +45 -0
- package/src/providers/index.ts +29 -12
- package/src/providers/mock-provider.ts +200 -199
- package/src/providers/openai-provider.ts +88 -88
- package/src/skill-engine/context.ts +90 -0
- package/src/skill-engine/discovery.ts +57 -0
- package/src/skill-engine/graph.ts +136 -0
- package/src/skill-engine/index.ts +55 -0
- package/src/skill-engine/pipeline.ts +112 -0
- package/src/skill-engine/registry.ts +75 -0
- package/src/skill-engine/types.ts +81 -0
- package/src/skill-engine/validator.ts +135 -0
- package/src/skills/fix.ts +45 -0
- package/src/skills/generate-artifact.ts +48 -0
- package/src/skills/normalize.ts +32 -0
- package/src/skills/register-core.ts +27 -0
- 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,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,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,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,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,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
|
-
"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": "
|
|
26
|
-
"test": "
|
|
27
|
-
"verify:release": "
|
|
28
|
-
},
|
|
29
|
-
"keywords": [
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
"@
|
|
43
|
-
"
|
|
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,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
|
+
}
|