@shahmarasy/prodo 0.1.5 → 0.1.7

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 (69) hide show
  1. package/dist/agents/system-prompts.js +12 -12
  2. package/dist/cli/agent-command-installer.d.ts +1 -1
  3. package/dist/cli/agent-command-installer.js +125 -19
  4. package/dist/cli/doctor.js +2 -2
  5. package/dist/cli/index.js +32 -30
  6. package/dist/cli/init-tui.d.ts +2 -2
  7. package/dist/cli/init-tui.js +43 -36
  8. package/dist/cli/init.d.ts +1 -0
  9. package/dist/cli/init.js +3 -2
  10. package/dist/core/artifacts.js +72 -72
  11. package/dist/core/settings.d.ts +1 -0
  12. package/dist/core/settings.js +10 -2
  13. package/dist/core/templates.js +248 -248
  14. package/dist/i18n/en.json +45 -45
  15. package/dist/i18n/tr.json +45 -45
  16. package/dist/providers/mock-provider.js +5 -5
  17. package/dist/providers/openai-provider.js +12 -12
  18. package/dist/skill-engine/context.d.ts +7 -0
  19. package/dist/skill-engine/context.js +76 -0
  20. package/dist/skill-engine/discovery.d.ts +2 -0
  21. package/dist/skill-engine/discovery.js +52 -0
  22. package/dist/skill-engine/graph.d.ts +4 -0
  23. package/dist/skill-engine/graph.js +114 -0
  24. package/dist/skill-engine/index.d.ts +11 -0
  25. package/dist/skill-engine/index.js +49 -0
  26. package/dist/skill-engine/pipeline.d.ts +9 -0
  27. package/dist/skill-engine/pipeline.js +84 -0
  28. package/dist/skill-engine/registry.d.ts +12 -0
  29. package/dist/skill-engine/registry.js +74 -0
  30. package/dist/skill-engine/types.d.ts +66 -0
  31. package/dist/skill-engine/types.js +2 -0
  32. package/dist/skill-engine/validator.d.ts +4 -0
  33. package/dist/skill-engine/validator.js +90 -0
  34. package/dist/skills/fix.d.ts +2 -0
  35. package/dist/skills/fix.js +41 -0
  36. package/dist/skills/generate-artifact.d.ts +2 -0
  37. package/dist/skills/generate-artifact.js +42 -0
  38. package/dist/skills/normalize.d.ts +2 -0
  39. package/dist/skills/normalize.js +29 -0
  40. package/dist/skills/register-core.d.ts +2 -0
  41. package/dist/skills/register-core.js +21 -0
  42. package/dist/skills/validate.d.ts +2 -0
  43. package/dist/skills/validate.js +37 -0
  44. package/package.json +4 -6
  45. package/src/cli/agent-command-installer.ts +115 -1
  46. package/src/cli/doctor.ts +2 -2
  47. package/src/cli/index.ts +35 -31
  48. package/src/cli/init-tui.ts +220 -208
  49. package/src/cli/init.ts +4 -3
  50. package/src/core/settings.ts +41 -35
  51. package/src/skill-engine/context.ts +90 -0
  52. package/src/skill-engine/discovery.ts +57 -0
  53. package/src/skill-engine/graph.ts +136 -0
  54. package/src/skill-engine/index.ts +55 -0
  55. package/src/skill-engine/pipeline.ts +112 -0
  56. package/src/skill-engine/registry.ts +75 -0
  57. package/src/skill-engine/types.ts +81 -0
  58. package/src/skill-engine/validator.ts +135 -0
  59. package/src/skills/fix.ts +45 -0
  60. package/src/skills/generate-artifact.ts +48 -0
  61. package/src/skills/{normalize-skill.ts → normalize.ts} +15 -12
  62. package/src/skills/register-core.ts +27 -0
  63. package/src/skills/validate.ts +40 -0
  64. package/src/skills/engine.ts +0 -94
  65. package/src/skills/fix-skill.ts +0 -38
  66. package/src/skills/generate-artifact-skill.ts +0 -32
  67. package/src/skills/generate-pipeline-skill.ts +0 -49
  68. package/src/skills/types.ts +0 -36
  69. package/src/skills/validate-skill.ts +0 -29
