@shahmarasy/prodo 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -97
- package/bin/prodo.cjs +6 -6
- package/dist/agents/agent-registry.d.ts +13 -0
- package/dist/agents/agent-registry.js +79 -0
- package/dist/agents/anthropic/index.d.ts +9 -0
- package/dist/agents/anthropic/index.js +55 -0
- package/dist/agents/base.d.ts +25 -0
- package/dist/agents/base.js +71 -0
- package/dist/agents/google/index.d.ts +9 -0
- package/dist/agents/google/index.js +53 -0
- package/dist/agents/mock/index.d.ts +11 -0
- package/dist/agents/mock/index.js +26 -0
- package/dist/agents/openai/index.d.ts +9 -0
- package/dist/agents/openai/index.js +57 -0
- package/dist/agents/system-prompts.d.ts +3 -0
- package/dist/agents/system-prompts.js +32 -0
- package/dist/cli/agent-command-installer.d.ts +4 -0
- package/dist/cli/agent-command-installer.js +148 -0
- package/dist/cli/agent-ids.d.ts +15 -0
- package/dist/cli/agent-ids.js +49 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +144 -0
- package/dist/cli/fix-tui.d.ts +4 -0
- package/dist/cli/fix-tui.js +79 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +465 -0
- package/dist/cli/init-tui.d.ts +23 -0
- package/dist/cli/init-tui.js +176 -0
- package/dist/cli/init.d.ts +11 -0
- package/dist/cli/init.js +334 -0
- package/dist/cli/normalize-interactive.d.ts +8 -0
- package/dist/cli/normalize-interactive.js +167 -0
- package/dist/cli/preset-loader.d.ts +4 -0
- package/dist/cli/preset-loader.js +210 -0
- package/dist/core/artifact-registry.d.ts +11 -0
- package/dist/core/artifact-registry.js +49 -0
- package/dist/core/artifacts.d.ts +10 -0
- package/dist/core/artifacts.js +892 -0
- package/dist/core/clean.d.ts +10 -0
- package/dist/core/clean.js +74 -0
- package/dist/core/consistency.d.ts +8 -0
- package/dist/core/consistency.js +328 -0
- package/dist/core/constants.d.ts +7 -0
- package/dist/core/constants.js +64 -0
- package/dist/core/errors.d.ts +3 -0
- package/dist/core/errors.js +10 -0
- package/dist/core/fix.d.ts +31 -0
- package/dist/core/fix.js +188 -0
- package/dist/core/hook-executor.d.ts +1 -0
- package/dist/core/hook-executor.js +175 -0
- package/dist/core/markdown.d.ts +16 -0
- package/dist/core/markdown.js +81 -0
- package/dist/core/normalize.d.ts +8 -0
- package/dist/core/normalize.js +125 -0
- package/dist/core/normalized-brief.d.ts +48 -0
- package/dist/core/normalized-brief.js +182 -0
- package/dist/core/output-index.d.ts +13 -0
- package/dist/core/output-index.js +55 -0
- package/dist/core/paths.d.ts +17 -0
- package/dist/core/paths.js +80 -0
- package/dist/core/project-config.d.ts +14 -0
- package/dist/core/project-config.js +69 -0
- package/dist/core/registry.d.ts +13 -0
- package/dist/core/registry.js +115 -0
- package/dist/core/settings.d.ts +7 -0
- package/dist/core/settings.js +35 -0
- package/dist/core/template-engine.d.ts +3 -0
- package/dist/core/template-engine.js +43 -0
- package/dist/core/template-resolver.d.ts +15 -0
- package/dist/core/template-resolver.js +46 -0
- package/dist/core/templates.d.ts +33 -0
- package/dist/core/templates.js +440 -0
- package/dist/core/terminology.d.ts +21 -0
- package/dist/core/terminology.js +143 -0
- package/dist/core/tracing.d.ts +21 -0
- package/dist/core/tracing.js +74 -0
- package/dist/core/types.d.ts +35 -0
- package/dist/core/types.js +5 -0
- package/dist/core/utils.d.ts +7 -0
- package/dist/core/utils.js +66 -0
- package/dist/core/validate.d.ts +10 -0
- package/dist/core/validate.js +226 -0
- package/dist/core/validator.d.ts +5 -0
- package/dist/core/validator.js +76 -0
- package/dist/core/version.d.ts +1 -0
- package/dist/core/version.js +30 -0
- package/dist/core/workflow-commands.d.ts +7 -0
- package/dist/core/workflow-commands.js +29 -0
- package/dist/i18n/en.json +45 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.js +63 -0
- package/dist/i18n/tr.json +45 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.js +20 -6
- package/dist/providers/mock-provider.d.ts +1 -1
- package/dist/providers/mock-provider.js +7 -6
- package/dist/providers/openai-provider.d.ts +1 -1
- package/dist/providers/openai-provider.js +1 -1
- package/dist/skills/engine.d.ts +10 -0
- package/dist/skills/engine.js +75 -0
- package/dist/skills/fix-skill.d.ts +2 -0
- package/dist/skills/fix-skill.js +38 -0
- package/dist/skills/generate-artifact-skill.d.ts +2 -0
- package/dist/skills/generate-artifact-skill.js +32 -0
- package/dist/skills/generate-pipeline-skill.d.ts +2 -0
- package/dist/skills/generate-pipeline-skill.js +45 -0
- package/dist/skills/normalize-skill.d.ts +2 -0
- package/dist/skills/normalize-skill.js +29 -0
- package/dist/skills/types.d.ts +28 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/validate-skill.d.ts +2 -0
- package/dist/skills/validate-skill.js +29 -0
- package/package.json +74 -45
- package/src/agents/agent-registry.ts +93 -0
- package/src/agents/anthropic/index.ts +86 -0
- package/src/agents/anthropic/manifest.json +7 -0
- package/src/agents/base.ts +77 -0
- package/src/agents/google/index.ts +79 -0
- package/src/agents/google/manifest.json +7 -0
- package/src/agents/mock/index.ts +32 -0
- package/src/agents/mock/manifest.json +7 -0
- package/src/agents/openai/index.ts +83 -0
- package/src/agents/openai/manifest.json +7 -0
- package/src/agents/system-prompts.ts +35 -0
- package/src/{agent-command-installer.ts → cli/agent-command-installer.ts} +164 -164
- package/src/{agents.ts → cli/agent-ids.ts} +58 -58
- package/src/{doctor.ts → cli/doctor.ts} +157 -137
- package/src/cli/fix-tui.ts +111 -0
- package/src/{cli.ts → cli/index.ts} +459 -410
- package/src/{init-tui.ts → cli/init-tui.ts} +208 -208
- package/src/{init.ts → cli/init.ts} +398 -398
- package/src/cli/normalize-interactive.ts +241 -0
- package/src/{preset-loader.ts → cli/preset-loader.ts} +237 -237
- package/src/{artifact-registry.ts → core/artifact-registry.ts} +69 -69
- package/src/{artifacts.ts → core/artifacts.ts} +1081 -1072
- package/src/core/clean.ts +88 -0
- package/src/{consistency.ts → core/consistency.ts} +374 -303
- package/src/{constants.ts → core/constants.ts} +72 -72
- package/src/{errors.ts → core/errors.ts} +7 -7
- package/src/core/fix.ts +253 -0
- package/src/{hook-executor.ts → core/hook-executor.ts} +196 -196
- package/src/{markdown.ts → core/markdown.ts} +93 -73
- package/src/{normalize.ts → core/normalize.ts} +145 -137
- package/src/{normalized-brief.ts → core/normalized-brief.ts} +227 -206
- package/src/{output-index.ts → core/output-index.ts} +59 -59
- package/src/{paths.ts → core/paths.ts} +75 -71
- package/src/{project-config.ts → core/project-config.ts} +78 -78
- package/src/{registry.ts → core/registry.ts} +119 -119
- package/src/{settings.ts → core/settings.ts} +35 -35
- package/src/core/template-engine.ts +45 -0
- package/src/{template-resolver.ts → core/template-resolver.ts} +54 -54
- package/src/{templates.ts → core/templates.ts} +452 -452
- package/src/core/terminology.ts +177 -0
- package/src/core/tracing.ts +110 -0
- package/src/{types.ts → core/types.ts} +46 -46
- package/src/{utils.ts → core/utils.ts} +64 -64
- package/src/{validate.ts → core/validate.ts} +252 -246
- package/src/{validator.ts → core/validator.ts} +92 -92
- package/src/{version.ts → core/version.ts} +24 -24
- package/src/{workflow-commands.ts → core/workflow-commands.ts} +32 -32
- package/src/i18n/en.json +45 -0
- package/src/i18n/index.ts +58 -0
- package/src/i18n/tr.json +45 -0
- package/src/providers/index.ts +29 -12
- package/src/providers/mock-provider.ts +200 -199
- package/src/providers/openai-provider.ts +88 -88
- package/src/skills/engine.ts +94 -0
- package/src/skills/fix-skill.ts +38 -0
- package/src/skills/generate-artifact-skill.ts +32 -0
- package/src/skills/generate-pipeline-skill.ts +49 -0
- package/src/skills/normalize-skill.ts +29 -0
- package/src/skills/types.ts +36 -0
- package/src/skills/validate-skill.ts +29 -0
|
@@ -1,164 +1,164 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import yaml from "js-yaml";
|
|
4
|
-
import { UserError } from "
|
|
5
|
-
import { ensureDir, fileExists } from "
|
|
6
|
-
|
|
7
|
-
export const AI_ALIASES: Record<string, "codex" | "gemini-cli" | "claude-cli"> = {
|
|
8
|
-
codex: "codex",
|
|
9
|
-
gemini: "gemini-cli",
|
|
10
|
-
"gemmini-cli": "gemini-cli",
|
|
11
|
-
"gemmini": "gemini-cli",
|
|
12
|
-
"gemini-cli": "gemini-cli",
|
|
13
|
-
claude: "claude-cli",
|
|
14
|
-
"claude-cli": "claude-cli"
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export type SupportedAi = "codex" | "gemini-cli" | "claude-cli";
|
|
18
|
-
|
|
19
|
-
type AgentConfig = {
|
|
20
|
-
baseDir: string;
|
|
21
|
-
format: "markdown" | "toml" | "skill";
|
|
22
|
-
extension: string;
|
|
23
|
-
argsPlaceholder: string;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const AGENT_CONFIG: Record<SupportedAi, AgentConfig> = {
|
|
27
|
-
"claude-cli": {
|
|
28
|
-
baseDir: ".claude/commands",
|
|
29
|
-
format: "markdown",
|
|
30
|
-
extension: ".md",
|
|
31
|
-
argsPlaceholder: "$ARGUMENTS"
|
|
32
|
-
},
|
|
33
|
-
"gemini-cli": {
|
|
34
|
-
baseDir: ".gemini/commands",
|
|
35
|
-
format: "toml",
|
|
36
|
-
extension: ".toml",
|
|
37
|
-
argsPlaceholder: "{{args}}"
|
|
38
|
-
},
|
|
39
|
-
codex: {
|
|
40
|
-
baseDir: ".agents/skills",
|
|
41
|
-
format: "skill",
|
|
42
|
-
extension: "/SKILL.md",
|
|
43
|
-
argsPlaceholder: "$ARGUMENTS"
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
type ParsedTemplate = {
|
|
48
|
-
frontmatter: Record<string, unknown>;
|
|
49
|
-
body: string;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
function parseFrontmatter(content: string): ParsedTemplate {
|
|
53
|
-
if (!content.startsWith("---")) return { frontmatter: {}, body: content };
|
|
54
|
-
const end = content.indexOf("\n---", 4);
|
|
55
|
-
if (end === -1) return { frontmatter: {}, body: content };
|
|
56
|
-
const fmRaw = content.slice(3, end).trim();
|
|
57
|
-
const body = content.slice(end + 4).trimStart();
|
|
58
|
-
const frontmatter = (yaml.load(fmRaw) as Record<string, unknown>) ?? {};
|
|
59
|
-
return { frontmatter, body };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function renderFrontmatter(frontmatter: Record<string, unknown>): string {
|
|
63
|
-
if (Object.keys(frontmatter).length === 0) return "";
|
|
64
|
-
return `---\n${yaml.dump(frontmatter)}---\n`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function sanitizeFrontmatter(frontmatter: Record<string, unknown>): Record<string, unknown> {
|
|
68
|
-
const out = { ...frontmatter };
|
|
69
|
-
delete out.run;
|
|
70
|
-
delete out.scripts;
|
|
71
|
-
return out;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function toTomlPrompt(body: string, frontmatter: Record<string, unknown>, argsPlaceholder: string): string {
|
|
75
|
-
const description = String(frontmatter.description ?? "Prodo command");
|
|
76
|
-
const promptBody = body.replaceAll("$ARGUMENTS", argsPlaceholder);
|
|
77
|
-
return `description = "${description.replace(/"/g, '\\"')}"
|
|
78
|
-
|
|
79
|
-
prompt = """
|
|
80
|
-
Important execution rule:
|
|
81
|
-
- This is an agent slash command, not a shell command.
|
|
82
|
-
- Do NOT run \`prodo-normalize\`, \`prodo-prd\`, or \`prodo ...\` in shell.
|
|
83
|
-
- Execute the workflow directly using workspace files.
|
|
84
|
-
|
|
85
|
-
${promptBody}
|
|
86
|
-
"""`;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function toSkill(name: string, body: string, frontmatter: Record<string, unknown>): string {
|
|
90
|
-
const description = String(frontmatter.description ?? "Prodo workflow command");
|
|
91
|
-
return `---
|
|
92
|
-
name: ${name}
|
|
93
|
-
description: ${description}
|
|
94
|
-
compatibility: Requires Prodo project scaffold (.prodo)
|
|
95
|
-
metadata:
|
|
96
|
-
author: prodo
|
|
97
|
-
source: .prodo/commands/${name}.md
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
${body}`;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function resolveAi(ai?: string): SupportedAi | undefined {
|
|
104
|
-
if (!ai) return undefined;
|
|
105
|
-
const normalized = AI_ALIASES[ai.trim().toLowerCase()];
|
|
106
|
-
if (!normalized) {
|
|
107
|
-
throw new UserError("Unsupported --ai value. Use: codex | gemini-cli | claude-cli");
|
|
108
|
-
}
|
|
109
|
-
return normalized;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async function loadCommandTemplateNames(commandTemplatesDir: string): Promise<string[]> {
|
|
113
|
-
if (!(await fileExists(commandTemplatesDir))) {
|
|
114
|
-
throw new UserError(`Missing command templates directory: ${commandTemplatesDir}`);
|
|
115
|
-
}
|
|
116
|
-
const entries = await fs.readdir(commandTemplatesDir, { withFileTypes: true });
|
|
117
|
-
return entries
|
|
118
|
-
.filter((entry) => entry.isFile())
|
|
119
|
-
.map((entry) => entry.name)
|
|
120
|
-
.filter((name) => name.endsWith(".md") && name.startsWith("prodo-"))
|
|
121
|
-
.map((name) => name.replace(/\.md$/, ""))
|
|
122
|
-
.sort();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export async function installAgentCommands(projectRoot: string, ai: SupportedAi): Promise<string[]> {
|
|
126
|
-
const cfg = AGENT_CONFIG[ai];
|
|
127
|
-
const target = path.join(projectRoot, cfg.baseDir);
|
|
128
|
-
const commandTemplatesDir = path.join(projectRoot, ".prodo", "commands");
|
|
129
|
-
const commandNames = await loadCommandTemplateNames(commandTemplatesDir);
|
|
130
|
-
await ensureDir(target);
|
|
131
|
-
|
|
132
|
-
const written: string[] = [];
|
|
133
|
-
for (const commandName of commandNames) {
|
|
134
|
-
const templatePath = path.join(commandTemplatesDir, `${commandName}.md`);
|
|
135
|
-
if (!(await fileExists(templatePath))) {
|
|
136
|
-
throw new UserError(`Missing command template: ${templatePath}`);
|
|
137
|
-
}
|
|
138
|
-
const raw = await fs.readFile(templatePath, "utf8");
|
|
139
|
-
const parsed = parseFrontmatter(raw);
|
|
140
|
-
|
|
141
|
-
if (cfg.format === "skill") {
|
|
142
|
-
const skillDir = path.join(target, commandName);
|
|
143
|
-
await ensureDir(skillDir);
|
|
144
|
-
const outPath = path.join(skillDir, "SKILL.md");
|
|
145
|
-
const content = toSkill(commandName, parsed.body.replaceAll("$ARGUMENTS", cfg.argsPlaceholder), parsed.frontmatter);
|
|
146
|
-
await fs.writeFile(outPath, content, "utf8");
|
|
147
|
-
written.push(outPath);
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (cfg.format === "toml") {
|
|
152
|
-
const outPath = path.join(target, `${commandName}${cfg.extension}`);
|
|
153
|
-
await fs.writeFile(outPath, toTomlPrompt(parsed.body, parsed.frontmatter, cfg.argsPlaceholder), "utf8");
|
|
154
|
-
written.push(outPath);
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const outPath = path.join(target, `${commandName}${cfg.extension}`);
|
|
159
|
-
const replacedBody = parsed.body.replaceAll("$ARGUMENTS", cfg.argsPlaceholder);
|
|
160
|
-
await fs.writeFile(outPath, `${renderFrontmatter(sanitizeFrontmatter(parsed.frontmatter))}\n${replacedBody}`, "utf8");
|
|
161
|
-
written.push(outPath);
|
|
162
|
-
}
|
|
163
|
-
return written;
|
|
164
|
-
}
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import { UserError } from "../core/errors";
|
|
5
|
+
import { ensureDir, fileExists } from "../core/utils";
|
|
6
|
+
|
|
7
|
+
export const AI_ALIASES: Record<string, "codex" | "gemini-cli" | "claude-cli"> = {
|
|
8
|
+
codex: "codex",
|
|
9
|
+
gemini: "gemini-cli",
|
|
10
|
+
"gemmini-cli": "gemini-cli",
|
|
11
|
+
"gemmini": "gemini-cli",
|
|
12
|
+
"gemini-cli": "gemini-cli",
|
|
13
|
+
claude: "claude-cli",
|
|
14
|
+
"claude-cli": "claude-cli"
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type SupportedAi = "codex" | "gemini-cli" | "claude-cli";
|
|
18
|
+
|
|
19
|
+
type AgentConfig = {
|
|
20
|
+
baseDir: string;
|
|
21
|
+
format: "markdown" | "toml" | "skill";
|
|
22
|
+
extension: string;
|
|
23
|
+
argsPlaceholder: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const AGENT_CONFIG: Record<SupportedAi, AgentConfig> = {
|
|
27
|
+
"claude-cli": {
|
|
28
|
+
baseDir: ".claude/commands",
|
|
29
|
+
format: "markdown",
|
|
30
|
+
extension: ".md",
|
|
31
|
+
argsPlaceholder: "$ARGUMENTS"
|
|
32
|
+
},
|
|
33
|
+
"gemini-cli": {
|
|
34
|
+
baseDir: ".gemini/commands",
|
|
35
|
+
format: "toml",
|
|
36
|
+
extension: ".toml",
|
|
37
|
+
argsPlaceholder: "{{args}}"
|
|
38
|
+
},
|
|
39
|
+
codex: {
|
|
40
|
+
baseDir: ".agents/skills",
|
|
41
|
+
format: "skill",
|
|
42
|
+
extension: "/SKILL.md",
|
|
43
|
+
argsPlaceholder: "$ARGUMENTS"
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type ParsedTemplate = {
|
|
48
|
+
frontmatter: Record<string, unknown>;
|
|
49
|
+
body: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function parseFrontmatter(content: string): ParsedTemplate {
|
|
53
|
+
if (!content.startsWith("---")) return { frontmatter: {}, body: content };
|
|
54
|
+
const end = content.indexOf("\n---", 4);
|
|
55
|
+
if (end === -1) return { frontmatter: {}, body: content };
|
|
56
|
+
const fmRaw = content.slice(3, end).trim();
|
|
57
|
+
const body = content.slice(end + 4).trimStart();
|
|
58
|
+
const frontmatter = (yaml.load(fmRaw) as Record<string, unknown>) ?? {};
|
|
59
|
+
return { frontmatter, body };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function renderFrontmatter(frontmatter: Record<string, unknown>): string {
|
|
63
|
+
if (Object.keys(frontmatter).length === 0) return "";
|
|
64
|
+
return `---\n${yaml.dump(frontmatter)}---\n`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function sanitizeFrontmatter(frontmatter: Record<string, unknown>): Record<string, unknown> {
|
|
68
|
+
const out = { ...frontmatter };
|
|
69
|
+
delete out.run;
|
|
70
|
+
delete out.scripts;
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function toTomlPrompt(body: string, frontmatter: Record<string, unknown>, argsPlaceholder: string): string {
|
|
75
|
+
const description = String(frontmatter.description ?? "Prodo command");
|
|
76
|
+
const promptBody = body.replaceAll("$ARGUMENTS", argsPlaceholder);
|
|
77
|
+
return `description = "${description.replace(/"/g, '\\"')}"
|
|
78
|
+
|
|
79
|
+
prompt = """
|
|
80
|
+
Important execution rule:
|
|
81
|
+
- This is an agent slash command, not a shell command.
|
|
82
|
+
- Do NOT run \`prodo-normalize\`, \`prodo-prd\`, or \`prodo ...\` in shell.
|
|
83
|
+
- Execute the workflow directly using workspace files.
|
|
84
|
+
|
|
85
|
+
${promptBody}
|
|
86
|
+
"""`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function toSkill(name: string, body: string, frontmatter: Record<string, unknown>): string {
|
|
90
|
+
const description = String(frontmatter.description ?? "Prodo workflow command");
|
|
91
|
+
return `---
|
|
92
|
+
name: ${name}
|
|
93
|
+
description: ${description}
|
|
94
|
+
compatibility: Requires Prodo project scaffold (.prodo)
|
|
95
|
+
metadata:
|
|
96
|
+
author: prodo
|
|
97
|
+
source: .prodo/commands/${name}.md
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
${body}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function resolveAi(ai?: string): SupportedAi | undefined {
|
|
104
|
+
if (!ai) return undefined;
|
|
105
|
+
const normalized = AI_ALIASES[ai.trim().toLowerCase()];
|
|
106
|
+
if (!normalized) {
|
|
107
|
+
throw new UserError("Unsupported --ai value. Use: codex | gemini-cli | claude-cli");
|
|
108
|
+
}
|
|
109
|
+
return normalized;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function loadCommandTemplateNames(commandTemplatesDir: string): Promise<string[]> {
|
|
113
|
+
if (!(await fileExists(commandTemplatesDir))) {
|
|
114
|
+
throw new UserError(`Missing command templates directory: ${commandTemplatesDir}`);
|
|
115
|
+
}
|
|
116
|
+
const entries = await fs.readdir(commandTemplatesDir, { withFileTypes: true });
|
|
117
|
+
return entries
|
|
118
|
+
.filter((entry) => entry.isFile())
|
|
119
|
+
.map((entry) => entry.name)
|
|
120
|
+
.filter((name) => name.endsWith(".md") && name.startsWith("prodo-"))
|
|
121
|
+
.map((name) => name.replace(/\.md$/, ""))
|
|
122
|
+
.sort();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function installAgentCommands(projectRoot: string, ai: SupportedAi): Promise<string[]> {
|
|
126
|
+
const cfg = AGENT_CONFIG[ai];
|
|
127
|
+
const target = path.join(projectRoot, cfg.baseDir);
|
|
128
|
+
const commandTemplatesDir = path.join(projectRoot, ".prodo", "commands");
|
|
129
|
+
const commandNames = await loadCommandTemplateNames(commandTemplatesDir);
|
|
130
|
+
await ensureDir(target);
|
|
131
|
+
|
|
132
|
+
const written: string[] = [];
|
|
133
|
+
for (const commandName of commandNames) {
|
|
134
|
+
const templatePath = path.join(commandTemplatesDir, `${commandName}.md`);
|
|
135
|
+
if (!(await fileExists(templatePath))) {
|
|
136
|
+
throw new UserError(`Missing command template: ${templatePath}`);
|
|
137
|
+
}
|
|
138
|
+
const raw = await fs.readFile(templatePath, "utf8");
|
|
139
|
+
const parsed = parseFrontmatter(raw);
|
|
140
|
+
|
|
141
|
+
if (cfg.format === "skill") {
|
|
142
|
+
const skillDir = path.join(target, commandName);
|
|
143
|
+
await ensureDir(skillDir);
|
|
144
|
+
const outPath = path.join(skillDir, "SKILL.md");
|
|
145
|
+
const content = toSkill(commandName, parsed.body.replaceAll("$ARGUMENTS", cfg.argsPlaceholder), parsed.frontmatter);
|
|
146
|
+
await fs.writeFile(outPath, content, "utf8");
|
|
147
|
+
written.push(outPath);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (cfg.format === "toml") {
|
|
152
|
+
const outPath = path.join(target, `${commandName}${cfg.extension}`);
|
|
153
|
+
await fs.writeFile(outPath, toTomlPrompt(parsed.body, parsed.frontmatter, cfg.argsPlaceholder), "utf8");
|
|
154
|
+
written.push(outPath);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const outPath = path.join(target, `${commandName}${cfg.extension}`);
|
|
159
|
+
const replacedBody = parsed.body.replaceAll("$ARGUMENTS", cfg.argsPlaceholder);
|
|
160
|
+
await fs.writeFile(outPath, `${renderFrontmatter(sanitizeFrontmatter(parsed.frontmatter))}\n${replacedBody}`, "utf8");
|
|
161
|
+
written.push(outPath);
|
|
162
|
+
}
|
|
163
|
+
return written;
|
|
164
|
+
}
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
import { UserError } from "
|
|
2
|
-
|
|
3
|
-
export const AGENT_IDS = ["codex", "gemini-cli", "claude-cli"] as const;
|
|
4
|
-
export type AgentId = (typeof AGENT_IDS)[number];
|
|
5
|
-
|
|
6
|
-
export function isSupportedAgent(agent?: string): agent is AgentId {
|
|
7
|
-
if (!agent) return false;
|
|
8
|
-
return AGENT_IDS.includes(agent as AgentId);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function resolveAgent(agent?: string): AgentId | undefined {
|
|
12
|
-
if (!agent) return undefined;
|
|
13
|
-
if (!isSupportedAgent(agent)) {
|
|
14
|
-
throw new UserError(`Unsupported agent: ${agent}. Supported: ${AGENT_IDS.join(", ")}`);
|
|
15
|
-
}
|
|
16
|
-
return agent;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type CommandSetItem = {
|
|
20
|
-
command: string;
|
|
21
|
-
purpose: string;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type AgentCommandSet = {
|
|
25
|
-
agent: string;
|
|
26
|
-
description?: string;
|
|
27
|
-
recommended_sequence?: CommandSetItem[];
|
|
28
|
-
artifact_shortcuts?: Record<string, string>;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export async function loadAgentCommandSet(_cwd: string, agent: AgentId): Promise<AgentCommandSet> {
|
|
32
|
-
const prefix = "/prodo";
|
|
33
|
-
return {
|
|
34
|
-
agent,
|
|
35
|
-
description: "Agent-specific command set for Prodo artifact pipeline.",
|
|
36
|
-
recommended_sequence: [
|
|
37
|
-
{ command: "prodo init . --ai <agent> --lang <en|tr>", purpose: "Initialize Prodo scaffold and agent commands." },
|
|
38
|
-
{ command: `${prefix}-normalize`, purpose: "Normalize start brief into normalized brief JSON." },
|
|
39
|
-
{ command: `${prefix}-prd`, purpose: "Generate PRD artifact." },
|
|
40
|
-
{ command: `${prefix}-workflow`, purpose: "Generate workflow artifact." },
|
|
41
|
-
{ command: `${prefix}-wireframe`, purpose: "Generate wireframe artifact." },
|
|
42
|
-
{ command: `${prefix}-stories`, purpose: "Generate stories artifact." },
|
|
43
|
-
{ command: `${prefix}-techspec`, purpose: "Generate techspec artifact." },
|
|
44
|
-
{ command: `${prefix}-validate`, purpose: "Run validation report." },
|
|
45
|
-
{ command: `${prefix}-fix`, purpose: "Fix artifacts when validation fails." }
|
|
46
|
-
],
|
|
47
|
-
artifact_shortcuts: {
|
|
48
|
-
normalize: `${prefix}-normalize`,
|
|
49
|
-
prd: `${prefix}-prd`,
|
|
50
|
-
workflow: `${prefix}-workflow`,
|
|
51
|
-
wireframe: `${prefix}-wireframe`,
|
|
52
|
-
stories: `${prefix}-stories`,
|
|
53
|
-
techspec: `${prefix}-techspec`,
|
|
54
|
-
validate: `${prefix}-validate`,
|
|
55
|
-
fix: `${prefix}-fix`
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
}
|
|
1
|
+
import { UserError } from "../core/errors";
|
|
2
|
+
|
|
3
|
+
export const AGENT_IDS = ["codex", "gemini-cli", "claude-cli"] as const;
|
|
4
|
+
export type AgentId = (typeof AGENT_IDS)[number];
|
|
5
|
+
|
|
6
|
+
export function isSupportedAgent(agent?: string): agent is AgentId {
|
|
7
|
+
if (!agent) return false;
|
|
8
|
+
return AGENT_IDS.includes(agent as AgentId);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function resolveAgent(agent?: string): AgentId | undefined {
|
|
12
|
+
if (!agent) return undefined;
|
|
13
|
+
if (!isSupportedAgent(agent)) {
|
|
14
|
+
throw new UserError(`Unsupported agent: ${agent}. Supported: ${AGENT_IDS.join(", ")}`);
|
|
15
|
+
}
|
|
16
|
+
return agent;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type CommandSetItem = {
|
|
20
|
+
command: string;
|
|
21
|
+
purpose: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type AgentCommandSet = {
|
|
25
|
+
agent: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
recommended_sequence?: CommandSetItem[];
|
|
28
|
+
artifact_shortcuts?: Record<string, string>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export async function loadAgentCommandSet(_cwd: string, agent: AgentId): Promise<AgentCommandSet> {
|
|
32
|
+
const prefix = "/prodo";
|
|
33
|
+
return {
|
|
34
|
+
agent,
|
|
35
|
+
description: "Agent-specific command set for Prodo artifact pipeline.",
|
|
36
|
+
recommended_sequence: [
|
|
37
|
+
{ command: "prodo init . --ai <agent> --lang <en|tr>", purpose: "Initialize Prodo scaffold and agent commands." },
|
|
38
|
+
{ command: `${prefix}-normalize`, purpose: "Normalize start brief into normalized brief JSON." },
|
|
39
|
+
{ command: `${prefix}-prd`, purpose: "Generate PRD artifact." },
|
|
40
|
+
{ command: `${prefix}-workflow`, purpose: "Generate workflow artifact." },
|
|
41
|
+
{ command: `${prefix}-wireframe`, purpose: "Generate wireframe artifact." },
|
|
42
|
+
{ command: `${prefix}-stories`, purpose: "Generate stories artifact." },
|
|
43
|
+
{ command: `${prefix}-techspec`, purpose: "Generate techspec artifact." },
|
|
44
|
+
{ command: `${prefix}-validate`, purpose: "Run validation report." },
|
|
45
|
+
{ command: `${prefix}-fix`, purpose: "Fix artifacts when validation fails." }
|
|
46
|
+
],
|
|
47
|
+
artifact_shortcuts: {
|
|
48
|
+
normalize: `${prefix}-normalize`,
|
|
49
|
+
prd: `${prefix}-prd`,
|
|
50
|
+
workflow: `${prefix}-workflow`,
|
|
51
|
+
wireframe: `${prefix}-wireframe`,
|
|
52
|
+
stories: `${prefix}-stories`,
|
|
53
|
+
techspec: `${prefix}-techspec`,
|
|
54
|
+
validate: `${prefix}-validate`,
|
|
55
|
+
fix: `${prefix}-fix`
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|