@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,208 +1,208 @@
|
|
|
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 "
|
|
5
|
-
import { fileExists } from "
|
|
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
|
-
}
|
|
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
|
+
}
|