@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,210 @@
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.applyConfiguredPresets = applyConfiguredPresets;
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const js_yaml_1 = __importDefault(require("js-yaml"));
10
+ const errors_1 = require("../core/errors");
11
+ const project_config_1 = require("../core/project-config");
12
+ const utils_1 = require("../core/utils");
13
+ function parseVersion(version) {
14
+ return version.split(".").map((part) => Number(part.replace(/[^0-9]/g, "")) || 0).slice(0, 3);
15
+ }
16
+ function cmpVersion(a, b) {
17
+ const left = parseVersion(a);
18
+ const right = parseVersion(b);
19
+ for (let i = 0; i < 3; i++) {
20
+ if ((left[i] ?? 0) > (right[i] ?? 0))
21
+ return 1;
22
+ if ((left[i] ?? 0) < (right[i] ?? 0))
23
+ return -1;
24
+ }
25
+ return 0;
26
+ }
27
+ async function readPresetManifest(presetDir) {
28
+ const candidates = ["preset.yaml", "preset.yml", "preset.json"];
29
+ for (const name of candidates) {
30
+ const file = node_path_1.default.join(presetDir, name);
31
+ if (!(await (0, utils_1.fileExists)(file)))
32
+ continue;
33
+ if (name.endsWith(".json")) {
34
+ const parsed = JSON.parse(await promises_1.default.readFile(file, "utf8"));
35
+ const presetName = typeof parsed.name === "string" ? parsed.name.trim() : node_path_1.default.basename(presetDir);
36
+ return {
37
+ name: presetName,
38
+ version: typeof parsed.version === "string" ? parsed.version : undefined,
39
+ priority: typeof parsed.priority === "number" ? parsed.priority : 0,
40
+ min_prodo_version: typeof parsed.min_prodo_version === "string" ? parsed.min_prodo_version : undefined,
41
+ max_prodo_version: typeof parsed.max_prodo_version === "string" ? parsed.max_prodo_version : undefined,
42
+ command_packs: Array.isArray(parsed.command_packs)
43
+ ? parsed.command_packs.filter((item) => typeof item === "string")
44
+ : []
45
+ };
46
+ }
47
+ const parsed = js_yaml_1.default.load(await promises_1.default.readFile(file, "utf8")) ?? {};
48
+ const presetName = typeof parsed.name === "string" ? parsed.name.trim() : node_path_1.default.basename(presetDir);
49
+ return {
50
+ name: presetName,
51
+ version: typeof parsed.version === "string" ? parsed.version : undefined,
52
+ priority: typeof parsed.priority === "number" ? parsed.priority : 0,
53
+ min_prodo_version: typeof parsed.min_prodo_version === "string" ? parsed.min_prodo_version : undefined,
54
+ max_prodo_version: typeof parsed.max_prodo_version === "string" ? parsed.max_prodo_version : undefined,
55
+ command_packs: Array.isArray(parsed.command_packs)
56
+ ? parsed.command_packs.filter((item) => typeof item === "string")
57
+ : []
58
+ };
59
+ }
60
+ throw new errors_1.UserError(`Preset manifest is missing in ${presetDir} (expected preset.yaml or preset.json).`);
61
+ }
62
+ async function collectFilesRecursive(rootDir) {
63
+ if (!(await (0, utils_1.fileExists)(rootDir)))
64
+ return [];
65
+ const out = [];
66
+ const walk = async (current) => {
67
+ const entries = await promises_1.default.readdir(current, { withFileTypes: true });
68
+ for (const entry of entries) {
69
+ const full = node_path_1.default.join(current, entry.name);
70
+ if (entry.isDirectory()) {
71
+ await walk(full);
72
+ }
73
+ else {
74
+ out.push(full);
75
+ }
76
+ }
77
+ };
78
+ await walk(rootDir);
79
+ return out;
80
+ }
81
+ async function collectPresetOps(presetDir, prodoRoot, priority, order) {
82
+ const lanes = ["prompts", "schemas", "templates", "commands"];
83
+ const ops = [];
84
+ for (const lane of lanes) {
85
+ const sourceBase = node_path_1.default.join(presetDir, lane);
86
+ const files = await collectFilesRecursive(sourceBase);
87
+ for (const source of files) {
88
+ const relative = node_path_1.default.relative(sourceBase, source);
89
+ ops.push({
90
+ source,
91
+ target: node_path_1.default.join(prodoRoot, lane, relative),
92
+ priority,
93
+ order
94
+ });
95
+ }
96
+ }
97
+ return ops;
98
+ }
99
+ async function resolvePresetDir(projectRoot, presetName) {
100
+ const candidates = [
101
+ node_path_1.default.join(projectRoot, "presets", presetName),
102
+ node_path_1.default.resolve(__dirname, "..", "..", "presets", presetName)
103
+ ];
104
+ for (const candidate of candidates) {
105
+ if (await (0, utils_1.fileExists)(candidate))
106
+ return candidate;
107
+ }
108
+ throw new errors_1.UserError(`Preset not found: ${presetName}. Create presets/${presetName} with a preset manifest.`);
109
+ }
110
+ async function writeInstalledPresets(prodoRoot, names) {
111
+ const file = node_path_1.default.join(prodoRoot, "presets", "installed.json");
112
+ await (0, utils_1.ensureDir)(node_path_1.default.dirname(file));
113
+ await promises_1.default.writeFile(file, `${JSON.stringify(Array.from(new Set(names)).sort(), null, 2)}\n`, "utf8");
114
+ }
115
+ async function readInstalledPresets(prodoRoot) {
116
+ const file = node_path_1.default.join(prodoRoot, "presets", "installed.json");
117
+ if (!(await (0, utils_1.fileExists)(file)))
118
+ return [];
119
+ try {
120
+ const parsed = JSON.parse(await promises_1.default.readFile(file, "utf8"));
121
+ if (!Array.isArray(parsed))
122
+ return [];
123
+ return parsed
124
+ .filter((item) => typeof item === "string")
125
+ .map((item) => item.trim())
126
+ .filter((item) => item.length > 0);
127
+ }
128
+ catch {
129
+ return [];
130
+ }
131
+ }
132
+ async function applyCopyOps(ops) {
133
+ const selected = new Map();
134
+ for (const op of ops) {
135
+ const current = selected.get(op.target);
136
+ if (!current) {
137
+ selected.set(op.target, op);
138
+ continue;
139
+ }
140
+ if (op.priority > current.priority || (op.priority === current.priority && op.order > current.order)) {
141
+ selected.set(op.target, op);
142
+ }
143
+ }
144
+ for (const op of selected.values()) {
145
+ await (0, utils_1.ensureDir)(node_path_1.default.dirname(op.target));
146
+ await promises_1.default.copyFile(op.source, op.target);
147
+ }
148
+ }
149
+ async function collectCommandPackOps(projectRoot, prodoRoot, names) {
150
+ const ops = [];
151
+ for (const [index, name] of names.entries()) {
152
+ const base = node_path_1.default.join(projectRoot, "command-packs", name);
153
+ if (!(await (0, utils_1.fileExists)(base))) {
154
+ throw new errors_1.UserError(`Command pack not found: command-packs/${name}`);
155
+ }
156
+ const laneMap = {
157
+ commands: "commands"
158
+ };
159
+ for (const [sourceLane, targetLane] of Object.entries(laneMap)) {
160
+ const sourceBase = node_path_1.default.join(base, sourceLane);
161
+ const files = await collectFilesRecursive(sourceBase);
162
+ for (const source of files) {
163
+ const relative = node_path_1.default.relative(sourceBase, source);
164
+ ops.push({
165
+ source,
166
+ target: node_path_1.default.join(prodoRoot, targetLane, relative),
167
+ priority: 100,
168
+ order: index
169
+ });
170
+ }
171
+ }
172
+ }
173
+ return ops;
174
+ }
175
+ async function applyConfiguredPresets(projectRoot, prodoRoot, prodoVersion, presetOverride) {
176
+ const config = await (0, project_config_1.readProjectConfig)(projectRoot);
177
+ const presets = Array.from(new Set([...(config.presets ?? []), ...(presetOverride ? [presetOverride] : [])]));
178
+ const existingInstalled = await readInstalledPresets(prodoRoot);
179
+ const allOps = [];
180
+ const installedNames = [...existingInstalled];
181
+ const commandPacks = new Set(config.command_packs ?? []);
182
+ for (const [order, presetName] of presets.entries()) {
183
+ const presetDir = await resolvePresetDir(projectRoot, presetName);
184
+ const manifest = await readPresetManifest(presetDir);
185
+ if (manifest.min_prodo_version && cmpVersion(prodoVersion, manifest.min_prodo_version) < 0) {
186
+ throw new errors_1.UserError(`Preset ${presetName} requires prodo >= ${manifest.min_prodo_version}, current is ${prodoVersion}.`);
187
+ }
188
+ if (manifest.max_prodo_version && cmpVersion(prodoVersion, manifest.max_prodo_version) > 0) {
189
+ throw new errors_1.UserError(`Preset ${presetName} supports prodo <= ${manifest.max_prodo_version}, current is ${prodoVersion}.`);
190
+ }
191
+ for (const pack of manifest.command_packs ?? []) {
192
+ if (pack.trim())
193
+ commandPacks.add(pack.trim());
194
+ }
195
+ installedNames.push(manifest.name || presetName);
196
+ allOps.push(...(await collectPresetOps(presetDir, prodoRoot, manifest.priority ?? 0, order)));
197
+ }
198
+ const commandPackList = Array.from(commandPacks);
199
+ if (commandPackList.length > 0) {
200
+ const commandPackOps = await collectCommandPackOps(projectRoot, prodoRoot, commandPackList);
201
+ allOps.push(...commandPackOps);
202
+ }
203
+ if (allOps.length > 0)
204
+ await applyCopyOps(allOps);
205
+ await writeInstalledPresets(prodoRoot, installedNames);
206
+ return {
207
+ installedPresets: Array.from(new Set(installedNames)),
208
+ appliedFiles: Array.from(new Set(allOps.map((item) => item.target)))
209
+ };
210
+ }
package/dist/cli.js CHANGED
@@ -29,6 +29,8 @@ function mapForcedCommand(forcedCommand) {
29
29
  return "validate";
30
30
  if (forcedCommand === "prodo-normalize")
31
31
  return "normalize";
32
+ if (forcedCommand === "prodo-fix")
33
+ return "fix";
32
34
  if (forcedCommand === "prodo-prd")
33
35
  return "prd";
34
36
  if (forcedCommand === "prodo-workflow")
@@ -49,7 +51,8 @@ async function runArtifactCommand(type, opts, cwd, log, options) {
49
51
  cwd,
50
52
  normalizedBriefOverride: opts.from,
51
53
  outPath: opts.out,
52
- agent
54
+ agent,
55
+ revisionType: opts.revisionType
53
56
  });
