@shahmarasy/prodo 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +201 -97
  2. package/bin/prodo.cjs +6 -6
  3. package/dist/agents/agent-registry.d.ts +13 -0
  4. package/dist/agents/agent-registry.js +79 -0
  5. package/dist/agents/anthropic/index.d.ts +9 -0
  6. package/dist/agents/anthropic/index.js +55 -0
  7. package/dist/agents/base.d.ts +25 -0
  8. package/dist/agents/base.js +71 -0
  9. package/dist/agents/google/index.d.ts +9 -0
  10. package/dist/agents/google/index.js +53 -0
  11. package/dist/agents/mock/index.d.ts +11 -0
  12. package/dist/agents/mock/index.js +26 -0
  13. package/dist/agents/openai/index.d.ts +9 -0
  14. package/dist/agents/openai/index.js +57 -0
  15. package/dist/agents/system-prompts.d.ts +3 -0
  16. package/dist/agents/system-prompts.js +32 -0
  17. package/dist/agents.js +4 -2
  18. package/dist/artifacts.d.ts +1 -0
  19. package/dist/artifacts.js +265 -31
  20. package/dist/cli/agent-command-installer.d.ts +4 -0
  21. package/dist/cli/agent-command-installer.js +148 -0
  22. package/dist/cli/agent-ids.d.ts +15 -0
  23. package/dist/cli/agent-ids.js +49 -0
  24. package/dist/cli/doctor.d.ts +1 -0
  25. package/dist/cli/doctor.js +144 -0
  26. package/dist/cli/fix-tui.d.ts +4 -0
  27. package/dist/cli/fix-tui.js +79 -0
  28. package/dist/cli/index.d.ts +9 -0
  29. package/dist/cli/index.js +465 -0
  30. package/dist/cli/init-tui.d.ts +23 -0
  31. package/dist/cli/init-tui.js +176 -0
  32. package/dist/cli/init.d.ts +11 -0
  33. package/dist/cli/init.js +334 -0
  34. package/dist/cli/normalize-interactive.d.ts +8 -0
  35. package/dist/cli/normalize-interactive.js +167 -0
  36. package/dist/cli/preset-loader.d.ts +4 -0
  37. package/dist/cli/preset-loader.js +210 -0
  38. package/dist/cli.js +80 -3
  39. package/dist/core/artifact-registry.d.ts +11 -0
  40. package/dist/core/artifact-registry.js +49 -0
  41. package/dist/core/artifacts.d.ts +10 -0
  42. package/dist/core/artifacts.js +892 -0
  43. package/dist/core/clean.d.ts +10 -0
  44. package/dist/core/clean.js +74 -0
  45. package/dist/core/consistency.d.ts +8 -0
  46. package/dist/core/consistency.js +328 -0
  47. package/dist/core/constants.d.ts +7 -0
  48. package/dist/core/constants.js +64 -0
  49. package/dist/core/errors.d.ts +3 -0
  50. package/dist/core/errors.js +10 -0
  51. package/dist/core/fix.d.ts +31 -0
  52. package/dist/core/fix.js +188 -0
  53. package/dist/core/hook-executor.d.ts +1 -0
  54. package/dist/core/hook-executor.js +175 -0
  55. package/dist/core/markdown.d.ts +16 -0
  56. package/dist/core/markdown.js +81 -0
  57. package/dist/core/normalize.d.ts +8 -0
  58. package/dist/core/normalize.js +125 -0
  59. package/dist/core/normalized-brief.d.ts +48 -0
  60. package/dist/core/normalized-brief.js +182 -0
  61. package/dist/core/output-index.d.ts +13 -0
  62. package/dist/core/output-index.js +55 -0
  63. package/dist/core/paths.d.ts +17 -0
  64. package/dist/core/paths.js +80 -0
  65. package/dist/core/project-config.d.ts +14 -0
  66. package/dist/core/project-config.js +69 -0
  67. package/dist/core/registry.d.ts +13 -0
  68. package/dist/core/registry.js +115 -0
  69. package/dist/core/settings.d.ts +7 -0
  70. package/dist/core/settings.js +35 -0
  71. package/dist/core/template-engine.d.ts +3 -0
  72. package/dist/core/template-engine.js +43 -0
  73. package/dist/core/template-resolver.d.ts +15 -0
  74. package/dist/core/template-resolver.js +46 -0
  75. package/dist/core/templates.d.ts +33 -0
  76. package/dist/core/templates.js +440 -0
  77. package/dist/core/terminology.d.ts +21 -0
  78. package/dist/core/terminology.js +143 -0
  79. package/dist/core/tracing.d.ts +21 -0
  80. package/dist/core/tracing.js +74 -0
  81. package/dist/core/types.d.ts +35 -0
  82. package/dist/core/types.js +5 -0
  83. package/dist/core/utils.d.ts +7 -0
  84. package/dist/core/utils.js +66 -0
  85. package/dist/core/validate.d.ts +10 -0
  86. package/dist/core/validate.js +226 -0
  87. package/dist/core/validator.d.ts +5 -0
  88. package/dist/core/validator.js +76 -0
  89. package/dist/core/version.d.ts +1 -0
  90. package/dist/core/version.js +30 -0
  91. package/dist/core/workflow-commands.d.ts +7 -0
  92. package/dist/core/workflow-commands.js +29 -0
  93. package/dist/i18n/en.json +45 -0
  94. package/dist/i18n/index.d.ts +5 -0
  95. package/dist/i18n/index.js +63 -0
  96. package/dist/i18n/tr.json +45 -0
  97. package/dist/init-tui.d.ts +3 -0
  98. package/dist/init-tui.js +28 -1
  99. package/dist/init.d.ts +1 -0
  100. package/dist/init.js +9 -3
  101. package/dist/normalize.js +55 -7
  102. package/dist/providers/index.d.ts +2 -1
  103. package/dist/providers/index.js +20 -6
  104. package/dist/providers/mock-provider.d.ts +1 -1
  105. package/dist/providers/mock-provider.js +7 -6
  106. package/dist/providers/openai-provider.d.ts +1 -1
  107. package/dist/providers/openai-provider.js +3 -2
  108. package/dist/settings.d.ts +1 -0
  109. package/dist/settings.js +2 -1
  110. package/dist/skills/engine.d.ts +10 -0
  111. package/dist/skills/engine.js +75 -0
  112. package/dist/skills/fix-skill.d.ts +2 -0
  113. package/dist/skills/fix-skill.js +38 -0
  114. package/dist/skills/generate-artifact-skill.d.ts +2 -0
  115. package/dist/skills/generate-artifact-skill.js +32 -0
  116. package/dist/skills/generate-pipeline-skill.d.ts +2 -0
  117. package/dist/skills/generate-pipeline-skill.js +45 -0
  118. package/dist/skills/normalize-skill.d.ts +2 -0
  119. package/dist/skills/normalize-skill.js +29 -0
  120. package/dist/skills/types.d.ts +28 -0
  121. package/dist/skills/types.js +2 -0
  122. package/dist/skills/validate-skill.d.ts +2 -0
  123. package/dist/skills/validate-skill.js +29 -0
  124. package/dist/templates.d.ts +1 -1
  125. package/dist/templates.js +2 -0
  126. package/dist/utils.d.ts +1 -0
  127. package/dist/utils.js +13 -0
  128. package/dist/validator.js +0 -4
  129. package/dist/workflow-commands.js +2 -1
  130. package/package.json +74 -45
  131. package/presets/fintech/preset.json +48 -1
  132. package/presets/fintech/prompts/prd.md +99 -2
  133. package/presets/marketplace/preset.json +51 -1
  134. package/presets/marketplace/prompts/prd.md +140 -2
  135. package/presets/saas/preset.json +53 -1
  136. package/presets/saas/prompts/prd.md +150 -2
  137. package/src/agents/agent-registry.ts +93 -0
  138. package/src/agents/anthropic/index.ts +86 -0
  139. package/src/agents/anthropic/manifest.json +7 -0
  140. package/src/agents/base.ts +77 -0
  141. package/src/agents/google/index.ts +79 -0
  142. package/src/agents/google/manifest.json +7 -0
  143. package/src/agents/mock/index.ts +32 -0
  144. package/src/agents/mock/manifest.json +7 -0
  145. package/src/agents/openai/index.ts +83 -0
  146. package/src/agents/openai/manifest.json +7 -0
  147. package/src/agents/system-prompts.ts +35 -0
  148. package/src/{agent-command-installer.ts → cli/agent-command-installer.ts} +164 -164
  149. package/src/{agents.ts → cli/agent-ids.ts} +58 -56
  150. package/src/{doctor.ts → cli/doctor.ts} +157 -137
  151. package/src/cli/fix-tui.ts +111 -0
  152. package/src/{cli.ts → cli/index.ts} +459 -319
  153. package/src/{init-tui.ts → cli/init-tui.ts} +208 -179
  154. package/src/{init.ts → cli/init.ts} +398 -391
  155. package/src/cli/normalize-interactive.ts +241 -0
  156. package/src/{preset-loader.ts → cli/preset-loader.ts} +237 -237
  157. package/src/{artifact-registry.ts → core/artifact-registry.ts} +69 -69
  158. package/src/{artifacts.ts → core/artifacts.ts} +1081 -777
  159. package/src/core/clean.ts +88 -0
  160. package/src/{consistency.ts → core/consistency.ts} +374 -303
  161. package/src/{constants.ts → core/constants.ts} +72 -72
  162. package/src/{errors.ts → core/errors.ts} +7 -7
  163. package/src/core/fix.ts +253 -0
  164. package/src/{hook-executor.ts → core/hook-executor.ts} +196 -196
  165. package/src/{markdown.ts → core/markdown.ts} +93 -73
  166. package/src/core/normalize.ts +145 -0
  167. package/src/{normalized-brief.ts → core/normalized-brief.ts} +227 -206
  168. package/src/{output-index.ts → core/output-index.ts} +59 -59
  169. package/src/{paths.ts → core/paths.ts} +75 -71
  170. package/src/{project-config.ts → core/project-config.ts} +78 -78
  171. package/src/{registry.ts → core/registry.ts} +119 -119
  172. package/src/{settings.ts → core/settings.ts} +35 -34
  173. package/src/core/template-engine.ts +45 -0
  174. package/src/{template-resolver.ts → core/template-resolver.ts} +54 -54
  175. package/src/{templates.ts → core/templates.ts} +452 -450
  176. package/src/core/terminology.ts +177 -0
  177. package/src/core/tracing.ts +110 -0
  178. package/src/{types.ts → core/types.ts} +46 -46
  179. package/src/{utils.ts → core/utils.ts} +64 -50
  180. package/src/{validate.ts → core/validate.ts} +252 -246
  181. package/src/{validator.ts → core/validator.ts} +92 -96
  182. package/src/{version.ts → core/version.ts} +24 -24
  183. package/src/{workflow-commands.ts → core/workflow-commands.ts} +32 -31
  184. package/src/i18n/en.json +45 -0
  185. package/src/i18n/index.ts +58 -0
  186. package/src/i18n/tr.json +45 -0
  187. package/src/providers/index.ts +29 -12
  188. package/src/providers/mock-provider.ts +200 -199
  189. package/src/providers/openai-provider.ts +88 -87
  190. package/src/skills/engine.ts +94 -0
  191. package/src/skills/fix-skill.ts +38 -0
  192. package/src/skills/generate-artifact-skill.ts +32 -0
  193. package/src/skills/generate-pipeline-skill.ts +49 -0
  194. package/src/skills/normalize-skill.ts +29 -0
  195. package/src/skills/types.ts +36 -0
  196. package/src/skills/validate-skill.ts +29 -0
  197. package/templates/commands/prodo-fix.md +46 -0
  198. package/templates/commands/prodo-normalize.md +118 -23
  199. package/templates/commands/prodo-prd.md +138 -17
  200. package/templates/commands/prodo-stories.md +153 -17
  201. package/templates/commands/prodo-techspec.md +167 -17
  202. package/templates/commands/prodo-validate.md +184 -26
  203. package/templates/commands/prodo-wireframe.md +188 -17
  204. package/templates/commands/prodo-workflow.md +200 -17
  205. package/src/normalize.ts +0 -89
