@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
@@ -1,179 +1,208 @@
1
- import path from "node:path";
2
- import { resolveAi, type SupportedAi } from "./agent-command-installer";
3
- import { UserError } from "./errors";
4
- import { fileExists } from "./utils";
5
-
6
- export type InitSelections = {
7
- ai?: SupportedAi;
8
- script: "sh" | "ps";
9
- lang: "tr" | "en";
10
- interactive: boolean;
11
- };
12
-
13
- type GatherInitUiOptions = {
14
- projectRoot: string;
15
- aiInput?: string;
16
- langInput?: string;
17
- };
18
-
19
- type ClackPrompts = typeof import("@clack/prompts");
20
-
21
- const dynamicImport = new Function("specifier", "return import(specifier)") as (
22
- specifier: string
23
- ) => Promise<unknown>;
24
-
25
- async function loadClack(): Promise<ClackPrompts> {
26
- return (await dynamicImport("@clack/prompts")) as ClackPrompts;
27
- }
28
-
29
- function isInteractiveTerminal(): boolean {
30
- return Boolean(process.stdin.isTTY && process.stdout.isTTY && !process.env.CI);
31
- }
32
-
33
- function color(text: string, code: string): string {
34
- return `${code}${text}\u001B[0m`;
35
- }
36
-
37
- function stripAnsi(input: string): string {
38
- return input.replace(/\u001B\[[0-9;]*m/g, "");
39
- }
40
-
41
- function centerLine(line: string, width: number): string {
42
- const visible = stripAnsi(line).length;
43
- const left = Math.max(0, Math.floor((width - visible) / 2));
44
- return `${" ".repeat(left)}${line}`;
45
- }
46
-
47
- function centerBlock(lines: string[], width: number): string {
48
- return lines.map((line) => centerLine(line, width)).join("\n");
49
- }
50
-
51
- function terminalWidth(): number {
52
- const columns = process.stdout.columns ?? 100;
53
- return Math.max(80, columns);
54
- }
55
-
56
- function renderLogo(): string {
57
- const cyan = "\u001B[38;5;45m";
58
- const blue = "\u001B[38;5;39m";
59
- const lines = [
60
- "██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ",
61
- "██╔══██╗██╔══██╗██╔═══██╗██╔══██╗██╔═══██╗",
62
- "██████╔╝██████╔╝██║ ██║██║ ██║██║ ██║",
63
- "██╔═══╝ ██╔══██╗██║ ██║██║ ██║██║ ██║",
64
- "██║ ██║ ██║╚██████╔╝██████╔╝╚██████╔╝"
65
- ];
66
- return lines.map((line, idx) => color(line, idx % 2 === 0 ? cyan : blue)).join("\n");
67
- }
68
-
69
- function renderProjectBox(projectName: string, projectRoot: string): string {
70
- const content = [`Project ${projectName}`, `Directory ${projectRoot}`];
71
- const innerWidth = Math.max(...content.map((line) => line.length)) + 2;
72
- const top = `┌${"─".repeat(innerWidth)}┐`;
73
- const rows = content.map((line) => `│ ${line.padEnd(innerWidth - 1)}│`);
74
- const bottom = `└${"─".repeat(innerWidth)}┘`;
75
- return [top, ...rows, bottom].join("\n");
76
- }
77
-
78
- async function detectAi(projectRoot: string): Promise<SupportedAi | undefined> {
79
- if (await fileExists(path.join(projectRoot, ".agents"))) return "codex";
80
- if (await fileExists(path.join(projectRoot, ".gemini"))) return "gemini-cli";
81
- if (await fileExists(path.join(projectRoot, ".claude"))) return "claude-cli";
82
- return undefined;
83
- }
84
-
85
- function normalizeLang(lang?: string): "tr" | "en" {
86
- if ((lang ?? "").trim().toLowerCase().startsWith("tr")) return "tr";
87
- return "en";
88
- }
89
-
90
- function modelChoiceFromAi(ai?: SupportedAi): "codex" | "gemini" | "auto-detect" {
91
- if (ai === "codex") return "codex";
92
- if (ai === "gemini-cli") return "gemini";
93
- return "auto-detect";
94
- }
95
-
96
- export async function gatherInitSelections(options: GatherInitUiOptions): Promise<InitSelections> {
97
- const clack = await loadClack();
98
- const defaultLang = normalizeLang(options.langInput);
99
- const fallbackScript: "sh" | "ps" = process.platform === "win32" ? "ps" : "sh";
100
- const parsedAi = resolveAi(options.aiInput);
101
-
102
- if (!isInteractiveTerminal()) {
103
- return {
104
- ai: parsedAi,
105
- script: fallbackScript,
106
- lang: defaultLang,
107
- interactive: false
108
- };
109
- }
110
-
111
- const detectedAi = await detectAi(options.projectRoot);
112
- const initialModel = modelChoiceFromAi(parsedAi ?? detectedAi);
113
- const projectName = path.basename(options.projectRoot) || ".";
114
- const width = terminalWidth();
115
- const subtitle = color("Prodo — Product Artifact Toolkit", "\u001B[1;37m");
116
- const signature = color("Crafted by Codex, guided by Shahmarasy intelligence", "\u001B[38;5;244m");
117
- const hero = [
118
- "",
119
- centerBlock(renderLogo().split("\n"), width),
120
- "",
121
- centerLine(subtitle, width),
122
- centerLine(signature, width),
123
- ""
124
- ].join("\n");
125
-
126
- clack.intro(hero);
127
- clack.note(renderProjectBox(projectName, options.projectRoot), "Project Setup");
128
-
129
- const model = await clack.select({
130
- message: "Select model",
131
- initialValue: initialModel,
132
- options: [
133
- { value: "codex", label: "codex", hint: "Native Codex flow" },
134
- { value: "gemini", label: "gemini", hint: "Gemini CLI command set" },
135
- { value: "auto-detect", label: "auto-detect", hint: "Detect from local agent directories" }
136
- ]
137
- });
138
- if (clack.isCancel(model)) {
139
- clack.cancel("Initialization cancelled.");
140
- throw new UserError("Initialization cancelled.");
141
- }
142
-
143
- const lang = await clack.select({
144
- message: "Select language",
145
- initialValue: defaultLang,
146
- options: [
147
- { value: "tr", label: "tr", hint: "Turkish" },
148
- { value: "en", label: "en", hint: "English" }
149
- ]
150
- });
151
- if (clack.isCancel(lang)) {
152
- clack.cancel("Initialization cancelled.");
153
- throw new UserError("Initialization cancelled.");
154
- }
155
-
156
- let selectedAi: SupportedAi | undefined;
157
- if (model === "codex") selectedAi = "codex";
158
- else if (model === "gemini") selectedAi = "gemini-cli";
159
- else selectedAi = detectedAi;
160
-
161
- return {
162
- ai: selectedAi,
163
- script: fallbackScript,
164
- lang,
165
- interactive: true
166
- };
167
- }
168
-
169
- export function finishInitInteractive(summary: {
170
- projectRoot: string;
171
- settingsPath: string;
172
- ai?: SupportedAi;
173
- lang: "tr" | "en";
174
- }): Promise<void> {
175
- const aiText = summary.ai ?? "none";
176
- return loadClack().then((clack) => clack.outro(
177
- `Scaffold complete.\nAI: ${aiText}\nLanguage: ${summary.lang}\nSettings: ${summary.settingsPath}\nNext: edit brief.md`
178
- ));
179
- }
1
+ import path from "node:path";
2
+ import os from "node:os";
3
+ import { resolveAi, type SupportedAi } from "./agent-command-installer";
4
+ import { UserError } from "../core/errors";
5
+ import { fileExists } from "../core/utils";
6
+
7
+ export type InitSelections = {
8
+ ai?: SupportedAi;
9
+ script: "sh" | "ps";
10
+ lang: "tr" | "en";
11
+ author: string;
12
+ interactive: boolean;
13
+ };
14
+
15
+ type GatherInitUiOptions = {
16
+ projectRoot: string;
17
+ aiInput?: string;
18
+ langInput?: string;
19
+ authorInput?: string;
20
+ };
21
+
22
+ type ClackPrompts = typeof import("@clack/prompts");
23
+
24
+ const dynamicImport = new Function("specifier", "return import(specifier)") as (
25
+ specifier: string
26
+ ) => Promise<unknown>;
27
+
28
+ async function loadClack(): Promise<ClackPrompts> {
29
+ return (await dynamicImport("@clack/prompts")) as ClackPrompts;
30
+ }
31
+
32
+ function isInteractiveTerminal(): boolean {
33
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY && !process.env.CI);
34
+ }
35
+
36
+ function color(text: string, code: string): string {
37
+ return `${code}${text}\u001B[0m`;
38
+ }
39
+
40
+ function stripAnsi(input: string): string {
41
+ return input.replace(/\u001B\[[0-9;]*m/g, "");
42
+ }
43
+
44
+ function centerLine(line: string, width: number): string {
45
+ const visible = stripAnsi(line).length;
46
+ const left = Math.max(0, Math.floor((width - visible) / 2));
47
+ return `${" ".repeat(left)}${line}`;
48
+ }
49
+
50
+ function centerBlock(lines: string[], width: number): string {
51
+ return lines.map((line) => centerLine(line, width)).join("\n");
52
+ }
53
+
54
+ function terminalWidth(): number {
55
+ const columns = process.stdout.columns ?? 100;
56
+ return Math.max(80, columns);
57
+ }
58
+
59
+ function renderLogo(): string {
60
+ const cyan = "\u001B[38;5;45m";
61
+ const blue = "\u001B[38;5;39m";
62
+ const lines = [
63
+ "██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ",
64
+ "██╔══██╗██╔══██╗██╔═══██╗██╔══██╗██╔═══██╗",
65
+ "██████╔╝██████╔╝██║ ██║██║ ██║██║ ██║",
66
+ "██╔═══╝ ██╔══██╗██║ ██║██║ ██║██║ ██║",
67
+ "██║ ██║ ██║╚██████╔╝██████╔╝╚██████╔╝"
68
+ ];
69
+ return lines.map((line, idx) => color(line, idx % 2 === 0 ? cyan : blue)).join("\n");
70
+ }
71
+
72
+ function renderProjectBox(projectName: string, projectRoot: string): string {
73
+ const content = [`Project ${projectName}`, `Directory ${projectRoot}`];
74
+ const innerWidth = Math.max(...content.map((line) => line.length)) + 2;
75
+ const top = `┌${"─".repeat(innerWidth)}┐`;
76
+ const rows = content.map((line) => `│ ${line.padEnd(innerWidth - 1)}│`);
77
+ const bottom = `└${"─".repeat(innerWidth)}┘`;
78
+ return [top, ...rows, bottom].join("\n");
79
+ }
80
+
81
+ async function detectAi(projectRoot: string): Promise<SupportedAi | undefined> {
82
+ if (await fileExists(path.join(projectRoot, ".agents"))) return "codex";
83
+ if (await fileExists(path.join(projectRoot, ".gemini"))) return "gemini-cli";
84
+ if (await fileExists(path.join(projectRoot, ".claude"))) return "claude-cli";
85
+ return undefined;
86
+ }
87
+
88
+ function normalizeLang(lang?: string): "tr" | "en" {
89
+ if ((lang ?? "").trim().toLowerCase().startsWith("tr")) return "tr";
90
+ return "en";
91
+ }
92
+
93
+ function modelChoiceFromAi(ai?: SupportedAi): "codex" | "gemini" | "auto-detect" {
94
+ if (ai === "codex") return "codex";
95
+ if (ai === "gemini-cli") return "gemini";
96
+ return "auto-detect";
97
+ }
98
+
99
+ function defaultAuthorName(authorInput?: string): string {
100
+ const explicit = (authorInput ?? "").trim();
101
+ if (explicit.length > 0) return explicit;
102
+ try {
103
+ const username = os.userInfo().username.trim();
104
+ if (username.length > 0) return username;
105
+ } catch {
106
+ // ignore lookup errors and continue with fallback
107
+ }
108
+ return "Product Author";
109
+ }
110
+
111
+ export async function gatherInitSelections(options: GatherInitUiOptions): Promise<InitSelections> {
112
+ const clack = await loadClack();
113
+ const defaultLang = normalizeLang(options.langInput);
114
+ const fallbackScript: "sh" | "ps" = process.platform === "win32" ? "ps" : "sh";
115
+ const parsedAi = resolveAi(options.aiInput);
116
+ const defaultAuthor = defaultAuthorName(options.authorInput);
117
+
118
+ if (!isInteractiveTerminal()) {
119
+ return {
120
+ ai: parsedAi,
121
+ script: fallbackScript,
122
+ lang: defaultLang,
123
+ author: defaultAuthor,
124
+ interactive: false
125
+ };
126
+ }
127
+
128
+ const detectedAi = await detectAi(options.projectRoot);
129
+ const initialModel = modelChoiceFromAi(parsedAi ?? detectedAi);
130
+ const projectName = path.basename(options.projectRoot) || ".";
131
+ const width = terminalWidth();
132
+ const subtitle = color("Prodo — Product Artifact Toolkit", "\u001B[1;37m");
133
+ const signature = color("Crafted by Codex, guided by Shahmarasy intelligence", "\u001B[38;5;244m");
134
+ const hero = [
135
+ "",
136
+ centerBlock(renderLogo().split("\n"), width),
137
+ "",
138
+ centerLine(subtitle, width),
139
+ centerLine(signature, width),
140
+ ""
141
+ ].join("\n");
142
+
143
+ clack.intro(hero);
144
+ clack.note(renderProjectBox(projectName, options.projectRoot), "Project Setup");
145
+
146
+ const model = await clack.select({
147
+ message: "Select model",
148
+ initialValue: initialModel,
149
+ options: [
150
+ { value: "codex", label: "codex", hint: "Native Codex flow" },
151
+ { value: "gemini", label: "gemini", hint: "Gemini CLI command set" },
152
+ { value: "auto-detect", label: "auto-detect", hint: "Detect from local agent directories" }
153
+ ]
154
+ });
155
+ if (clack.isCancel(model)) {
156
+ clack.cancel("Initialization cancelled.");
157
+ throw new UserError("Initialization cancelled.");
158
+ }
159
+
160
+ const lang = await clack.select({
161
+ message: "Select language",
162
+ initialValue: defaultLang,
163
+ options: [
164
+ { value: "tr", label: "tr", hint: "Turkish" },
165
+ { value: "en", label: "en", hint: "English" }
166
+ ]
167
+ });
168
+ if (clack.isCancel(lang)) {
169
+ clack.cancel("Initialization cancelled.");
170
+ throw new UserError("Initialization cancelled.");
171
+ }
172
+
173
+ const author = await clack.text({
174
+ message: "Author name",
175
+ placeholder: "Shahmarasy",
176
+ defaultValue: defaultAuthor
177
+ });
178
+ if (clack.isCancel(author)) {
179
+ clack.cancel("Initialization cancelled.");
180
+ throw new UserError("Initialization cancelled.");
181
+ }
182
+
183
+ let selectedAi: SupportedAi | undefined;
184
+ if (model === "codex") selectedAi = "codex";
185
+ else if (model === "gemini") selectedAi = "gemini-cli";
186
+ else selectedAi = detectedAi;
187
+
188
+ return {
189
+ ai: selectedAi,
190
+ script: fallbackScript,
191
+ lang,
192
+ author: String(author).trim() || defaultAuthor,
193
+ interactive: true
194
+ };
195
+ }
196
+
197
+ export function finishInitInteractive(summary: {
198
+ projectRoot: string;
199
+ settingsPath: string;
200
+ ai?: SupportedAi;
201
+ lang: "tr" | "en";
202
+ author: string;
203
+ }): Promise<void> {
204
+ const aiText = summary.ai ?? "none";
205
+ return loadClack().then((clack) => clack.outro(
206
+ `Scaffold complete.\nAI: ${aiText}\nLanguage: ${summary.lang}\nAuthor: ${summary.author}\nSettings: ${summary.settingsPath}\nNext: edit brief.md`
207
+ ));
208
+ }