54
57
  const agentMsg = agent ? ` [agent=${agent}]` : "";
55
58
  log(`${type.toUpperCase()} generated${agentMsg}: ${file}`);
@@ -100,13 +103,15 @@ async function runCli(options = {}) {
100
103
  .command("init [target]")
101
104
  .option("--ai <name>", "agent integration: codex | gemini-cli | claude-cli")
102
105
  .option("--lang <code>", "document language (e.g. en, tr)")
106
+ .option("--author <name>", "document author name")
103
107
  .option("--preset <name>", "preset to install during initialization")
104
108
  .action(async (target, opts) => {
105
109
  const projectRoot = node_path_1.default.resolve(cwd, target ?? ".");
106
110
  const selected = await (0, init_tui_1.gatherInitSelections)({
107
111
  projectRoot,
108
112
  aiInput: opts.ai,
109
- langInput: opts.lang
113
+ langInput: opts.lang,
114
+ authorInput: opts.author
110
115
  });
111
116
  const selectedAi = selected.ai;
112
117
  if (selected.interactive) {
@@ -116,6 +121,7 @@ async function runCli(options = {}) {
116
121
  const result = await (0, init_1.runInit)(projectRoot, {
117
122
  ai: selectedAi,
118
123
  lang: selected.lang,
124
+ author: selected.author,
119
125
  preset: opts.preset,
120
126
  script: selected.script
121
127
  });
@@ -124,13 +130,15 @@ async function runCli(options = {}) {
124
130
  projectRoot,
125
131
  settingsPath: result.settingsPath,
126
132
  ai: selectedAi,
127
- lang: selected.lang
133
+ lang: selected.lang,
134
+ author: selected.author
128
135
  });
129
136
  return;
130
137
  }
131
138
  const result = await (0, init_1.runInit)(projectRoot, {
132
139
  ai: selectedAi,
133
140
  lang: selected.lang,
141
+ author: selected.author,
134
142
  preset: opts.preset,
135
143
  script: selected.script
136
144
  });
@@ -144,6 +152,7 @@ async function runCli(options = {}) {
144
152
  out("No agent selected. Use `prodo generate` for end-to-end generation.");
145
153
  }
146
154
  out(`Settings file: ${result.settingsPath}`);
155
+ out(`Author: ${selected.author}`);
147
156
  out("Next: edit brief.md.");
148
157
  });
149
158
  program
@@ -178,6 +187,51 @@ async function runCli(options = {}) {
178
187
  await (0, hook_executor_1.runHookPhase)(cwd, "after_validate", out);
179
188
  });