@@ -0,0 +1,66 @@
1
+ import type { ValidationIssue } from "../core/types";
2
+ export type SkillInputType = "string" | "path" | "boolean" | "json" | "number";
3
+ export type SkillInput = {
4
+ name: string;
5
+ type: SkillInputType;
6
+ required: boolean;
7
+ description?: string;
8
+ default?: unknown;
9
+ };
10
+ export type SkillOutput = {
11
+ name: string;
12
+ type: SkillInputType;
13
+ description?: string;
14
+ };
15
+ export type SkillManifest = {
16
+ name: string;
17
+ version: string;
18
+ description: string;
19
+ category: "core" | "artifact" | "validation" | "custom";
20
+ depends_on: string[];
21
+ inputs: SkillInput[];
22
+ outputs: SkillOutput[];
23
+ tags?: string[];
24
+ };
25
+ export type PipelineState = {
26
+ cwd: string;
27
+ normalizedBriefPath?: string;
28
+ generatedArtifacts: Map<string, string>;
29
+ validationResult?: {
30
+ pass: boolean;
31
+ reportPath: string;
32
+ issues: ValidationIssue[];
33
+ };
34
+ custom: Record<string, unknown>;
35
+ startedAt: string;
36
+ completedSkills: string[];
37
+ };
38
+ export type ProgressCallback = (step: number, total: number, message: string) => void;
39
+ export type SkillContext = {
40
+ state: PipelineState;
41
+ progress: ProgressCallback;
42
+ log: (message: string) => void;
43
+ agent?: string;
44
+ };
45
+ export type SkillExecuteFn = (context: SkillContext, inputs: Record<string, unknown>) => Promise<Record<string, unknown>>;
46
+ export type Skill = {
47
+ manifest: SkillManifest;
48
+ execute: SkillExecuteFn;
49
+ };
50
+ export type SkillError = {
51
+ skillName: string;
52
+ phase: "input_validation" | "execution" | "output_validation";
53
+ message: string;
54
+ inputContext?: Record<string, unknown>;
55
+ stack?: string;
56
+ recoveryHints: string[];
57
+ };
58
+ export type ExecutionTier = {
59
+ tier: number;
60
+ skills: string[];
61
+ };
62
+ export type PipelineOptions = {
63
+ log?: (message: string) => void;
64
+ progress?: ProgressCallback;
65
+ agent?: string;
66
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ import type { SkillError, SkillManifest } from "./types";
2
+ export declare function validateInputs(manifest: SkillManifest, inputs: Record<string, unknown>): SkillError | null;
3
+ export declare function validateInputPaths(manifest: SkillManifest, inputs: Record<string, unknown>): Promise<SkillError | null>;
4
+ export declare function validateOutputs(manifest: SkillManifest, outputs: Record<string, unknown>): SkillError | null;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateInputs = validateInputs;
4
+ exports.validateInputPaths = validateInputPaths;
5
+ exports.validateOutputs = validateOutputs;
6
+ const utils_1 = require("../core/utils");
7
+ function makeError(skillName, phase, message, hints = []) {
8
+ return {
9
+ skillName,
10
+ phase,
11
+ message,
12
+ recoveryHints: hints
13
+ };
14
+ }
15
+ function checkType(value, expectedType, fieldName) {
16
+ switch (expectedType) {
17
+ case "string":
18
+ if (typeof value !== "string")
19
+ return `"${fieldName}" must be a string, got ${typeof value}`;
20
+ return null;
21
+ case "path":
22
+ if (typeof value !== "string")
23
+ return `"${fieldName}" must be a path (string), got ${typeof value}`;
24
+ return null;
25
+ case "boolean":
26
+ if (typeof value !== "boolean")
27
+ return `"${fieldName}" must be a boolean, got ${typeof value}`;
28
+ return null;
29
+ case "number":
30
+ if (typeof value !== "number" || !Number.isFinite(value)) {
31
+ return `"${fieldName}" must be a finite number, got ${typeof value}`;
32
+ }
33
+ return null;
34
+ case "json":
35
+ if (typeof value === "string") {
36
+ try {
37
+ JSON.parse(value);
38
+ }
39
+ catch {
40
+ return `"${fieldName}" must be valid JSON string`;
41
+ }
42
+ }
43
+ return null;
44
+ default:
45
+ return null;
46
+ }
47
+ }
48
+ function validateInputs(manifest, inputs) {
49
+ for (const input of manifest.inputs) {
50
+ const value = inputs[input.name];
51
+ if (input.required && (value === undefined || value === null)) {
52
+ if (input.default !== undefined)
53
+ continue;
54
+ return makeError(manifest.name, "input_validation", `Required input "${input.name}" is missing`, [`Provide "${input.name}" (${input.type}): ${input.description ?? ""}`]);
55
+ }
56
+ if (value === undefined || value === null)
57
+ continue;
58
+ const typeError = checkType(value, input.type, input.name);
59
+ if (typeError) {
60
+ return makeError(manifest.name, "input_validation", typeError, [`Expected type: ${input.type}`]);
61
+ }
62
+ }
63
+ return null;
64
+ }
65
+ async function validateInputPaths(manifest, inputs) {
66
+ for (const input of manifest.inputs) {
67
+ if (input.type !== "path")
68
+ continue;
69
+ const value = inputs[input.name];
70
+ if (typeof value !== "string")
71
+ continue;
72
+ if (input.required && !(await (0, utils_1.fileExists)(value))) {
73
+ return makeError(manifest.name, "input_validation", `Path "${input.name}" does not exist: ${value}`, [`Ensure the file or directory exists: ${value}`]);
74
+ }
75
+ }
76
+ return null;
77
+ }
78
+ function validateOutputs(manifest, outputs) {
79
+ for (const output of manifest.outputs) {
80
+ const value = outputs[output.name];
81
+ if (value === undefined || value === null) {
82
+ return makeError(manifest.name, "output_validation", `Expected output "${output.name}" was not produced`, [`Skill "${manifest.name}" should return "${output.name}" (${output.type})`]);
83
+ }
84
+ const typeError = checkType(value, output.type, output.name);
85
+ if (typeError) {
86
+ return makeError(manifest.name, "output_validation", typeError, [`Skill "${manifest.name}" returned wrong type for "${output.name}"`]);
87
+ }
88
+ }
89
+ return null;
90
+ }
@@ -0,0 +1,2 @@
1
+ import type { Skill } from "../skill-engine/types";
2
+ export declare const fixSkill: Skill;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fixSkill = void 0;
4
+ const fix_1 = require("../core/fix");
5
+ exports.fixSkill = {
6
+ manifest: {
7
+ name: "fix",
8
+ version: "2.0.0",
9
+ description: "Auto-regenerate failing artifacts with backup, proposal, and validation",
10
+ category: "validation",
11
+ depends_on: [],
12
+ inputs: [
13
+ { name: "cwd", type: "path", required: true, description: "Project working directory" }
14
+ ],
15
+ outputs: [
16
+ { name: "validationResult", type: "json", description: "Final validation result after fix" }
17
+ ],
18
+ tags: ["fix", "repair", "validation"]
19
+ },
20
+ async execute(context, inputs) {
21
+ const cwd = inputs.cwd ?? context.state.cwd;
22
+ context.progress(1, 3, "Running fix pipeline...");
23
+ const result = await (0, fix_1.runFix)({
24
+ cwd,
25
+ agent: context.agent,
26
+ log: context.log
27
+ });
28
+ context.progress(3, 3, result.finalPass ? "Fix complete." : "Fix applied, issues remain.");
29
+ context.state.validationResult = result.finalPass
30
+ ? { pass: true, reportPath: result.reportPath, issues: [] }
31
+ : undefined;
32
+ return {
33
+ validationResult: {
34
+ pass: result.finalPass,
35
+ reportPath: result.reportPath,
36
+ applied: result.applied,
37
+ targetCount: result.proposal.targets.length
38
+ }
39
+ };
40
+ }
41
+ };
@@ -0,0 +1,2 @@
1
+ import type { Skill } from "../skill-engine/types";
2
+ export declare function createArtifactSkill(artifactType: string, upstream?: string[]): Skill;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createArtifactSkill = createArtifactSkill;
4
+ const artifacts_1 = require("../core/artifacts");
5
+ const constants_1 = require("../core/constants");
6
+ function createArtifactSkill(artifactType, upstream) {
7
+ const resolvedUpstream = upstream ?? (0, constants_1.defaultUpstreamByArtifact)(artifactType);
8
+ const depends_on = ["normalize", ...resolvedUpstream];
9
+ const manifest = {
10
+ name: artifactType,
11
+ version: "2.0.0",
12
+ description: `Generate ${artifactType} artifact from normalized brief`,
13
+ category: "artifact",
14
+ depends_on,
15
+ inputs: [
16
+ { name: "cwd", type: "path", required: true, description: "Project working directory" },
17
+ { name: "normalizedBriefPath", type: "path", required: false, description: "Path to normalized brief" }
18
+ ],
19
+ outputs: [
20
+ { name: "artifactPath", type: "path", description: `Path to generated ${artifactType}` }
21
+ ],
22
+ tags: ["artifact", artifactType]
23
+ };
24
+ return {
25
+ manifest,
26
+ async execute(context, inputs) {
27
+ const cwd = inputs.cwd ?? context.state.cwd;
28
+ const normalizedBriefOverride = inputs.normalizedBriefPath ?? context.state.normalizedBriefPath;
29
+ context.progress(1, 2, `Generating ${artifactType}...`);
30
+ const filePath = await (0, artifacts_1.generateArtifact)({
31
+ artifactType,
32
+ cwd,
33
+ normalizedBriefOverride,
34
+ agent: context.agent
35
+ });
36
+ context.progress(2, 2, `${artifactType} generated.`);
37
+ context.log(`${artifactType.toUpperCase()} generated: ${filePath}`);
38
+ context.state.generatedArtifacts.set(artifactType, filePath);
39
+ return { artifactPath: filePath };
40
+ }
41
+ };
42
+ }
@@ -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,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,6 +1,6 @@
1
1
  {
2
2
  "name": "@shahmarasy/prodo",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "CLI-first, prompt-powered product artifact kit",
5
5
  "main": "dist/cli/index.js",
6
6
  "types": "dist/cli/index.d.ts",
@@ -22,11 +22,9 @@
22
22
  "node": ">=20"
23
23
  },
24
24
  "scripts": {
25
- "build:ts": "tsc -p tsconfig.json",
26
- "build:i18n": "node -e \"const fs=require('fs');const p=require('path');['en','tr'].forEach(l=>{const s=p.join('src','i18n',l+'.json');const d=p.join('dist','i18n',l+'.json');fs.copyFileSync(s,d)})\"",
27
- "build": "npm run build:ts && npm run build:i18n",
28
- "test": "npm run build && node --test tests/*.test.cjs",
29
- "verify:release": "npm run build && node scripts/verify-release-build.cjs"
25
+ "build": "node scripts/build.cjs",
26
+ "test": "node scripts/test.cjs",
27
+ "verify:release": "node scripts/build.cjs"
30
28
  },
31
29
  "keywords": [
32
30
  "product-management",
@@ -122,7 +122,97 @@ async function loadCommandTemplateNames(commandTemplatesDir: string): Promise<st
122
122
  .sort();
123
123
  }
124
124
 
125
- export async function installAgentCommands(projectRoot: string, ai: SupportedAi): Promise<string[]> {
125
+ function generateClaudeMd(projectRoot: string, lang: string): string {
126
+ const projectName = path.basename(projectRoot) || "Product";
127
+ return `# Prodo — Product Artifact Workspace
128
+
129
+ This project uses **Prodo** to generate product documentation from a brief.
130
+
131
+ ## Project Structure
132
+
133
+ - \`brief.md\` — Product brief (INPUT — read-only during generation)
134
+ - \`product-docs/\` — Generated artifacts (OUTPUT)
135
+ - \`.prodo/\` — Internal scaffold (templates, schemas, prompts, state)
136
+
137
+ ## Workflow
138
+
139
+ Run commands in this sequence:
140
+
141
+ 1. \`/prodo-normalize\` — Parse brief into structured JSON
142
+ 2. \`/prodo-prd\` — Generate Product Requirements Document
143
+ 3. \`/prodo-workflow\` — Generate workflow diagrams (Markdown + Mermaid)
144
+ 4. \`/prodo-wireframe\` — Generate wireframes (Markdown + HTML)
145
+ 5. \`/prodo-stories\` — Generate user stories with Gherkin scenarios
146
+ 6. \`/prodo-techspec\` — Generate technical specification
147
+ 7. \`/prodo-validate\` — Cross-artifact validation (7 gates)
148
+
149
+ ## Rules
150
+
151
+ - **Never modify \`brief.md\` during command execution** — it is read-only input
152
+ - **All artifacts must include contract tags** like \`[G1]\`, \`[F2]\`, \`[C1]\` for traceability
153
+ - **Output language is \`${lang}\`** — do not mix languages in generated content
154
+ - **Follow templates strictly** — each artifact has a template in \`.prodo/templates/\`
155
+ - **Validate after generation** — run \`/prodo-validate\` to check consistency
156
+ `;
157
+ }
158
+
159
+ function generateArtifactRules(lang: string): string {
160
+ return `# Artifact Format Rules
161
+
162
+ ## Contract Tags
163
+ Every artifact must reference brief requirements using tags:
164
+ - \`[G1]\`, \`[G2]\` — Goal references
165
+ - \`[F1]\`, \`[F2]\` — Feature references
166
+ - \`[C1]\`, \`[C2]\` — Constraint references
167
+
168
+ Tags must appear inline where the requirement is addressed.
169
+
170
+ ## Frontmatter
171
+ Every generated artifact must include YAML frontmatter:
172
+ \`\`\`yaml
173
+ artifact_type: prd | workflow | wireframe | stories | techspec
174
+ version: <timestamp>
175
+ source_brief: <path to normalized-brief.json>
176
+ generated_at: <ISO timestamp>
177
+ status: draft
178
+ contract_coverage:
179
+ goals: [G1, G2]
180
+ core_features: [F1, F2]
181
+ constraints: [C1]
182
+ \`\`\`
183
+
184
+ ## Paired Outputs
185
+ - **Workflow**: Produces \`.md\` (narrative) + \`.mmd\` (Mermaid diagram)
186
+ - **Wireframe**: Produces \`.md\` (description) + \`.html\` (low-fidelity prototype)
187
+
188
+ ## Language
189
+ Output language: **${lang}**. Do not mix languages. Required heading names stay in English.
190
+ `;
191
+ }
192
+
193
+ function generateBriefRules(): string {
194
+ return `# Brief Writing Rules
195
+
196
+ ## Structure
197
+ The brief (\`brief.md\`) should contain these sections:
198
+
199
+ - **Product Name** — Clear, specific product name
200
+ - **Problem** — Core problem the product solves (be specific)
201
+ - **Audience** — Target users (list distinct personas)
202
+ - **Goals** — Measurable objectives (not vague aspirations)
203
+ - **Core Features** — One capability per bullet point
204
+ - **Constraints** — Technical, business, or regulatory limits
205
+ - **Assumptions** — Open questions or working assumptions
206
+
207
+ ## Tips
208
+ - Be specific: "Reduce checkout abandonment by 30%" not "Improve UX"
209
+ - Use consistent terminology throughout
210
+ - Each feature should be distinct and independently describable
211
+ - Constraints should be concrete: "Node.js 20+", "GDPR compliant"
212
+ `;
213
+ }
214
+
215
+ export async function installAgentCommands(projectRoot: string, ai: SupportedAi, lang?: string): Promise<string[]> {
126
216
  const cfg = AGENT_CONFIG[ai];
127
217
  const target = path.join(projectRoot, cfg.baseDir);
128
218
  const commandTemplatesDir = path.join(projectRoot, ".prodo", "commands");
@@ -130,6 +220,30 @@ export async function installAgentCommands(projectRoot: string, ai: SupportedAi)
130
220
  await ensureDir(target);
131
221
 
132
222
  const written: string[] = [];
223
+
224
+ if (ai === "claude-cli") {
225
+ const claudeMdPath = path.join(projectRoot, "CLAUDE.md");
226
+ if (!(await fileExists(claudeMdPath))) {
227
+ await fs.writeFile(claudeMdPath, generateClaudeMd(projectRoot, lang ?? "en"), "utf8");
228
+ written.push(claudeMdPath);
229
+ }
230
+
231
+ const rulesDir = path.join(projectRoot, ".claude", "rules");
232
+ await ensureDir(rulesDir);
233
+
234
+ const artifactRulesPath = path.join(rulesDir, "artifact-format.md");
235
+ if (!(await fileExists(artifactRulesPath))) {
236
+ await fs.writeFile(artifactRulesPath, generateArtifactRules(lang ?? "en"), "utf8");
237
+ written.push(artifactRulesPath);
238
+ }
239
+
240
+ const briefRulesPath = path.join(rulesDir, "brief-rules.md");
241
+ if (!(await fileExists(briefRulesPath))) {
242
+ await fs.writeFile(briefRulesPath, generateBriefRules(), "utf8");
243
+ written.push(briefRulesPath);
244
+ }
245
+ }
246
+
133
247
  for (const commandName of commandNames) {
134
248
  const templatePath = path.join(commandTemplatesDir, `${commandName}.md`);
135
249
  if (!(await fileExists(templatePath))) {
package/src/cli/doctor.ts CHANGED
@@ -137,8 +137,8 @@ export async function runDoctor(cwd: string, out: (line: string) => void): Promi
137
137
  out("");
138
138
  out(renderLogo(width));
139
139
  out("");
140
- out(center(color("Prodo — Product Artifact Toolkit", "\u001B[1;37m"), width));
141
- out(center(color("Crafted by Codex, guided by Shahmarasy intelligence", "\u001B[2;37m"), width));
140
+ out(center(color("Prodo — AI-Powered Product Owner", "\u001B[1;37m"), width));
141
+ out(center(color("Built by Shahmarasy · Works with Claude, Codex, and Gemini", "\u001B[2;37m"), width));
142
142
  out("");
143
143
  out("Checking environment...");
144
144
  out("");
package/src/cli/index.ts CHANGED
@@ -120,7 +120,7 @@ export async function runCli(options: RunOptions = {}): Promise<number> {
120
120
 
121
121
  program
122
122
  .command("init [target]")
123
- .option("--ai <name>", "agent integration: codex | gemini-cli | claude-cli")
123
+ .option("--ai <name>", "agent: codex | gemini-cli | claude-cli")
124
124
  .option("--lang <code>", "document language (e.g. en, tr)")
125
125
  .option("--author <name>", "document author name")
126
126
  .option("--preset <name>", "preset to install during initialization")
@@ -165,15 +165,14 @@ export async function runCli(options: RunOptions = {}): Promise<number> {
165
165
  });
166
166
  out(`Initialized Prodo scaffold at ${path.join(projectRoot, ".prodo")}`);
167
167
  if (selectedAi) {
168
- out(`Agent command set installed for ${selectedAi}.`);
168
+ const label = selectedAi === "claude-cli" ? "Claude Code"
169
+ : selectedAi === "codex" ? "Codex" : "Gemini CLI";
170
+ out(`Agent commands installed for ${label}.`);
169
171
  out(`Installed ${result.installedAgentFiles.length} command files.`);
170
- out("Agent workflow: edit brief.md, then run slash commands in your agent.");
172
+ out(`Next: edit brief.md, open in ${label}, run /prodo-normalize`);
171
173
  } else {
172
- out("No agent selected. Use `prodo generate` for end-to-end generation.");
174
+ out("Next: edit brief.md, then run `prodo generate`.");
173
175
  }
174
- out(`Settings file: ${result.settingsPath}`);
175
- out(`Author: ${selected.author}`);
176
- out("Next: edit brief.md.");
177
176
  });
178
177
 
179
178
  program
@@ -196,28 +195,30 @@ export async function runCli(options: RunOptions = {}): Promise<number> {
196
195
  return;
197
196
  }
198
197
  await withBriefReadOnlyGuard(cwd, async () => {
199
- await runHookPhase(cwd, "before_normalize", out);
200
- const normalizedPath = await runNormalize({ cwd });
201
- out(`Normalized brief written to: ${normalizedPath}`);
202
- await runHookPhase(cwd, "after_normalize", out);
198
+ const { createEngine, createPipelineState } = await import("../skill-engine");
199
+ const engine = await createEngine(cwd, out);
200
+ const state = createPipelineState(cwd);
201
+ const pipelineSkills = ["normalize", ...artifactTypes, "validate"];
203
202
 
204
- for (const type of artifactTypes) {
205
- await runArtifactCommand(type, { from: normalizedPath, agent: opts.agent }, cwd, out, {
206
- suggestValidate: false
207
- });
208
- }
203
+ await runHookPhase(cwd, "before_normalize", out);
209
204
 
210
- await runHookPhase(cwd, "before_validate", out);
211
- const result = await runValidate(cwd, {
212
- strict: Boolean(opts.strict),
213
- report: opts.report
205
+ const finalState = await engine.runPipeline(pipelineSkills, state, {
206
+ log: (msg) => {
207
+ out(msg);
208
+ },
209
+ agent: opts.agent
214
210
  });
215
- out(`Validation report written to: ${result.reportPath}`);
216
- if (!result.pass) {
211
+
212
+ await runHookPhase(cwd, "after_validate", out);
213
+
214
+ if (finalState.validationResult && !finalState.validationResult.pass) {
215
+ out(`Validation report written to: ${finalState.validationResult.reportPath}`);
217
216
  throw new UserError("Validation failed. Review report and fix issues.");
218
217
  }
218
+ if (finalState.validationResult) {
219
+ out(`Validation report written to: ${finalState.validationResult.reportPath}`);
220
+ }
219
221
  out("Generation pipeline completed. Validation passed.");
220
- await runHookPhase(cwd, "after_validate", out);
221
222
  });
222
223
  });
223
224
 
@@ -388,35 +389,38 @@ export async function runCli(options: RunOptions = {}): Promise<number> {
388
389
  });
389
390
 
390
391
  program
391
- .command("skills", { hidden: true })
392
- .description("Advanced: manage and run skills")
392
+ .command("skills")
393
+ .description("Manage and run skills")
393
394
  .argument("[action]", "list or run", "list")
394
395
  .argument("[name]", "skill name (for run)")
395
396
  .option("--input <json>", "JSON input for skill execution")
396
397
  .action(async (action: string, name: string | undefined, opts: { input?: string }) => {
397
- const { getGlobalSkillEngine } = await import("../skills/engine");
398
- const engine = getGlobalSkillEngine();
398
+ const { createEngine, createHydratedState } = await import("../skill-engine");
399
+ const engine = await createEngine(cwd, out);
400
+ const registry = engine.getRegistry();
399
401
 
400
402
  if (action === "list") {
401
- const manifests = engine.listSkills();
403
+ const manifests = registry.listManifests();
402
404
  if (manifests.length === 0) {
403
405
  out("No skills registered.");
404
406
  return;
405
407
  }
406
408
  out("Available skills:\n");
407
409
  for (const m of manifests) {
408
- out(` ${m.name.padEnd(25)} [${m.category}] ${m.description}`);
410
+ const deps = m.depends_on.length > 0 ? ` (deps: ${m.depends_on.join(", ")})` : "";
411
+ out(` ${m.name.padEnd(25)} [${m.category}] v${m.version} ${m.description}${deps}`);
409
412
  }
410
413
  return;
411
414
  }
412
415
 
413
416
  if (action === "run") {
414
417
  if (!name) throw new UserError("Skill name is required. Usage: prodo skills run <name>");
418
+ const state = await createHydratedState(cwd);
415
419
  const inputs = opts.input ? JSON.parse(opts.input) as Record<string, unknown> : {};
416
420
  inputs.cwd = inputs.cwd ?? cwd;
417
- const result = await engine.execute(name, { cwd, log: out }, inputs);
421
+ const result = await engine.runSkill(name, state, { log: out });
418
422
  out(`\nSkill "${name}" completed.`);
419
- out(JSON.stringify(result, null, 2));
423
+ out(`Completed skills: ${result.completedSkills.join(" ")}`);
420
424
  return;
421
425
  }
422
426