@friehub/blueprint 0.1.0

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 (138) hide show
  1. package/.github/workflows/ci.yml +122 -0
  2. package/.github/workflows/publish.yml +24 -0
  3. package/README.md +266 -0
  4. package/adapters/analytics/amplitude.yaml +44 -0
  5. package/adapters/analytics/mixpanel.yaml +47 -0
  6. package/adapters/analytics/segment.yaml +40 -0
  7. package/adapters/auth/auth0.yaml +56 -0
  8. package/adapters/auth/clerk.yaml +53 -0
  9. package/adapters/auth/supertokens.yaml +55 -0
  10. package/adapters/billing/paddle.yaml +57 -0
  11. package/adapters/billing/stripe.yaml +49 -0
  12. package/adapters/caching/memcached.yaml +28 -0
  13. package/adapters/caching/redis.yaml +37 -0
  14. package/adapters/chargebacks/chargebacks911.yaml +45 -0
  15. package/adapters/chargebacks/stripe.yaml +45 -0
  16. package/adapters/crm_leads/hubspot.yaml +43 -0
  17. package/adapters/crm_leads/salesforce.yaml +60 -0
  18. package/adapters/customer_support/intercom.yaml +44 -0
  19. package/adapters/customer_support/zendesk.yaml +51 -0
  20. package/adapters/donations/paypal.yaml +47 -0
  21. package/adapters/donations/stripe.yaml +47 -0
  22. package/adapters/emails/mailgun.yaml +47 -0
  23. package/adapters/emails/resend.yaml +43 -0
  24. package/adapters/emails/sendgrid.yaml +43 -0
  25. package/adapters/error_tracking/bugsnag.yaml +52 -0
  26. package/adapters/error_tracking/sentry.yaml +58 -0
  27. package/adapters/feature_flags/flagsmith.yaml +41 -0
  28. package/adapters/feature_flags/launchdarkly.yaml +41 -0
  29. package/adapters/feature_flags/unleash.yaml +41 -0
  30. package/adapters/fraud_detection/riskified.yaml +41 -0
  31. package/adapters/fraud_detection/sift.yaml +40 -0
  32. package/adapters/fulfillment/easyship.yaml +51 -0
  33. package/adapters/fulfillment/shipengine.yaml +51 -0
  34. package/adapters/incident_management/opsgenie.yaml +49 -0
  35. package/adapters/incident_management/pagerduty.yaml +48 -0
  36. package/adapters/invoicing/freshbooks.yaml +54 -0
  37. package/adapters/invoicing/stripe.yaml +47 -0
  38. package/adapters/ip_intelligence/ipinfo.yaml +37 -0
  39. package/adapters/ip_intelligence/maxmind.yaml +39 -0
  40. package/adapters/jobs/bullmq.yaml +54 -0
  41. package/adapters/jobs/temporal.yaml +53 -0
  42. package/adapters/kyc/jumio.yaml +54 -0
  43. package/adapters/kyc/onfido.yaml +53 -0
  44. package/adapters/media/cloudinary.yaml +48 -0
  45. package/adapters/media/imgix.yaml +47 -0
  46. package/adapters/notifications/firebase.yaml +45 -0
  47. package/adapters/notifications/novu.yaml +46 -0
  48. package/adapters/notifications/onesignal.yaml +45 -0
  49. package/adapters/payments/adyen.yaml +46 -0
  50. package/adapters/payments/paystack.yaml +45 -0
  51. package/adapters/payments/stripe.yaml +49 -0
  52. package/adapters/payouts/paypal.yaml +49 -0
  53. package/adapters/payouts/stripe.yaml +49 -0
  54. package/adapters/projects/asana.yaml +49 -0
  55. package/adapters/projects/jira.yaml +58 -0
  56. package/adapters/projects/linear.yaml +49 -0
  57. package/adapters/queues/bullmq.yaml +47 -0
  58. package/adapters/queues/rabbitmq.yaml +51 -0
  59. package/adapters/queues/sqs.yaml +45 -0
  60. package/adapters/rate_limiting/cloudflare.yaml +37 -0
  61. package/adapters/rate_limiting/upstash.yaml +35 -0
  62. package/adapters/search/algolia.yaml +39 -0
  63. package/adapters/search/meilisearch.yaml +39 -0
  64. package/adapters/search/typesense.yaml +42 -0
  65. package/adapters/shipping/easyship.yaml +45 -0
  66. package/adapters/shipping/shipengine.yaml +45 -0
  67. package/adapters/sms/twilio.yaml +41 -0
  68. package/adapters/sms/vonage.yaml +41 -0
  69. package/adapters/storage/azure-blob.yaml +42 -0
  70. package/adapters/storage/gcs.yaml +41 -0
  71. package/adapters/storage/s3.yaml +49 -0
  72. package/adapters/subscriptions/chargebee.yaml +32 -0
  73. package/adapters/subscriptions/stripe.yaml +37 -0
  74. package/adapters/tasks/asana.yaml +50 -0
  75. package/adapters/tasks/jira.yaml +59 -0
  76. package/adapters/tasks/linear.yaml +50 -0
  77. package/adapters/taxation/avalara.yaml +52 -0
  78. package/adapters/taxation/taxjar.yaml +48 -0
  79. package/adapters/trace_query/datadog.yaml +49 -0
  80. package/adapters/trace_query/honeycomb.yaml +42 -0
  81. package/adapters/trace_query/jaeger.yaml +42 -0
  82. package/adapters/web_analytics/google-analytics.yaml +42 -0
  83. package/adapters/web_analytics/plausible.yaml +34 -0
  84. package/adapters/web_analytics/posthog.yaml +34 -0
  85. package/adapters/webhooks/relay.yaml +35 -0
  86. package/adapters/webhooks/svix.yaml +41 -0
  87. package/blueprint.json +5 -0
  88. package/blueprinter_system_design.svg +139 -0
  89. package/catalog.json +37943 -0
  90. package/dist/cli/commands.js +362 -0
  91. package/dist/cli/help.js +211 -0
  92. package/dist/cli/render.js +109 -0
  93. package/dist/cli.js +69 -0
  94. package/dist/core/adapters/adapter-audit.test.js +85 -0
  95. package/dist/core/adapters/adapter.test.js +66 -0
  96. package/dist/core/adapters/index.js +4 -0
  97. package/dist/core/adapters/load.js +131 -0
  98. package/dist/core/adapters/resolve.js +78 -0
  99. package/dist/core/adapters/select.js +80 -0
  100. package/dist/core/adapters/types.js +1 -0
  101. package/dist/core/adapters/validate.js +121 -0
  102. package/dist/core/catalog.js +3 -0
  103. package/dist/core/collectors.js +126 -0
  104. package/dist/core/discovery.js +57 -0
  105. package/dist/core/edge-cases.test.js +147 -0
  106. package/dist/core/envelope.js +1 -0
  107. package/dist/core/graph.js +123 -0
  108. package/dist/core/graph.test.js +62 -0
  109. package/dist/core/implement.js +48 -0
  110. package/dist/core/index.js +9 -0
  111. package/dist/core/load-catalog.js +24 -0
  112. package/dist/core/parse-document.js +114 -0
  113. package/dist/core/parse-document.test.js +104 -0
  114. package/dist/core/parser.js +6 -0
  115. package/dist/core/parser.test.js +134 -0
  116. package/dist/core/resolve.js +119 -0
  117. package/dist/core/resolve.test.js +108 -0
  118. package/dist/core/scanner.js +163 -0
  119. package/dist/core/search.js +34 -0
  120. package/dist/core/search.test.js +43 -0
  121. package/dist/core/section-body.js +258 -0
  122. package/dist/core/sections.js +53 -0
  123. package/dist/core/verify-implement.test.js +123 -0
  124. package/dist/core/verify.js +156 -0
  125. package/dist/generators/engine.js +86 -0
  126. package/dist/generators/generator.test.js +112 -0
  127. package/dist/generators/index.js +3 -0
  128. package/dist/generators/prototype/index.js +242 -0
  129. package/dist/generators/render.js +146 -0
  130. package/dist/generators/types.js +124 -0
  131. package/dist/generators/typescript/helpers.js +125 -0
  132. package/dist/generators/typescript/index.js +206 -0
  133. package/dist/index.js +8 -0
  134. package/dist/mcp/server.js +202 -0
  135. package/dist/mcp/server.test.js +136 -0
  136. package/dist/utils/args.js +142 -0
  137. package/package.json +38 -0
  138. package/tsconfig.json +16 -0