180
189
  });
190
+ program
191
+ .command("fix", { hidden: true })
192
+ .description("Advanced: auto-regenerate affected artifacts from validation findings")
193
+ .option("--agent <name>", "agent profile: codex | gemini-cli | claude-cli")
194
+ .option("--strict", "treat validation warnings as errors")
195
+ .option("--report <path>", "validation report output path")
196
+ .action(async (opts) => {
197
+ if (opts.agent)
198
+ (0, agents_1.resolveAgent)(opts.agent);
199
+ await withBriefReadOnlyGuard(cwd, async () => {
200
+ await (0, hook_executor_1.runHookPhase)(cwd, "before_validate", out);
201
+ const initial = await (0, validate_1.runValidate)(cwd, {
202
+ strict: Boolean(opts.strict),
203
+ report: opts.report
204
+ });
205
+ out(`Validation report written to: ${initial.reportPath}`);
206
+ await (0, hook_executor_1.runHookPhase)(cwd, "after_validate", out);
207
+ if (initial.pass) {
208
+ out("No blocking issues found. Nothing to fix.");
209
+ return;
210
+ }
211
+ const targets = await resolveFixTargets(cwd, artifactTypes, initial.issues);
212
+ out(`Validation failed. Regenerating impacted artifacts: ${targets.join(", ")}`);
213
+ await (0, hook_executor_1.runHookPhase)(cwd, "before_normalize", out);
214
+ const normalizedPath = await (0, normalize_1.runNormalize)({ cwd });
215
+ out(`Normalized brief refreshed: ${normalizedPath}`);
216
+ await (0, hook_executor_1.runHookPhase)(cwd, "after_normalize", out);
217
+ for (const type of targets) {
218
+ await runArtifactCommand(type, { from: normalizedPath, agent: opts.agent, revisionType: "fix" }, cwd, out, {
219
+ suggestValidate: false
220
+ });
221
+ }
222
+ await (0, hook_executor_1.runHookPhase)(cwd, "before_validate", out);
223
+ const final = await (0, validate_1.runValidate)(cwd, {
224
+ strict: Boolean(opts.strict),
225
+ report: opts.report
226
+ });
227
+ out(`Validation report written to: ${final.reportPath}`);
228
+ if (!final.pass) {
229
+ throw new errors_1.UserError("Fix completed but validation is still failing. Review report and retry.");
230
+ }
231
+ out("Fix pipeline completed. Validation passed.");
232
+ await (0, hook_executor_1.runHookPhase)(cwd, "after_validate", out);
233
+ });
234
+ });
181
235
  program
