@shahmarasy/prodo 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -97
- package/bin/prodo.cjs +6 -6
- package/dist/agents/agent-registry.d.ts +13 -0
- package/dist/agents/agent-registry.js +79 -0
- package/dist/agents/anthropic/index.d.ts +9 -0
- package/dist/agents/anthropic/index.js +55 -0
- package/dist/agents/base.d.ts +25 -0
- package/dist/agents/base.js +71 -0
- package/dist/agents/google/index.d.ts +9 -0
- package/dist/agents/google/index.js +53 -0
- package/dist/agents/mock/index.d.ts +11 -0
- package/dist/agents/mock/index.js +26 -0
- package/dist/agents/openai/index.d.ts +9 -0
- package/dist/agents/openai/index.js +57 -0
- package/dist/agents/system-prompts.d.ts +3 -0
- package/dist/agents/system-prompts.js +32 -0
- package/dist/cli/agent-command-installer.d.ts +4 -0
- package/dist/cli/agent-command-installer.js +148 -0
- package/dist/cli/agent-ids.d.ts +15 -0
- package/dist/cli/agent-ids.js +49 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +144 -0
- package/dist/cli/fix-tui.d.ts +4 -0
- package/dist/cli/fix-tui.js +79 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +467 -0
- package/dist/cli/init-tui.d.ts +23 -0
- package/dist/cli/init-tui.js +183 -0
- package/dist/cli/init.d.ts +12 -0
- package/dist/cli/init.js +335 -0
- package/dist/cli/normalize-interactive.d.ts +8 -0
- package/dist/cli/normalize-interactive.js +167 -0
- package/dist/cli/preset-loader.d.ts +4 -0
- package/dist/cli/preset-loader.js +210 -0
- package/dist/core/artifact-registry.d.ts +11 -0
- package/dist/core/artifact-registry.js +49 -0
- package/dist/core/artifacts.d.ts +10 -0
- package/dist/core/artifacts.js +892 -0
- package/dist/core/clean.d.ts +10 -0
- package/dist/core/clean.js +74 -0
- package/dist/core/consistency.d.ts +8 -0
- package/dist/core/consistency.js +328 -0
- package/dist/core/constants.d.ts +7 -0
- package/dist/core/constants.js +64 -0
- package/dist/core/errors.d.ts +3 -0
- package/dist/core/errors.js +10 -0
- package/dist/core/fix.d.ts +31 -0
- package/dist/core/fix.js +188 -0
- package/dist/core/hook-executor.d.ts +1 -0
- package/dist/core/hook-executor.js +175 -0
- package/dist/core/markdown.d.ts +16 -0
- package/dist/core/markdown.js +81 -0
- package/dist/core/normalize.d.ts +8 -0
- package/dist/core/normalize.js +125 -0
- package/dist/core/normalized-brief.d.ts +48 -0
- package/dist/core/normalized-brief.js +182 -0
- package/dist/core/output-index.d.ts +13 -0
- package/dist/core/output-index.js +55 -0
- package/dist/core/paths.d.ts +17 -0
- package/dist/core/paths.js +80 -0
- package/dist/core/project-config.d.ts +14 -0
- package/dist/core/project-config.js +69 -0
- package/dist/core/registry.d.ts +13 -0
- package/dist/core/registry.js +115 -0
- package/dist/core/settings.d.ts +8 -0
- package/dist/core/settings.js +43 -0
- package/dist/core/template-engine.d.ts +3 -0
- package/dist/core/template-engine.js +43 -0
- package/dist/core/template-resolver.d.ts +15 -0
- package/dist/core/template-resolver.js +46 -0
- package/dist/core/templates.d.ts +33 -0
- package/dist/core/templates.js +440 -0
- package/dist/core/terminology.d.ts +21 -0
- package/dist/core/terminology.js +143 -0
- package/dist/core/tracing.d.ts +21 -0
- package/dist/core/tracing.js +74 -0
- package/dist/core/types.d.ts +35 -0
- package/dist/core/types.js +5 -0
- package/dist/core/utils.d.ts +7 -0
- package/dist/core/utils.js +66 -0
- package/dist/core/validate.d.ts +10 -0
- package/dist/core/validate.js +226 -0
- package/dist/core/validator.d.ts +5 -0
- package/dist/core/validator.js +76 -0
- package/dist/core/version.d.ts +1 -0
- package/dist/core/version.js +30 -0
- package/dist/core/workflow-commands.d.ts +7 -0
- package/dist/core/workflow-commands.js +29 -0
- package/dist/i18n/en.json +45 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.js +63 -0
- package/dist/i18n/tr.json +45 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.js +20 -6
- package/dist/providers/mock-provider.d.ts +1 -1
- package/dist/providers/mock-provider.js +12 -11
- package/dist/providers/openai-provider.d.ts +1 -1
- package/dist/providers/openai-provider.js +13 -13
- package/dist/skill-engine/context.d.ts +7 -0
- package/dist/skill-engine/context.js +76 -0
- package/dist/skill-engine/discovery.d.ts +2 -0
- package/dist/skill-engine/discovery.js +52 -0
- package/dist/skill-engine/graph.d.ts +4 -0
- package/dist/skill-engine/graph.js +114 -0
- package/dist/skill-engine/index.d.ts +11 -0
- package/dist/skill-engine/index.js +49 -0
- package/dist/skill-engine/pipeline.d.ts +9 -0
- package/dist/skill-engine/pipeline.js +84 -0
- package/dist/skill-engine/registry.d.ts +12 -0
- package/dist/skill-engine/registry.js +74 -0
- package/dist/skill-engine/types.d.ts +66 -0
- package/dist/skill-engine/types.js +2 -0
- package/dist/skill-engine/validator.d.ts +4 -0
- package/dist/skill-engine/validator.js +90 -0
- 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/fix.d.ts +2 -0
- package/dist/skills/fix.js +41 -0
- package/dist/skills/generate-artifact-skill.d.ts +2 -0
- package/dist/skills/generate-artifact-skill.js +32 -0
- package/dist/skills/generate-artifact.d.ts +2 -0
- package/dist/skills/generate-artifact.js +42 -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/normalize.d.ts +2 -0
- package/dist/skills/normalize.js +29 -0
- package/dist/skills/register-core.d.ts +2 -0
- package/dist/skills/register-core.js +21 -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/skills/validate.d.ts +2 -0
- package/dist/skills/validate.js +37 -0
- package/package.json +72 -45
- package/src/agents/agent-registry.ts +93 -0
- package/src/agents/anthropic/index.ts +86 -0
- package/src/agents/anthropic/manifest.json +7 -0
- package/src/agents/base.ts +77 -0
- package/src/agents/google/index.ts +79 -0
- package/src/agents/google/manifest.json +7 -0
- package/src/agents/mock/index.ts +32 -0
- package/src/agents/mock/manifest.json +7 -0
- package/src/agents/openai/index.ts +83 -0
- package/src/agents/openai/manifest.json +7 -0
- package/src/agents/system-prompts.ts +35 -0
- package/src/{agent-command-installer.ts → cli/agent-command-installer.ts} +164 -164
- package/src/{agents.ts → cli/agent-ids.ts} +58 -58
- package/src/{doctor.ts → cli/doctor.ts} +157 -137
- package/src/cli/fix-tui.ts +111 -0
- package/src/{cli.ts → cli/index.ts} +463 -410
- package/src/{init-tui.ts → cli/init-tui.ts} +49 -37
- package/src/{init.ts → cli/init.ts} +399 -398
- package/src/cli/normalize-interactive.ts +241 -0
- package/src/{preset-loader.ts → cli/preset-loader.ts} +237 -237
- package/src/{artifact-registry.ts → core/artifact-registry.ts} +69 -69
- package/src/{artifacts.ts → core/artifacts.ts} +1081 -1072
- package/src/core/clean.ts +88 -0
- package/src/{consistency.ts → core/consistency.ts} +374 -303
- package/src/{constants.ts → core/constants.ts} +72 -72
- package/src/{errors.ts → core/errors.ts} +7 -7
- package/src/core/fix.ts +253 -0
- package/src/{hook-executor.ts → core/hook-executor.ts} +196 -196
- package/src/{markdown.ts → core/markdown.ts} +93 -73
- package/src/{normalize.ts → core/normalize.ts} +145 -137
- package/src/{normalized-brief.ts → core/normalized-brief.ts} +227 -206
- package/src/{output-index.ts → core/output-index.ts} +59 -59
- package/src/{paths.ts → core/paths.ts} +75 -71
- package/src/{project-config.ts → core/project-config.ts} +78 -78
- package/src/{registry.ts → core/registry.ts} +119 -119
- package/src/{settings.ts → core/settings.ts} +8 -2
- package/src/core/template-engine.ts +45 -0
- package/src/{template-resolver.ts → core/template-resolver.ts} +54 -54
- package/src/{templates.ts → core/templates.ts} +452 -452
- package/src/core/terminology.ts +177 -0
- package/src/core/tracing.ts +110 -0
- package/src/{types.ts → core/types.ts} +46 -46
- package/src/{utils.ts → core/utils.ts} +64 -64
- package/src/{validate.ts → core/validate.ts} +252 -246
- package/src/{validator.ts → core/validator.ts} +92 -92
- package/src/{version.ts → core/version.ts} +24 -24
- package/src/{workflow-commands.ts → core/workflow-commands.ts} +32 -32
- package/src/i18n/en.json +45 -0
- package/src/i18n/index.ts +58 -0
- package/src/i18n/tr.json +45 -0
- package/src/providers/index.ts +29 -12
- package/src/providers/mock-provider.ts +200 -199
- package/src/providers/openai-provider.ts +88 -88
- package/src/skill-engine/context.ts +90 -0
- package/src/skill-engine/discovery.ts +57 -0
- package/src/skill-engine/graph.ts +136 -0
- package/src/skill-engine/index.ts +55 -0
- package/src/skill-engine/pipeline.ts +112 -0
- package/src/skill-engine/registry.ts +75 -0
- package/src/skill-engine/types.ts +81 -0
- package/src/skill-engine/validator.ts +135 -0
- package/src/skills/fix.ts +45 -0
- package/src/skills/generate-artifact.ts +48 -0
- package/src/skills/normalize.ts +32 -0
- package/src/skills/register-core.ts +27 -0
- package/src/skills/validate.ts +40 -0
|
@@ -1,199 +1,200 @@
|
|
|
1
|
-
import type { BriefContractItem } from "../normalized-brief";
|
|
2
|
-
import type { LLMProvider, ProviderSchemaHint } from "../types";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
collect
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
if (schemaHint.requiredContracts.includes("
|
|
99
|
-
if (schemaHint.requiredContracts.includes("
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
.
|
|
182
|
-
.
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (schemaHint.artifactType === "
|
|
196
|
-
if (schemaHint.artifactType === "
|
|
197
|
-
return { body:
|
|
198
|
-
|
|
199
|
-
}
|
|
1
|
+
import type { BriefContractItem } from "../core/normalized-brief";
|
|
2
|
+
import type { LLMProvider, ProviderSchemaHint } from "../core/types";
|
|
3
|
+
import { t } from "../i18n";
|
|
4
|
+
|
|
5
|
+
function asStringArray(value: unknown): string[] {
|
|
6
|
+
if (!Array.isArray(value)) return [];
|
|
7
|
+
return value.filter((item): item is string => typeof item === "string");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function asContracts(value: unknown): BriefContractItem[] {
|
|
11
|
+
if (!Array.isArray(value)) return [];
|
|
12
|
+
return value
|
|
13
|
+
.map((item) => {
|
|
14
|
+
if (!item || typeof item !== "object") return null;
|
|
15
|
+
const record = item as Record<string, unknown>;
|
|
16
|
+
if (typeof record.id !== "string" || typeof record.text !== "string") return null;
|
|
17
|
+
return { id: record.id, text: record.text };
|
|
18
|
+
})
|
|
19
|
+
.filter((item): item is BriefContractItem => item !== null);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function extractValue(markdown: string, heading: string): string[] {
|
|
23
|
+
const aliases = heading.split("|").map((item) => item.trim().toLowerCase());
|
|
24
|
+
const lines = markdown.split(/\r?\n/);
|
|
25
|
+
let collect = false;
|
|
26
|
+
const out: string[] = [];
|
|
27
|
+
for (const raw of lines) {
|
|
28
|
+
const headingMatch = raw.match(/^#{1,6}\s+(.+?)\s*$/);
|
|
29
|
+
if (headingMatch) {
|
|
30
|
+
const current = headingMatch[1].trim().toLowerCase();
|
|
31
|
+
if (collect && !aliases.includes(current)) break;
|
|
32
|
+
collect = aliases.includes(current);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (!collect) continue;
|
|
36
|
+
const cleaned = raw.replace(/^\s*[-*]\s*/, "").trim();
|
|
37
|
+
if (cleaned.length > 0) out.push(cleaned);
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function normalizeWithMock(inputContext: Record<string, unknown>): string {
|
|
43
|
+
const brief = typeof inputContext.briefMarkdown === "string" ? inputContext.briefMarkdown : "";
|
|
44
|
+
const product = extractValue(brief, "Product Name|Name")[0] ?? "";
|
|
45
|
+
const problem = extractValue(brief, "Problem|Problem Statement").join(" ");
|
|
46
|
+
const audience = extractValue(brief, "Audience|Users|Persona");
|
|
47
|
+
const goals = extractValue(brief, "Goals|Objectives");
|
|
48
|
+
const features = extractValue(brief, "Core Features|Features|Capabilities");
|
|
49
|
+
const constraints = extractValue(brief, "Constraints|Limitations|Restrictions");
|
|
50
|
+
const assumptions = extractValue(brief, "Assumptions|Open Questions");
|
|
51
|
+
|
|
52
|
+
const contracts = {
|
|
53
|
+
goals: goals.map((text, index) => ({ id: `G${index + 1}`, text })),
|
|
54
|
+
core_features: features.map((text, index) => ({ id: `F${index + 1}`, text })),
|
|
55
|
+
constraints: constraints.map((text, index) => ({ id: `C${index + 1}`, text }))
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const confidence = {
|
|
59
|
+
product_name: product ? 0.95 : 0.25,
|
|
60
|
+
problem: problem ? 0.95 : 0.25,
|
|
61
|
+
audience: audience.length > 0 ? 0.95 : 0.25,
|
|
62
|
+
goals: goals.length > 0 ? 0.95 : 0.25,
|
|
63
|
+
core_features: features.length > 0 ? 0.95 : 0.25
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return JSON.stringify(
|
|
67
|
+
{
|
|
68
|
+
schema_version: "1.0",
|
|
69
|
+
product_name: product,
|
|
70
|
+
problem,
|
|
71
|
+
audience,
|
|
72
|
+
goals,
|
|
73
|
+
core_features: features,
|
|
74
|
+
constraints,
|
|
75
|
+
assumptions,
|
|
76
|
+
contracts,
|
|
77
|
+
confidence
|
|
78
|
+
},
|
|
79
|
+
null,
|
|
80
|
+
2
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeSectionItems(inputContext: Record<string, unknown>): string[] {
|
|
85
|
+
const normalizedBrief = (inputContext.normalizedBrief ?? {}) as Record<string, unknown>;
|
|
86
|
+
const goals = asStringArray(normalizedBrief.goals);
|
|
87
|
+
const features = asStringArray(normalizedBrief.core_features);
|
|
88
|
+
const constraints = asStringArray(normalizedBrief.constraints);
|
|
89
|
+
return [...goals, ...features, ...constraints];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function coverageItems(
|
|
93
|
+
schemaHint: ProviderSchemaHint,
|
|
94
|
+
inputContext: Record<string, unknown>
|
|
95
|
+
): Array<{ id: string; text: string }> {
|
|
96
|
+
const catalog = (inputContext.contractCatalog ?? {}) as Record<string, unknown>;
|
|
97
|
+
const values: Array<{ id: string; text: string }> = [];
|
|
98
|
+
if (schemaHint.requiredContracts.includes("goals")) values.push(...asContracts(catalog.goals));
|
|
99
|
+
if (schemaHint.requiredContracts.includes("core_features")) values.push(...asContracts(catalog.core_features));
|
|
100
|
+
if (schemaHint.requiredContracts.includes("constraints")) values.push(...asContracts(catalog.constraints));
|
|
101
|
+
return values;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function headingBlock(
|
|
105
|
+
heading: string,
|
|
106
|
+
items: string[],
|
|
107
|
+
fallbackText: string,
|
|
108
|
+
coverage: Array<{ id: string; text: string }>
|
|
109
|
+
): string {
|
|
110
|
+
const selected = items.slice(0, 2);
|
|
111
|
+
const contractBullets = coverage.map((item) => `- [${item.id}] ${item.text}`);
|
|
112
|
+
const bullets = [...contractBullets, ...selected.map((item) => `- ${item}`)];
|
|
113
|
+
const finalBullets = bullets.length > 0 ? bullets.join("\n") : `- ${fallbackText}`;
|
|
114
|
+
return `${heading}\n${finalBullets}\n`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function buildArtifactBody(schemaHint: ProviderSchemaHint, inputContext: Record<string, unknown>): string {
|
|
118
|
+
const normalizedBrief = (inputContext.normalizedBrief ?? {}) as Record<string, unknown>;
|
|
119
|
+
const productName =
|
|
120
|
+
typeof normalizedBrief.product_name === "string" ? normalizedBrief.product_name : "Product";
|
|
121
|
+
const lang = typeof inputContext.outputLanguage === "string" ? inputContext.outputLanguage.toLowerCase() : "en";
|
|
122
|
+
const items = normalizeSectionItems(inputContext);
|
|
123
|
+
const coverage = coverageItems(schemaHint, inputContext);
|
|
124
|
+
const localizedItems =
|
|
125
|
+
lang === "tr" ? items.map((_, index) => `${t("requirement_item", lang)} ${index + 1}`) : items;
|
|
126
|
+
const localizedCoverage =
|
|
127
|
+
lang === "tr"
|
|
128
|
+
? coverage.map((item, index) => ({
|
|
129
|
+
id: item.id,
|
|
130
|
+
text: `${t("contract_coverage", lang)} ${index + 1}`
|
|
131
|
+
}))
|
|
132
|
+
: coverage;
|
|
133
|
+
const fallback = t("to_be_refined", lang);
|
|
134
|
+
const sections = schemaHint.requiredHeadings.map((heading) =>
|
|
135
|
+
headingBlock(heading, localizedItems, fallback, localizedCoverage)
|
|
136
|
+
);
|
|
137
|
+
const title =
|
|
138
|
+
lang === "tr"
|
|
139
|
+
? `# ${productName} ${t("for_artifact", lang)} ${schemaHint.artifactType.toUpperCase()}`
|
|
140
|
+
: `# ${schemaHint.artifactType.toUpperCase()} ${t("for_artifact", lang)} ${productName}`;
|
|
141
|
+
if (schemaHint.artifactType === "workflow") {
|
|
142
|
+
return `${title}\n\n${sections.join("\n")}\n\n\`\`\`mermaid
|
|
143
|
+
flowchart TD
|
|
144
|
+
A[Start] --> B[[F1] User Action]
|
|
145
|
+
B --> C[System Step]
|
|
146
|
+
C --> D[Done]
|
|
147
|
+
\`\`\``.trim();
|
|
148
|
+
}
|
|
149
|
+
return `${title}\n\n${sections.join("\n")}\n\n${t("note", lang)}: ${fallback}`.trim();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function semanticIssuesWithMock(inputContext: Record<string, unknown>): string {
|
|
153
|
+
const pair = inputContext.pair as Record<string, unknown>;
|
|
154
|
+
const leftBody = typeof pair?.left_body === "string" ? pair.left_body : "";
|
|
155
|
+
const rightBody = typeof pair?.right_body === "string" ? pair.right_body : "";
|
|
156
|
+
const leftFile = typeof pair?.left_file === "string" ? pair.left_file : "";
|
|
157
|
+
const rightFile = typeof pair?.right_file === "string" ? pair.right_file : "";
|
|
158
|
+
const issues: Array<Record<string, unknown>> = [];
|
|
159
|
+
|
|
160
|
+
const contradiction =
|
|
161
|
+
/guest checkout/i.test(leftBody) && /(auth required|requires auth|must login)/i.test(rightBody);
|
|
162
|
+
if (contradiction) {
|
|
163
|
+
issues.push({
|
|
164
|
+
level: "error",
|
|
165
|
+
code: "semantic_contradiction",
|
|
166
|
+
check: "semantic_consistency",
|
|
167
|
+
contract_id: "F1",
|
|
168
|
+
file: rightFile || leftFile,
|
|
169
|
+
message: "Workflow allows guest checkout but paired artifact requires authentication.",
|
|
170
|
+
suggestion: "Align auth behavior across both artifacts."
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return JSON.stringify({ issues }, null, 2);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function contractRelevanceWithMock(inputContext: Record<string, unknown>): string {
|
|
178
|
+
const contractText = typeof inputContext.contract_text === "string" ? inputContext.contract_text.toLowerCase() : "";
|
|
179
|
+
const contextText = typeof inputContext.context_text === "string" ? inputContext.context_text.toLowerCase() : "";
|
|
180
|
+
const terms = contractText
|
|
181
|
+
.split(/\W+/)
|
|
182
|
+
.map((term) => term.trim())
|
|
183
|
+
.filter((term) => term.length > 3);
|
|
184
|
+
const overlap = terms.filter((term) => contextText.includes(term)).length;
|
|
185
|
+
const relevant = terms.length === 0 ? true : overlap / terms.length >= 0.25;
|
|
186
|
+
return JSON.stringify({ relevant, score: terms.length === 0 ? 1 : overlap / terms.length }, null, 2);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export class MockProvider implements LLMProvider {
|
|
190
|
+
async generate(
|
|
191
|
+
_prompt: string,
|
|
192
|
+
inputContext: Record<string, unknown>,
|
|
193
|
+
schemaHint: ProviderSchemaHint
|
|
194
|
+
): Promise<{ body: string; frontmatter?: Record<string, unknown> }> {
|
|
195
|
+
if (schemaHint.artifactType === "normalize") return { body: normalizeWithMock(inputContext) };
|
|
196
|
+
if (schemaHint.artifactType === "semantic_consistency") return { body: semanticIssuesWithMock(inputContext) };
|
|
197
|
+
if (schemaHint.artifactType === "contract_relevance") return { body: contractRelevanceWithMock(inputContext) };
|
|
198
|
+
return { body: buildArtifactBody(schemaHint, inputContext) };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
import type { LLMProvider, ProviderSchemaHint } from "../types";
|
|
2
|
-
import { UserError } from "../errors";
|
|
3
|
-
|
|
4
|
-
type ChatResponse = {
|
|
5
|
-
choices?: Array<{
|
|
6
|
-
message?: {
|
|
7
|
-
content?: string;
|
|
8
|
-
};
|
|
9
|
-
}>;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export class OpenAIProvider implements LLMProvider {
|
|
13
|
-
private readonly apiKey: string;
|
|
14
|
-
private readonly model: string;
|
|
15
|
-
private readonly baseUrl: string;
|
|
16
|
-
|
|
17
|
-
constructor() {
|
|
18
|
-
const key = process.env.OPENAI_API_KEY;
|
|
19
|
-
if (!key) {
|
|
20
|
-
throw new UserError("OPENAI_API_KEY missing. Set it or use PRODO_LLM_PROVIDER=mock.");
|
|
21
|
-
}
|
|
22
|
-
this.apiKey = key;
|
|
23
|
-
this.model = process.env.PRODO_OPENAI_MODEL ?? "gpt-4o-mini";
|
|
24
|
-
this.baseUrl = process.env.PRODO_OPENAI_BASE_URL ?? "https://api.openai.com/v1";
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async generate(
|
|
28
|
-
prompt: string,
|
|
29
|
-
inputContext: Record<string, unknown>,
|
|
30
|
-
schemaHint: ProviderSchemaHint
|
|
31
|
-
): Promise<{ body: string; frontmatter?: Record<string, unknown> }> {
|
|
32
|
-
const outputLanguage =
|
|
33
|
-
typeof inputContext.outputLanguage === "string" && inputContext.outputLanguage.trim()
|
|
34
|
-
? inputContext.outputLanguage.trim()
|
|
35
|
-
: "en";
|
|
36
|
-
const mode = schemaHint.artifactType;
|
|
37
|
-
const system =
|
|
38
|
-
mode === "normalize"
|
|
39
|
-
? `You normalize messy human product briefs into strict JSON.
|
|
40
|
-
Return valid JSON only, no markdown. Include confidence scores (0..1) for critical fields.
|
|
41
|
-
Preserve source language and Unicode characters exactly; never transliterate Turkish letters to ASCII.`
|
|
42
|
-
: mode === "semantic_consistency"
|
|
43
|
-
? `You detect semantic inconsistencies between paired artifacts.
|
|
44
|
-
Return valid JSON only: { "issues": [{level, code, check, contract_id, file, message, suggestion}] }.`
|
|
45
|
-
: mode === "contract_relevance"
|
|
46
|
-
? `You verify whether tagged content actually matches the referenced contract text.
|
|
47
|
-
Return valid JSON only: { "relevant": boolean, "score": number, "reason": string }.`
|
|
48
|
-
: `You are a product-document generator.
|
|
49
|
-
Return only Markdown body content.
|
|
50
|
-
Headings required:
|
|
51
|
-
${schemaHint.requiredHeadings.join("\n")}
|
|
52
|
-
Required contract tags:
|
|
53
|
-
${schemaHint.requiredContracts.join(", ")}
|
|
54
|
-
Use tags like [G1], [F2], [C1] where relevant.
|
|
55
|
-
Output language: ${outputLanguage}
|
|
56
|
-
Do not translate required headings.`;
|
|
57
|
-
const user = `${prompt}\n\nContext JSON:\n${JSON.stringify(inputContext, null, 2)}`;
|
|
58
|
-
|
|
59
|
-
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
60
|
-
method: "POST",
|
|
61
|
-
headers: {
|
|
62
|
-
"Content-Type": "application/json",
|
|
63
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
64
|
-
},
|
|
65
|
-
body: JSON.stringify({
|
|
66
|
-
model: this.model,
|
|
67
|
-
messages: [
|
|
68
|
-
{ role: "system", content: system },
|
|
69
|
-
{ role: "user", content: user }
|
|
70
|
-
],
|
|
71
|
-
temperature: 0.2
|
|
72
|
-
})
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
if (!response.ok) {
|
|
76
|
-
const text = await response.text();
|
|
77
|
-
throw new UserError(`OpenAI request failed (${response.status}): ${text}`);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const payload = (await response.json()) as ChatResponse;
|
|
81
|
-
const content = payload.choices?.[0]?.message?.content?.trim();
|
|
82
|
-
if (!content) {
|
|
83
|
-
throw new UserError("OpenAI provider returned an empty response.");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return { body: content };
|
|
87
|
-
}
|
|
88
|
-
}
|
|
1
|
+
import type { LLMProvider, ProviderSchemaHint } from "../core/types";
|
|
2
|
+
import { UserError } from "../core/errors";
|
|
3
|
+
|
|
4
|
+
type ChatResponse = {
|
|
5
|
+
choices?: Array<{
|
|
6
|
+
message?: {
|
|
7
|
+
content?: string;
|
|
8
|
+
};
|
|
9
|
+
}>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export class OpenAIProvider implements LLMProvider {
|
|
13
|
+
private readonly apiKey: string;
|
|
14
|
+
private readonly model: string;
|
|
15
|
+
private readonly baseUrl: string;
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
const key = process.env.OPENAI_API_KEY;
|
|
19
|
+
if (!key) {
|
|
20
|
+
throw new UserError("OPENAI_API_KEY missing. Set it or use PRODO_LLM_PROVIDER=mock.");
|
|
21
|
+
}
|
|
22
|
+
this.apiKey = key;
|
|
23
|
+
this.model = process.env.PRODO_OPENAI_MODEL ?? "gpt-4o-mini";
|
|
24
|
+
this.baseUrl = process.env.PRODO_OPENAI_BASE_URL ?? "https://api.openai.com/v1";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async generate(
|
|
28
|
+
prompt: string,
|
|
29
|
+
inputContext: Record<string, unknown>,
|
|
30
|
+
schemaHint: ProviderSchemaHint
|
|
31
|
+
): Promise<{ body: string; frontmatter?: Record<string, unknown> }> {
|
|
32
|
+
const outputLanguage =
|
|
33
|
+
typeof inputContext.outputLanguage === "string" && inputContext.outputLanguage.trim()
|
|
34
|
+
? inputContext.outputLanguage.trim()
|
|
35
|
+
: "en";
|
|
36
|
+
const mode = schemaHint.artifactType;
|
|
37
|
+
const system =
|
|
38
|
+
mode === "normalize"
|
|
39
|
+
? `You normalize messy human product briefs into strict JSON.
|
|
40
|
+
Return valid JSON only, no markdown. Include confidence scores (0..1) for critical fields.
|
|
41
|
+
Preserve source language and Unicode characters exactly; never transliterate Turkish letters to ASCII.`
|
|
42
|
+
: mode === "semantic_consistency"
|
|
43
|
+
? `You detect semantic inconsistencies between paired artifacts.
|
|
44
|
+
Return valid JSON only: { "issues": [{level, code, check, contract_id, file, message, suggestion}] }.`
|
|
45
|
+
: mode === "contract_relevance"
|
|
46
|
+
? `You verify whether tagged content actually matches the referenced contract text.
|
|
47
|
+
Return valid JSON only: { "relevant": boolean, "score": number, "reason": string }.`
|
|
48
|
+
: `You are a product-document generator.
|
|
49
|
+
Return only Markdown body content.
|
|
50
|
+
Headings required:
|
|
51
|
+
${schemaHint.requiredHeadings.join("\n")}
|
|
52
|
+
Required contract tags:
|
|
53
|
+
${schemaHint.requiredContracts.join(", ")}
|
|
54
|
+
Use tags like [G1], [F2], [C1] where relevant.
|
|
55
|
+
Output language: ${outputLanguage}
|
|
56
|
+
Do not translate required headings.`;
|
|
57
|
+
const user = `${prompt}\n\nContext JSON:\n${JSON.stringify(inputContext, null, 2)}`;
|
|
58
|
+
|
|
59
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: {
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
model: this.model,
|
|
67
|
+
messages: [
|
|
68
|
+
{ role: "system", content: system },
|
|
69
|
+
{ role: "user", content: user }
|
|
70
|
+
],
|
|
71
|
+
temperature: 0.2
|
|
72
|
+
})
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
const text = await response.text();
|
|
77
|
+
throw new UserError(`OpenAI request failed (${response.status}): ${text}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const payload = (await response.json()) as ChatResponse;
|
|
81
|
+
const content = payload.choices?.[0]?.message?.content?.trim();
|
|
82
|
+
if (!content) {
|
|
83
|
+
throw new UserError("OpenAI provider returned an empty response.");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return { body: content };
|
|
87
|
+
}
|
|
88
|
+
}
|