@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.
- package/dist/agents/system-prompts.js +12 -12
- package/dist/cli/agent-command-installer.d.ts +1 -1
- package/dist/cli/agent-command-installer.js +125 -19
- package/dist/cli/doctor.js +2 -2
- package/dist/cli/index.js +32 -30
- package/dist/cli/init-tui.d.ts +2 -2
- package/dist/cli/init-tui.js +43 -36
- package/dist/cli/init.d.ts +1 -0
- package/dist/cli/init.js +3 -2
- package/dist/core/artifacts.js +72 -72
- package/dist/core/settings.d.ts +1 -0
- package/dist/core/settings.js +10 -2
- package/dist/core/templates.js +248 -248
- package/dist/i18n/en.json +45 -45
- package/dist/i18n/tr.json +45 -45
- package/dist/providers/mock-provider.js +5 -5
- package/dist/providers/openai-provider.js +12 -12
- 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/fix.d.ts +2 -0
- package/dist/skills/fix.js +41 -0
- package/dist/skills/generate-artifact.d.ts +2 -0
- package/dist/skills/generate-artifact.js +42 -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/validate.d.ts +2 -0
- package/dist/skills/validate.js +37 -0
- package/package.json +4 -6
- package/src/cli/agent-command-installer.ts +115 -1
- package/src/cli/doctor.ts +2 -2
- package/src/cli/index.ts +35 -31
- package/src/cli/init-tui.ts +220 -208
- package/src/cli/init.ts +4 -3
- package/src/core/settings.ts +41 -35
- 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-skill.ts → normalize.ts} +15 -12
- package/src/skills/register-core.ts +27 -0
- package/src/skills/validate.ts +40 -0
- package/src/skills/engine.ts +0 -94
- package/src/skills/fix-skill.ts +0 -38
- package/src/skills/generate-artifact-skill.ts +0 -32
- package/src/skills/generate-pipeline-skill.ts +0 -49
- package/src/skills/types.ts +0 -36
- 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
|
-
|
|
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))) {
|
package/dist/cli/doctor.js
CHANGED
|
@@ -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
|
|
124
|
-
out(center(color("
|
|
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
|
|
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
|
-
|
|
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(
|
|
188
|
+
out(`Next: edit brief.md, open in ${label}, run /prodo-normalize`);
|
|
187
189
|
}
|
|
188
190
|
else {
|
|
189
|
-
out("
|
|
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
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
231
|
-
if (!
|
|
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"
|
|
397
|
-
.description("
|
|
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 {
|
|
403
|
-
const engine =
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
423
|
+
const result = await engine.runSkill(name, state, { log: out });
|
|
422
424
|
out(`\nSkill "${name}" completed.`);
|
|
423
|
-
out(
|
|
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`);
|
package/dist/cli/init-tui.d.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
20
|
+
lang: string;
|
|
21
21
|
author: string;
|
|
22
22
|
}): Promise<void>;
|
|
23
23
|
export {};
|
package/dist/cli/init-tui.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
113
|
-
const signature = color("
|
|
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
|
|
125
|
-
message: "
|
|
126
|
-
initialValue:
|
|
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: "
|
|
129
|
-
{ value: "
|
|
130
|
-
{ value: "
|
|
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(
|
|
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: "
|
|
131
|
+
message: "Document language",
|
|
139
132
|
initialValue: defaultLang,
|
|
140
133
|
options: [
|
|
141
|
-
{ value: "
|
|
142
|
-
{ value: "
|
|
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: "
|
|
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:
|
|
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
|
|
175
|
-
|
|
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
|
}
|
package/dist/cli/init.d.ts
CHANGED
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
|
}
|