@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,63 @@
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.t = t;
7
+ exports.loadTranslations = loadTranslations;
8
+ exports.availableLanguages = availableLanguages;
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const cache = new Map();
12
+ function loadJson(lang) {
13
+ if (cache.has(lang))
14
+ return cache.get(lang);
15
+ const filePath = node_path_1.default.resolve(__dirname, `${lang}.json`);
16
+ try {
17
+ const raw = node_fs_1.default.readFileSync(filePath, "utf8");
18
+ const parsed = JSON.parse(raw);
19
+ cache.set(lang, parsed);
20
+ return parsed;
21
+ }
22
+ catch {
23
+ cache.set(lang, {});
24
+ return {};
25
+ }
26
+ }
27
+ function normalizeLang(lang) {
28
+ if (!lang)
29
+ return "en";
30
+ const code = lang.toLowerCase().trim();
31
+ if (code.startsWith("tr"))
32
+ return "tr";
33
+ if (code.startsWith("en"))
34
+ return "en";
35
+ return code;
36
+ }
37
+ function t(key, lang) {
38
+ const normalized = normalizeLang(lang);
39
+ const translations = loadJson(normalized);
40
+ if (key in translations)
41
+ return translations[key];
42
+ if (normalized !== "en") {
43
+ const fallback = loadJson("en");
44
+ if (key in fallback)
45
+ return fallback[key];
46
+ }
47
+ return key;
48
+ }
49
+ function loadTranslations(lang) {
50
+ return { ...loadJson(normalizeLang(lang)) };
51
+ }
52
+ function availableLanguages() {
53
+ const dir = node_path_1.default.resolve(__dirname);
54
+ try {
55
+ return node_fs_1.default.readdirSync(dir)
56
+ .filter((f) => f.endsWith(".json"))
57
+ .map((f) => f.replace(".json", ""))
58
+ .sort();
59
+ }
60
+ catch {
61
+ return ["en"];
62
+ }
63
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "user_action": "Kullanıcı işlemi",
3
+ "main_flow": "Ana Akış",
4
+ "user": "Kullanıcı",
5
+ "success": "Başarı",
6
+ "error": "Hata",
7
+ "flow_focus": "Akış Odağı",
8
+ "initial_version": "İlk sürüm",
9
+ "fix_revision": "Doğrulama sonrası düzeltme revizyonu",
10
+ "primary_user": "Birincil kullanıcı",
11
+ "back": "Geri",
12
+ "next": "Devam",
13
+ "save": "Kaydet",
14
+ "content": "İçerik",
15
+ "primary_info_area": "Birincil bilgi alanı",
16
+ "status_indicator": "Durum göstergesi",
17
+ "form": "Form",
18
+ "field": "Alan",
19
+ "description": "Açıklama",
20
+ "contract": "Kontrat",
21
+ "actor": "Aktör",
22
+ "detailed_input_area": "Detaylı Giriş Alanı",
23
+ "upload_area": "Dosya Alanı",
24
+ "low_fidelity_wireframe": "Düşük sadakatli wireframe.",
25
+ "confirmation_text": "Onay metni",
26
+ "header_and_navigation": "Başlık ve gezinme",
27
+ "content_section": "İçerik bölümü",
28
+ "form_section": "Form bölümü",
29
+ "text_input": "Metin alanı",
30
+ "to_be_refined": "Detay daha sonra netleştirilecek.",
31
+ "note": "Not",
32
+ "requirement_item": "Gereksinim maddesi",
33
+ "contract_coverage": "Kontrat kapsamı",
34
+ "screen": "Ekran",
35
+ "for_artifact": "için",
36
+ "fix_proposal": "Düzeltme Önerisi",
37
+ "fix_complete": "Düzeltme tamamlandı — doğrulama geçti.",
38
+ "fix_still_failing": "Düzeltme uygulandı ama doğrulama hâlâ başarısız.",
39
+ "fix_cancelled": "Düzeltme iptal edildi.",
40
+ "no_issues": "Engel teşkil eden sorun bulunamadı. Düzeltilecek bir şey yok.",
41
+ "issues_found": "Bulunan sorunlar",
42
+ "artifacts_to_regenerate": "Yeniden oluşturulacak artefaktlar",
43
+ "general": "Genel",
44
+ "suggestion_prefix": "→"
45
+ }
@@ -3,12 +3,14 @@ export type InitSelections = {
3
3
  ai?: SupportedAi;
4
4
  script: "sh" | "ps";
5
5
  lang: "tr" | "en";
6
+ author: string;
6
7
  interactive: boolean;
7
8
  };
