@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.
- 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/agents.js +4 -2
- package/dist/artifacts.d.ts +1 -0
- package/dist/artifacts.js +265 -31
- 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/cli.js +80 -3
- 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/init-tui.d.ts +3 -0
- package/dist/init-tui.js +28 -1
- package/dist/init.d.ts +1 -0
- package/dist/init.js +9 -3
- package/dist/normalize.js +55 -7
- 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 +3 -2
- package/dist/settings.d.ts +1 -0
- package/dist/settings.js +2 -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/dist/templates.d.ts +1 -1
- package/dist/templates.js +2 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +13 -0
- package/dist/validator.js +0 -4
- package/dist/workflow-commands.js +2 -1
- package/package.json +74 -45
- package/presets/fintech/preset.json +48 -1
- package/presets/fintech/prompts/prd.md +99 -2
- package/presets/marketplace/preset.json +51 -1
- package/presets/marketplace/prompts/prd.md +140 -2
- package/presets/saas/preset.json +53 -1
- package/presets/saas/prompts/prd.md +150 -2
- 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 -56
- 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 -319
- package/src/{init-tui.ts → cli/init-tui.ts} +208 -179
- package/src/{init.ts → cli/init.ts} +398 -391
- 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 -777
- 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/core/normalize.ts +145 -0
- 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 -34
- 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 -450
- 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 -50
- package/src/{validate.ts → core/validate.ts} +252 -246
- package/src/{validator.ts → core/validator.ts} +92 -96
- package/src/{version.ts → core/version.ts} +24 -24
- package/src/{workflow-commands.ts → core/workflow-commands.ts} +32 -31
- 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 -87
- 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
- package/templates/commands/prodo-fix.md +46 -0
- package/templates/commands/prodo-normalize.md +118 -23
- package/templates/commands/prodo-prd.md +138 -17
- package/templates/commands/prodo-stories.md +153 -17
- package/templates/commands/prodo-techspec.md +167 -17
- package/templates/commands/prodo-validate.md +184 -26
- package/templates/commands/prodo-wireframe.md +188 -17
- package/templates/commands/prodo-workflow.md +200 -17
- 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>;
|