@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
@@ -5,26 +5,26 @@ exports.buildUserMessage = buildUserMessage;
5
5
  function buildSystemPrompt(schemaHint, outputLanguage) {
6
6
  const mode = schemaHint.artifactType;
7
7
  if (mode === "normalize") {
8
- return `You normalize messy human product briefs into strict JSON.
9
- Return valid JSON only, no markdown. Include confidence scores (0..1) for critical fields.
8
+ return `You normalize messy human product briefs into strict JSON.
9
+ Return valid JSON only, no markdown. Include confidence scores (0..1) for critical fields.
10
10
  Preserve source language and Unicode characters exactly; never transliterate Turkish letters to ASCII.`;
11
11
  }
12
12
  if (mode === "semantic_consistency") {
13
- return `You detect semantic inconsistencies between paired artifacts.
13
+ return `You detect semantic inconsistencies between paired artifacts.
14
14
  Return valid JSON only: { "issues": [{level, code, check, contract_id, file, message, suggestion}] }.`;
15
15
  }
16
16
  if (mode === "contract_relevance") {
17
- return `You verify whether tagged content actually matches the referenced contract text.
17
+ return `You verify whether tagged content actually matches the referenced contract text.
18
18
  Return valid JSON only: { "relevant": boolean, "score": number, "reason": string }.`;
19
19
  }
20
- return `You are a product-document generator.
21
- Return only Markdown body content.
22
- Headings required:
23
- ${schemaHint.requiredHeadings.join("\n")}
24
- Required contract tags:
25
- ${schemaHint.requiredContracts.join(", ")}
26
- Use tags like [G1], [F2], [C1] where relevant.
27
- Output language: ${outputLanguage}
20
+ return `You are a product-document generator.
21
+ Return only Markdown body content.
22
+ Headings required:
23
+ ${schemaHint.requiredHeadings.join("\n")}
24
+ Required contract tags:
25
+ ${schemaHint.requiredContracts.join(", ")}
26
+ Use tags like [G1], [F2], [C1] where relevant.
27
+ Output language: ${outputLanguage}
28
28
  Do not translate required headings.`;
29
29
  }
30
30
  function buildUserMessage(prompt, inputContext) {
@@ -1,4 +1,4 @@
1
1
  export declare const AI_ALIASES: Record<string, "codex" | "gemini-cli" | "claude-cli">;
2
2
  export type SupportedAi = "codex" | "gemini-cli" | "claude-cli";
3
3
  export declare function resolveAi(ai?: string): SupportedAi | undefined;
4
- export declare function installAgentCommands(projectRoot: string, ai: SupportedAi): Promise<string[]>;
4
+ export declare function installAgentCommands(projectRoot: string, ai: SupportedAi, lang?: string): Promise<string[]>;
@@ -65,28 +65,28 @@ function sanitizeFrontmatter(frontmatter) {
65
65
  function toTomlPrompt(body, frontmatter, argsPlaceholder) {
66
66
  const description = String(frontmatter.description ?? "Prodo command");
67
67
  const promptBody = body.replaceAll("$ARGUMENTS", argsPlaceholder);
68
- return `description = "${description.replace(/"/g, '\\"')}"
69
-
70
- prompt = """
71
- Important execution rule:
72
- - This is an agent slash command, not a shell command.
73
- - Do NOT run \`prodo-normalize\`, \`prodo-prd\`, or \`prodo ...\` in shell.
74
- - Execute the workflow directly using workspace files.
75
-
76
- ${promptBody}
68
+ return `description = "${description.replace(/"/g, '\\"')}"
69
+
70
+ prompt = """
71
+ Important execution rule:
72
+ - This is an agent slash command, not a shell command.
73
+ - Do NOT run \`prodo-normalize\`, \`prodo-prd\`, or \`prodo ...\` in shell.
74
+ - Execute the workflow directly using workspace files.
75
+
76
+ ${promptBody}
77
77
  """`;
78
78
  }
79
79
  function toSkill(name, body, frontmatter) {
80
80
  const description = String(frontmatter.description ?? "Prodo workflow command");
81
- return `---
82
- name: ${name}
83
- description: ${description}
84
- compatibility: Requires Prodo project scaffold (.prodo)
85
- metadata:
86
- author: prodo
87
- source: .prodo/commands/${name}.md
88
- ---
89
-
81
+ return `---
82
+ name: ${name}
83
+ description: ${description}
84
+ compatibility: Requires Prodo project scaffold (.prodo)
85
+ metadata:
86
+ author: prodo
87
+ source: .prodo/commands/${name}.md
88
+ ---
89
+
90
90
  ${body}`;
91
91
  }
92
92
  function resolveAi(ai) {
@@ -110,13 +110,119 @@ async function loadCommandTemplateNames(commandTemplatesDir) {
110
110
  .map((name) => name.replace(/\.md$/, ""))
111
111
  .sort();
112
112
  }
113
- async function installAgentCommands(projectRoot, ai) {
113
+ function generateClaudeMd(projectRoot, lang) {
114
+ const projectName = node_path_1.default.basename(projectRoot) || "Product";
115
+ return `# Prodo — Product Artifact Workspace
116
+
117
+ This project uses **Prodo** to generate product documentation from a brief.
118
+
119
+ ## Project Structure
120
+
121
+ - \`brief.md\` — Product brief (INPUT — read-only during generation)
122
+ - \`product-docs/\` — Generated artifacts (OUTPUT)
123
+ - \`.prodo/\` — Internal scaffold (templates, schemas, prompts, state)
124
+
125
+ ## Workflow
126
+
127
+ Run commands in this sequence:
128
+
129
+ 1. \`/prodo-normalize\` — Parse brief into structured JSON
130
+ 2. \`/prodo-prd\` — Generate Product Requirements Document
131
+ 3. \`/prodo-workflow\` — Generate workflow diagrams (Markdown + Mermaid)
132
+ 4. \`/prodo-wireframe\` — Generate wireframes (Markdown + HTML)
133
+ 5. \`/prodo-stories\` — Generate user stories with Gherkin scenarios
134
+ 6. \`/prodo-techspec\` — Generate technical specification
135
+ 7. \`/prodo-validate\` — Cross-artifact validation (7 gates)
136
+
137
+ ## Rules
138
+
139
+ - **Never modify \`brief.md\` during command execution** — it is read-only input
140
+ - **All artifacts must include contract tags** like \`[G1]\`, \`[F2]\`, \`[C1]\` for traceability
141
+ - **Output language is \`${lang}\`** — do not mix languages in generated content
142
+ - **Follow templates strictly** — each artifact has a template in \`.prodo/templates/\`
143
+ - **Validate after generation** — run \`/prodo-validate\` to check consistency
144
+ `;
145
+ }
146
+ function generateArtifactRules(lang) {
147
+ return `# Artifact Format Rules
148
+
149
+ ## Contract Tags
150
+ Every artifact must reference brief requirements using tags:
151
+ - \`[G1]\`, \`[G2]\` — Goal references
152
+ - \`[F1]\`, \`[F2]\` — Feature references
153
+ - \`[C1]\`, \`[C2]\` — Constraint references
154
+
155
+ Tags must appear inline where the requirement is addressed.
156
+
157
+ ## Frontmatter
158
+ Every generated artifact must include YAML frontmatter:
159
+ \`\`\`yaml
160
+ artifact_type: prd | workflow | wireframe | stories | techspec
161
+ version: <timestamp>
162
+ source_brief: <path to normalized-brief.json>
163
+ generated_at: <ISO timestamp>
164
+ status: draft
165
+ contract_coverage:
166
+ goals: [G1, G2]
167
+ core_features: [F1, F2]
168
+ constraints: [C1]
169
+ \`\`\`
170
+
171
+ ## Paired Outputs
172
+ - **Workflow**: Produces \`.md\` (narrative) + \`.mmd\` (Mermaid diagram)
173
+ - **Wireframe**: Produces \`.md\` (description) + \`.html\` (low-fidelity prototype)
174
+
175
+ ## Language
176
+ Output language: **${lang}**. Do not mix languages. Required heading names stay in English.
177
+ `;
178
+ }
179
+ function generateBriefRules() {
180
+ return `# Brief Writing Rules
181
+
182
+ ## Structure
183
+ The brief (\`brief.md\`) should contain these sections:
184
+
185
+ - **Product Name** — Clear, specific product name
186
+ - **Problem** — Core problem the product solves (be specific)
187
+ - **Audience** — Target users (list distinct personas)
188
+ - **Goals** — Measurable objectives (not vague aspirations)
189
+ - **Core Features** — One capability per bullet point
190
+ - **Constraints** — Technical, business, or regulatory limits
191
+ - **Assumptions** — Open questions or working assumptions
192
+
193
+ ## Tips
194
+ - Be specific: "Reduce checkout abandonment by 30%" not "Improve UX"
195
+ - Use consistent terminology throughout
196
+ - Each feature should be distinct and independently describable
197
+ - Constraints should be concrete: "Node.js 20+", "GDPR compliant"
198
+ `;
199
+ }
200
+ async function installAgentCommands(projectRoot, ai, lang) {
114
201
  const cfg = AGENT_CONFIG[ai];
115
202
  const target = node_path_1.default.join(projectRoot, cfg.baseDir);
116
203
  const commandTemplatesDir = node_path_1.default.join(projectRoot, ".prodo", "commands");
117
204
  const commandNames = await loadCommandTemplateNames(commandTemplatesDir);
118
205
  await (0, utils_1.ensureDir)(target);
119
206
  const written = [];
207
+ if (ai === "claude-cli") {
208
+ const claudeMdPath = node_path_1.default.join(projectRoot, "CLAUDE.md");
209
+ if (!(await (0, utils_1.fileExists)(claudeMdPath))) {
210
+ await promises_1.default.writeFile(claudeMdPath, generateClaudeMd(projectRoot, lang ?? "en"), "utf8");
211
+ written.push(claudeMdPath);
212
+ }
213
+ const rulesDir = node_path_1.default.join(projectRoot, ".claude", "rules");
214
+ await (0, utils_1.ensureDir)(rulesDir);
215
+ const artifactRulesPath = node_path_1.default.join(rulesDir, "artifact-format.md");
216
+ if (!(await (0, utils_1.fileExists)(artifactRulesPath))) {
217
+ await promises_1.default.writeFile(artifactRulesPath, generateArtifactRules(lang ?? "en"), "utf8");
218
+ written.push(artifactRulesPath);
219
+ }
220
+ const briefRulesPath = node_path_1.default.join(rulesDir, "brief-rules.md");
221
+ if (!(await (0, utils_1.fileExists)(briefRulesPath))) {
222
+ await promises_1.default.writeFile(briefRulesPath, generateBriefRules(), "utf8");
223
+ written.push(briefRulesPath);
224
+ }
225
+ }
120
226
  for (const commandName of commandNames) {
121
227
  const templatePath = node_path_1.default.join(commandTemplatesDir, `${commandName}.md`);
122
228
  if (!(await (0, utils_1.fileExists)(templatePath))) {
@@ -120,8 +120,8 @@ async function runDoctor(cwd, out) {
120
120
  out("");
121
121
  out(renderLogo(width));
122
122
  out("");
123
- out(center(color("Prodo — Product Artifact Toolkit", "\u001B[1;37m"), width));
124
- out(center(color("Crafted by Codex, guided by Shahmarasy intelligence", "\u001B[2;37m"), width));
123
+ out(center(color("Prodo — AI-Powered Product Owner", "\u001B[1;37m"), width));
124
+ out(center(color("Built by Shahmarasy · Works with Claude, Codex, and Gemini", "\u001B[2;37m"), width));
125
125
  out("");
126
126
  out("Checking environment...");
127
127
  out("");
package/dist/cli/index.js CHANGED
@@ -138,7 +138,7 @@ async function runCli(options = {}) {
138
138
  const artifactTypes = await (0, artifact_registry_1.listArtifactTypes)(cwd);
139
139
  program
140
140
  .command("init [target]")
141
- .option("--ai <name>", "agent integration: codex | gemini-cli | claude-cli")
141
+ .option("--ai <name>", "agent: codex | gemini-cli | claude-cli")
142
142
  .option("--lang <code>", "document language (e.g. en, tr)")
143
143
  .option("--author <name>", "document author name")
144
144
  .option("--preset <name>", "preset to install during initialization")
@@ -181,16 +181,15 @@ async function runCli(options = {}) {
181
181
  });
182
182
  out(`Initialized Prodo scaffold at ${node_path_1.default.join(projectRoot, ".prodo")}`);
183
183
  if (selectedAi) {
184
- out(`Agent command set installed for ${selectedAi}.`);
184
+ const label = selectedAi === "claude-cli" ? "Claude Code"
185
+ : selectedAi === "codex" ? "Codex" : "Gemini CLI";
186
+ out(`Agent commands installed for ${label}.`);
185
187
  out(`Installed ${result.installedAgentFiles.length} command files.`);
186
- out("Agent workflow: edit brief.md, then run slash commands in your agent.");
188
+ out(`Next: edit brief.md, open in ${label}, run /prodo-normalize`);
187
189
  }
188
190
  else {
189
- out("No agent selected. Use `prodo generate` for end-to-end generation.");
191
+ out("Next: edit brief.md, then run `prodo generate`.");
190
192
  }
191
- out(`Settings file: ${result.settingsPath}`);
192
- out(`Author: ${selected.author}`);
193
- out("Next: edit brief.md.");
194
193
  });
195
194
  program
196
195
  .command("generate")
@@ -213,26 +212,26 @@ async function runCli(options = {}) {
213
212
  return;
214
213
  }
215
214
  await withBriefReadOnlyGuard(cwd, async () => {
215
+ const { createEngine, createPipelineState } = await Promise.resolve().then(() => __importStar(require("../skill-engine")));
216
+ const engine = await createEngine(cwd, out);
217
+ const state = createPipelineState(cwd);
218
+ const pipelineSkills = ["normalize", ...artifactTypes, "validate"];
216
219
  await (0, hook_executor_1.runHookPhase)(cwd, "before_normalize", out);
217
- const normalizedPath = await (0, normalize_1.runNormalize)({ cwd });
218
- out(`Normalized brief written to: ${normalizedPath}`);
219
- await (0, hook_executor_1.runHookPhase)(cwd, "after_normalize", out);
220
- for (const type of artifactTypes) {
221
- await runArtifactCommand(type, { from: normalizedPath, agent: opts.agent }, cwd, out, {
222
- suggestValidate: false
223
- });
224
- }
225
- await (0, hook_executor_1.runHookPhase)(cwd, "before_validate", out);
226
- const result = await (0, validate_1.runValidate)(cwd, {
227
- strict: Boolean(opts.strict),
228
- report: opts.report
220
+ const finalState = await engine.runPipeline(pipelineSkills, state, {
221
+ log: (msg) => {
222
+ out(msg);
223
+ },
224
+ agent: opts.agent
229
225
  });
230
- out(`Validation report written to: ${result.reportPath}`);
231
- if (!result.pass) {
226
+ await (0, hook_executor_1.runHookPhase)(cwd, "after_validate", out);
227
+ if (finalState.validationResult && !finalState.validationResult.pass) {
228
+ out(`Validation report written to: ${finalState.validationResult.reportPath}`);
232
229
  throw new errors_1.UserError("Validation failed. Review report and fix issues.");
233
230
  }
231
+ if (finalState.validationResult) {
232
+ out(`Validation report written to: ${finalState.validationResult.reportPath}`);
233
+ }
234
234
  out("Generation pipeline completed. Validation passed.");
235
- await (0, hook_executor_1.runHookPhase)(cwd, "after_validate", out);
236
235
  });
237
236
  });
238
237
  program
@@ -393,34 +392,37 @@ async function runCli(options = {}) {
393
392
  });
394
393
  });
395
394
  program
396
- .command("skills", { hidden: true })
397
- .description("Advanced: manage and run skills")
395
+ .command("skills")
396
+ .description("Manage and run skills")
398
397
  .argument("[action]", "list or run", "list")
399
398
  .argument("[name]", "skill name (for run)")
400
399
  .option("--input <json>", "JSON input for skill execution")
401
400
  .action(async (action, name, opts) => {
402
- const { getGlobalSkillEngine } = await Promise.resolve().then(() => __importStar(require("../skills/engine")));
403
- const engine = getGlobalSkillEngine();
401
+ const { createEngine, createHydratedState } = await Promise.resolve().then(() => __importStar(require("../skill-engine")));
402
+ const engine = await createEngine(cwd, out);
403
+ const registry = engine.getRegistry();
404
404
  if (action === "list") {
405
- const manifests = engine.listSkills();
405
+ const manifests = registry.listManifests();
406
406
  if (manifests.length === 0) {
407
407
  out("No skills registered.");
408
408
  return;
409
409
  }
410
410
  out("Available skills:\n");
411
411
  for (const m of manifests) {
412
- out(` ${m.name.padEnd(25)} [${m.category}] ${m.description}`);
412
+ const deps = m.depends_on.length > 0 ? ` (deps: ${m.depends_on.join(", ")})` : "";
413
+ out(` ${m.name.padEnd(25)} [${m.category}] v${m.version} ${m.description}${deps}`);
413
414
  }
414
415
  return;
415
416
  }
416
417
  if (action === "run") {
417
418
  if (!name)
418
419
  throw new errors_1.UserError("Skill name is required. Usage: prodo skills run <name>");
420
+ const state = await createHydratedState(cwd);
419
421
  const inputs = opts.input ? JSON.parse(opts.input) : {};
420
422
  inputs.cwd = inputs.cwd ?? cwd;
421
- const result = await engine.execute(name, { cwd, log: out }, inputs);
423
+ const result = await engine.runSkill(name, state, { log: out });
422
424
  out(`\nSkill "${name}" completed.`);
423
- out(JSON.stringify(result, null, 2));
425
+ out(`Completed skills: ${result.completedSkills.join(" ")}`);
424
426
  return;
425
427
  }
426
428
  throw new errors_1.UserError(`Unknown skills action: "${action}". Use: list or run`);
@@ -2,7 +2,7 @@ import { type SupportedAi } from "./agent-command-installer";
2
2
  export type InitSelections = {
3
3
  ai?: SupportedAi;
4
4
  script: "sh" | "ps";
5
- lang: "tr" | "en";
5
+ lang: string;
6
6
  author: string;
7
7
  interactive: boolean;
8
8
  };
@@ -17,7 +17,7 @@ export declare function finishInitInteractive(summary: {
17
17
  projectRoot: string;
18
18
  settingsPath: string;
19
19
  ai?: SupportedAi;
20
- lang: "tr" | "en";
20
+ lang: string;
21
21
  author: string;
22
22
  }): Promise<void>;
23
23
  export {};
@@ -56,26 +56,20 @@ function renderProjectBox(projectName, projectRoot) {
56
56
  return [top, ...rows, bottom].join("\n");
57
57
  }
58
58
  async function detectAi(projectRoot) {
59
+ if (await (0, utils_1.fileExists)(node_path_1.default.join(projectRoot, ".claude")))
60
+ return "claude-cli";
59
61
  if (await (0, utils_1.fileExists)(node_path_1.default.join(projectRoot, ".agents")))
60
62
  return "codex";
61
63
  if (await (0, utils_1.fileExists)(node_path_1.default.join(projectRoot, ".gemini")))
62
64
  return "gemini-cli";
63
- if (await (0, utils_1.fileExists)(node_path_1.default.join(projectRoot, ".claude")))
64
- return "claude-cli";
65
65
  return undefined;
66
66
  }
67
67
  function normalizeLang(lang) {
68
- if ((lang ?? "").trim().toLowerCase().startsWith("tr"))
68
+ const raw = (lang ?? "").trim().toLowerCase();
69
+ if (raw.startsWith("tr"))
69
70
  return "tr";
70
71
  return "en";
71
72
  }
72
- function modelChoiceFromAi(ai) {
73
- if (ai === "codex")
74
- return "codex";
75
- if (ai === "gemini-cli")
76
- return "gemini";
77
- return "auto-detect";
78
- }
79
73
  function defaultAuthorName(authorInput) {
80
74
  const explicit = (authorInput ?? "").trim();
81
75
  if (explicit.length > 0)
@@ -86,7 +80,7 @@ function defaultAuthorName(authorInput) {
86
80
  return username;
87
81
  }
88
82
  catch {
89
- // ignore lookup errors and continue with fallback
83
+ // ignore
90
84
  }
91
85
  return "Product Author";
92
86
  }
@@ -106,11 +100,10 @@ async function gatherInitSelections(options) {
106
100
  };
107
101
  }
108
102
  const detectedAi = await detectAi(options.projectRoot);
109
- const initialModel = modelChoiceFromAi(parsedAi ?? detectedAi);
110
103
  const projectName = node_path_1.default.basename(options.projectRoot) || ".";
111
104
  const width = terminalWidth();
112
- const subtitle = color("Prodo — Product Artifact Toolkit", "\u001B[1;37m");
113
- const signature = color("Crafted by Codex, guided by Shahmarasy intelligence", "\u001B[38;5;244m");
105
+ const subtitle = color("Prodo — AI-Powered Product Owner", "\u001B[1;37m");
106
+ const signature = color("Built by Shahmarasy · Works with Claude, Codex, and Gemini", "\u001B[38;5;244m");
114
107
  const hero = [
115
108
  "",
116
109
  centerBlock(renderLogo().split("\n"), width),
@@ -121,25 +114,25 @@ async function gatherInitSelections(options) {
121
114
  ].join("\n");
122
115
  clack.intro(hero);
123
116
  clack.note(renderProjectBox(projectName, options.projectRoot), "Project Setup");
124
- const model = await clack.select({
125
- message: "Select model",
126
- initialValue: initialModel,
117
+ const agentChoice = await clack.select({
118
+ message: "Which AI agent will you use?",
119
+ initialValue: parsedAi ?? detectedAi ?? "claude-cli",
127
120
  options: [
128
- { value: "codex", label: "codex", hint: "Native Codex flow" },
129
- { value: "gemini", label: "gemini", hint: "Gemini CLI command set" },
130
- { value: "auto-detect", label: "auto-detect", hint: "Detect from local agent directories" }
121
+ { value: "claude-cli", label: "Claude Code", hint: "Slash commands → .claude/commands/" },
122
+ { value: "codex", label: "Codex", hint: "Skills .agents/skills/" },
123
+ { value: "gemini-cli", label: "Gemini CLI", hint: "Commands .gemini/commands/" }
131
124
  ]
132
125
  });
133
- if (clack.isCancel(model)) {
126
+ if (clack.isCancel(agentChoice)) {
134
127
  clack.cancel("Initialization cancelled.");
135
128
  throw new errors_1.UserError("Initialization cancelled.");
136
129
  }
137
130
  const lang = await clack.select({
138
- message: "Select language",
131
+ message: "Document language",
139
132
  initialValue: defaultLang,
140
133
  options: [
141
- { value: "tr", label: "tr", hint: "Turkish" },
142
- { value: "en", label: "en", hint: "English" }
134
+ { value: "en", label: "English" },
135
+ { value: "tr", label: "Türkçe" }
143
136
  ]
144
137
  });
145
138
  if (clack.isCancel(lang)) {
@@ -148,29 +141,43 @@ async function gatherInitSelections(options) {
148
141
  }
149
142
  const author = await clack.text({
150
143
  message: "Author name",
151
- placeholder: "Shahmarasy",
144
+ placeholder: "Your name",
152
145
  defaultValue: defaultAuthor
153
146
  });
154
147
  if (clack.isCancel(author)) {
155
148
  clack.cancel("Initialization cancelled.");
156
149
  throw new errors_1.UserError("Initialization cancelled.");
157
150
  }
158
- let selectedAi;
159
- if (model === "codex")
160
- selectedAi = "codex";
161
- else if (model === "gemini")
162
- selectedAi = "gemini-cli";
163
- else
164
- selectedAi = detectedAi;
165
151
  return {
166
- ai: selectedAi,
152
+ ai: agentChoice,
167
153
  script: fallbackScript,
168
- lang,
154
+ lang: String(lang),
169
155
  author: String(author).trim() || defaultAuthor,
170
156
  interactive: true
171
157
  };
172
158
  }
173
159
  function finishInitInteractive(summary) {
174
- const aiText = summary.ai ?? "none";
175
- return loadClack().then((clack) => clack.outro(`Scaffold complete.\nAI: ${aiText}\nLanguage: ${summary.lang}\nAuthor: ${summary.author}\nSettings: ${summary.settingsPath}\nNext: edit brief.md`));
160
+ const agentLabel = summary.ai === "claude-cli" ? "Claude Code"
161
+ : summary.ai === "codex" ? "Codex"
162
+ : summary.ai === "gemini-cli" ? "Gemini CLI"
163
+ : "none";
164
+ const commandPrefix = summary.ai === "codex" ? "$" : "/";
165
+ const commands = [
166
+ `${commandPrefix}prodo-normalize`,
167
+ `${commandPrefix}prodo-prd`,
168
+ `${commandPrefix}prodo-workflow`,
169
+ `${commandPrefix}prodo-wireframe`,
170
+ `${commandPrefix}prodo-stories`,
171
+ `${commandPrefix}prodo-techspec`,
172
+ `${commandPrefix}prodo-validate`
173
+ ];
174
+ return loadClack().then((clack) => clack.outro(`Scaffold complete!\n\n` +
175
+ ` Agent: ${agentLabel}\n` +
176
+ ` Language: ${summary.lang}\n` +
177
+ ` Author: ${summary.author}\n\n` +
178
+ `Next steps:\n` +
179
+ ` 1. Edit brief.md with your product description\n` +
180
+ ` 2. Open this folder in ${agentLabel}\n` +
181
+ ` 3. Run commands in sequence:\n` +
182
+ ` ${commands.join("\n ")}`));
176
183
  }
@@ -5,6 +5,7 @@ export declare function runInit(cwd: string, options?: {
5
5
  author?: string;
6
6
  preset?: string;
7
7
  script?: "sh" | "ps";
8
+ provider?: string;
8
9
  }): Promise<{
9
10
  installedAgentFiles: string[];
10
11
  settingsPath: string;
package/dist/cli/init.js CHANGED
@@ -312,7 +312,7 @@ async function runInit(cwd, options) {
312
312
  await rollbackFiles(backup);
313
313
  throw error;
314
314
  }
315
- const installedAgentFiles = options?.ai ? await (0, agent_command_installer_1.installAgentCommands)(cwd, options.ai) : [];
315
+ const installedAgentFiles = options?.ai ? await (0, agent_command_installer_1.installAgentCommands)(cwd, options.ai, options?.lang) : [];
316
316
  const manifest = {
317
317
  schema_version: "1.0",
318
318
  generated_at: new Date().toISOString(),
@@ -328,7 +328,8 @@ async function runInit(cwd, options) {
328
328
  const settingsPath = await (0, settings_1.writeSettings)(cwd, {
329
329
  lang: (options?.lang ?? "en").trim() || "en",
330
330
  ai: options?.ai,
331
- author: (options?.author ?? "").trim() || undefined
331
+ author: (options?.author ?? "").trim() || undefined,
332
+ provider: options?.provider
332
333
  });
333
334
  return { installedAgentFiles, settingsPath };
334
335
  }