8
9
  type GatherInitUiOptions = {
9
10
  projectRoot: string;
10
11
  aiInput?: string;
11
12
  langInput?: string;
13
+ authorInput?: string;
12
14
  };
13
15
  export declare function gatherInitSelections(options: GatherInitUiOptions): Promise<InitSelections>;
14
16
  export declare function finishInitInteractive(summary: {
@@ -16,5 +18,6 @@ export declare function finishInitInteractive(summary: {
16
18
  settingsPath: string;
17
19
  ai?: SupportedAi;
18
20
  lang: "tr" | "en";
21
+ author: string;
19
22
  }): Promise<void>;
20
23
  export {};
package/dist/init-tui.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.gatherInitSelections = gatherInitSelections;
7
7
  exports.finishInitInteractive = finishInitInteractive;
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
+ const node_os_1 = __importDefault(require("node:os"));
9
10
  const agent_command_installer_1 = require("./agent-command-installer");
10
11
  const errors_1 = require("./errors");
11
12
  const utils_1 = require("./utils");
@@ -75,16 +76,32 @@ function modelChoiceFromAi(ai) {
75
76
  return "gemini";
76
77
  return "auto-detect";
77
78
  }
79
+ function defaultAuthorName(authorInput) {
80
+ const explicit = (authorInput ?? "").trim();
81
+ if (explicit.length > 0)
82
+ return explicit;
83
+ try {
84
+ const username = node_os_1.default.userInfo().username.trim();
85
+ if (username.length > 0)
86
+ return username;
87
+ }
88
+ catch {
89
+ // ignore lookup errors and continue with fallback
90
+ }
91
+ return "Product Author";
92
+ }
78
93
  async function gatherInitSelections(options) {
79
94
  const clack = await loadClack();
80
95
  const defaultLang = normalizeLang(options.langInput);
81
96
  const fallbackScript = process.platform === "win32" ? "ps" : "sh";
82
97
  const parsedAi = (0, agent_command_installer_1.resolveAi)(options.aiInput);
98
+ const defaultAuthor = defaultAuthorName(options.authorInput);
83
99
  if (!isInteractiveTerminal()) {
84
100
  return {
85
101
  ai: parsedAi,
86
102
  script: fallbackScript,
87
103
  lang: defaultLang,
104
+ author: defaultAuthor,
88
105
  interactive: false
89
106
  };
90
107
  }
@@ -129,6 +146,15 @@ async function gatherInitSelections(options) {
129
146
  clack.cancel("Initialization cancelled.");
130
147
  throw new errors_1.UserError("Initialization cancelled.");
131
148
  }
149
+ const author = await clack.text({
150
+ message: "Author name",
151
+ placeholder: "Shahmarasy",
152
+ defaultValue: defaultAuthor
153
+ });
154
+ if (clack.isCancel(author)) {
155
+ clack.cancel("Initialization cancelled.");
156
+ throw new errors_1.UserError("Initialization cancelled.");
157
+ }
132
158
  let selectedAi;
133
159
  if (model === "codex")
134
160
  selectedAi = "codex";
@@ -140,10 +166,11 @@ async function gatherInitSelections(options) {
140
166
  ai: selectedAi,
141
167
  script: fallbackScript,
142
168
  lang,
169
+ author: String(author).trim() || defaultAuthor,
143
170
  interactive: true
144
171
  };
145
172
  }
146
173
  function finishInitInteractive(summary) {
147
174
  const aiText = summary.ai ?? "none";
148
- return loadClack().then((clack) => clack.outro(`Scaffold complete.\nAI: ${aiText}\nLanguage: ${summary.lang}\nSettings: ${summary.settingsPath}\nNext: edit brief.md`));
175
+ return loadClack().then((clack) => clack.outro(`Scaffold complete.\nAI: ${aiText}\nLanguage: ${summary.lang}\nAuthor: ${summary.author}\nSettings: ${summary.settingsPath}\nNext: edit brief.md`));
149
176
  }
package/dist/init.d.ts CHANGED
@@ -2,6 +2,7 @@ import { type SupportedAi } from "./agent-command-installer";
2
2
  export declare function runInit(cwd: string, options?: {
3
3
  ai?: SupportedAi;
4
4
  lang?: string;
5
+ author?: string;
5
6
  preset?: string;
6
7
  script?: "sh" | "ps";
7
8
  }): Promise<{
package/dist/init.js CHANGED
@@ -15,6 +15,7 @@ const paths_1 = require("./paths");
15
15
  const preset_loader_1 = require("./preset-loader");
16
16
  const registry_1 = require("./registry");
17
17
  const settings_1 = require("./settings");
18
+ const template_resolver_1 = require("./template-resolver");
18
19
  const workflow_commands_1 = require("./workflow-commands");
19
20
  const templates_1 = require("./templates");
20
21
  function templateFileName(artifactType) {
@@ -272,12 +273,16 @@ async function runInit(cwd, options) {
272
273
  await writeFileIfMissing(node_path_1.default.join(root, "hooks.yml"), templates_1.HOOKS_TEMPLATE);
273
274
  await writeFileIfMissing(node_path_1.default.join(root, "prompts", "normalize.md"), `${templates_1.NORMALIZE_PROMPT_TEMPLATE}\n`);
274
275
  const scriptType = options?.script ?? (process.platform === "win32" ? "ps" : "sh");
275
- await promises_1.default.writeFile(node_path_1.default.join(root, "init-options.json"), `${JSON.stringify({ ai: options?.ai ?? null, lang: options?.lang ?? "en", preset: options?.preset ?? null, script: scriptType }, null, 2)}\n`, "utf8");
276
+ await promises_1.default.writeFile(node_path_1.default.join(root, "init-options.json"), `${JSON.stringify({ ai: options?.ai ?? null, lang: options?.lang ?? "en", author: options?.author ?? null, preset: options?.preset ?? null, script: scriptType }, null, 2)}\n`, "utf8");
276
277
  await copyDirIfMissing(node_path_1.default.join(projectScaffoldTemplates, "artifacts"), node_path_1.default.join(root, "templates"), copiedAssets);
277
278
  for (const artifact of artifactDefs) {
279
+ const markdownTemplatePath = node_path_1.default.join(root, "templates", `${artifact.name}.md`);
280
+ const templateHeadings = (await (0, utils_1.fileExists)(markdownTemplatePath))
281
+ ? (0, template_resolver_1.extractRequiredHeadingsFromTemplate)(await promises_1.default.readFile(markdownTemplatePath, "utf8"))
282
+ : [];
278
283
  const schema = {
279
284
  ...(0, templates_1.schemaTemplate)(artifact.name),
280
- x_required_headings: artifact.required_headings
285
+ x_required_headings: templateHeadings.length > 0 ? templateHeadings : artifact.required_headings
281
286
  };
282
287
  await writeFileIfMissing(node_path_1.default.join(root, "schemas", `${artifact.name}.yaml`), js_yaml_1.default.dump(schema));
283
288
  await writeFileIfMissing(node_path_1.default.join(root, "prompts", `${artifact.name}.md`), `${(0, templates_1.promptTemplate)(artifact.name, options?.lang ?? "en")}\n`);
@@ -322,7 +327,8 @@ async function runInit(cwd, options) {
322
327
  await (0, registry_1.syncRegistry)(cwd);
323
328
  const settingsPath = await (0, settings_1.writeSettings)(cwd, {
324
329
  lang: (options?.lang ?? "en").trim() || "en",
325
- ai: options?.ai
330
+ ai: options?.ai,
331
+ author: (options?.author ?? "").trim() || undefined
326
332
  });
327
333
  return { installedAgentFiles, settingsPath };
328
334
  }
package/dist/normalize.js CHANGED
@@ -12,6 +12,53 @@ const paths_1 = require("./paths");
12
12
  const providers_1 = require("./providers");
13
13
  const settings_1 = require("./settings");
14
14
  const utils_1 = require("./utils");
15
+ function normalizedKey(value) {
16
+ return value
17
+ .normalize("NFD")
18
+ .replace(/[\u0300-\u036f]/g, "")
19
+ .replace(/ı/g, "i")
20
+ .replace(/İ/g, "I")
21
+ .toLowerCase()
22
+ .replace(/[^a-z0-9]+/g, " ")
23
+ .trim();
24
+ }
25
+ function extractBriefProductName(rawBrief) {
26
+ const lines = rawBrief.split(/\r?\n/);
27
+ for (let index = 0; index < lines.length; index += 1) {
28
+ const headingMatch = lines[index].match(/^\s*#{1,6}\s+(.+?)\s*$/);
29
+ if (!headingMatch)
30
+ continue;
31
+ const headingKey = normalizedKey(headingMatch[1]);
32
+ const isProductHeading = headingKey === "product name" ||
33
+ headingKey === "project name" ||
34
+ headingKey === "urun adi" ||
35
+ headingKey === "urun ismi";
36
+ if (!isProductHeading)
37
+ continue;
38
+ for (let cursor = index + 1; cursor < lines.length; cursor += 1) {
39
+ const rawLine = lines[cursor].trim();
40
+ if (!rawLine)
41
+ continue;
42
+ if (/^\s*#{1,6}\s+/.test(rawLine))
43
+ break;
44
+ const cleaned = rawLine.replace(/^\s*[-*]\s*/, "").trim();
45
+ if (cleaned.length > 0)
46
+ return cleaned;
47
+ }
48
+ }
49
+ return undefined;
50
+ }
51
+ function preserveOriginalProductName(parsed, rawBrief) {
52
+ const briefProductName = extractBriefProductName(rawBrief);
53
+ if (!briefProductName)
54
+ return parsed;
55
+ const generated = typeof parsed.product_name === "string" ? parsed.product_name : "";
56
+ if (!generated.trim())
57
+ return { ...parsed, product_name: briefProductName };
58
+ if (normalizedKey(generated) !== normalizedKey(briefProductName))
59
+ return parsed;
60
+ return { ...parsed, product_name: briefProductName };
61
+ }
15
62
  function extractJsonObject(raw) {
16
63
  const trimmed = raw.trim();
17
64
  const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
@@ -48,16 +95,17 @@ async function runNormalize(options) {
48
95
  requiredContracts: []
49
96
  });
50
97
  const parsed = extractJsonObject(generated.body);
98
+ const preserved = preserveOriginalProductName(parsed, rawBrief);
51
99
  const withContracts = {
52
- ...parsed,
53
- contracts: parsed.contracts ??
100
+ ...preserved,
101
+ contracts: preserved.contracts ??
54
102
  (0, normalized_brief_1.buildContractsFromArrays)({
55
- goals: Array.isArray(parsed.goals) ? parsed.goals.filter((x) => typeof x === "string") : [],
56
- core_features: Array.isArray(parsed.core_features)
57
- ? parsed.core_features.filter((x) => typeof x === "string")
103
+ goals: Array.isArray(preserved.goals) ? preserved.goals.filter((x) => typeof x === "string") : [],
104
+ core_features: Array.isArray(preserved.core_features)
105
+ ? preserved.core_features.filter((x) => typeof x === "string")
58
106
  : [],
59
- constraints: Array.isArray(parsed.constraints)
60
- ? parsed.constraints.filter((x) => typeof x === "string")
107
+ constraints: Array.isArray(preserved.constraints)
108
+ ? preserved.constraints.filter((x) => typeof x === "string")
61
109
  : []
62
110
  })
63
111
  };
@@ -1,2 +1,3 @@
1
- import type { LLMProvider } from "../types";
1
+ import type { LLMProvider } from "../core/types";
2
2
  export declare function createProvider(): LLMProvider;
3
+ export { getGlobalRegistry } from "../agents/agent-registry";
@@ -1,12 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getGlobalRegistry = void 0;
3
4
  exports.createProvider = createProvider;
4
- const mock_provider_1 = require("./mock-provider");
5
- const openai_provider_1 = require("./openai-provider");
5
+ const agent_registry_1 = require("../agents/agent-registry");
6
+ let cachedProvider = null;
7
+ let cachedAgentName = null;
6
8
  function createProvider() {
7
- const provider = (process.env.PRODO_LLM_PROVIDER ?? "mock").toLowerCase();
8
- if (provider === "openai") {
9
- return new openai_provider_1.OpenAIProvider();
9
+ const agentName = process.env.PRODO_AGENT ??
10
+ process.env.PRODO_LLM_PROVIDER ??
11
+ "mock";
12
+ if (cachedProvider && cachedAgentName === agentName) {
13
+ return cachedProvider;
10
14
  }
11
- return new mock_provider_1.MockProvider();
15
+ const registry = (0, agent_registry_1.getGlobalRegistry)();
16
+ const agent = registry.get(agentName);
17
+ if (!agent) {
18
+ const available = registry.list().map((a) => a.name).join(", ");
19
+ throw new Error(`Unknown agent: "${agentName}". Available: ${available}`);
20
+ }
21
+ cachedProvider = registry.toProvider(agent);
22
+ cachedAgentName = agentName;
23
+ return cachedProvider;
12
24
  }
25
+ var agent_registry_2 = require("../agents/agent-registry");
26
+ Object.defineProperty(exports, "getGlobalRegistry", { enumerable: true, get: function () { return agent_registry_2.getGlobalRegistry; } });
@@ -1,4 +1,4 @@
1
- import type { LLMProvider, ProviderSchemaHint } from "../types";
1
+ import type { LLMProvider, ProviderSchemaHint } from "../core/types";
2
2
  export declare class MockProvider implements LLMProvider {
3
3
  generate(_prompt: string, inputContext: Record<string, unknown>, schemaHint: ProviderSchemaHint): Promise<{
4
4
  body: string;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MockProvider = void 0;
4
+ const i18n_1 = require("../i18n");
4
5
  function asStringArray(value) {
5
6
  if (!Array.isArray(value))
6
7
  return [];
@@ -107,18 +108,18 @@ function buildArtifactBody(schemaHint, inputContext) {
107
108
  const lang = typeof inputContext.outputLanguage === "string" ? inputContext.outputLanguage.toLowerCase() : "en";
108
109
  const items = normalizeSectionItems(inputContext);
109
110
  const coverage = coverageItems(schemaHint, inputContext);
110
- const localizedItems = lang === "tr" ? items.map((_, index) => `Gereksinim maddesi ${index + 1}`) : items;
111
+ const localizedItems = lang === "tr" ? items.map((_, index) => `${(0, i18n_1.t)("requirement_item", lang)} ${index + 1}`) : items;
111
112
  const localizedCoverage = lang === "tr"
112
113
  ? coverage.map((item, index) => ({
113
114
  id: item.id,
114
- text: `Kontrat kapsami ${index + 1}`
115
+ text: `${(0, i18n_1.t)("contract_coverage", lang)} ${index + 1}`
115
116
  }))
116
117
  : coverage;
117
- const fallback = lang === "tr" ? "Detay daha sonra netlestirilecek." : "To be refined.";
118
+ const fallback = (0, i18n_1.t)("to_be_refined", lang);
118
119
  const sections = schemaHint.requiredHeadings.map((heading) => headingBlock(heading, localizedItems, fallback, localizedCoverage));
119
120
  const title = lang === "tr"
120
- ? `# ${productName} icin ${schemaHint.artifactType.toUpperCase()}`
121
- : `# ${schemaHint.artifactType.toUpperCase()} for ${productName}`;
121
+ ? `# ${productName} ${(0, i18n_1.t)("for_artifact", lang)} ${schemaHint.artifactType.toUpperCase()}`
122
+ : `# ${schemaHint.artifactType.toUpperCase()} ${(0, i18n_1.t)("for_artifact", lang)} ${productName}`;
122
123
  if (schemaHint.artifactType === "workflow") {
123
124
  return `${title}\n\n${sections.join("\n")}\n\n\`\`\`mermaid
124
125
  flowchart TD
@@ -127,7 +128,7 @@ flowchart TD
127
128
  C --> D[Done]
128
129
  \`\`\``.trim();
129
130
  }
130
- return `${title}\n\n${sections.join("\n")}\n\n${lang === "tr" ? "Not" : "Note"}: ${fallback}`.trim();
131
+ return `${title}\n\n${sections.join("\n")}\n\n${(0, i18n_1.t)("note", lang)}: ${fallback}`.trim();
131
132
  }
132
133
  function semanticIssuesWithMock(inputContext) {
133
134
  const pair = inputContext.pair;
@@ -1,4 +1,4 @@
1
- import type { LLMProvider, ProviderSchemaHint } from "../types";
1
+ import type { LLMProvider, ProviderSchemaHint } from "../core/types";
2
2
  export declare class OpenAIProvider implements LLMProvider {
3
3
  private readonly apiKey;
4
4
  private readonly model;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OpenAIProvider = void 0;
4
- const errors_1 = require("../errors");
4
+ const errors_1 = require("../core/errors");
5
5
  class OpenAIProvider {
6
6
  apiKey;
7
7
  model;
@@ -22,7 +22,8 @@ class OpenAIProvider {
22
22
  const mode = schemaHint.artifactType;
23
23
  const system = mode === "normalize"
24
24
  ? `You normalize messy human product briefs into strict JSON.
25
- Return valid JSON only, no markdown. Include confidence scores (0..1) for critical fields.`
25
+ Return valid JSON only, no markdown. Include confidence scores (0..1) for critical fields.
26
+ Preserve source language and Unicode characters exactly; never transliterate Turkish letters to ASCII.`
26
27
  : mode === "semantic_consistency"
27
28
  ? `You detect semantic inconsistencies between paired artifacts.
28
29
  Return valid JSON only: { "issues": [{level, code, check, contract_id, file, message, suggestion}] }.`
@@ -1,6 +1,7 @@
1
1
  export type ProdoSettings = {
2
2
  lang: string;
3
3
  ai?: string;
4
+ author?: string;
4
5
  };
5
6
  export declare function readSettings(cwd: string): Promise<ProdoSettings>;
6
7
  export declare function writeSettings(cwd: string, settings: ProdoSettings): Promise<string>;
package/dist/settings.js CHANGED
@@ -20,7 +20,8 @@ async function readSettings(cwd) {
20
20
  const parsed = JSON.parse(raw);
21
21
  return {
22
22
  lang: typeof parsed.lang === "string" && parsed.lang.trim() ? parsed.lang.trim() : "en",
23
- ai: typeof parsed.ai === "string" && parsed.ai.trim() ? parsed.ai.trim() : undefined
23
+ ai: typeof parsed.ai === "string" && parsed.ai.trim() ? parsed.ai.trim() : undefined,
24
+ author: typeof parsed.author === "string" && parsed.author.trim() ? parsed.author.trim() : undefined
24
25
  };
25
26
  }
26
27
  catch {
@@ -0,0 +1,10 @@
1
+ import type { Skill, SkillContext, SkillManifest } from "./types";
2
+ export type SkillEngine = {
3
+ register(skill: Skill): void;
4
+ getSkill(name: string): Skill | undefined;
5
+ listSkills(): SkillManifest[];
6
+ execute(name: string, context: SkillContext, inputs: Record<string, unknown>): Promise<Record<string, unknown>>;
7
+ };
8
+ export declare function createSkillEngine(): SkillEngine;
9
+ export declare function getGlobalSkillEngine(): SkillEngine;
10
+ export declare function resetGlobalSkillEngine(): void;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSkillEngine = createSkillEngine;
4
+ exports.getGlobalSkillEngine = getGlobalSkillEngine;
5
+ exports.resetGlobalSkillEngine = resetGlobalSkillEngine;
6
+ const errors_1 = require("../core/errors");
7
+ function validateInputs(skill, inputs) {
8
+ for (const input of skill.manifest.inputs) {
9
+ if (input.required && !(input.name in inputs)) {
10
+ throw new errors_1.UserError(`Skill "${skill.manifest.name}" requires input "${input.name}" (${input.description})`);
11
+ }
12
+ }
13
+ }
14
+ function createSkillEngine() {
15
+ const skills = new Map();
16
+ return {
17
+ register(skill) {
18
+ skills.set(skill.manifest.name, skill);
19
+ },
20
+ getSkill(name) {
21
+ return skills.get(name);
22
+ },
23
+ listSkills() {
24
+ return Array.from(skills.values()).map((s) => s.manifest);
25
+ },
26
+ async execute(name, context, inputs) {
27
+ const skill = skills.get(name);
28
+ if (!skill) {
29
+ const available = Array.from(skills.keys()).join(", ");
30
+ throw new errors_1.UserError(`Unknown skill: "${name}". Available: ${available}`);
31
+ }
32
+ validateInputs(skill, inputs);
33
+ return skill.execute(context, inputs);
34
+ }
35
+ };
36
+ }
37
+ let globalEngine = null;
38
+ function getGlobalSkillEngine() {
39
+ if (!globalEngine) {
40
+ globalEngine = createSkillEngine();
41
+ loadBuiltinSkills(globalEngine);
42
+ }
43
+ return globalEngine;
44
+ }
45
+ function resetGlobalSkillEngine() {
46
+ globalEngine = null;
47
+ }
48
+ function loadBuiltinSkills(engine) {
49
+ // Lazy-load to avoid circular dependencies
50
+ try {
51
+ const { normalizeSkill } = require("./normalize-skill");
52
+ engine.register(normalizeSkill);
53
+ }
54
+ catch { /* skill not available */ }
55
+ try {
56
+ const { validateSkill } = require("./validate-skill");
57
+ engine.register(validateSkill);
58
+ }
59
+ catch { /* skill not available */ }
60
+ try {
61
+ const { fixSkill } = require("./fix-skill");
62
+ engine.register(fixSkill);
63
+ }
64
+ catch { /* skill not available */ }
65
+ try {
66
+ const { generateArtifactSkill } = require("./generate-artifact-skill");
67
+ engine.register(generateArtifactSkill);
68
+ }
69
+ catch { /* skill not available */ }
70
+ try {
71
+ const { generatePipelineSkill } = require("./generate-pipeline-skill");
72
+ engine.register(generatePipelineSkill);
73
+ }
74
+ catch { /* skill not available */ }
75
+ }
@@ -0,0 +1,2 @@
1
+ import type { Skill } from "./types";
2
+ export declare const fixSkill: Skill;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fixSkill = void 0;
4
+ const fix_1 = require("../core/fix");
5
+ exports.fixSkill = {
6
+ manifest: {
7
+ name: "fix",
8
+ description: "Auto-regenerate artifacts that failed validation with backup and rollback",
9
+ category: "validation",
10
+ inputs: [
11
+ { name: "cwd", type: "path", required: true, description: "Project working directory" },
12
+ { name: "agent", type: "string", required: false, description: "Agent profile name" },
13
+ { name: "strict", type: "boolean", required: false, description: "Treat warnings as errors" },
14
+ { name: "dryRun", type: "boolean", required: false, description: "Preview without applying" }
15
+ ],
16
+ outputs: [
17
+ { name: "applied", type: "string", description: "Whether fix was applied" },
18
+ { name: "finalPass", type: "string", description: "Whether validation passed after fix" },
19
+ { name: "reportPath", type: "path", description: "Path to validation report" }
20
+ ]
21
+ },
22
+ async execute(context, inputs) {
23
+ const cwd = inputs.cwd ?? context.cwd;
24
+ const result = await (0, fix_1.runFix)({
25
+ cwd,
26
+ agent: inputs.agent ?? context.agent,
27
+ strict: Boolean(inputs.strict),
28
+ dryRun: Boolean(inputs.dryRun),
29
+ log: context.log
30
+ });
31
+ return {
32
+ applied: result.applied,
33
+ finalPass: result.finalPass,
34
+ reportPath: result.reportPath,
35
+ targetCount: result.proposal.targets.length
36
+ };
37
+ }
38
+ };
@@ -0,0 +1,2 @@
1
+ import type { Skill } from "./types";
2
+ export declare const generateArtifactSkill: Skill;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateArtifactSkill = void 0;
4
+ const artifacts_1 = require("../core/artifacts");
5
+ exports.generateArtifactSkill = {
6
+ manifest: {
7
+ name: "generate-artifact",
8
+ description: "Generate a single artifact (PRD, workflow, wireframe, stories, techspec)",
9
+ category: "artifact",
10
+ inputs: [
11
+ { name: "cwd", type: "path", required: true, description: "Project working directory" },
12
+ { name: "artifactType", type: "string", required: true, description: "Artifact type to generate" },
13
+ { name: "from", type: "path", required: false, description: "Override path to normalized-brief.json" },
14
+ { name: "out", type: "path", required: false, description: "Override output path" }
15
+ ],
16
+ outputs: [
17
+ { name: "filePath", type: "path", description: "Path to generated artifact" }
18
+ ]
19
+ },
20
+ async execute(context, inputs) {
21
+ const cwd = inputs.cwd ?? context.cwd;
22
+ const filePath = await (0, artifacts_1.generateArtifact)({
23
+ artifactType: inputs.artifactType,
24
+ cwd,
25
+ normalizedBriefOverride: inputs.from,
26
+ outPath: inputs.out,
27
+ agent: context.agent
28
+ });
29
+ context.log(`${inputs.artifactType.toUpperCase()} generated: ${filePath}`);
30
+ return { filePath };
31
+ }
32
+ };
@@ -0,0 +1,2 @@
1
+ import type { Skill } from "./types";
2
+ export declare const generatePipelineSkill: Skill;