@shahmarasy/prodo 0.1.3 → 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.
Files changed (205) hide show
  1. package/README.md +201 -97
  2. package/bin/prodo.cjs +6 -6
  3. package/dist/agents/agent-registry.d.ts +13 -0
  4. package/dist/agents/agent-registry.js +79 -0
  5. package/dist/agents/anthropic/index.d.ts +9 -0
  6. package/dist/agents/anthropic/index.js +55 -0
  7. package/dist/agents/base.d.ts +25 -0
  8. package/dist/agents/base.js +71 -0
  9. package/dist/agents/google/index.d.ts +9 -0
  10. package/dist/agents/google/index.js +53 -0
  11. package/dist/agents/mock/index.d.ts +11 -0
  12. package/dist/agents/mock/index.js +26 -0
  13. package/dist/agents/openai/index.d.ts +9 -0
  14. package/dist/agents/openai/index.js +57 -0
  15. package/dist/agents/system-prompts.d.ts +3 -0
  16. package/dist/agents/system-prompts.js +32 -0
  17. package/dist/agents.js +4 -2
  18. package/dist/artifacts.d.ts +1 -0
  19. package/dist/artifacts.js +265 -31
  20. package/dist/cli/agent-command-installer.d.ts +4 -0
  21. package/dist/cli/agent-command-installer.js +148 -0
  22. package/dist/cli/agent-ids.d.ts +15 -0
  23. package/dist/cli/agent-ids.js +49 -0
  24. package/dist/cli/doctor.d.ts +1 -0
  25. package/dist/cli/doctor.js +144 -0
  26. package/dist/cli/fix-tui.d.ts +4 -0
  27. package/dist/cli/fix-tui.js +79 -0
  28. package/dist/cli/index.d.ts +9 -0
  29. package/dist/cli/index.js +465 -0
  30. package/dist/cli/init-tui.d.ts +23 -0
  31. package/dist/cli/init-tui.js +176 -0
  32. package/dist/cli/init.d.ts +11 -0
  33. package/dist/cli/init.js +334 -0
  34. package/dist/cli/normalize-interactive.d.ts +8 -0
  35. package/dist/cli/normalize-interactive.js +167 -0
  36. package/dist/cli/preset-loader.d.ts +4 -0
  37. package/dist/cli/preset-loader.js +210 -0
  38. package/dist/cli.js +80 -3
  39. package/dist/core/artifact-registry.d.ts +11 -0
  40. package/dist/core/artifact-registry.js +49 -0
  41. package/dist/core/artifacts.d.ts +10 -0
  42. package/dist/core/artifacts.js +892 -0
  43. package/dist/core/clean.d.ts +10 -0
  44. package/dist/core/clean.js +74 -0
  45. package/dist/core/consistency.d.ts +8 -0
  46. package/dist/core/consistency.js +328 -0
  47. package/dist/core/constants.d.ts +7 -0
  48. package/dist/core/constants.js +64 -0
  49. package/dist/core/errors.d.ts +3 -0
  50. package/dist/core/errors.js +10 -0
  51. package/dist/core/fix.d.ts +31 -0
  52. package/dist/core/fix.js +188 -0
  53. package/dist/core/hook-executor.d.ts +1 -0
  54. package/dist/core/hook-executor.js +175 -0
  55. package/dist/core/markdown.d.ts +16 -0
  56. package/dist/core/markdown.js +81 -0
  57. package/dist/core/normalize.d.ts +8 -0
  58. package/dist/core/normalize.js +125 -0
  59. package/dist/core/normalized-brief.d.ts +48 -0
  60. package/dist/core/normalized-brief.js +182 -0
  61. package/dist/core/output-index.d.ts +13 -0
  62. package/dist/core/output-index.js +55 -0
  63. package/dist/core/paths.d.ts +17 -0
  64. package/dist/core/paths.js +80 -0
  65. package/dist/core/project-config.d.ts +14 -0
  66. package/dist/core/project-config.js +69 -0
  67. package/dist/core/registry.d.ts +13 -0
  68. package/dist/core/registry.js +115 -0
  69. package/dist/core/settings.d.ts +7 -0
  70. package/dist/core/settings.js +35 -0
  71. package/dist/core/template-engine.d.ts +3 -0
  72. package/dist/core/template-engine.js +43 -0
  73. package/dist/core/template-resolver.d.ts +15 -0
  74. package/dist/core/template-resolver.js +46 -0
  75. package/dist/core/templates.d.ts +33 -0
  76. package/dist/core/templates.js +440 -0
  77. package/dist/core/terminology.d.ts +21 -0
  78. package/dist/core/terminology.js +143 -0
  79. package/dist/core/tracing.d.ts +21 -0
  80. package/dist/core/tracing.js +74 -0
  81. package/dist/core/types.d.ts +35 -0
  82. package/dist/core/types.js +5 -0
  83. package/dist/core/utils.d.ts +7 -0
  84. package/dist/core/utils.js +66 -0
  85. package/dist/core/validate.d.ts +10 -0
  86. package/dist/core/validate.js +226 -0
  87. package/dist/core/validator.d.ts +5 -0
  88. package/dist/core/validator.js +76 -0
  89. package/dist/core/version.d.ts +1 -0
  90. package/dist/core/version.js +30 -0
  91. package/dist/core/workflow-commands.d.ts +7 -0
  92. package/dist/core/workflow-commands.js +29 -0
  93. package/dist/i18n/en.json +45 -0
  94. package/dist/i18n/index.d.ts +5 -0
  95. package/dist/i18n/index.js +63 -0
  96. package/dist/i18n/tr.json +45 -0
  97. package/dist/init-tui.d.ts +3 -0
  98. package/dist/init-tui.js +28 -1
  99. package/dist/init.d.ts +1 -0
  100. package/dist/init.js +9 -3
  101. package/dist/normalize.js +55 -7
  102. package/dist/providers/index.d.ts +2 -1
  103. package/dist/providers/index.js +20 -6
  104. package/dist/providers/mock-provider.d.ts +1 -1
  105. package/dist/providers/mock-provider.js +7 -6
  106. package/dist/providers/openai-provider.d.ts +1 -1
  107. package/dist/providers/openai-provider.js +3 -2
  108. package/dist/settings.d.ts +1 -0
  109. package/dist/settings.js +2 -1
  110. package/dist/skills/engine.d.ts +10 -0
  111. package/dist/skills/engine.js +75 -0
  112. package/dist/skills/fix-skill.d.ts +2 -0
  113. package/dist/skills/fix-skill.js +38 -0
  114. package/dist/skills/generate-artifact-skill.d.ts +2 -0
  115. package/dist/skills/generate-artifact-skill.js +32 -0
  116. package/dist/skills/generate-pipeline-skill.d.ts +2 -0
  117. package/dist/skills/generate-pipeline-skill.js +45 -0
  118. package/dist/skills/normalize-skill.d.ts +2 -0
  119. package/dist/skills/normalize-skill.js +29 -0
  120. package/dist/skills/types.d.ts +28 -0
  121. package/dist/skills/types.js +2 -0
  122. package/dist/skills/validate-skill.d.ts +2 -0
  123. package/dist/skills/validate-skill.js +29 -0
  124. package/dist/templates.d.ts +1 -1
  125. package/dist/templates.js +2 -0
  126. package/dist/utils.d.ts +1 -0
  127. package/dist/utils.js +13 -0
  128. package/dist/validator.js +0 -4
  129. package/dist/workflow-commands.js +2 -1
  130. package/package.json +74 -45
  131. package/presets/fintech/preset.json +48 -1
  132. package/presets/fintech/prompts/prd.md +99 -2
  133. package/presets/marketplace/preset.json +51 -1
  134. package/presets/marketplace/prompts/prd.md +140 -2
  135. package/presets/saas/preset.json +53 -1
  136. package/presets/saas/prompts/prd.md +150 -2
  137. package/src/agents/agent-registry.ts +93 -0
  138. package/src/agents/anthropic/index.ts +86 -0
  139. package/src/agents/anthropic/manifest.json +7 -0
  140. package/src/agents/base.ts +77 -0
  141. package/src/agents/google/index.ts +79 -0
  142. package/src/agents/google/manifest.json +7 -0
  143. package/src/agents/mock/index.ts +32 -0
  144. package/src/agents/mock/manifest.json +7 -0
  145. package/src/agents/openai/index.ts +83 -0
  146. package/src/agents/openai/manifest.json +7 -0
  147. package/src/agents/system-prompts.ts +35 -0
  148. package/src/{agent-command-installer.ts → cli/agent-command-installer.ts} +164 -164
  149. package/src/{agents.ts → cli/agent-ids.ts} +58 -56
  150. package/src/{doctor.ts → cli/doctor.ts} +157 -137
  151. package/src/cli/fix-tui.ts +111 -0
  152. package/src/{cli.ts → cli/index.ts} +459 -319
  153. package/src/{init-tui.ts → cli/init-tui.ts} +208 -179
  154. package/src/{init.ts → cli/init.ts} +398 -391
  155. package/src/cli/normalize-interactive.ts +241 -0
  156. package/src/{preset-loader.ts → cli/preset-loader.ts} +237 -237
  157. package/src/{artifact-registry.ts → core/artifact-registry.ts} +69 -69
  158. package/src/{artifacts.ts → core/artifacts.ts} +1081 -777
  159. package/src/core/clean.ts +88 -0
  160. package/src/{consistency.ts → core/consistency.ts} +374 -303
  161. package/src/{constants.ts → core/constants.ts} +72 -72
  162. package/src/{errors.ts → core/errors.ts} +7 -7
  163. package/src/core/fix.ts +253 -0
  164. package/src/{hook-executor.ts → core/hook-executor.ts} +196 -196
  165. package/src/{markdown.ts → core/markdown.ts} +93 -73
  166. package/src/core/normalize.ts +145 -0
  167. package/src/{normalized-brief.ts → core/normalized-brief.ts} +227 -206
  168. package/src/{output-index.ts → core/output-index.ts} +59 -59
  169. package/src/{paths.ts → core/paths.ts} +75 -71
  170. package/src/{project-config.ts → core/project-config.ts} +78 -78
  171. package/src/{registry.ts → core/registry.ts} +119 -119
  172. package/src/{settings.ts → core/settings.ts} +35 -34
  173. package/src/core/template-engine.ts +45 -0
  174. package/src/{template-resolver.ts → core/template-resolver.ts} +54 -54
  175. package/src/{templates.ts → core/templates.ts} +452 -450
  176. package/src/core/terminology.ts +177 -0
  177. package/src/core/tracing.ts +110 -0
  178. package/src/{types.ts → core/types.ts} +46 -46
  179. package/src/{utils.ts → core/utils.ts} +64 -50
  180. package/src/{validate.ts → core/validate.ts} +252 -246
  181. package/src/{validator.ts → core/validator.ts} +92 -96
  182. package/src/{version.ts → core/version.ts} +24 -24
  183. package/src/{workflow-commands.ts → core/workflow-commands.ts} +32 -31
  184. package/src/i18n/en.json +45 -0
  185. package/src/i18n/index.ts +58 -0
  186. package/src/i18n/tr.json +45 -0
  187. package/src/providers/index.ts +29 -12
  188. package/src/providers/mock-provider.ts +200 -199
  189. package/src/providers/openai-provider.ts +88 -87
  190. package/src/skills/engine.ts +94 -0
  191. package/src/skills/fix-skill.ts +38 -0
  192. package/src/skills/generate-artifact-skill.ts +32 -0
  193. package/src/skills/generate-pipeline-skill.ts +49 -0
  194. package/src/skills/normalize-skill.ts +29 -0
  195. package/src/skills/types.ts +36 -0
  196. package/src/skills/validate-skill.ts +29 -0
  197. package/templates/commands/prodo-fix.md +46 -0
  198. package/templates/commands/prodo-normalize.md +118 -23
  199. package/templates/commands/prodo-prd.md +138 -17
  200. package/templates/commands/prodo-stories.md +153 -17
  201. package/templates/commands/prodo-techspec.md +167 -17
  202. package/templates/commands/prodo-validate.md +184 -26
  203. package/templates/commands/prodo-wireframe.md +188 -17
  204. package/templates/commands/prodo-workflow.md +200 -17
  205. package/src/normalize.ts +0 -89
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AI_ALIASES = void 0;
7
+ exports.resolveAi = resolveAi;
8
+ exports.installAgentCommands = installAgentCommands;
9
+ const promises_1 = __importDefault(require("node:fs/promises"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const js_yaml_1 = __importDefault(require("js-yaml"));
12
+ const errors_1 = require("../core/errors");
13
+ const utils_1 = require("../core/utils");
14
+ exports.AI_ALIASES = {
15
+ codex: "codex",
16
+ gemini: "gemini-cli",
17
+ "gemmini-cli": "gemini-cli",
18
+ "gemmini": "gemini-cli",
19
+ "gemini-cli": "gemini-cli",
20
+ claude: "claude-cli",
21
+ "claude-cli": "claude-cli"
22
+ };
23
+ const AGENT_CONFIG = {
24
+ "claude-cli": {
25
+ baseDir: ".claude/commands",
26
+ format: "markdown",
27
+ extension: ".md",
28
+ argsPlaceholder: "$ARGUMENTS"
29
+ },
30
+ "gemini-cli": {
31
+ baseDir: ".gemini/commands",
32
+ format: "toml",
33
+ extension: ".toml",
34
+ argsPlaceholder: "{{args}}"
35
+ },
36
+ codex: {
37
+ baseDir: ".agents/skills",
38
+ format: "skill",
39
+ extension: "/SKILL.md",
40
+ argsPlaceholder: "$ARGUMENTS"
41
+ }
42
+ };
43
+ function parseFrontmatter(content) {
44
+ if (!content.startsWith("---"))
45
+ return { frontmatter: {}, body: content };
46
+ const end = content.indexOf("\n---", 4);
47
+ if (end === -1)
48
+ return { frontmatter: {}, body: content };
49
+ const fmRaw = content.slice(3, end).trim();
50
+ const body = content.slice(end + 4).trimStart();
51
+ const frontmatter = js_yaml_1.default.load(fmRaw) ?? {};
52
+ return { frontmatter, body };
53
+ }
54
+ function renderFrontmatter(frontmatter) {
55
+ if (Object.keys(frontmatter).length === 0)
56
+ return "";
57
+ return `---\n${js_yaml_1.default.dump(frontmatter)}---\n`;
58
+ }
59
+ function sanitizeFrontmatter(frontmatter) {
60
+ const out = { ...frontmatter };
61
+ delete out.run;
62
+ delete out.scripts;
63
+ return out;
64
+ }
65
+ function toTomlPrompt(body, frontmatter, argsPlaceholder) {
66
+ const description = String(frontmatter.description ?? "Prodo command");
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}
77
+ """`;
78
+ }
79
+ function toSkill(name, body, frontmatter) {
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
+
90
+ ${body}`;
91
+ }
92
+ function resolveAi(ai) {
93
+ if (!ai)
94
+ return undefined;
95
+ const normalized = exports.AI_ALIASES[ai.trim().toLowerCase()];
96
+ if (!normalized) {
97
+ throw new errors_1.UserError("Unsupported --ai value. Use: codex | gemini-cli | claude-cli");
98
+ }
99
+ return normalized;
100
+ }
101
+ async function loadCommandTemplateNames(commandTemplatesDir) {
102
+ if (!(await (0, utils_1.fileExists)(commandTemplatesDir))) {
103
+ throw new errors_1.UserError(`Missing command templates directory: ${commandTemplatesDir}`);
104
+ }
105
+ const entries = await promises_1.default.readdir(commandTemplatesDir, { withFileTypes: true });
106
+ return entries
107
+ .filter((entry) => entry.isFile())
108
+ .map((entry) => entry.name)
109
+ .filter((name) => name.endsWith(".md") && name.startsWith("prodo-"))
110
+ .map((name) => name.replace(/\.md$/, ""))
111
+ .sort();
112
+ }
113
+ async function installAgentCommands(projectRoot, ai) {
114
+ const cfg = AGENT_CONFIG[ai];
115
+ const target = node_path_1.default.join(projectRoot, cfg.baseDir);
116
+ const commandTemplatesDir = node_path_1.default.join(projectRoot, ".prodo", "commands");
117
+ const commandNames = await loadCommandTemplateNames(commandTemplatesDir);
118
+ await (0, utils_1.ensureDir)(target);
119
+ const written = [];
120
+ for (const commandName of commandNames) {
121
+ const templatePath = node_path_1.default.join(commandTemplatesDir, `${commandName}.md`);
122
+ if (!(await (0, utils_1.fileExists)(templatePath))) {
123
+ throw new errors_1.UserError(`Missing command template: ${templatePath}`);
124
+ }
125
+ const raw = await promises_1.default.readFile(templatePath, "utf8");
126
+ const parsed = parseFrontmatter(raw);
127
+ if (cfg.format === "skill") {
128
+ const skillDir = node_path_1.default.join(target, commandName);
129
+ await (0, utils_1.ensureDir)(skillDir);
130
+ const outPath = node_path_1.default.join(skillDir, "SKILL.md");
131
+ const content = toSkill(commandName, parsed.body.replaceAll("$ARGUMENTS", cfg.argsPlaceholder), parsed.frontmatter);
132
+ await promises_1.default.writeFile(outPath, content, "utf8");
133
+ written.push(outPath);
134
+ continue;
135
+ }
136
+ if (cfg.format === "toml") {
137
+ const outPath = node_path_1.default.join(target, `${commandName}${cfg.extension}`);
138
+ await promises_1.default.writeFile(outPath, toTomlPrompt(parsed.body, parsed.frontmatter, cfg.argsPlaceholder), "utf8");
139
+ written.push(outPath);
140
+ continue;
141
+ }
142
+ const outPath = node_path_1.default.join(target, `${commandName}${cfg.extension}`);
143
+ const replacedBody = parsed.body.replaceAll("$ARGUMENTS", cfg.argsPlaceholder);
144
+ await promises_1.default.writeFile(outPath, `${renderFrontmatter(sanitizeFrontmatter(parsed.frontmatter))}\n${replacedBody}`, "utf8");
145
+ written.push(outPath);
146
+ }
147
+ return written;
148
+ }
@@ -0,0 +1,15 @@
1
+ export declare const AGENT_IDS: readonly ["codex", "gemini-cli", "claude-cli"];
2
+ export type AgentId = (typeof AGENT_IDS)[number];
3
+ export declare function isSupportedAgent(agent?: string): agent is AgentId;
4
+ export declare function resolveAgent(agent?: string): AgentId | undefined;
5
+ export type CommandSetItem = {
6
+ command: string;
7
+ purpose: string;
8
+ };
9
+ export type AgentCommandSet = {
10
+ agent: string;
11
+ description?: string;
12
+ recommended_sequence?: CommandSetItem[];
13
+ artifact_shortcuts?: Record<string, string>;
14
+ };
15
+ export declare function loadAgentCommandSet(_cwd: string, agent: AgentId): Promise<AgentCommandSet>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AGENT_IDS = void 0;
4
+ exports.isSupportedAgent = isSupportedAgent;
5
+ exports.resolveAgent = resolveAgent;
6
+ exports.loadAgentCommandSet = loadAgentCommandSet;
7
+ const errors_1 = require("../core/errors");
8
+ exports.AGENT_IDS = ["codex", "gemini-cli", "claude-cli"];
9
+ function isSupportedAgent(agent) {
10
+ if (!agent)
11
+ return false;
12
+ return exports.AGENT_IDS.includes(agent);
13
+ }
14
+ function resolveAgent(agent) {
15
+ if (!agent)
16
+ return undefined;
17
+ if (!isSupportedAgent(agent)) {
18
+ throw new errors_1.UserError(`Unsupported agent: ${agent}. Supported: ${exports.AGENT_IDS.join(", ")}`);
19
+ }
20
+ return agent;
21
+ }
22
+ async function loadAgentCommandSet(_cwd, agent) {
23
+ const prefix = "/prodo";
24
+ return {
25
+ agent,
26
+ description: "Agent-specific command set for Prodo artifact pipeline.",
27
+ recommended_sequence: [
28
+ { command: "prodo init . --ai <agent> --lang <en|tr>", purpose: "Initialize Prodo scaffold and agent commands." },
29
+ { command: `${prefix}-normalize`, purpose: "Normalize start brief into normalized brief JSON." },
30
+ { command: `${prefix}-prd`, purpose: "Generate PRD artifact." },
31
+ { command: `${prefix}-workflow`, purpose: "Generate workflow artifact." },
32
+ { command: `${prefix}-wireframe`, purpose: "Generate wireframe artifact." },
33
+ { command: `${prefix}-stories`, purpose: "Generate stories artifact." },
34
+ { command: `${prefix}-techspec`, purpose: "Generate techspec artifact." },
35
+ { command: `${prefix}-validate`, purpose: "Run validation report." },
36
+ { command: `${prefix}-fix`, purpose: "Fix artifacts when validation fails." }
37
+ ],
38
+ artifact_shortcuts: {
39
+ normalize: `${prefix}-normalize`,
40
+ prd: `${prefix}-prd`,
41
+ workflow: `${prefix}-workflow`,
42
+ wireframe: `${prefix}-wireframe`,
43
+ stories: `${prefix}-stories`,
44
+ techspec: `${prefix}-techspec`,
45
+ validate: `${prefix}-validate`,
46
+ fix: `${prefix}-fix`
47
+ }
48
+ };
49
+ }
@@ -0,0 +1 @@
1
+ export declare function runDoctor(cwd: string, out: (line: string) => void): Promise<void>;
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runDoctor = runDoctor;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const agent_registry_1 = require("../agents/agent-registry");
6
+ const paths_1 = require("../core/paths");
7
+ const utils_1 = require("../core/utils");
8
+ const version_1 = require("../core/version");
9
+ function termWidth() {
10
+ return Math.max(80, process.stdout.columns ?? 100);
11
+ }
12
+ function stripAnsi(input) {
13
+ return input.replace(/\u001B\[[0-9;]*m/g, "");
14
+ }
15
+ function color(input, code) {
16
+ if (!process.stdout.isTTY)
17
+ return input;
18
+ return `${code}${input}\u001B[0m`;
19
+ }
20
+ function center(input, width) {
21
+ const visible = stripAnsi(input).length;
22
+ const left = Math.max(0, Math.floor((width - visible) / 2));
23
+ return `${" ".repeat(left)}${input}`;
24
+ }
25
+ function iconFor(status) {
26
+ if (status === "available")
27
+ return color("✔", "\u001B[32m");
28
+ if (status === "not_found")
29
+ return color("✖", "\u001B[31m");
30
+ return color("•", "\u001B[33m");
31
+ }
32
+ function labelFor(status) {
33
+ if (status === "available")
34
+ return color("available", "\u001B[32m");
35
+ if (status === "not_found")
36
+ return color("not found", "\u001B[31m");
37
+ return color("IDE-based", "\u001B[2;33m");
38
+ }
39
+ function renderRows(rows) {
40
+ const leftWidth = Math.max(...rows.map((row) => row.name.length), 10);
41
+ return rows.map((row) => {
42
+ const left = row.name.padEnd(leftWidth, " ");
43
+ const status = `${labelFor(row.status)}${row.detail ? ` (${row.detail})` : ""}`;
44
+ return ` ${iconFor(row.status)} ${left} ${status}`;
45
+ });
46
+ }
47
+ async function commandExists(command) {
48
+ return new Promise((resolve) => {
49
+ const lookup = process.platform === "win32" ? "where" : "which";
50
+ const child = (0, node_child_process_1.spawn)(lookup, [command], { stdio: "ignore" });
51
+ child.on("error", () => resolve(false));
52
+ child.on("close", (code) => resolve(code === 0));
53
+ });
54
+ }
55
+ async function firstAvailable(commands) {
56
+ for (const command of commands) {
57
+ if (await commandExists(command))
58
+ return true;
59
+ }
60
+ return false;
61
+ }
62
+ function renderLogo(width) {
63
+ const cyan = "\u001B[38;5;45m";
64
+ const blue = "\u001B[38;5;39m";
65
+ const logo = [
66
+ "██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ",
67
+ "██╔══██╗██╔══██╗██╔═══██╗██╔══██╗██╔═══██╗",
68
+ "██████╔╝██████╔╝██║ ██║██║ ██║██║ ██║",
69
+ "██╔═══╝ ██╔══██╗██║ ██║██║ ██║██║ ██║",
70
+ "██║ ██║ ██║╚██████╔╝██████╔╝╚██████╔╝"
71
+ ];
72
+ const painted = logo.map((line, idx) => color(line, idx % 2 === 0 ? cyan : blue));
73
+ return painted.map((line) => center(line, width)).join("\n");
74
+ }
75
+ async function runDoctor(cwd, out) {
76
+ const width = termWidth();
77
+ const version = await (0, version_1.readCliVersion)(cwd);
78
+ const isInitialized = await (0, utils_1.fileExists)((0, paths_1.prodoPath)(cwd));
79
+ const codex = await firstAvailable(["codex"]);
80
+ const gemini = await firstAvailable(["gemini", "gemini-cli"]);
81
+ const claude = await firstAvailable(["claude", "claude-cli"]);
82
+ const git = await firstAvailable(["git"]);
83
+ const node = await firstAvailable(["node"]);
84
+ const vscode = await firstAvailable(["code"]);
85
+ const coreRows = [
86
+ { name: "Prodo CLI", status: "available", detail: `v${version}` },
87
+ {
88
+ name: "Project initialized",
89
+ status: isInitialized ? "available" : "not_found",
90
+ detail: isInitialized ? ".prodo found" : ".prodo missing"
91
+ }
92
+ ];
93
+ const aiCliRows = [
94
+ { name: "Codex CLI", status: codex ? "available" : "not_found", detail: codex ? "available" : "not found" },
95
+ { name: "Gemini CLI", status: gemini ? "available" : "not_found", detail: gemini ? "available" : "not found" },
96
+ { name: "Claude CLI", status: claude ? "available" : "not_found", detail: claude ? "available" : "not found" }
97
+ ];
98
+ const registry = (0, agent_registry_1.getGlobalRegistry)();
99
+ const agentRows = [];
100
+ for (const agent of registry.list()) {
101
+ if (agent.name === "mock")
102
+ continue;
103
+ const available = await agent.isAvailable();
104
+ const config = agent.getConfig();
105
+ const sdkLabel = config.sdkRequired ? `SDK: ${config.sdkRequired}` : "";
106
+ const envLabel = config.envVars.filter((v) => !process.env[v]).map((v) => `${v} missing`).join(", ");
107
+ const detail = available ? "ready" : [sdkLabel, envLabel].filter(Boolean).join(", ") || "not configured";
108
+ agentRows.push({
109
+ name: config.displayName,
110
+ status: available ? "available" : "not_found",
111
+ detail
112
+ });
113
+ }
114
+ const devRows = [
115
+ { name: "Git", status: git ? "available" : "not_found", detail: git ? "available" : "not found" },
116
+ { name: "Node.js", status: node ? "available" : "not_found", detail: node ? "available" : "not found" },
117
+ { name: "Visual Studio Code", status: vscode ? "available" : "not_found", detail: vscode ? "available" : "not found" },
118
+ { name: "Cursor", status: "ide", detail: "IDE-based, no CLI check" }
119
+ ];
120
+ out("");
121
+ out(renderLogo(width));
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));
125
+ out("");
126
+ out("Checking environment...");
127
+ out("");
128
+ out(color("Core", "\u001B[1m"));
129
+ for (const line of renderRows(coreRows))
130
+ out(line);
131
+ out("");
132
+ out(color("AI CLI Tools", "\u001B[1m"));
133
+ for (const line of renderRows(aiCliRows))
134
+ out(line);
135
+ out("");
136
+ out(color("LLM Agents (SDK)", "\u001B[1m"));
137
+ for (const line of renderRows(agentRows))
138
+ out(line);
139
+ out("");
140
+ out(color("Dev Tools", "\u001B[1m"));
141
+ for (const line of renderRows(devRows))
142
+ out(line);
143
+ out("");
144
+ }
@@ -0,0 +1,4 @@
1
+ import type { FixProposal, FixResult } from "../core/fix";
2
+ export declare function displayFixProposal(proposal: FixProposal, log: (message: string) => void): Promise<void>;
3
+ export declare function confirmFixExecution(proposal: FixProposal): Promise<boolean>;
4
+ export declare function displayFixResult(result: FixResult, log: (message: string) => void): Promise<void>;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.displayFixProposal = displayFixProposal;
4
+ exports.confirmFixExecution = confirmFixExecution;
5
+ exports.displayFixResult = displayFixResult;
6
+ const dynamicImport = new Function("specifier", "return import(specifier)");
7
+ async function loadClack() {
8
+ try {
9
+ return (await dynamicImport("@clack/prompts"));
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ async function displayFixProposal(proposal, log) {
16
+ const errorCount = proposal.issues.filter((i) => i.level === "error").length;
17
+ const warningCount = proposal.issues.filter((i) => i.level === "warning").length;
18
+ log("");
19
+ log(`Fix Proposal`);
20
+ log(`${"─".repeat(50)}`);
21
+ log(`Issues found: ${errorCount} error(s), ${warningCount} warning(s)`);
22
+ log(`Artifacts to regenerate: ${proposal.targets.join(", ")}`);
23
+ log("");
24
+ for (const [type, issues] of proposal.issuesByArtifact) {
25
+ log(` ${type.toUpperCase()} (${issues.length} issue${issues.length > 1 ? "s" : ""}):`);
26
+ for (const issue of issues) {
27
+ const prefix = issue.level === "error" ? " ✖" : " ⚠";
28
+ log(` ${prefix} [${issue.code}] ${issue.message}`);
29
+ if (issue.suggestion) {
30
+ log(` → ${issue.suggestion}`);
31
+ }
32
+ }
33
+ log("");
34
+ }
35
+ const orphanIssues = proposal.issues.filter((i) => !i.artifactType);
36
+ if (orphanIssues.length > 0) {
37
+ log(` General (${orphanIssues.length} issue${orphanIssues.length > 1 ? "s" : ""}):`);
38
+ for (const issue of orphanIssues) {
39
+ const prefix = issue.level === "error" ? " ✖" : " ⚠";
40
+ log(` ${prefix} [${issue.code}] ${issue.message}`);
41
+ }
42
+ log("");
43
+ }
44
+ }
45
+ async function confirmFixExecution(proposal) {
46
+ if (process.stdout.isTTY !== true) {
47
+ return true;
48
+ }
49
+ const clack = await loadClack();
50
+ if (!clack) {
51
+ return true;
52
+ }
53
+ clack.intro("Prodo Fix");
54
+ const proceed = await clack.confirm({
55
+ message: `Regenerate ${proposal.targets.length} artifact(s): ${proposal.targets.join(", ")}?`
56
+ });
57
+ if (clack.isCancel(proceed)) {
58
+ clack.cancel("Fix cancelled.");
59
+ return false;
60
+ }
61
+ return proceed === true;
62
+ }
63
+ async function displayFixResult(result, log) {
64
+ log("");
65
+ if (result.finalPass) {
66
+ log("Fix complete — validation passed.");
67
+ }
68
+ else {
69
+ log("Fix applied but validation still failing.");
70
+ log(`Review report: ${result.reportPath}`);
71
+ if (result.backupDir) {
72
+ log(`Backup saved: ${result.backupDir}`);
73
+ }
74
+ }
75
+ const clack = await loadClack();
76
+ if (clack && process.stdout.isTTY) {
77
+ clack.outro(result.finalPass ? "All gates passed!" : "Review report and retry.");
78
+ }
79
+ }
@@ -0,0 +1,9 @@
1
+ type RunOptions = {
2
+ forcedCommand?: string;
3
+ cwd?: string;
4
+ argv?: string[];
5
+ log?: (message: string) => void;
6
+ error?: (message: string) => void;
7
+ };
8
+ export declare function runCli(options?: RunOptions): Promise<number>;
9
+ export {};