@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,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GoogleAgent = void 0;
|
|
4
|
+
const base_1 = require("../base");
|
|
5
|
+
const system_prompts_1 = require("../system-prompts");
|
|
6
|
+
const errors_1 = require("../../core/errors");
|
|
7
|
+
const dynamicImport = new Function("specifier", "return import(specifier)");
|
|
8
|
+
class GoogleAgent extends base_1.BaseAgent {
|
|
9
|
+
name = "google";
|
|
10
|
+
displayName = "Google Gemini";
|
|
11
|
+
sdkRequired = "@google/generative-ai";
|
|
12
|
+
getConfig() {
|
|
13
|
+
return {
|
|
14
|
+
name: this.name,
|
|
15
|
+
displayName: this.displayName,
|
|
16
|
+
sdkRequired: this.sdkRequired,
|
|
17
|
+
envVars: ["GOOGLE_API_KEY"],
|
|
18
|
+
defaultModel: "gemini-2.0-flash"
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async generate(prompt, inputContext, schemaHint) {
|
|
22
|
+
const apiKey = process.env.GOOGLE_API_KEY;
|
|
23
|
+
if (!apiKey) {
|
|
24
|
+
throw new errors_1.UserError("GOOGLE_API_KEY is not set. Set it to use the Google Gemini agent.");
|
|
25
|
+
}
|
|
26
|
+
const model = process.env.PRODO_GOOGLE_MODEL ?? "gemini-2.0-flash";
|
|
27
|
+
const outputLanguage = typeof inputContext.outputLanguage === "string" && inputContext.outputLanguage.trim()
|
|
28
|
+
? inputContext.outputLanguage.trim()
|
|
29
|
+
: "en";
|
|
30
|
+
const system = (0, system_prompts_1.buildSystemPrompt)(schemaHint, outputLanguage);
|
|
31
|
+
const user = (0, system_prompts_1.buildUserMessage)(prompt, inputContext);
|
|
32
|
+
let GoogleGenerativeAI;
|
|
33
|
+
try {
|
|
34
|
+
const mod = (await dynamicImport("@google/generative-ai"));
|
|
35
|
+
GoogleGenerativeAI = mod.GoogleGenerativeAI;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new errors_1.UserError("Google Generative AI SDK is not installed. Run: npm install @google/generative-ai");
|
|
39
|
+
}
|
|
40
|
+
const client = new GoogleGenerativeAI(apiKey);
|
|
41
|
+
const generativeModel = client.getGenerativeModel({
|
|
42
|
+
model,
|
|
43
|
+
systemInstruction: system
|
|
44
|
+
});
|
|
45
|
+
const result = await generativeModel.generateContent(user);
|
|
46
|
+
const content = result.response.text().trim();
|
|
47
|
+
if (!content) {
|
|
48
|
+
throw new errors_1.UserError("Google Gemini agent returned an empty response.");
|
|
49
|
+
}
|
|
50
|
+
return { body: content };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.GoogleAgent = GoogleAgent;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseAgent, type AgentConfig } from "../base";
|
|
2
|
+
import type { GenerateResult, ProviderSchemaHint } from "../../core/types";
|
|
3
|
+
export declare class MockAgent extends BaseAgent {
|
|
4
|
+
readonly name = "mock";
|
|
5
|
+
readonly displayName = "Mock (Testing)";
|
|
6
|
+
readonly sdkRequired: null;
|
|
7
|
+
private readonly provider;
|
|
8
|
+
generate(prompt: string, inputContext: Record<string, unknown>, schemaHint: ProviderSchemaHint): Promise<GenerateResult>;
|
|
9
|
+
isAvailable(): Promise<boolean>;
|
|
10
|
+
getConfig(): AgentConfig;
|
|
11
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MockAgent = void 0;
|
|
4
|
+
const base_1 = require("../base");
|
|
5
|
+
const mock_provider_1 = require("../../providers/mock-provider");
|
|
6
|
+
class MockAgent extends base_1.BaseAgent {
|
|
7
|
+
name = "mock";
|
|
8
|
+
displayName = "Mock (Testing)";
|
|
9
|
+
sdkRequired = null;
|
|
10
|
+
provider = new mock_provider_1.MockProvider();
|
|
11
|
+
async generate(prompt, inputContext, schemaHint) {
|
|
12
|
+
return this.provider.generate(prompt, inputContext, schemaHint);
|
|
13
|
+
}
|
|
14
|
+
async isAvailable() {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
getConfig() {
|
|
18
|
+
return {
|
|
19
|
+
name: this.name,
|
|
20
|
+
displayName: this.displayName,
|
|
21
|
+
sdkRequired: null,
|
|
22
|
+
envVars: []
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.MockAgent = MockAgent;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BaseAgent, type AgentConfig } from "../base";
|
|
2
|
+
import type { GenerateResult, ProviderSchemaHint } from "../../core/types";
|
|
3
|
+
export declare class OpenAIAgent extends BaseAgent {
|
|
4
|
+
readonly name = "openai";
|
|
5
|
+
readonly displayName = "OpenAI";
|
|
6
|
+
readonly sdkRequired = "openai";
|
|
7
|
+
getConfig(): AgentConfig;
|
|
8
|
+
generate(prompt: string, inputContext: Record<string, unknown>, schemaHint: ProviderSchemaHint): Promise<GenerateResult>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenAIAgent = void 0;
|
|
4
|
+
const base_1 = require("../base");
|
|
5
|
+
const system_prompts_1 = require("../system-prompts");
|
|
6
|
+
const errors_1 = require("../../core/errors");
|
|
7
|
+
const dynamicImport = new Function("specifier", "return import(specifier)");
|
|
8
|
+
class OpenAIAgent extends base_1.BaseAgent {
|
|
9
|
+
name = "openai";
|
|
10
|
+
displayName = "OpenAI";
|
|
11
|
+
sdkRequired = "openai";
|
|
12
|
+
getConfig() {
|
|
13
|
+
return {
|
|
14
|
+
name: this.name,
|
|
15
|
+
displayName: this.displayName,
|
|
16
|
+
sdkRequired: this.sdkRequired,
|
|
17
|
+
envVars: ["OPENAI_API_KEY"],
|
|
18
|
+
defaultModel: "gpt-4o-mini"
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async generate(prompt, inputContext, schemaHint) {
|
|
22
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
23
|
+
if (!apiKey) {
|
|
24
|
+
throw new errors_1.UserError("OPENAI_API_KEY is not set. Set it to use the OpenAI agent.");
|
|
25
|
+
}
|
|
26
|
+
const model = process.env.PRODO_OPENAI_MODEL ?? "gpt-4o-mini";
|
|
27
|
+
const baseURL = process.env.PRODO_OPENAI_BASE_URL ?? undefined;
|
|
28
|
+
const outputLanguage = typeof inputContext.outputLanguage === "string" && inputContext.outputLanguage.trim()
|
|
29
|
+
? inputContext.outputLanguage.trim()
|
|
30
|
+
: "en";
|
|
31
|
+
const system = (0, system_prompts_1.buildSystemPrompt)(schemaHint, outputLanguage);
|
|
32
|
+
const user = (0, system_prompts_1.buildUserMessage)(prompt, inputContext);
|
|
33
|
+
let OpenAIConstructor;
|
|
34
|
+
try {
|
|
35
|
+
const mod = (await dynamicImport("openai"));
|
|
36
|
+
OpenAIConstructor = mod.default;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new errors_1.UserError("OpenAI SDK is not installed. Run: npm install openai");
|
|
40
|
+
}
|
|
41
|
+
const client = new OpenAIConstructor({ apiKey, baseURL });
|
|
42
|
+
const response = await client.chat.completions.create({
|
|
43
|
+
model,
|
|
44
|
+
messages: [
|
|
45
|
+
{ role: "system", content: system },
|
|
46
|
+
{ role: "user", content: user }
|
|
47
|
+
],
|
|
48
|
+
temperature: 0.2
|
|
49
|
+
});
|
|
50
|
+
const content = response.choices[0]?.message?.content?.trim();
|
|
51
|
+
if (!content) {
|
|
52
|
+
throw new errors_1.UserError("OpenAI agent returned an empty response.");
|
|
53
|
+
}
|
|
54
|
+
return { body: content };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.OpenAIAgent = OpenAIAgent;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildSystemPrompt = buildSystemPrompt;
|
|
4
|
+
exports.buildUserMessage = buildUserMessage;
|
|
5
|
+
function buildSystemPrompt(schemaHint, outputLanguage) {
|
|
6
|
+
const mode = schemaHint.artifactType;
|
|
7
|
+
if (mode === "normalize") {
|
|
8
|
+
return `You normalize messy human product briefs into strict JSON.
|
|
9
|
+
Return valid JSON only, no markdown. Include confidence scores (0..1) for critical fields.
|
|
10
|
+
Preserve source language and Unicode characters exactly; never transliterate Turkish letters to ASCII.`;
|
|
11
|
+
}
|
|
12
|
+
if (mode === "semantic_consistency") {
|
|
13
|
+
return `You detect semantic inconsistencies between paired artifacts.
|
|
14
|
+
Return valid JSON only: { "issues": [{level, code, check, contract_id, file, message, suggestion}] }.`;
|
|
15
|
+
}
|
|
16
|
+
if (mode === "contract_relevance") {
|
|
17
|
+
return `You verify whether tagged content actually matches the referenced contract text.
|
|
18
|
+
Return valid JSON only: { "relevant": boolean, "score": number, "reason": string }.`;
|
|
19
|
+
}
|
|
20
|
+
return `You are a product-document generator.
|
|
21
|
+
Return only Markdown body content.
|
|
22
|
+
Headings required:
|
|
23
|
+
${schemaHint.requiredHeadings.join("\n")}
|
|
24
|
+
Required contract tags:
|
|
25
|
+
${schemaHint.requiredContracts.join(", ")}
|
|
26
|
+
Use tags like [G1], [F2], [C1] where relevant.
|
|
27
|
+
Output language: ${outputLanguage}
|
|
28
|
+
Do not translate required headings.`;
|
|
29
|
+
}
|
|
30
|
+
function buildUserMessage(prompt, inputContext) {
|
|
31
|
+
return `${prompt}\n\nContext JSON:\n${JSON.stringify(inputContext, null, 2)}`;
|
|
32
|
+
}
|
package/dist/agents.js
CHANGED
|
@@ -32,7 +32,8 @@ async function loadAgentCommandSet(_cwd, agent) {
|
|
|
32
32
|
{ command: `${prefix}-wireframe`, purpose: "Generate wireframe artifact." },
|
|
33
33
|
{ command: `${prefix}-stories`, purpose: "Generate stories artifact." },
|
|
34
34
|
{ command: `${prefix}-techspec`, purpose: "Generate techspec artifact." },
|
|
35
|
-
{ command: `${prefix}-validate`, purpose: "Run validation report." }
|
|
35
|
+
{ command: `${prefix}-validate`, purpose: "Run validation report." },
|
|
36
|
+
{ command: `${prefix}-fix`, purpose: "Fix artifacts when validation fails." }
|
|
36
37
|
],
|
|
37
38
|
artifact_shortcuts: {
|
|
38
39
|
normalize: `${prefix}-normalize`,
|
|
@@ -41,7 +42,8 @@ async function loadAgentCommandSet(_cwd, agent) {
|
|
|
41
42
|
wireframe: `${prefix}-wireframe`,
|
|
42
43
|
stories: `${prefix}-stories`,
|
|
43
44
|
techspec: `${prefix}-techspec`,
|
|
44
|
-
validate: `${prefix}-validate
|
|
45
|
+
validate: `${prefix}-validate`,
|
|
46
|
+
fix: `${prefix}-fix`
|
|
45
47
|
}
|
|
46
48
|
};
|
|
47
49
|
}
|
package/dist/artifacts.d.ts
CHANGED
package/dist/artifacts.js
CHANGED
|
@@ -20,11 +20,7 @@ const markdown_1 = require("./markdown");
|
|
|
20
20
|
const utils_1 = require("./utils");
|
|
21
21
|
const validator_1 = require("./validator");
|
|
22
22
|
function defaultFilename(type) {
|
|
23
|
-
|
|
24
|
-
return `${type}-${(0, utils_1.timestampSlug)()}.md`;
|
|
25
|
-
if (type === "wireframe")
|
|
26
|
-
return `${type}-${(0, utils_1.timestampSlug)()}.md`;
|
|
27
|
-
return `${type}-${(0, utils_1.timestampSlug)()}.md`;
|
|
23
|
+
return `${type}-${(0, utils_1.artifactFileStamp)()}.md`;
|
|
28
24
|
}
|
|
29
25
|
function sidecarPath(filePath) {
|
|
30
26
|
const parsed = node_path_1.default.parse(filePath);
|
|
@@ -147,7 +143,166 @@ function renderWorkflowMermaidTemplate(templateContent, normalized, coverage, la
|
|
|
147
143
|
return token;
|
|
148
144
|
});
|
|
149
145
|
}
|
|
150
|
-
|
|
146
|
+
function normalizeAuthor(author) {
|
|
147
|
+
if (!author)
|
|
148
|
+
return undefined;
|
|
149
|
+
const normalized = author.trim();
|
|
150
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
151
|
+
}
|
|
152
|
+
function replaceAuthorPlaceholders(body, author) {
|
|
153
|
+
const safeAuthor = normalizeAuthor(author);
|
|
154
|
+
if (!safeAuthor)
|
|
155
|
+
return body;
|
|
156
|
+
return body.replace(/\{\{\s*author\s*\}\}/gi, safeAuthor);
|
|
157
|
+
}
|
|
158
|
+
function todayYmd() {
|
|
159
|
+
const now = new Date();
|
|
160
|
+
const y = now.getFullYear();
|
|
161
|
+
const m = String(now.getMonth() + 1).padStart(2, "0");
|
|
162
|
+
const d = String(now.getDate()).padStart(2, "0");
|
|
163
|
+
return `${y}-${m}-${d}`;
|
|
164
|
+
}
|
|
165
|
+
function headingKey(value) {
|
|
166
|
+
return value
|
|
167
|
+
.toLowerCase()
|
|
168
|
+
.replace(/[^a-z0-9]+/g, " ")
|
|
169
|
+
.trim();
|
|
170
|
+
}
|
|
171
|
+
function defaultDocumentControlValues(lang, revisionType, version, author) {
|
|
172
|
+
const tr = lang.toLowerCase().startsWith("tr");
|
|
173
|
+
const safeAuthor = normalizeAuthor(author) ?? (tr ? "Prodo" : "Prodo");
|
|
174
|
+
const description = revisionType === "fix"
|
|
175
|
+
? (tr ? "Dogrulama sonrasi duzeltme revizyonu" : "Post-validation fix revision")
|
|
176
|
+
: (tr ? "Ilk surum" : "Initial version");
|
|
177
|
+
return {
|
|
178
|
+
version,
|
|
179
|
+
date: todayYmd(),
|
|
180
|
+
author: safeAuthor,
|
|
181
|
+
description
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function applyDocumentControlDefaults(body, options) {
|
|
185
|
+
const defaults = defaultDocumentControlValues(options.lang, options.revisionType, options.version, options.author);
|
|
186
|
+
let out = body
|
|
187
|
+
.replace(/\{\{\s*date\s*\}\}/gi, defaults.date)
|
|
188
|
+
.replace(/\{\{\s*description\s*\}\}/gi, defaults.description)
|
|
189
|
+
.replace(/\{\{\s*version\s*\}\}/gi, defaults.version);
|
|
190
|
+
const lines = out.split(/\r?\n/);
|
|
191
|
+
const headingIndex = lines.findIndex((line) => {
|
|
192
|
+
const match = line.match(/^\s*##+\s+(.+?)\s*$/);
|
|
193
|
+
if (!match)
|
|
194
|
+
return false;
|
|
195
|
+
const key = headingKey(match[1]);
|
|
196
|
+
return key.includes("document control") || key.includes("belge kontrol");
|
|
197
|
+
});
|
|
198
|
+
if (headingIndex === -1)
|
|
199
|
+
return out;
|
|
200
|
+
const row = `| ${defaults.version} | ${defaults.date} | ${defaults.author} | ${defaults.description} |`;
|
|
201
|
+
let tableSeparatorIndex = -1;
|
|
202
|
+
let tableDataIndex = -1;
|
|
203
|
+
for (let i = headingIndex + 1; i < lines.length; i += 1) {
|
|
204
|
+
if (/^\s*##+\s+/.test(lines[i]))
|
|
205
|
+
break;
|
|
206
|
+
if (tableSeparatorIndex === -1 && /\|/.test(lines[i]) && /-/.test(lines[i])) {
|
|
207
|
+
tableSeparatorIndex = i;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (tableSeparatorIndex !== -1 && /^\s*\|/.test(lines[i])) {
|
|
211
|
+
tableDataIndex = i;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (tableDataIndex !== -1) {
|
|
216
|
+
lines[tableDataIndex] = row;
|
|
217
|
+
}
|
|
218
|
+
else if (tableSeparatorIndex !== -1) {
|
|
219
|
+
lines.splice(tableSeparatorIndex + 1, 0, row);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
lines.splice(headingIndex + 1, 0, "", "| Version | Date | Author | Description |", "|--------|------|--------|-------------|", row, "");
|
|
223
|
+
}
|
|
224
|
+
out = lines.join("\n");
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
function parseVersionToken(input) {
|
|
228
|
+
const match = input.match(/v?\s*(\d+)(?:\.(\d+))?/i);
|
|
229
|
+
if (!match)
|
|
230
|
+
return null;
|
|
231
|
+
const major = Number(match[1]);
|
|
232
|
+
const minor = Number(match[2] ?? "0");
|
|
233
|
+
if (!Number.isFinite(major) || !Number.isFinite(minor))
|
|
234
|
+
return null;
|
|
235
|
+
return { major, minor };
|
|
236
|
+
}
|
|
237
|
+
function extractDocumentControlVersion(body) {
|
|
238
|
+
const tableMatch = body.match(/\|\s*(v?\d+(?:\.\d+)?)\s*\|/i);
|
|
239
|
+
if (tableMatch?.[1])
|
|
240
|
+
return tableMatch[1].trim().startsWith("v") ? tableMatch[1].trim() : `v${tableMatch[1].trim()}`;
|
|
241
|
+
const looseMatch = body.match(/\bv?\d+\.\d+\b/i);
|
|
242
|
+
if (looseMatch?.[0])
|
|
243
|
+
return looseMatch[0].startsWith("v") ? looseMatch[0] : `v${looseMatch[0]}`;
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
async function resolveDocumentControlVersion(cwd, artifactType, revisionType) {
|
|
247
|
+
if (revisionType !== "fix")
|
|
248
|
+
return "v1.0";
|
|
249
|
+
const activePath = await (0, output_index_1.getActiveArtifactPath)(cwd, artifactType);
|
|
250
|
+
const fallbackPath = activePath ?? (await loadLatestArtifactPath(cwd, artifactType));
|
|
251
|
+
if (!fallbackPath || !(await (0, utils_1.fileExists)(fallbackPath))) {
|
|
252
|
+
return "v1.1";
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
const previous = await loadArtifactDoc(fallbackPath);
|
|
256
|
+
const previousVersion = extractDocumentControlVersion(previous.body) ?? String(previous.frontmatter.version ?? "");
|
|
257
|
+
const parsed = parseVersionToken(previousVersion);
|
|
258
|
+
if (!parsed)
|
|
259
|
+
return "v1.1";
|
|
260
|
+
return `v${parsed.major}.${parsed.minor + 1}`;
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return "v1.1";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function enforceAuthorInControlTables(body, author) {
|
|
267
|
+
const safeAuthor = normalizeAuthor(author);
|
|
268
|
+
if (!safeAuthor)
|
|
269
|
+
return body;
|
|
270
|
+
return body.replace(/(\|\s*v?[0-9.]+\s*\|\s*[^|]*\|\s*)([^|]*)(\|\s*[^|]*\|)/gi, (_match, left, _current, right) => `${left}${safeAuthor} ${right}`);
|
|
271
|
+
}
|
|
272
|
+
async function resolveUniqueOutputPath(targetDir, fileName) {
|
|
273
|
+
const parsed = node_path_1.default.parse(fileName);
|
|
274
|
+
let candidate = node_path_1.default.join(targetDir, fileName);
|
|
275
|
+
let index = 2;
|
|
276
|
+
while (await (0, utils_1.fileExists)(candidate)) {
|
|
277
|
+
candidate = node_path_1.default.join(targetDir, `${parsed.name}-${String(index).padStart(2, "0")}${parsed.ext}`);
|
|
278
|
+
index += 1;
|
|
279
|
+
}
|
|
280
|
+
return candidate;
|
|
281
|
+
}
|
|
282
|
+
function workflowFeatureTargets(normalized, coverage) {
|
|
283
|
+
const byId = new Map(normalized.contracts.core_features.map((item) => [item.id, item]));
|
|
284
|
+
const explicit = coverage.core_features
|
|
285
|
+
.map((id) => byId.get(id))
|
|
286
|
+
.filter((item) => Boolean(item));
|
|
287
|
+
if (explicit.length > 1)
|
|
288
|
+
return explicit;
|
|
289
|
+
if (normalized.contracts.core_features.length > 1)
|
|
290
|
+
return normalized.contracts.core_features.slice(0, 6);
|
|
291
|
+
if (explicit.length === 1)
|
|
292
|
+
return explicit;
|
|
293
|
+
return normalized.contracts.core_features.slice(0, 1);
|
|
294
|
+
}
|
|
295
|
+
function renderWorkflowMarkdownForFeature(markdown, feature, lang) {
|
|
296
|
+
const tr = lang.toLowerCase().startsWith("tr");
|
|
297
|
+
const noteHeading = tr ? "## Akis Odagi" : "## Flow Focus";
|
|
298
|
+
const noteLine = tr
|
|
299
|
+
? `- [${feature.id}] Bu akis ${feature.text} ihtiyacina odaklanir.`
|
|
300
|
+
: `- [${feature.id}] This flow focuses on ${feature.text}.`;
|
|
301
|
+
if (markdown.includes(noteHeading))
|
|
302
|
+
return markdown;
|
|
303
|
+
return `${markdown.trim()}\n\n${noteHeading}\n${noteLine}`.trim();
|
|
304
|
+
}
|
|
305
|
+
async function resolvePrompt(cwd, artifactType, templateContent, requiredHeadings, companionTemplate, outputAuthor, agent) {
|
|
151
306
|
const base = await promises_1.default.readFile((0, paths_1.promptPath)(cwd, artifactType), "utf8");
|
|
152
307
|
const authority = `Template authority (STRICT):
|
|
153
308
|
- Treat this template as the single output structure source.
|
|
@@ -187,12 +342,19 @@ Wireframe paired output contract (STRICT):
|
|
|
187
342
|
- Generate companion HTML screens based on native wireframe template.
|
|
188
343
|
- HTML must stay low-fidelity and structure-first.`
|
|
189
344
|
: "";
|
|
345
|
+
const authorPolicy = outputAuthor && outputAuthor.trim().length > 0
|
|
346
|
+
? `
|
|
347
|
+
Author policy (STRICT):
|
|
348
|
+
- Use this exact author name wherever author is required: ${outputAuthor.trim()}
|
|
349
|
+
- Do not invent random author names.`
|
|
350
|
+
: "";
|
|
190
351
|
const withTemplate = `${base}
|
|
191
352
|
|
|
192
353
|
${authority}
|
|
193
354
|
${companionAuthority}
|
|
194
355
|
${workflowPairing}
|
|
195
|
-
${wireframePairing}
|
|
356
|
+
${wireframePairing}
|
|
357
|
+
${authorPolicy}`;
|
|
196
358
|
if (!agent)
|
|
197
359
|
return withTemplate;
|
|
198
360
|
return `${withTemplate}
|
|
@@ -345,12 +507,59 @@ function splitWorkflowPair(raw) {
|
|
|
345
507
|
}
|
|
346
508
|
return { markdown, mermaid };
|
|
347
509
|
}
|
|
510
|
+
async function writeWorkflowFlows(targetDir, baseName, normalized, coverage, lang, markdownBody, mermaidBody, mermaidTemplateContent) {
|
|
511
|
+
const targets = workflowFeatureTargets(normalized, coverage);
|
|
512
|
+
const fallbackFeature = normalized.contracts.core_features[0] ?? { id: "F1", text: "Primary flow" };
|
|
513
|
+
const flows = targets.length > 0 ? targets : [fallbackFeature];
|
|
514
|
+
const summaryBodies = [];
|
|
515
|
+
const renderedArtifacts = [];
|
|
516
|
+
let primaryMdPath = "";
|
|
517
|
+
for (const [index, flowFeature] of flows.entries()) {
|
|
518
|
+
const flowBase = flows.length === 1
|
|
519
|
+
? baseName
|
|
520
|
+
: (index === 0
|
|
521
|
+
? baseName
|
|
522
|
+
: `${baseName}-${index + 1}-${toSlug(extractTurkishTitle(flowFeature.text))}`);
|
|
523
|
+
const mdPath = node_path_1.default.join(targetDir, `${flowBase}.md`);
|
|
524
|
+
const mmdPath = node_path_1.default.join(targetDir, `${flowBase}.mmd`);
|
|
525
|
+
const featureCoverage = {
|
|
526
|
+
...coverage,
|
|
527
|
+
core_features: [flowFeature.id]
|
|
528
|
+
};
|
|
529
|
+
const renderedMarkdown = renderWorkflowMarkdownForFeature(markdownBody, flowFeature, lang);
|
|
530
|
+
const renderedMermaid = (mermaidTemplateContent && mermaidTemplateContent.trim().length > 0)
|
|
531
|
+
? renderWorkflowMermaidTemplate(mermaidTemplateContent, normalized, featureCoverage, lang).trim()
|
|
532
|
+
: (mermaidBody ?? "").trim();
|
|
533
|
+
if (!/(^|\n)\s*(flowchart|graph)\s+/i.test(renderedMermaid)) {
|
|
534
|
+
throw new errors_1.UserError("Workflow Mermaid output is invalid.");
|
|
535
|
+
}
|
|
536
|
+
enforceLanguage(renderedMarkdown, lang, "workflow");
|
|
537
|
+
enforceLanguage(renderedMermaid, lang, "workflow");
|
|
538
|
+
await promises_1.default.writeFile(mdPath, `${renderedMarkdown}\n`, "utf8");
|
|
539
|
+
await promises_1.default.writeFile(mmdPath, `${renderedMermaid}\n`, "utf8");
|
|
540
|
+
if (!primaryMdPath)
|
|
541
|
+
primaryMdPath = mdPath;
|
|
542
|
+
summaryBodies.push(renderedMarkdown);
|
|
543
|
+
renderedArtifacts.push({ mdPath, body: renderedMarkdown });
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
primaryPath: primaryMdPath,
|
|
547
|
+
summaryBody: summaryBodies.join("\n\n"),
|
|
548
|
+
rendered: renderedArtifacts
|
|
549
|
+
};
|
|
550
|
+
}
|
|
348
551
|
async function writeWireframeScreens(targetDir, baseName, normalized, coverage, lang, headings, htmlTemplateContent) {
|
|
349
552
|
const tr = lang.toLowerCase().startsWith("tr");
|
|
350
|
-
const
|
|
553
|
+
const explicitScreens = normalized.contracts.core_features
|
|
351
554
|
.filter((item) => coverage.core_features.includes(item.id))
|
|
352
555
|
.slice(0, 6);
|
|
353
|
-
const screens =
|
|
556
|
+
const screens = explicitScreens.length > 1
|
|
557
|
+
? explicitScreens
|
|
558
|
+
: (normalized.contracts.core_features.length > 1
|
|
559
|
+
? normalized.contracts.core_features.slice(0, 6)
|
|
560
|
+
: (explicitScreens.length === 1
|
|
561
|
+
? explicitScreens
|
|
562
|
+
: normalized.contracts.core_features.slice(0, 1)));
|
|
354
563
|
const summaryBodies = [];
|
|
355
564
|
let primaryMdPath = "";
|
|
356
565
|
for (const [index, screen] of screens.entries()) {
|
|
@@ -486,9 +695,11 @@ async function writeWireframeScreens(targetDir, baseName, normalized, coverage,
|
|
|
486
695
|
}
|
|
487
696
|
async function generateArtifact(options) {
|
|
488
697
|
const { cwd, artifactType, outPath, agent } = options;
|
|
698
|
+
const revisionType = options.revisionType ?? "default";
|
|
489
699
|
const def = await (0, artifact_registry_1.getArtifactDefinition)(cwd, artifactType);
|
|
490
700
|
const normalizedPath = options.normalizedBriefOverride ?? (0, paths_1.normalizedBriefPath)(cwd);
|
|
491
701
|
await ensurePipelinePrereqs(cwd, normalizedPath);
|
|
702
|
+
const documentControlVersion = await resolveDocumentControlVersion(cwd, artifactType, revisionType);
|
|
492
703
|
const settings = await (0, settings_1.readSettings)(cwd);
|
|
493
704
|
const normalizedBriefRaw = await (0, utils_1.readJsonFile)(normalizedPath);
|
|
494
705
|
const normalizedBrief = (0, normalized_brief_1.parseNormalizedBriefOrThrow)(normalizedBriefRaw);
|
|
@@ -510,7 +721,7 @@ async function generateArtifact(options) {
|
|
|
510
721
|
const computedHeadings = templateHeadings.length > 0
|
|
511
722
|
? templateHeadings
|
|
512
723
|
: (def.required_headings.length > 0 ? def.required_headings : (0, constants_1.defaultRequiredHeadings)(artifactType));
|
|
513
|
-
const prompt = await resolvePrompt(cwd, artifactType, template?.content ?? "", computedHeadings, companionTemplate, agent);
|
|
724
|
+
const prompt = await resolvePrompt(cwd, artifactType, template?.content ?? "", computedHeadings, companionTemplate, settings.author, agent);
|
|
514
725
|
const provider = (0, providers_1.createProvider)();
|
|
515
726
|
const upstreamArtifacts = await buildUpstreamArtifacts(cwd, artifactType, def.upstream);
|
|
516
727
|
const schemaHint = {
|
|
@@ -526,14 +737,15 @@ async function generateArtifact(options) {
|
|
|
526
737
|
templatePath: template?.path ?? "",
|
|
527
738
|
companionTemplateContent: companionTemplate?.content ?? "",
|
|
528
739
|
companionTemplatePath: companionTemplate?.path ?? "",
|
|
529
|
-
outputLanguage: settings.lang
|
|
740
|
+
outputLanguage: settings.lang,
|
|
741
|
+
outputAuthor: settings.author
|
|
530
742
|
}, schemaHint);
|
|
531
|
-
let generatedBody = generated.body.trim();
|
|
743
|
+
let generatedBody = enforceAuthorInControlTables(replaceAuthorPlaceholders(generated.body.trim(), settings.author), settings.author);
|
|
532
744
|
let workflowMermaidBody = null;
|
|
533
745
|
if (artifactType === "workflow") {
|
|
534
746
|
const paired = splitWorkflowPair(generatedBody);
|
|
535
|
-
generatedBody = paired.markdown;
|
|
536
|
-
workflowMermaidBody = paired.mermaid;
|
|
747
|
+
generatedBody = enforceAuthorInControlTables(replaceAuthorPlaceholders(paired.markdown, settings.author), settings.author);
|
|
748
|
+
workflowMermaidBody = replaceAuthorPlaceholders(paired.mermaid, settings.author);
|
|
537
749
|
}
|
|
538
750
|
let contractCoverage = extractCoverageFromBody(generatedBody);
|
|
539
751
|
if (artifactType === "workflow") {
|
|
@@ -552,8 +764,15 @@ async function generateArtifact(options) {
|
|
|
552
764
|
};
|
|
553
765
|
}
|
|
554
766
|
}
|
|
767
|
+
generatedBody = applyDocumentControlDefaults(generatedBody, {
|
|
768
|
+
lang: settings.lang,
|
|
769
|
+
revisionType,
|
|
770
|
+
version: documentControlVersion,
|
|
771
|
+
author: settings.author
|
|
772
|
+
});
|
|
555
773
|
if (artifactType === "workflow" && companionTemplate?.content) {
|
|
556
774
|
workflowMermaidBody = renderWorkflowMermaidTemplate(companionTemplate.content, normalizedBrief, contractCoverage, settings.lang).trim();
|
|
775
|
+
workflowMermaidBody = replaceAuthorPlaceholders(workflowMermaidBody, settings.author);
|
|
557
776
|
}
|
|
558
777
|
enforceLanguage(generatedBody, settings.lang, artifactType);
|
|
559
778
|
if (artifactType === "workflow" && workflowMermaidBody) {
|
|
@@ -574,9 +793,13 @@ async function generateArtifact(options) {
|
|
|
574
793
|
status: constants_1.DEFAULT_STATUS,
|
|
575
794
|
upstream_artifacts: upstreamArtifacts.map((item) => item.file),
|
|
576
795
|
contract_coverage: contractCoverage,
|
|
577
|
-
language: settings.lang
|
|
796
|
+
language: settings.lang,
|
|
797
|
+
...(normalizeAuthor(settings.author) ? { author: normalizeAuthor(settings.author) } : {})
|
|
578
798
|
};
|
|
579
799
|
const mergedFrontmatter = { ...frontmatter, ...(generated.frontmatter ?? {}) };
|
|
800
|
+
if (normalizeAuthor(settings.author)) {
|
|
801
|
+
mergedFrontmatter.author = normalizeAuthor(settings.author);
|
|
802
|
+
}
|
|
580
803
|
let doc = {
|
|
581
804
|
frontmatter: mergedFrontmatter,
|
|
582
805
|
body: generatedBody
|
|
@@ -588,29 +811,40 @@ async function generateArtifact(options) {
|
|
|
588
811
|
throw new errors_1.UserError(`Artifact failed schema checks:\n${details}`);
|
|
589
812
|
}
|
|
590
813
|
const targetDir = (0, paths_1.outputDirPath)(cwd, artifactType, def.output_dir);
|
|
591
|
-
const finalPath = outPath
|
|
814
|
+
const finalPath = outPath
|
|
815
|
+
? node_path_1.default.resolve(cwd, outPath)
|
|
816
|
+
: await resolveUniqueOutputPath(targetDir, defaultFilename(artifactType));
|
|
592
817
|
if (!(0, utils_1.isPathInside)(node_path_1.default.join(cwd, "product-docs"), finalPath)) {
|
|
593
818
|
throw new errors_1.UserError("Artifact output must be inside `product-docs/`.");
|
|
594
819
|
}
|
|
595
820
|
await promises_1.default.mkdir(node_path_1.default.dirname(finalPath), { recursive: true });
|
|
596
821
|
if (artifactType === "workflow") {
|
|
597
822
|
const basePath = node_path_1.default.join(node_path_1.default.dirname(finalPath), node_path_1.default.parse(finalPath).name);
|
|
598
|
-
const
|
|
599
|
-
const mmdPath = `${basePath}.mmd`;
|
|
600
|
-
await promises_1.default.writeFile(mdPath, gray_matter_1.default.stringify(doc.body, doc.frontmatter), "utf8");
|
|
601
|
-
await promises_1.default.writeFile(mmdPath, `${(workflowMermaidBody ?? "").trim()}\n`, "utf8");
|
|
602
|
-
await writeSidecar(mdPath, doc);
|
|
603
|
-
const derivedContext = {
|
|
604
|
-
artifact_type: artifactType,
|
|
605
|
-
artifact_file: mdPath,
|
|
606
|
-
generated_at: new Date().toISOString(),
|
|
607
|
-
contract_coverage: contractCoverage,
|
|
608
|
-
...deriveStructuredContext(artifactType, doc.body, schemaHint.requiredHeadings)
|
|
609
|
-
};
|
|
823
|
+
const workflow = await writeWorkflowFlows(node_path_1.default.dirname(basePath), node_path_1.default.parse(basePath).name, normalizedBrief, contractCoverage, settings.lang, doc.body, workflowMermaidBody, companionTemplate?.content ?? null);
|
|
610
824
|
await promises_1.default.mkdir((0, paths_1.outputContextDirPath)(cwd), { recursive: true });
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
825
|
+
for (const rendered of workflow.rendered) {
|
|
826
|
+
const renderedDoc = {
|
|
827
|
+
frontmatter: doc.frontmatter,
|
|
828
|
+
body: rendered.body
|
|
829
|
+
};
|
|
830
|
+
await promises_1.default.writeFile(rendered.mdPath, gray_matter_1.default.stringify(renderedDoc.body, renderedDoc.frontmatter), "utf8");
|
|
831
|
+
await writeSidecar(rendered.mdPath, renderedDoc);
|
|
832
|
+
const renderedContext = {
|
|
833
|
+
artifact_type: artifactType,
|
|
834
|
+
artifact_file: rendered.mdPath,
|
|
835
|
+
generated_at: new Date().toISOString(),
|
|
836
|
+
contract_coverage: contractCoverage,
|
|
837
|
+
...deriveStructuredContext(artifactType, renderedDoc.body, schemaHint.requiredHeadings)
|
|
838
|
+
};
|
|
839
|
+
await promises_1.default.writeFile(contextFilePath(cwd, rendered.mdPath), `${JSON.stringify(renderedContext, null, 2)}\n`, "utf8");
|
|
840
|
+
}
|
|
841
|
+
const primaryRendered = workflow.rendered.find((item) => item.mdPath === workflow.primaryPath) ?? workflow.rendered[0];
|
|
842
|
+
doc = {
|
|
843
|
+
frontmatter: doc.frontmatter,
|
|
844
|
+
body: primaryRendered?.body ?? doc.body
|
|
845
|
+
};
|
|
846
|
+
await (0, output_index_1.setActiveArtifact)(cwd, artifactType, workflow.primaryPath);
|
|
847
|
+
return workflow.primaryPath;
|
|
614
848
|
}
|
|
615
849
|
else if (artifactType === "wireframe") {
|
|
616
850
|
const base = node_path_1.default.parse(finalPath).name;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const AI_ALIASES: Record<string, "codex" | "gemini-cli" | "claude-cli">;
|
|
2
|
+
export type SupportedAi = "codex" | "gemini-cli" | "claude-cli";
|
|
3
|
+
export declare function resolveAi(ai?: string): SupportedAi | undefined;
|
|
4
|
+
export declare function installAgentCommands(projectRoot: string, ai: SupportedAi): Promise<string[]>;
|