@@ -0,0 +1,182 @@
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.buildContractsFromArrays = buildContractsFromArrays;
7
+ exports.parseNormalizedBrief = parseNormalizedBrief;
8
+ exports.parseNormalizedBriefOrThrow = parseNormalizedBriefOrThrow;
9
+ exports.requireConfidenceOrThrow = requireConfidenceOrThrow;
10
+ exports.checkConfidence = checkConfidence;
11
+ exports.contractIds = contractIds;
12
+ const _2020_1 = __importDefault(require("ajv/dist/2020"));
13
+ const errors_1 = require("./errors");
14
+ const schema = {
15
+ $schema: "https://json-schema.org/draft/2020-12/schema",
16
+ type: "object",
17
+ required: [
18
+ "schema_version",
19
+ "product_name",
20
+ "problem",
21
+ "audience",
22
+ "goals",
23
+ "core_features",
24
+ "constraints",
25
+ "assumptions",
26
+ "contracts"
27
+ ],
28
+ properties: {
29
+ schema_version: { type: "string", minLength: 1 },
30
+ product_name: { type: "string", minLength: 2 },
31
+ problem: { type: "string", minLength: 10 },
32
+ audience: { type: "array", minItems: 1, items: { type: "string", minLength: 2 } },
33
+ goals: { type: "array", minItems: 1, items: { type: "string", minLength: 2 } },
34
+ core_features: { type: "array", minItems: 1, items: { type: "string", minLength: 2 } },
35
+ constraints: { type: "array", items: { type: "string", minLength: 2 } },
36
+ assumptions: { type: "array", items: { type: "string", minLength: 2 } },
37
+ contracts: {
38
+ type: "object",
39
+ required: ["goals", "core_features", "constraints"],
40
+ properties: {
41
+ goals: { $ref: "#/$defs/contractArray" },
42
+ core_features: { $ref: "#/$defs/contractArray" },
43
+ constraints: { $ref: "#/$defs/contractArray" }
44
+ },
45
+ additionalProperties: false
46
+ },
47
+ confidence: {
48
+ type: "object",
49
+ additionalProperties: { type: "number", minimum: 0, maximum: 1 }
50
+ }
51
+ },
52
+ $defs: {
53
+ contractItem: {
54
+ type: "object",
55
+ required: ["id", "text"],
56
+ properties: {
57
+ id: { type: "string", pattern: "^[A-Z]+[0-9]+$" },
58
+ text: { type: "string", minLength: 2 }
59
+ },
60
+ additionalProperties: false
61
+ },
62
+ contractArray: { type: "array", items: { $ref: "#/$defs/contractItem" } }
63
+ },
64
+ additionalProperties: false
65
+ };
66
+ const ajv = new _2020_1.default({ allErrors: true, strict: false });
67
+ const validateFn = ajv.compile(schema);
68
+ function asString(value) {
69
+ if (typeof value !== "string")
70
+ return undefined;
71
+ const trimmed = value.trim();
72
+ return trimmed.length > 0 ? trimmed : undefined;
73
+ }
74
+ function asStringArray(value) {
75
+ if (!Array.isArray(value))
76
+ return [];
77
+ return value
78
+ .map((item) => asString(item))
79
+ .filter((item) => typeof item === "string");
80
+ }
81
+ function asContracts(value) {
82
+ if (!Array.isArray(value))
83
+ return [];
84
+ return value
85
+ .map((item) => {
86
+ if (!item || typeof item !== "object")
87
+ return null;
88
+ const record = item;
89
+ const id = asString(record.id);
90
+ const text = asString(record.text);
91
+ if (!id || !text)
92
+ return null;
93
+ return { id, text };
94
+ })
95
+ .filter((item) => item !== null);
96
+ }
97
+ function asConfidence(value) {
98
+ if (!value || typeof value !== "object")
99
+ return undefined;
100
+ const result = {};
101
+ for (const [key, raw] of Object.entries(value)) {
102
+ if (typeof raw !== "number" || Number.isNaN(raw))
103
+ continue;
104
+ result[key] = raw;
105
+ }
106
+ return Object.keys(result).length > 0 ? result : undefined;
107
+ }
108
+ function normalizeInput(input) {
109
+ const rawContracts = input.contracts ?? {};
110
+ return {
111
+ schema_version: asString(input.schema_version) ?? "1.0",
112
+ product_name: asString(input.product_name) ?? "",
113
+ problem: asString(input.problem) ?? "",
114
+ audience: asStringArray(input.audience),
115
+ goals: asStringArray(input.goals),
116
+ core_features: asStringArray(input.core_features),
117
+ constraints: asStringArray(input.constraints),
118
+ assumptions: asStringArray(input.assumptions),
119
+ contracts: {
120
+ goals: asContracts(rawContracts.goals),
121
+ core_features: asContracts(rawContracts.core_features),
122
+ constraints: asContracts(rawContracts.constraints)
123
+ },
124
+ confidence: asConfidence(input.confidence)
125
+ };
126
+ }
127
+ function buildContractsFromArrays(input) {
128
+ return {
129
+ goals: input.goals.map((text, index) => ({ id: `G${index + 1}`, text })),
130
+ core_features: input.core_features.map((text, index) => ({ id: `F${index + 1}`, text })),
131
+ constraints: input.constraints.map((text, index) => ({ id: `C${index + 1}`, text }))
132
+ };
133
+ }
134
+ function parseNormalizedBrief(input) {
135
+ const normalized = normalizeInput(input);
136
+ const valid = validateFn(normalized);
137
+ if (valid)
138
+ return { brief: normalized, issues: [] };
139
+ const errors = validateFn.errors ?? [];
140
+ const issues = errors.map((error) => ({
141
+ level: "error",
142
+ code: "normalized_brief_invalid",
143
+ check: "schema",
144
+ field: error.instancePath || error.schemaPath,
145
+ message: `Normalized brief schema error: ${error.message ?? "unknown error"}`,
146
+ suggestion: "Fix missing content in start brief and rerun `prodo normalize`."
147
+ }));
148
+ return { brief: normalized, issues };
149
+ }
150
+ function parseNormalizedBriefOrThrow(input) {
151
+ const { brief, issues } = parseNormalizedBrief(input);
152
+ if (issues.length > 0) {
153
+ const detail = issues.map((issue) => `- ${issue.message}`).join("\n");
154
+ throw new errors_1.UserError(`Normalized brief is invalid:\n${detail}`);
155
+ }
156
+ return brief;
157
+ }
158
+ function requireConfidenceOrThrow(brief, fields, threshold = 0.7) {
159
+ const confidence = brief.confidence ?? {};
160
+ const missing = fields.filter((field) => (confidence[field] ?? 0) < threshold);
161
+ if (missing.length > 0) {
162
+ throw new errors_1.UserError(`Normalization confidence too low for: ${missing.join(", ")}. Improve brief clarity and rerun \`prodo normalize\`.`);
163
+ }
164
+ }
165
+ function checkConfidence(brief, fields, threshold = 0.7) {
166
+ const confidence = brief.confidence ?? {};
167
+ const lowFields = fields
168
+ .filter((field) => (confidence[field] ?? 0) < threshold)
169
+ .map((field) => ({
170
+ field,
171
+ confidence: confidence[field] ?? 0,
172
+ threshold
173
+ }));
174
+ return { pass: lowFields.length === 0, lowFields };
175
+ }
176
+ function contractIds(contracts) {
177
+ return {
178
+ goals: contracts.goals.map((item) => item.id),
179
+ core_features: contracts.core_features.map((item) => item.id),
180
+ constraints: contracts.constraints.map((item) => item.id)
181
+ };
182
+ }
@@ -0,0 +1,13 @@
1
+ import type { ArtifactType } from "./types";
2
+ type ArtifactMap = Partial<Record<ArtifactType, string>>;
3
+ type ArtifactHistoryMap = Partial<Record<ArtifactType, string[]>>;
4
+ export type OutputIndex = {
5
+ active: ArtifactMap;
6
+ history: ArtifactHistoryMap;
7
+ updated_at: string;
8
+ };
9
+ export declare function loadOutputIndex(cwd: string): Promise<OutputIndex>;
10
+ export declare function saveOutputIndex(cwd: string, index: OutputIndex): Promise<void>;
11
+ export declare function setActiveArtifact(cwd: string, type: ArtifactType, filePath: string): Promise<void>;
12
+ export declare function getActiveArtifactPath(cwd: string, type: ArtifactType): Promise<string | undefined>;
13
+ export {};
@@ -0,0 +1,55 @@
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.loadOutputIndex = loadOutputIndex;
7
+ exports.saveOutputIndex = saveOutputIndex;
8
+ exports.setActiveArtifact = setActiveArtifact;
9
+ exports.getActiveArtifactPath = getActiveArtifactPath;
10
+ const promises_1 = __importDefault(require("node:fs/promises"));
11
+ const node_path_1 = __importDefault(require("node:path"));
12
+ const paths_1 = require("./paths");
13
+ const utils_1 = require("./utils");
14
+ function defaultIndex() {
15
+ return {
16
+ active: {},
17
+ history: {},
18
+ updated_at: new Date(0).toISOString()
19
+ };
20
+ }
21
+ async function loadOutputIndex(cwd) {
22
+ const indexPath = (0, paths_1.outputIndexPath)(cwd);
23
+ if (!(await (0, utils_1.fileExists)(indexPath)))
24
+ return defaultIndex();
25
+ const raw = await promises_1.default.readFile(indexPath, "utf8");
26
+ const parsed = JSON.parse(raw);
27
+ return {
28
+ active: parsed.active ?? {},
29
+ history: parsed.history ?? {},
30
+ updated_at: parsed.updated_at ?? new Date(0).toISOString()
31
+ };
32
+ }
33
+ async function saveOutputIndex(cwd, index) {
34
+ const indexPath = (0, paths_1.outputIndexPath)(cwd);
35
+ await (0, utils_1.ensureDir)(node_path_1.default.dirname(indexPath));
36
+ await promises_1.default.writeFile(indexPath, `${JSON.stringify(index, null, 2)}\n`, "utf8");
37
+ }
38
+ async function setActiveArtifact(cwd, type, filePath) {
39
+ const index = await loadOutputIndex(cwd);
40
+ const normalizedPath = node_path_1.default.resolve(filePath);
41
+ const existing = index.history[type] ?? [];
42
+ index.active[type] = normalizedPath;
43
+ index.history[type] = [normalizedPath, ...existing.filter((item) => item !== normalizedPath)].slice(0, 100);
44
+ index.updated_at = new Date().toISOString();
45
+ await saveOutputIndex(cwd, index);
46
+ }
47
+ async function getActiveArtifactPath(cwd, type) {
48
+ const index = await loadOutputIndex(cwd);
49
+ const candidate = index.active[type];
50
+ if (!candidate)
51
+ return undefined;
52
+ if (await (0, utils_1.fileExists)(candidate))
53
+ return candidate;
54
+ return undefined;
55
+ }
@@ -0,0 +1,17 @@
1
+ import type { ArtifactType } from "./types";
2
+ export declare function prodoPath(cwd: string): string;
3
+ export declare function briefPath(cwd: string): string;
4
+ export declare function normalizedBriefPath(cwd: string): string;
5
+ export declare function settingsPath(cwd: string): string;
6
+ export declare function registryPath(cwd: string): string;
7
+ export declare function promptPath(cwd: string, artifactType: ArtifactType): string;
8
+ export declare function templatePath(cwd: string, artifactType: ArtifactType): string;
9
+ export declare function overrideTemplatePath(cwd: string, artifactType: ArtifactType): string;
10
+ export declare function templateCandidatePaths(cwd: string, artifactType: ArtifactType): string[];
11
+ export declare function overrideTemplateCandidatePaths(cwd: string, artifactType: ArtifactType): string[];
12
+ export declare function schemaPath(cwd: string, artifactType: ArtifactType): string;
13
+ export declare function outputDirPath(cwd: string, artifactType: ArtifactType, outputDirOverride?: string): string;
14
+ export declare function reportPath(cwd: string): string;
15
+ export declare function outputIndexPath(cwd: string): string;
16
+ export declare function outputContextDirPath(cwd: string): string;
17
+ export declare function normHistoryPath(cwd: string): string;
@@ -0,0 +1,80 @@
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.prodoPath = prodoPath;
7
+ exports.briefPath = briefPath;
8
+ exports.normalizedBriefPath = normalizedBriefPath;
9
+ exports.settingsPath = settingsPath;
10
+ exports.registryPath = registryPath;
11
+ exports.promptPath = promptPath;
12
+ exports.templatePath = templatePath;
13
+ exports.overrideTemplatePath = overrideTemplatePath;
14
+ exports.templateCandidatePaths = templateCandidatePaths;
15
+ exports.overrideTemplateCandidatePaths = overrideTemplateCandidatePaths;
16
+ exports.schemaPath = schemaPath;
17
+ exports.outputDirPath = outputDirPath;
18
+ exports.reportPath = reportPath;
19
+ exports.outputIndexPath = outputIndexPath;
20
+ exports.outputContextDirPath = outputContextDirPath;
21
+ exports.normHistoryPath = normHistoryPath;
22
+ const node_path_1 = __importDefault(require("node:path"));
23
+ const constants_1 = require("./constants");
24
+ function prodoPath(cwd) {
25
+ return node_path_1.default.join(cwd, constants_1.PRODO_DIR);
26
+ }
27
+ function briefPath(cwd) {
28
+ return node_path_1.default.join(cwd, "brief.md");
29
+ }
30
+ function normalizedBriefPath(cwd) {
31
+ return node_path_1.default.join(prodoPath(cwd), "briefs", "normalized-brief.json");
32
+ }
33
+ function settingsPath(cwd) {
34
+ return node_path_1.default.join(prodoPath(cwd), "settings.json");
35
+ }
36
+ function registryPath(cwd) {
37
+ return node_path_1.default.join(prodoPath(cwd), "registry.json");
38
+ }
39
+ function promptPath(cwd, artifactType) {
40
+ return node_path_1.default.join(prodoPath(cwd), "prompts", `${artifactType}.md`);
41
+ }
42
+ function templatePath(cwd, artifactType) {
43
+ return node_path_1.default.join(prodoPath(cwd), "templates", `${artifactType}.md`);
44
+ }
45
+ function overrideTemplatePath(cwd, artifactType) {
46
+ return node_path_1.default.join(prodoPath(cwd), "templates", "overrides", `${artifactType}.md`);
47
+ }
48
+ function templateExtensionsForArtifact(artifactType) {
49
+ if (artifactType === "workflow")
50
+ return ["md", "mmd"];
51
+ if (artifactType === "wireframe")
52
+ return ["md", "html"];
53
+ return ["md"];
54
+ }
55
+ function templateCandidatePaths(cwd, artifactType) {
56
+ const root = node_path_1.default.join(prodoPath(cwd), "templates");
57
+ return templateExtensionsForArtifact(artifactType).map((ext) => node_path_1.default.join(root, `${artifactType}.${ext}`));
58
+ }
59
+ function overrideTemplateCandidatePaths(cwd, artifactType) {
60
+ const root = node_path_1.default.join(prodoPath(cwd), "templates", "overrides");
61
+ return templateExtensionsForArtifact(artifactType).map((ext) => node_path_1.default.join(root, `${artifactType}.${ext}`));
62
+ }
63
+ function schemaPath(cwd, artifactType) {
64
+ return node_path_1.default.join(prodoPath(cwd), "schemas", `${artifactType}.yaml`);
65
+ }
66
+ function outputDirPath(cwd, artifactType, outputDirOverride) {
67
+ return node_path_1.default.join(cwd, "product-docs", outputDirOverride ?? (0, constants_1.defaultOutputDir)(artifactType));
68
+ }
69
+ function reportPath(cwd) {
70
+ return node_path_1.default.join(cwd, "product-docs", "reports", "latest-validation.md");
71
+ }
72
+ function outputIndexPath(cwd) {
73
+ return node_path_1.default.join(prodoPath(cwd), "state", "index.json");
74
+ }
75
+ function outputContextDirPath(cwd) {
76
+ return node_path_1.default.join(prodoPath(cwd), "state", "context");
77
+ }
78
+ function normHistoryPath(cwd) {
79
+ return node_path_1.default.join(prodoPath(cwd), "_norm_history.json");
80
+ }
@@ -0,0 +1,14 @@
1
+ import type { ContractCoverage } from "./types";
2
+ export type ArtifactConfig = {
3
+ name: string;
4
+ output_dir?: string;
5
+ required_headings?: string[];
6
+ upstream?: string[];
7
+ required_contracts?: Array<keyof ContractCoverage>;
8
+ };
9
+ export type ProdoProjectConfig = {
10
+ presets?: string[];
11
+ artifacts?: ArtifactConfig[];
12
+ command_packs?: string[];
13
+ };
14
+ export declare function readProjectConfig(cwd: string): Promise<ProdoProjectConfig>;
@@ -0,0 +1,69 @@
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.readProjectConfig = readProjectConfig;
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const errors_1 = require("./errors");
10
+ const utils_1 = require("./utils");
11
+ function sanitizeStringArray(value) {
12
+ if (!Array.isArray(value))
13
+ return [];
14
+ return value
15
+ .filter((item) => typeof item === "string")
16
+ .map((item) => item.trim())
17
+ .filter((item) => item.length > 0);
18
+ }
19
+ function sanitizeArtifact(raw) {
20
+ if (!raw || typeof raw !== "object")
21
+ return null;
22
+ const rec = raw;
23
+ const name = typeof rec.name === "string" ? rec.name.trim() : "";
24
+ if (!name)
25
+ return null;
26
+ const outputDir = typeof rec.output_dir === "string" ? rec.output_dir.trim() : undefined;
27
+ const requiredHeadings = sanitizeStringArray(rec.required_headings);
28
+ const upstream = sanitizeStringArray(rec.upstream);
29
+ const requiredContracts = sanitizeStringArray(rec.required_contracts)
30
+ .filter((value) => value === "goals" || value === "core_features" || value === "constraints");
31
+ return {
32
+ name,
33
+ ...(outputDir ? { output_dir: outputDir } : {}),
34
+ ...(requiredHeadings.length > 0 ? { required_headings: requiredHeadings } : {}),
35
+ ...(upstream.length > 0 ? { upstream } : {}),
36
+ ...(requiredContracts.length > 0 ? { required_contracts: requiredContracts } : {})
37
+ };
38
+ }
39
+ function sanitizeConfig(raw) {
40
+ if (!raw || typeof raw !== "object")
41
+ return {};
42
+ const rec = raw;
43
+ const artifacts = Array.isArray(rec.artifacts)
44
+ ? rec.artifacts.map(sanitizeArtifact).filter((item) => item !== null)
45
+ : [];
46
+ return {
47
+ presets: sanitizeStringArray(rec.presets),
48
+ command_packs: sanitizeStringArray(rec.command_packs),
49
+ ...(artifacts.length > 0 ? { artifacts } : {})
50
+ };
51
+ }
52
+ async function readProjectConfig(cwd) {
53
+ const candidates = [
54
+ node_path_1.default.join(cwd, ".prodo", "config.json"),
55
+ node_path_1.default.join(cwd, "prodo.config.json")
56
+ ];
57
+ for (const candidate of candidates) {
58
+ if (!(await (0, utils_1.fileExists)(candidate)))
59
+ continue;
60
+ try {
61
+ const parsed = JSON.parse(await promises_1.default.readFile(candidate, "utf8"));
62
+ return sanitizeConfig(parsed);
63
+ }
64
+ catch {
65
+ throw new errors_1.UserError(`Invalid project config JSON: ${candidate}`);
66
+ }
67
+ }
68
+ return {};
69
+ }
@@ -0,0 +1,13 @@
1
+ export type OverrideRegistryEntry = {
2
+ artifact_type: string;
3
+ file: string;
4
+ sha256: string;
5
+ };
6
+ export type ProdoRegistry = {
7
+ schema_version: "1.0";
8
+ updated_at: string;
9
+ installed_presets: string[];
10
+ installed_overrides: OverrideRegistryEntry[];
11
+ };
12
+ export declare function readRegistry(cwd: string): Promise<ProdoRegistry>;
13
+ export declare function syncRegistry(cwd: string): Promise<ProdoRegistry>;
@@ -0,0 +1,115 @@
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.readRegistry = readRegistry;
7
+ exports.syncRegistry = syncRegistry;
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const node_crypto_1 = require("node:crypto");
11
+ const paths_1 = require("./paths");
12
+ const utils_1 = require("./utils");
13
+ const EMPTY_REGISTRY = {
14
+ schema_version: "1.0",
15
+ updated_at: new Date(0).toISOString(),
16
+ installed_presets: [],
17
+ installed_overrides: []
18
+ };
19
+ async function sha256(filePath) {
20
+ const raw = await promises_1.default.readFile(filePath);
21
+ return (0, node_crypto_1.createHash)("sha256").update(raw).digest("hex");
22
+ }
23
+ function sanitizeRegistry(input) {
24
+ if (!input || typeof input !== "object")
25
+ return { ...EMPTY_REGISTRY };
26
+ const raw = input;
27
+ const installedPresets = Array.isArray(raw.installed_presets)
28
+ ? raw.installed_presets
29
+ .filter((value) => typeof value === "string")
30
+ .map((value) => value.trim())
31
+ .filter((value) => value.length > 0)
32
+ : [];
33
+ const installedOverrides = Array.isArray(raw.installed_overrides)
34
+ ? raw.installed_overrides
35
+ .filter((item) => !!item && typeof item === "object")
36
+ .map((item) => ({
37
+ artifact_type: typeof item.artifact_type === "string" ? item.artifact_type.trim() : "",
38
+ file: typeof item.file === "string" ? item.file.trim() : "",
39
+ sha256: typeof item.sha256 === "string" ? item.sha256.trim() : ""
40
+ }))
41
+ .filter((item) => item.artifact_type && item.file && item.sha256)
42
+ : [];
43
+ return {
44
+ schema_version: "1.0",
45
+ updated_at: typeof raw.updated_at === "string" && raw.updated_at.trim() ? raw.updated_at : EMPTY_REGISTRY.updated_at,
46
+ installed_presets: Array.from(new Set(installedPresets)),
47
+ installed_overrides: installedOverrides
48
+ };
49
+ }
50
+ async function readRegistry(cwd) {
51
+ const file = (0, paths_1.registryPath)(cwd);
52
+ if (!(await (0, utils_1.fileExists)(file)))
53
+ return { ...EMPTY_REGISTRY };
54
+ try {
55
+ const parsed = JSON.parse(await promises_1.default.readFile(file, "utf8"));
56
+ return sanitizeRegistry(parsed);
57
+ }
58
+ catch {
59
+ return { ...EMPTY_REGISTRY };
60
+ }
61
+ }
62
+ async function readInstalledPresetsFromFile(cwd) {
63
+ const file = node_path_1.default.join(cwd, ".prodo", "presets", "installed.json");
64
+ if (!(await (0, utils_1.fileExists)(file)))
65
+ return [];
66
+ try {
67
+ const parsed = JSON.parse(await promises_1.default.readFile(file, "utf8"));
68
+ if (!Array.isArray(parsed))
69
+ return [];
70
+ return parsed
71
+ .filter((value) => typeof value === "string")
72
+ .map((value) => value.trim())
73
+ .filter((value) => value.length > 0);
74
+ }
75
+ catch {
76
+ return [];
77
+ }
78
+ }
79
+ async function discoverOverrides(cwd) {
80
+ const overridesDir = node_path_1.default.join(cwd, ".prodo", "templates", "overrides");
81
+ if (!(await (0, utils_1.fileExists)(overridesDir)))
82
+ return [];
83
+ const entries = await promises_1.default.readdir(overridesDir, { withFileTypes: true });
84
+ const out = [];
85
+ for (const entry of entries) {
86
+ if (!entry.isFile())
87
+ continue;
88
+ if (!entry.name.endsWith(".md"))
89
+ continue;
90
+ const fullPath = node_path_1.default.join(overridesDir, entry.name);
91
+ out.push({
92
+ artifact_type: entry.name.replace(/\.md$/, ""),
93
+ file: fullPath,
94
+ sha256: await sha256(fullPath)
95
+ });
96
+ }
97
+ out.sort((a, b) => a.artifact_type.localeCompare(b.artifact_type));
98
+ return out;
99
+ }
100
+ async function syncRegistry(cwd) {
101
+ const existing = await readRegistry(cwd);
102
+ const discoveredPresets = await readInstalledPresetsFromFile(cwd);
103
+ const discoveredOverrides = await discoverOverrides(cwd);
104
+ const mergedPresets = Array.from(new Set([...existing.installed_presets, ...discoveredPresets])).sort();
105
+ const merged = {
106
+ schema_version: "1.0",
107
+ updated_at: new Date().toISOString(),
108
+ installed_presets: mergedPresets,
109
+ installed_overrides: discoveredOverrides
110
+ };
111
+ const file = (0, paths_1.registryPath)(cwd);
112
+ await (0, utils_1.ensureDir)(node_path_1.default.dirname(file));
113
+ await promises_1.default.writeFile(file, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
114
+ return merged;
115
+ }
@@ -0,0 +1,7 @@
1
+ export type ProdoSettings = {
2
+ lang: string;
3
+ ai?: string;
4
+ author?: string;
5
+ };
6
+ export declare function readSettings(cwd: string): Promise<ProdoSettings>;
7
+ export declare function writeSettings(cwd: string, settings: ProdoSettings): Promise<string>;
@@ -0,0 +1,35 @@
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.readSettings = readSettings;
7
+ exports.writeSettings = writeSettings;
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const paths_1 = require("./paths");
10
+ const utils_1 = require("./utils");
11
+ const DEFAULT_SETTINGS = {
12
+ lang: "en"
13
+ };
14
+ async function readSettings(cwd) {
15
+ const path = (0, paths_1.settingsPath)(cwd);
16
+ if (!(await (0, utils_1.fileExists)(path)))
17
+ return { ...DEFAULT_SETTINGS };
18
+ try {
19
+ const raw = await promises_1.default.readFile(path, "utf8");
20
+ const parsed = JSON.parse(raw);
21
+ return {
22
+ lang: typeof parsed.lang === "string" && parsed.lang.trim() ? parsed.lang.trim() : "en",
23
+ ai: typeof parsed.ai === "string" && parsed.ai.trim() ? parsed.ai.trim() : undefined,
24
+ author: typeof parsed.author === "string" && parsed.author.trim() ? parsed.author.trim() : undefined
25
+ };
26
+ }
27
+ catch {
28
+ return { ...DEFAULT_SETTINGS };
29
+ }
30
+ }
31
+ async function writeSettings(cwd, settings) {
32
+ const path = (0, paths_1.settingsPath)(cwd);
33
+ await promises_1.default.writeFile(path, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
34
+ return path;
35
+ }
@@ -0,0 +1,3 @@
1
+ import nunjucks from "nunjucks";
2
+ export declare function renderTemplate(content: string, context: Record<string, unknown>): string;
3
+ export declare function getTemplateEnv(): nunjucks.Environment;
@@ -0,0 +1,43 @@
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.renderTemplate = renderTemplate;
7
+ exports.getTemplateEnv = getTemplateEnv;
8
+ const nunjucks_1 = __importDefault(require("nunjucks"));
9
+ const env = new nunjucks_1.default.Environment(null, {
10
+ autoescape: false,
11
+ throwOnUndefined: false,
12
+ trimBlocks: false,
13
+ lstripBlocks: false
14
+ });
15
+ env.addFilter("slug", (value) => {
16
+ return (value || "")
17
+ .toLowerCase()
18
+ .replace(/[^a-z0-9]+/g, "-")
19
+ .replace(/^-+|-+$/g, "") || "item";
20
+ });
21
+ env.addFilter("bold", (value) => {
22
+ return `**${value ?? ""}**`;
23
+ });
24
+ env.addFilter("dateFormat", (value) => {
25
+ if (!value)
26
+ return new Date().toISOString().split("T")[0];
27
+ const d = typeof value === "string" ? new Date(value) : value;
28
+ return d.toISOString().split("T")[0];
29
+ });
30
+ env.addFilter("upper", (value) => {
31
+ return (value ?? "").toUpperCase();
32
+ });
33
+ env.addFilter("default", (value, fallback) => {
34
+ if (value === null || value === undefined || value === "")
35
+ return fallback;
36
+ return String(value);
37
+ });
38
+ function renderTemplate(content, context) {
39
+ return env.renderString(content, context);
40
+ }
41
+ function getTemplateEnv() {
42
+ return env;
43
+ }
@@ -0,0 +1,15 @@
1
+ import type { ArtifactType } from "./types";
2
+ type ResolveOptions = {
3
+ cwd: string;
4
+ artifactType: ArtifactType;
5
+ };
6
+ export declare function resolveTemplate(options: ResolveOptions): Promise<{
7
+ path: string;
8
+ content: string;
9
+ } | null>;
10
+ export declare function resolveCompanionTemplate(options: ResolveOptions): Promise<{
11
+ path: string;
12
+ content: string;
13
+ } | null>;
14
+ export declare function extractRequiredHeadingsFromTemplate(content: string): string[];
15
+ export {};