@@ -0,0 +1,362 @@
1
+ import { resolve, detectCycles } from "../core/resolve.js";
2
+ import { buildGraph, renderAscii, renderMermaid } from "../core/graph.js";
3
+ import { searchModules } from "../core/search.js";
4
+ import { verifyImplementation } from "../core/verify.js";
5
+ import { generateImplementPrompts } from "../core/implement.js";
6
+ import { loadAdapters, loadSelection, addAdapter, removeAdapter, resolveAdapters, listAdaptersByModule } from "../core/adapters/index.js";
7
+ import { generateAndWrite } from "../generators/engine.js";
8
+ import { generatePrototype } from "../generators/prototype/index.js";
9
+ import { renderList, renderAdapterList, minimalCatalog, generateJsonSchema } from "./render.js";
10
+ import { writeFile, stat, mkdir } from "node:fs/promises";
11
+ import { join, dirname } from "node:path";
12
+ import * as readline from "node:readline";
13
+ async function readStdin() {
14
+ if (process.stdin.destroyed || process.stdin.readableEnded)
15
+ return "";
16
+ return new Promise((resolve) => {
17
+ const chunks = [];
18
+ process.stdin.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
19
+ process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
20
+ process.stdin.on("error", () => resolve(""));
21
+ process.stdin.resume();
22
+ });
23
+ }
24
+ function parseStdinModules(input) {
25
+ return input.split(/[\n,]/).map((s) => s.trim()).filter(Boolean);
26
+ }
27
+ async function interactivePicker(query, results) {
28
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
29
+ const question = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
30
+ console.log(`\nFound ${results.length} modules matching "${query}":\n`);
31
+ for (let i = 0; i < results.length; i++) {
32
+ const r = results[i];
33
+ console.log(` ${i + 1}. ${r.module.name}${r.module.summary ? ` - ${r.module.summary}` : ""}`);
34
+ }
35
+ console.log("\nEnter numbers (e.g., 1,3,5), or 'all':");
36
+ const answer = await question("> ");
37
+ rl.close();
38
+ const trimmed = answer.trim().toLowerCase();
39
+ if (trimmed === "all")
40
+ return results.map((r) => r.module.name);
41
+ return trimmed.split(",").map((s) => parseInt(s.trim(), 10) - 1)
42
+ .filter((i) => i >= 0 && i < results.length)
43
+ .map((i) => results[i].module.name);
44
+ }
45
+ async function writeOutput(outputData, outputPath) {
46
+ if (!outputPath) {
47
+ console.log(outputData);
48
+ return;
49
+ }
50
+ try {
51
+ await stat(dirname(outputPath));
52
+ }
53
+ catch {
54
+ console.error(`Error: output directory does not exist: ${dirname(outputPath)}`);
55
+ process.exit(1);
56
+ }
57
+ try {
58
+ await writeFile(outputPath, outputData, "utf8");
59
+ }
60
+ catch (error) {
61
+ console.error(`Error: could not write to ${outputPath}: ${error instanceof Error ? error.message : error}`);
62
+ process.exit(1);
63
+ }
64
+ }
65
+ export function checkErrors(result, strict, quiet) {
66
+ if (result.issues.length === 0)
67
+ return;
68
+ const errors = result.issues.filter((i) => i.severity === "error");
69
+ const warnings = result.issues.filter((i) => i.severity === "warning");
70
+ if (errors.length > 0) {
71
+ console.error("Errors:");
72
+ errors.forEach((e) => console.error(` - ${e.message}`));
73
+ if (strict)
74
+ process.exit(1);
75
+ else
76
+ process.exitCode = 1;
77
+ }
78
+ if (warnings.length > 0 && !quiet) {
79
+ console.warn("Warnings:");
80
+ warnings.forEach((w) => console.warn(` - ${w.message}`));
81
+ }
82
+ }
83
+ export async function handleList(result, config, root) {
84
+ const outputData = renderList(result.value);
85
+ await writeOutput(outputData, config.output);
86
+ }
87
+ export async function handleInspect(result, config, root) {
88
+ if (!config.target) {
89
+ console.error("Error: module name is required. Example: blueprint inspect billing");
90
+ process.exit(1);
91
+ }
92
+ const mod = result.value.modules.find((m) => m.name === config.target);
93
+ if (!mod) {
94
+ console.error(`Module not found: ${config.target}`);
95
+ process.exit(1);
96
+ }
97
+ const outputData = JSON.stringify(mod, null, config.compact ? undefined : 2);
98
+ await writeOutput(outputData, config.output);
99
+ }
100
+ export async function handleGraph(result, config, root) {
101
+ if (!config.target) {
102
+ console.error("Error: module name is required. Example: blueprint graph billing");
103
+ process.exit(1);
104
+ }
105
+ const graph = buildGraph(result.value, config.target);
106
+ if (graph.nodes.length === 0) {
107
+ console.error(`Module not found: ${config.target}`);
108
+ process.exit(1);
109
+ }
110
+ const outputData = config.format === "mermaid" ? renderMermaid(graph, config.target) : renderAscii(graph, config.target);
111
+ await writeOutput(outputData, config.output);
112
+ }
113
+ export async function handleSearch(result, config, root) {
114
+ let query = config.query;
115
+ if (!query && !process.stdin.isTTY) {
116
+ const input = await readStdin();
117
+ query = input.trim().split("\n")[0]?.trim();
118
+ }
119
+ if (!query) {
120
+ console.error("Error: search query is required. Example: blueprint search billing");
121
+ process.exit(1);
122
+ }
123
+ const results = searchModules(result.value, query);
124
+ if (results.length === 0) {
125
+ console.error(`No modules found matching "${query}"`);
126
+ process.exit(1);
127
+ }
128
+ if (process.stdin.isTTY) {
129
+ const selected = await interactivePicker(query, results);
130
+ if (selected.length === 0) {
131
+ console.error("No modules selected.");
132
+ process.exit(1);
133
+ }
134
+ const cycles = detectCycles(result.value);
135
+ if (cycles.length > 0) {
136
+ console.error("Cycle detected:");
137
+ cycles.forEach((c) => console.error(` ${c.join(" → ")}`));
138
+ process.exit(1);
139
+ }
140
+ const resolved = resolve(result.value, selected);
141
+ if (resolved.errors.length > 0) {
142
+ console.error("Resolve errors:");
143
+ resolved.errors.forEach((e) => console.error(` - ${e}`));
144
+ process.exit(1);
145
+ }
146
+ await writeOutput(JSON.stringify(resolved, null, config.compact ? undefined : 2), config.output);
147
+ }
148
+ else {
149
+ const names = results.map((r) => r.module.name);
150
+ await writeOutput(JSON.stringify(names, null, config.compact ? undefined : 2), config.output);
151
+ }
152
+ }
153
+ export async function handleResolve(result, config, root) {
154
+ let modules = config.modules;
155
+ if (modules.length === 0 && !process.stdin.isTTY) {
156
+ modules = parseStdinModules(await readStdin());
157
+ }
158
+ if (modules.length === 0) {
159
+ console.error("Error: --modules is required. Example: blueprint resolve --modules billing,payments,users");
160
+ process.exit(1);
161
+ }
162
+ const cycles = detectCycles(result.value);
163
+ if (cycles.length > 0) {
164
+ console.error("Cycle detected:");
165
+ cycles.forEach((c) => console.error(` ${c.join(" → ")}`));
166
+ process.exit(1);
167
+ }
168
+ const resolved = resolve(result.value, modules);
169
+ if (resolved.errors.length > 0) {
170
+ console.error("Resolve errors:");
171
+ resolved.errors.forEach((e) => console.error(` - ${e}`));
172
+ process.exit(1);
173
+ }
174
+ if (resolved.warnings.length > 0 && !config.quiet) {
175
+ resolved.warnings.forEach((w) => console.warn(` - ${w}`));
176
+ }
177
+ await writeOutput(JSON.stringify(resolved, null, config.compact ? undefined : 2), config.output);
178
+ }
179
+ export async function handleAdapters(result, config, root) {
180
+ const adaptersDir = join(root, "adapters");
181
+ const { adapters, errors: loadErrors } = await loadAdapters(adaptersDir);
182
+ if (loadErrors.length > 0 && !config.quiet)
183
+ loadErrors.forEach((e) => console.warn(` - ${e}`));
184
+ if (config.adapterSubcommand === "list") {
185
+ const byModule = listAdaptersByModule(adapters);
186
+ await writeOutput(renderAdapterList(byModule, config.query), config.output);
187
+ }
188
+ else if (config.adapterSubcommand === "add") {
189
+ if (!config.provider || !config.module) {
190
+ console.error("Error: provider and module required. Example: blueprint adapters add stripe payments");
191
+ process.exit(1);
192
+ }
193
+ const { selection, error } = await addAdapter(root, config.module, config.provider);
194
+ if (error) {
195
+ console.error(`Error: ${error}`);
196
+ process.exit(1);
197
+ }
198
+ console.log(`Added ${config.provider} as adapter for ${config.module}`);
199
+ await writeOutput(JSON.stringify(selection, null, config.compact ? undefined : 2), config.output);
200
+ }
201
+ else if (config.adapterSubcommand === "remove") {
202
+ if (!config.module) {
203
+ console.error("Error: module is required. Example: blueprint adapters remove payments");
204
+ process.exit(1);
205
+ }
206
+ const { selection, error } = await removeAdapter(root, config.module);
207
+ if (error) {
208
+ console.error(`Error: ${error}`);
209
+ process.exit(1);
210
+ }
211
+ console.log(`Removed adapter for ${config.module}`);
212
+ await writeOutput(JSON.stringify(selection, null, config.compact ? undefined : 2), config.output);
213
+ }
214
+ else if (config.adapterSubcommand === "show") {
215
+ const { selection } = await loadSelection(root);
216
+ await writeOutput(JSON.stringify(selection, null, config.compact ? undefined : 2), config.output);
217
+ }
218
+ else if (config.adapterSubcommand === "verify") {
219
+ const { selection } = await loadSelection(root);
220
+ const resolution = resolveAdapters(selection, adapters, result.value);
221
+ if (resolution.issues.length > 0) {
222
+ resolution.issues.forEach((issue) => {
223
+ const msg = `${issue.adapter ? `${issue.adapter} → ` : ""}${issue.module}: ${issue.message}`;
224
+ if (issue.severity === "error")
225
+ console.error(` ERROR: ${msg}`);
226
+ else if (!config.quiet)
227
+ console.warn(` WARN: ${msg}`);
228
+ });
229
+ if (resolution.issues.some((i) => i.severity === "error"))
230
+ process.exit(1);
231
+ }
232
+ else {
233
+ console.log("All adapters verified successfully.");
234
+ }
235
+ await writeOutput(JSON.stringify(resolution, null, config.compact ? undefined : 2), config.output);
236
+ }
237
+ else if (config.adapterSubcommand === "search") {
238
+ const query = config.query ?? "";
239
+ const results = adapters.filter((a) => a.name.includes(query) || a.module.includes(query) || a.description?.includes(query));
240
+ await writeOutput(JSON.stringify(results, null, config.compact ? undefined : 2), config.output);
241
+ }
242
+ else {
243
+ console.error("Error: adapter subcommand required. Available: list, add, remove, show, verify, search");
244
+ process.exit(1);
245
+ }
246
+ }
247
+ export async function handleGenerate(result, config, root) {
248
+ const language = config.language ?? "typescript";
249
+ const type = config.generateSubcommand ?? "all";
250
+ const outputDir = config.output ?? join(root, "generated");
251
+ const adaptersDir = join(root, "adapters");
252
+ const { adapters } = await loadAdapters(adaptersDir);
253
+ const { written, errors } = await generateAndWrite(result.value, adapters, { language, type, module: config.module, provider: config.provider, outputDir });
254
+ if (errors.length > 0) {
255
+ console.error("Generation errors:");
256
+ errors.forEach((e) => console.error(` - ${e}`));
257
+ }
258
+ console.log(`Generated ${written} files to ${outputDir}`);
259
+ }
260
+ export async function handlePrototype(result, config, root) {
261
+ const { selection } = await loadSelection(root);
262
+ const adaptersDir = join(root, "adapters");
263
+ const { adapters } = await loadAdapters(adaptersDir);
264
+ const selectedAdapters = {};
265
+ for (const [module, adapterRef] of Object.entries(selection.adapters)) {
266
+ selectedAdapters[module] = typeof adapterRef === "string" ? adapterRef : adapterRef.primary;
267
+ }
268
+ if (Object.keys(selectedAdapters).length === 0) {
269
+ console.error("Error: No adapters selected. Use 'blueprint adapters add' first.");
270
+ process.exit(1);
271
+ }
272
+ const moduleName = config.target ?? "my-project";
273
+ const outputDir = config.output ?? join(root, moduleName);
274
+ const { files, errors } = generatePrototype(result.value, adapters, {
275
+ name: moduleName, modules: Object.keys(selectedAdapters), adapters: selectedAdapters, outputDir,
276
+ });
277
+ if (errors.length > 0) {
278
+ console.error("Prototype generation errors:");
279
+ errors.forEach((e) => console.error(` - ${e}`));
280
+ }
281
+ for (const file of files) {
282
+ try {
283
+ const fullPath = join(outputDir, file.path);
284
+ await mkdir(dirname(fullPath), { recursive: true });
285
+ await writeFile(fullPath, file.content, "utf8");
286
+ }
287
+ catch (error) {
288
+ console.error(`Failed to write ${file.path}: ${error instanceof Error ? error.message : error}`);
289
+ }
290
+ }
291
+ console.log(`Generated prototype with ${files.length} files to ${outputDir}`);
292
+ }
293
+ export async function handleSchema(result, config, root) {
294
+ const outputData = JSON.stringify(generateJsonSchema(result.value), null, config.compact ? undefined : 2);
295
+ await writeOutput(outputData, config.output);
296
+ }
297
+ export async function handleBuild(result, config, root) {
298
+ if (process.exitCode) {
299
+ console.error("Catalog has errors. Use --strict to fail on errors, or fix the issues above.");
300
+ process.exit(1);
301
+ }
302
+ const outputData = config.minimal
303
+ ? JSON.stringify(minimalCatalog(result.value), null, config.compact ? undefined : 2)
304
+ : JSON.stringify(result.value, null, config.compact ? undefined : 2);
305
+ await writeOutput(outputData, config.output);
306
+ }
307
+ export async function handleImplement(result, config, root) {
308
+ if (!config.module) {
309
+ console.error("Error: --module is required. Example: blueprint implement payments --adapter stripe --prompts");
310
+ process.exit(1);
311
+ }
312
+ if (!config.provider) {
313
+ console.error("Error: --adapter is required. Example: blueprint implement payments --adapter stripe --prompts");
314
+ process.exit(1);
315
+ }
316
+ const adaptersDir = join(root, "adapters");
317
+ const { adapters } = await loadAdapters(adaptersDir);
318
+ const prompts = generateImplementPrompts(result.value, adapters, config.module, config.provider);
319
+ if (prompts.length === 0) {
320
+ console.error(`No functions to implement for ${config.provider}/${config.module}`);
321
+ process.exit(1);
322
+ }
323
+ if (config.prompts) {
324
+ console.log(`# Implementation prompts for ${config.provider} → ${config.module}`);
325
+ console.log(`# ${prompts.length} functions need implementation\n`);
326
+ for (const p of prompts) {
327
+ console.log(`--- ${p.function} ---`);
328
+ console.log(p.prompt);
329
+ console.log("");
330
+ }
331
+ }
332
+ else {
333
+ console.log(`${prompts.length} functions ready to implement for ${config.provider}/${config.module}`);
334
+ console.log("Use --prompts to generate AI prompts.");
335
+ for (const p of prompts) {
336
+ console.log(` - ${p.function}`);
337
+ }
338
+ }
339
+ }
340
+ export async function handleVerify(result, config, root) {
341
+ if (!config.target) {
342
+ console.error("Error: implementation file is required. Example: blueprint verify ./src/adapters/stripe.ts --module payments");
343
+ process.exit(1);
344
+ }
345
+ if (!config.module) {
346
+ console.error("Error: --module is required. Example: blueprint verify ./src/adapters/stripe.ts --module payments");
347
+ process.exit(1);
348
+ }
349
+ const verification = await verifyImplementation(config.target, config.module, result.value);
350
+ if (verification.issues.length > 0) {
351
+ console.error(`Verification issues for ${config.module}:`);
352
+ for (const issue of verification.issues) {
353
+ if (issue.kind === "missing") {
354
+ console.error(` MISSING: ${issue.message}`);
355
+ }
356
+ }
357
+ }
358
+ if (verification.valid) {
359
+ console.log(`\n${config.module}: All ${result.value.modules.find((m) => m.name === config.module).functions.length} functions implemented. ✓`);
360
+ }
361
+ await writeOutput(JSON.stringify(verification, null, config.compact ? undefined : 2), config.output);
362
+ }
@@ -0,0 +1,211 @@
1
+ export function printHelp(command) {
2
+ if (command === "resolve") {
3
+ console.log(`
4
+ Usage: blueprint resolve [options]
5
+
6
+ Options:
7
+ --modules <list> Comma-separated module names to resolve
8
+ --root <path> Project root directory (default: current directory)
9
+ --output <file> Write the resolved set to this file instead of stdout
10
+ --compact Output compact JSON (no indentation)
11
+ --quiet Suppress warnings
12
+
13
+ Reads module names from stdin if --modules is not provided.
14
+ Examples:
15
+ blueprint resolve --modules billing,payments,users
16
+ echo billing,payments | blueprint resolve
17
+ cat modules.txt | blueprint resolve --output resolved.json
18
+ `);
19
+ return;
20
+ }
21
+ if (command === "inspect") {
22
+ console.log(`
23
+ Usage: blueprint inspect <module> [options]
24
+
25
+ Options:
26
+ --root <path> Project root directory (default: current directory)
27
+ --output <file> Write the contract to this file instead of stdout
28
+ --compact Output compact JSON (no indentation)
29
+
30
+ Shows the full contract for a single module.
31
+ `);
32
+ return;
33
+ }
34
+ if (command === "graph") {
35
+ console.log(`
36
+ Usage: blueprint graph <module> [options]
37
+
38
+ Options:
39
+ --format <fmt> Output format: ascii (default) or mermaid
40
+ --root <path> Project root directory (default: current directory)
41
+ --output <file> Write the graph to this file instead of stdout
42
+
43
+ Shows the dependency graph for a module.
44
+ `);
45
+ return;
46
+ }
47
+ if (command === "search") {
48
+ console.log(`
49
+ Usage: blueprint search <query> [options]
50
+
51
+ Options:
52
+ --root <path> Project root directory (default: current directory)
53
+ --output <file> Write the resolved set to this file instead of stdout
54
+ --compact Output compact JSON (no indentation)
55
+
56
+ Searches for modules matching the query and interactively picks which to resolve.
57
+ In non-interactive mode (piped input), outputs matching module names as JSON.
58
+ Examples:
59
+ blueprint search billing
60
+ blueprint search "user management"
61
+ echo "payment" | blueprint search
62
+ `);
63
+ return;
64
+ }
65
+ if (command === "adapters") {
66
+ console.log(`
67
+ Usage: blueprint adapters <subcommand> [options]
68
+
69
+ Subcommands:
70
+ list [module] List available adapters
71
+ add <provider> <module> Select an adapter for a module
72
+ remove <module> Remove adapter selection
73
+ show Show current adapter selections
74
+ verify [module] Verify adapters against contracts
75
+ search <query> Search for adapters
76
+
77
+ Options:
78
+ --root <path> Project root directory (default: current directory)
79
+ --compact Output compact JSON (no indentation)
80
+ --quiet Suppress warnings
81
+
82
+ Examples:
83
+ blueprint adapters list
84
+ blueprint adapters list payments
85
+ blueprint adapters add stripe payments
86
+ blueprint adapters remove payments
87
+ blueprint adapters show
88
+ blueprint adapters verify
89
+ blueprint adapters search stripe
90
+ `);
91
+ return;
92
+ }
93
+ if (command === "generate") {
94
+ console.log(`
95
+ Usage: blueprint generate [subcommand] [options]
96
+
97
+ Subcommands:
98
+ interfaces Generate language interfaces from contracts
99
+ adapters Generate adapter skeletons
100
+ tests Generate conformance tests
101
+ all Generate all (default)
102
+
103
+ Options:
104
+ --lang <language> Target language: typescript (default), rust, python, go
105
+ --module <module> Generate for specific module only
106
+ --output <dir> Output directory (default: ./generated)
107
+ --root <path> Project root directory (default: current directory)
108
+
109
+ Examples:
110
+ blueprint generate
111
+ blueprint generate --lang typescript
112
+ blueprint generate interfaces --lang typescript
113
+ blueprint generate adapter stripe payments --lang typescript
114
+ blueprint generate tests --lang typescript
115
+ blueprint generate --module billing --lang typescript
116
+ `);
117
+ return;
118
+ }
119
+ if (command === "verify") {
120
+ console.log(`
121
+ Usage: blueprint verify <file> --module <module> [options]
122
+
123
+ Options:
124
+ --module <module> Module name to verify against (required)
125
+ --root <path> Project root directory (default: current directory)
126
+ --compact Output compact JSON (no indentation)
127
+
128
+ Checks that an implementation file covers all contract functions.
129
+
130
+ Examples:
131
+ blueprint verify ./src/adapters/payments/stripe.ts --module payments
132
+ blueprint verify ./src/adapters/payments/stripe.ts --module payments --compact
133
+ `);
134
+ return;
135
+ }
136
+ if (command === "schema") {
137
+ console.log(`
138
+ Usage: blueprint schema [options]
139
+
140
+ Options:
141
+ --output <file> Write schema to file instead of stdout
142
+ --compact Output compact JSON (no indentation)
143
+ --root <path> Project root directory (default: current directory)
144
+
145
+ Exports the catalog as a JSON Schema for programmatic validation.
146
+
147
+ Examples:
148
+ blueprint schema
149
+ blueprint schema --output catalog.schema.json
150
+ blueprint schema --compact
151
+ `);
152
+ return;
153
+ }
154
+ if (command === "prototype") {
155
+ console.log(`
156
+ Usage: blueprint prototype [options]
157
+
158
+ Options:
159
+ --name <name> Project name (default: my-project)
160
+ --output <dir> Output directory (default: ./<name>)
161
+ --root <path> Project root directory (default: current directory)
162
+
163
+ Generates a project scaffold based on selected adapters.
164
+ Requires adapters to be selected first with 'blueprint adapters add'.
165
+
166
+ Examples:
167
+ blueprint prototype
168
+ blueprint prototype --name my-saas
169
+ blueprint prototype --output ./my-project
170
+ `);
171
+ return;
172
+ }
173
+ console.log(`
174
+ Usage: blueprint [command] [options]
175
+
176
+ Commands:
177
+ build (default) Load all contracts and output catalog.json
178
+ list List all modules with dependencies
179
+ search <query> Search for modules and interactively pick to resolve
180
+ inspect <module> Show full contract for a module
181
+ graph <module> Show dependency graph for a module
182
+ resolve Resolve specific modules with dependencies
183
+ adapters Manage adapter selections
184
+ generate Generate code from contracts
185
+ prototype Generate project scaffold
186
+ schema Export catalog as JSON Schema
187
+ verify <file> Verify an implementation against its contract
188
+
189
+ Options:
190
+ --root <path> Project root directory (default: current directory)
191
+ --strict Exit with code 1 if there are any errors (warnings do not affect exit code)
192
+ --output <file> Write output to this file instead of stdout
193
+ --modules <list> Comma-separated module names (resolve command only)
194
+ --format <fmt> Output format for graph: ascii (default) or mermaid
195
+ --compact Output compact JSON (no indentation)
196
+ --quiet Suppress warnings
197
+ --minimal Output minimal JSON without raw sections
198
+ --help, -h Show this help message
199
+ --version, -v Show version number
200
+
201
+ Examples:
202
+ blueprint list
203
+ blueprint inspect billing
204
+ blueprint graph billing
205
+ blueprint graph billing --format mermaid
206
+ blueprint resolve --modules billing,payments,users
207
+ blueprint resolve --modules billing,payments,users --output resolved.json
208
+ echo billing,payments | blueprint resolve --compact
209
+ blueprint schema --output catalog.schema.json
210
+ `);
211
+ }
@@ -0,0 +1,109 @@
1
+ export function renderList(catalog) {
2
+ const lines = [];
3
+ lines.push("Modules:");
4
+ for (const mod of catalog.modules.sort((a, b) => a.name.localeCompare(b.name))) {
5
+ const deps = mod.hardDeps.length > 0 ? mod.hardDeps.join(", ") : "(none)";
6
+ const soft = mod.softDeps.length > 0 ? mod.softDeps.join(", ") : "(none)";
7
+ const coreInherits = mod.coreInherits.length > 0 ? mod.coreInherits.join(", ") : "(none)";
8
+ const summary = mod.functions.length > 0 ? ` (${mod.functions.length} functions)` : "";
9
+ lines.push(` ${mod.name}${summary}`);
10
+ lines.push(` deps: ${deps}`);
11
+ lines.push(` recommends: ${soft}`);
12
+ lines.push(` inherits: ${coreInherits}`);
13
+ }
14
+ lines.push("");
15
+ lines.push("Core contracts:");
16
+ for (const c of catalog.core.sort((a, b) => a.name.localeCompare(b.name))) {
17
+ lines.push(` ${c.name}${c.implicit ? " (implicit)" : ""}`);
18
+ }
19
+ return lines.join("\n");
20
+ }
21
+ export function renderAdapterList(byModule, filter) {
22
+ const lines = [];
23
+ const modules = Object.keys(byModule).sort();
24
+ lines.push("Available adapters:");
25
+ lines.push("");
26
+ for (const module of modules) {
27
+ if (filter && !module.includes(filter)) {
28
+ continue;
29
+ }
30
+ const adapters = byModule[module];
31
+ lines.push(` ${module}`);
32
+ for (const adapter of adapters) {
33
+ lines.push(` - ${adapter}`);
34
+ }
35
+ }
36
+ return lines.join("\n");
37
+ }
38
+ export function minimalCatalog(catalog) {
39
+ return {
40
+ modules: catalog.modules.map((mod) => ({
41
+ name: mod.name,
42
+ version: mod.version,
43
+ functions: mod.functions.map((fn) => ({
44
+ name: fn.name,
45
+ params: fn.params.map((p) => `${p.name}${p.optional ? "?" : ""}: ${p.type ?? "unknown"}`),
46
+ returns: fn.returns,
47
+ })),
48
+ hardDeps: mod.hardDeps,
49
+ softDeps: mod.softDeps,
50
+ coreInherits: mod.coreInherits,
51
+ })),
52
+ core: catalog.core.map((c) => ({
53
+ name: c.name,
54
+ implicit: c.implicit,
55
+ })),
56
+ };
57
+ }
58
+ export function generateJsonSchema(catalog) {
59
+ return {
60
+ "$schema": "http://json-schema.org/draft-07/schema#",
61
+ title: "Engineering Blueprinter Catalog",
62
+ type: "object",
63
+ properties: {
64
+ modules: {
65
+ type: "array",
66
+ items: {
67
+ type: "object",
68
+ properties: {
69
+ name: { type: "string" },
70
+ functions: {
71
+ type: "array",
72
+ items: {
73
+ type: "object",
74
+ properties: {
75
+ name: { type: "string" },
76
+ params: {
77
+ type: "array",
78
+ items: {
79
+ type: "object",
80
+ properties: {
81
+ name: { type: "string" },
82
+ type: { type: ["string", "null"] },
83
+ optional: { type: "boolean" },
84
+ },
85
+ },
86
+ },
87
+ returns: { type: "string" },
88
+ },
89
+ },
90
+ },
91
+ hardDeps: { type: "array", items: { type: "string" } },
92
+ softDeps: { type: "array", items: { type: "string" } },
93
+ coreInherits: { type: "array", items: { type: "string" } },
94
+ },
95
+ },
96
+ },
97
+ core: {
98
+ type: "array",
99
+ items: {
100
+ type: "object",
101
+ properties: {
102
+ name: { type: "string" },
103
+ implicit: { type: "boolean" },
104
+ },
105
+ },
106
+ },
107
+ },
108
+ };
109
+ }