@shahmarasy/prodo 0.1.5 → 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/dist/agents/system-prompts.js +12 -12
- package/dist/cli/agent-command-installer.js +18 -18
- 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 +2 -1
- 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/doctor.ts +2 -2
- package/src/cli/index.ts +35 -31
- package/src/cli/init-tui.ts +220 -208
- package/src/cli/init.ts +3 -2
- 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) {
|
|
@@ -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) {
|
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
|
@@ -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
|
}
|
package/dist/core/artifacts.js
CHANGED
|
@@ -311,62 +311,62 @@ function renderWorkflowMarkdownForFeature(markdown, feature, lang) {
|
|
|
311
311
|
}
|
|
312
312
|
async function resolvePrompt(cwd, artifactType, templateContent, requiredHeadings, companionTemplate, outputAuthor, agent) {
|
|
313
313
|
const base = await promises_1.default.readFile((0, paths_1.promptPath)(cwd, artifactType), "utf8");
|
|
314
|
-
const authority = `Template authority (STRICT):
|
|
315
|
-
- Treat this template as the single output structure source.
|
|
316
|
-
- Keep heading order and names exactly as listed.
|
|
317
|
-
- Do not invent new primary sections.
|
|
318
|
-
|
|
319
|
-
Required headings (from template):
|
|
320
|
-
${requiredHeadings.map((heading) => `- ${heading}`).join("\n")}
|
|
321
|
-
|
|
322
|
-
Resolved template:
|
|
323
|
-
\`\`\`md
|
|
324
|
-
${templateContent.trim()}
|
|
314
|
+
const authority = `Template authority (STRICT):
|
|
315
|
+
- Treat this template as the single output structure source.
|
|
316
|
+
- Keep heading order and names exactly as listed.
|
|
317
|
+
- Do not invent new primary sections.
|
|
318
|
+
|
|
319
|
+
Required headings (from template):
|
|
320
|
+
${requiredHeadings.map((heading) => `- ${heading}`).join("\n")}
|
|
321
|
+
|
|
322
|
+
Resolved template:
|
|
323
|
+
\`\`\`md
|
|
324
|
+
${templateContent.trim()}
|
|
325
325
|
\`\`\``;
|
|
326
326
|
const companionAuthority = companionTemplate
|
|
327
|
-
? `Native companion template (STRICT reference):
|
|
328
|
-
- Path: ${companionTemplate.path}
|
|
329
|
-
- Preserve this native format and structure when generating companion artifact.
|
|
330
|
-
\`\`\`${artifactType === "workflow" ? "mermaid" : "html"}
|
|
331
|
-
${companionTemplate.content.trim()}
|
|
327
|
+
? `Native companion template (STRICT reference):
|
|
328
|
+
- Path: ${companionTemplate.path}
|
|
329
|
+
- Preserve this native format and structure when generating companion artifact.
|
|
330
|
+
\`\`\`${artifactType === "workflow" ? "mermaid" : "html"}
|
|
331
|
+
${companionTemplate.content.trim()}
|
|
332
332
|
\`\`\``
|
|
333
333
|
: "";
|
|
334
334
|
const workflowPairing = artifactType === "workflow"
|
|
335
|
-
? `
|
|
336
|
-
Workflow paired output contract (STRICT):
|
|
337
|
-
- Output markdown explanation first (template headings).
|
|
338
|
-
- Then append a mermaid block for the same flow:
|
|
339
|
-
\`\`\`mermaid
|
|
340
|
-
flowchart TD
|
|
341
|
-
...
|
|
342
|
-
\`\`\`
|
|
335
|
+
? `
|
|
336
|
+
Workflow paired output contract (STRICT):
|
|
337
|
+
- Output markdown explanation first (template headings).
|
|
338
|
+
- Then append a mermaid block for the same flow:
|
|
339
|
+
\`\`\`mermaid
|
|
340
|
+
flowchart TD
|
|
341
|
+
...
|
|
342
|
+
\`\`\`
|
|
343
343
|
- Mermaid block is mandatory.`
|
|
344
344
|
: "";
|
|
345
345
|
const wireframePairing = artifactType === "wireframe"
|
|
346
|
-
? `
|
|
347
|
-
Wireframe paired output contract (STRICT):
|
|
348
|
-
- Output markdown explanation first (template headings).
|
|
349
|
-
- Generate companion HTML screens based on native wireframe template.
|
|
346
|
+
? `
|
|
347
|
+
Wireframe paired output contract (STRICT):
|
|
348
|
+
- Output markdown explanation first (template headings).
|
|
349
|
+
- Generate companion HTML screens based on native wireframe template.
|
|
350
350
|
- HTML must stay low-fidelity and structure-first.`
|
|
351
351
|
: "";
|
|
352
352
|
const authorPolicy = outputAuthor && outputAuthor.trim().length > 0
|
|
353
|
-
? `
|
|
354
|
-
Author policy (STRICT):
|
|
355
|
-
- Use this exact author name wherever author is required: ${outputAuthor.trim()}
|
|
353
|
+
? `
|
|
354
|
+
Author policy (STRICT):
|
|
355
|
+
- Use this exact author name wherever author is required: ${outputAuthor.trim()}
|
|
356
356
|
- Do not invent random author names.`
|
|
357
357
|
: "";
|
|
358
|
-
const withTemplate = `${base}
|
|
359
|
-
|
|
360
|
-
${authority}
|
|
361
|
-
${companionAuthority}
|
|
362
|
-
${workflowPairing}
|
|
363
|
-
${wireframePairing}
|
|
358
|
+
const withTemplate = `${base}
|
|
359
|
+
|
|
360
|
+
${authority}
|
|
361
|
+
${companionAuthority}
|
|
362
|
+
${workflowPairing}
|
|
363
|
+
${wireframePairing}
|
|
364
364
|
${authorPolicy}`;
|
|
365
365
|
if (!agent)
|
|
366
366
|
return withTemplate;
|
|
367
|
-
return `${withTemplate}
|
|
368
|
-
|
|
369
|
-
Agent execution profile: ${agent}
|
|
367
|
+
return `${withTemplate}
|
|
368
|
+
|
|
369
|
+
Agent execution profile: ${agent}
|
|
370
370
|
- Keep output deterministic and actionable.`;
|
|
371
371
|
}
|
|
372
372
|
async function loadLatestArtifactPath(cwd, type) {
|
|
@@ -574,38 +574,38 @@ async function writeWireframeScreens(targetDir, baseName, normalized, coverage,
|
|
|
574
574
|
const screenBase = `${baseName}-${index + 1}-${toSlug(title)}`;
|
|
575
575
|
const htmlPath = node_path_1.default.join(targetDir, `${screenBase}.html`);
|
|
576
576
|
const mdPath = node_path_1.default.join(targetDir, `${screenBase}.md`);
|
|
577
|
-
const fallbackHtml = `<!doctype html>
|
|
578
|
-
<html lang="${lang}">
|
|
579
|
-
<head>
|
|
580
|
-
<meta charset="utf-8" />
|
|
581
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
582
|
-
<title>${title}</title>
|
|
583
|
-
</head>
|
|
584
|
-
<body>
|
|
585
|
-
<!-- [${screen.id}] -->
|
|
586
|
-
<header>
|
|
587
|
-
<h1>${title}</h1>
|
|
588
|
-
<nav><button type="button">${(0, i18n_1.t)("back", lang)}</button><button type="button">${(0, i18n_1.t)("next", lang)}</button></nav>
|
|
589
|
-
</header>
|
|
590
|
-
<main>
|
|
591
|
-
<section>
|
|
592
|
-
<h2>${(0, i18n_1.t)("content", lang)}</h2>
|
|
593
|
-
<ul>
|
|
594
|
-
<li>${(0, i18n_1.t)("primary_info_area", lang)}</li>
|
|
595
|
-
<li>${(0, i18n_1.t)("status_indicator", lang)}</li>
|
|
596
|
-
</ul>
|
|
597
|
-
</section>
|
|
598
|
-
<section>
|
|
599
|
-
<h2>${(0, i18n_1.t)("form", lang)}</h2>
|
|
600
|
-
<form>
|
|
601
|
-
<label>${(0, i18n_1.t)("field", lang)}
|
|
602
|
-
<input type="text" name="field_${index + 1}" />
|
|
603
|
-
</label>
|
|
604
|
-
<button type="submit">${(0, i18n_1.t)("save", lang)}</button>
|
|
605
|
-
</form>
|
|
606
|
-
</section>
|
|
607
|
-
</main>
|
|
608
|
-
</body>
|
|
577
|
+
const fallbackHtml = `<!doctype html>
|
|
578
|
+
<html lang="${lang}">
|
|
579
|
+
<head>
|
|
580
|
+
<meta charset="utf-8" />
|
|
581
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
582
|
+
<title>${title}</title>
|
|
583
|
+
</head>
|
|
584
|
+
<body>
|
|
585
|
+
<!-- [${screen.id}] -->
|
|
586
|
+
<header>
|
|
587
|
+
<h1>${title}</h1>
|
|
588
|
+
<nav><button type="button">${(0, i18n_1.t)("back", lang)}</button><button type="button">${(0, i18n_1.t)("next", lang)}</button></nav>
|
|
589
|
+
</header>
|
|
590
|
+
<main>
|
|
591
|
+
<section>
|
|
592
|
+
<h2>${(0, i18n_1.t)("content", lang)}</h2>
|
|
593
|
+
<ul>
|
|
594
|
+
<li>${(0, i18n_1.t)("primary_info_area", lang)}</li>
|
|
595
|
+
<li>${(0, i18n_1.t)("status_indicator", lang)}</li>
|
|
596
|
+
</ul>
|
|
597
|
+
</section>
|
|
598
|
+
<section>
|
|
599
|
+
<h2>${(0, i18n_1.t)("form", lang)}</h2>
|
|
600
|
+
<form>
|
|
601
|
+
<label>${(0, i18n_1.t)("field", lang)}
|
|
602
|
+
<input type="text" name="field_${index + 1}" />
|
|
603
|
+
</label>
|
|
604
|
+
<button type="submit">${(0, i18n_1.t)("save", lang)}</button>
|
|
605
|
+
</form>
|
|
606
|
+
</section>
|
|
607
|
+
</main>
|
|
608
|
+
</body>
|
|
609
609
|
</html>`;
|
|
610
610
|
const htmlTemplate = htmlTemplateContent && htmlTemplateContent.trim().length > 0 ? htmlTemplateContent : fallbackHtml;
|
|
611
611
|
const html = replaceTemplateTokens(htmlTemplate, {
|
package/dist/core/settings.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type ProdoSettings = {
|
|
|
2
2
|
lang: string;
|
|
3
3
|
ai?: string;
|
|
4
4
|
author?: string;
|
|
5
|
+
provider?: string;
|
|
5
6
|
};
|
|
6
7
|
export declare function readSettings(cwd: string): Promise<ProdoSettings>;
|
|
7
8
|
export declare function writeSettings(cwd: string, settings: ProdoSettings): Promise<string>;
|
package/dist/core/settings.js
CHANGED
|
@@ -21,7 +21,8 @@ async function readSettings(cwd) {
|
|
|
21
21
|
return {
|
|
22
22
|
lang: typeof parsed.lang === "string" && parsed.lang.trim() ? parsed.lang.trim() : "en",
|
|
23
23
|
ai: typeof parsed.ai === "string" && parsed.ai.trim() ? parsed.ai.trim() : undefined,
|
|
24
|
-
author: typeof parsed.author === "string" && parsed.author.trim() ? parsed.author.trim() : undefined
|
|
24
|
+
author: typeof parsed.author === "string" && parsed.author.trim() ? parsed.author.trim() : undefined,
|
|
25
|
+
provider: typeof parsed.provider === "string" && parsed.provider.trim() ? parsed.provider.trim() : undefined
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
catch {
|
|
@@ -30,6 +31,13 @@ async function readSettings(cwd) {
|
|
|
30
31
|
}
|
|
31
32
|
async function writeSettings(cwd, settings) {
|
|
32
33
|
const path = (0, paths_1.settingsPath)(cwd);
|
|
33
|
-
|
|
34
|
+
const clean = { lang: settings.lang };
|
|
35
|
+
if (settings.ai)
|
|
36
|
+
clean.ai = settings.ai;
|
|
37
|
+
if (settings.author)
|
|
38
|
+
clean.author = settings.author;
|
|
39
|
+
if (settings.provider)
|
|
40
|
+
clean.provider = settings.provider;
|
|
41
|
+
await promises_1.default.writeFile(path, `${JSON.stringify(clean, null, 2)}\n`, "utf8");
|
|
34
42
|
return path;
|
|
35
43
|
}
|