182
236
  .command("normalize", { hidden: true })
183
237
  .description("Advanced: normalize brief without full pipeline")
@@ -276,6 +330,9 @@ async function runCli(options = {}) {
276
330
  else if (forced === "validate") {
277
331
  await program.parseAsync(["node", "prodo", "validate", ...argv.slice(2)]);
278
332
  }
333
+ else if (forced === "fix") {
334
+ await program.parseAsync(["node", "prodo", "fix", ...argv.slice(2)]);
335
+ }
279
336
  else {
280
337
  await program.parseAsync(["node", "prodo", forced, ...argv.slice(2)]);
281
338
  }
@@ -300,3 +357,23 @@ if (require.main === module) {
300
357
  process.exitCode = code;
301
358
  });
302
359
  }
360
+ async function resolveFixTargets(cwd, artifactTypes, issues) {
361
+ const direct = new Set(issues
362
+ .map((issue) => issue.artifactType)
363
+ .filter((artifactType) => typeof artifactType === "string" && artifactTypes.includes(artifactType)));
364
+ if (direct.size === 0)
365
+ return artifactTypes;
366
+ const defs = await (0, artifact_registry_1.listArtifactDefinitions)(cwd);
367
+ let changed = true;
368
+ while (changed) {
369
+ changed = false;
370
+ for (const def of defs) {
371
+ const needsRefresh = def.upstream.some((upstream) => direct.has(upstream));
372
+ if (needsRefresh && !direct.has(def.name)) {
373
+ direct.add(def.name);
374
+ changed = true;
375
+ }
376
+ }
377
+ }
378
+ return artifactTypes.filter((artifactType) => direct.has(artifactType));
379
+ }
@@ -0,0 +1,11 @@
1
+ import type { ArtifactType, ContractCoverage } from "./types";
2
+ export type ArtifactDefinition = {
3
+ name: ArtifactType;
4
+ output_dir: string;
5
+ required_headings: string[];
6
+ upstream: ArtifactType[];
7
+ required_contracts: Array<keyof ContractCoverage>;
8
+ };
9
+ export declare function listArtifactDefinitions(cwd: string): Promise<ArtifactDefinition[]>;
10
+ export declare function listArtifactTypes(cwd: string): Promise<ArtifactType[]>;
11
+ export declare function getArtifactDefinition(cwd: string, artifactType: string): Promise<ArtifactDefinition>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listArtifactDefinitions = listArtifactDefinitions;
4
+ exports.listArtifactTypes = listArtifactTypes;
5
+ exports.getArtifactDefinition = getArtifactDefinition;
6
+ const constants_1 = require("./constants");
7
+ const project_config_1 = require("./project-config");
8
+ const types_1 = require("./types");
9
+ function normalizeName(name) {
10
+ return name.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-");
11
+ }
12
+ function toDefinition(partial) {
13
+ const name = normalizeName(partial.name);
14
+ const outputDir = partial.output_dir?.trim() || (0, constants_1.defaultOutputDir)(name);
15
+ const requiredHeadings = partial.required_headings?.length ? partial.required_headings : (0, constants_1.defaultRequiredHeadings)(name);
16
+ const upstream = (partial.upstream?.length ? partial.upstream : (0, constants_1.defaultUpstreamByArtifact)(name)).map(normalizeName);
17
+ const requiredContracts = partial.required_contracts?.length
18
+ ? partial.required_contracts
19
+ : (0, constants_1.defaultRequiredContractsByArtifact)(name);
20
+ return {
21
+ name,
22
+ output_dir: outputDir,
23
+ required_headings: requiredHeadings,
24
+ upstream,
25
+ required_contracts: requiredContracts
26
+ };
27
+ }
28
+ async function listArtifactDefinitions(cwd) {
29
+ const config = await (0, project_config_1.readProjectConfig)(cwd);
30
+ const base = types_1.ARTIFACT_TYPES.map((name) => toDefinition({ name }));
31
+ const byName = new Map(base.map((item) => [item.name, item]));
32
+ for (const extra of config.artifacts ?? []) {
33
+ const merged = toDefinition(extra);
34
+ byName.set(merged.name, merged);
35
+ }
36
+ return Array.from(byName.values());
37
+ }
38
+ async function listArtifactTypes(cwd) {
39
+ const defs = await listArtifactDefinitions(cwd);
40
+ return defs.map((item) => item.name);
41
+ }
42
+ async function getArtifactDefinition(cwd, artifactType) {
43
+ const normalized = normalizeName(artifactType);
44
+ const defs = await listArtifactDefinitions(cwd);
45
+ const found = defs.find((item) => item.name === normalized);
46
+ if (found)
47
+ return found;
48
+ return toDefinition({ name: normalized });
49
+ }
@@ -0,0 +1,10 @@
1
+ import type { ArtifactType } from "./types";
2
+ export type GenerateOptions = {
3
+ artifactType: ArtifactType;
4
+ cwd: string;
5
+ normalizedBriefOverride?: string;
6
+ outPath?: string;
7
+ agent?: string;
8
+ revisionType?: "default" | "fix";
9
+ };
10
+ export declare function generateArtifact(options: GenerateOptions): Promise<string>;