@basaltkit/ai 0.11.0 → 1.0.1

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 (74) hide show
  1. package/README.md +10 -3
  2. package/dist/analyze/run.d.ts +19 -0
  3. package/dist/analyze/run.js +58 -0
  4. package/dist/commands.d.ts +19 -0
  5. package/dist/commands.js +272 -0
  6. package/dist/context/project.d.ts +78 -0
  7. package/dist/context/project.js +202 -0
  8. package/dist/doctor/fixes.d.ts +31 -0
  9. package/dist/doctor/fixes.js +82 -0
  10. package/dist/doctor/rules.d.ts +7 -0
  11. package/dist/doctor/rules.js +230 -0
  12. package/dist/doctor/run.d.ts +9 -0
  13. package/dist/doctor/run.js +16 -0
  14. package/dist/doctor/types.d.ts +27 -0
  15. package/dist/doctor/types.js +3 -0
  16. package/dist/index.d.ts +35 -791
  17. package/dist/index.js +40 -2389
  18. package/dist/make/fields.d.ts +35 -0
  19. package/dist/make/fields.js +139 -0
  20. package/dist/make/make.d.ts +14 -0
  21. package/dist/make/make.js +316 -0
  22. package/dist/make/openapi.d.ts +9 -0
  23. package/dist/make/openapi.js +36 -0
  24. package/dist/make/permissions.d.ts +13 -0
  25. package/dist/make/permissions.js +38 -0
  26. package/dist/make/relations.d.ts +23 -0
  27. package/dist/make/relations.js +62 -0
  28. package/dist/make/render.d.ts +4 -0
  29. package/dist/make/render.js +66 -0
  30. package/dist/make/repository.d.ts +16 -0
  31. package/dist/make/repository.js +176 -0
  32. package/dist/make/schema.d.ts +22 -0
  33. package/dist/make/schema.js +54 -0
  34. package/dist/make/types.d.ts +77 -0
  35. package/dist/make/types.js +1 -0
  36. package/dist/make/verify.d.ts +12 -0
  37. package/dist/make/verify.js +19 -0
  38. package/dist/make/wire.d.ts +23 -0
  39. package/dist/make/wire.js +76 -0
  40. package/dist/plan/context.d.ts +8 -0
  41. package/dist/plan/context.js +39 -0
  42. package/dist/plan/knowledge.d.ts +9 -0
  43. package/dist/plan/knowledge.js +50 -0
  44. package/dist/plan/plan.d.ts +15 -0
  45. package/dist/plan/plan.js +146 -0
  46. package/dist/plan/render.d.ts +4 -0
  47. package/dist/plan/render.js +55 -0
  48. package/dist/plan/types.d.ts +45 -0
  49. package/dist/plan/types.js +1 -0
  50. package/dist/provider/anthropic.d.ts +26 -0
  51. package/dist/provider/anthropic.js +72 -0
  52. package/dist/provider/factory.d.ts +27 -0
  53. package/dist/provider/factory.js +58 -0
  54. package/dist/provider/http.d.ts +24 -0
  55. package/dist/provider/http.js +36 -0
  56. package/dist/provider/ollama.d.ts +22 -0
  57. package/dist/provider/ollama.js +42 -0
  58. package/dist/provider/openai.d.ts +48 -0
  59. package/dist/provider/openai.js +94 -0
  60. package/dist/provider/sse.d.ts +28 -0
  61. package/dist/provider/sse.js +57 -0
  62. package/dist/provider/types.d.ts +49 -0
  63. package/dist/provider/types.js +9 -0
  64. package/dist/render.d.ts +10 -0
  65. package/dist/render.js +70 -0
  66. package/dist/review/knowledge.d.ts +6 -0
  67. package/dist/review/knowledge.js +25 -0
  68. package/dist/review/render.d.ts +4 -0
  69. package/dist/review/render.js +12 -0
  70. package/dist/review/review.d.ts +17 -0
  71. package/dist/review/review.js +80 -0
  72. package/dist/review/types.d.ts +12 -0
  73. package/dist/review/types.js +1 -0
  74. package/package.json +10 -11
package/dist/index.d.ts CHANGED
@@ -1,791 +1,35 @@
1
- import { GeneratedFile } from '@basaltkit/generator';
2
- import { CommandDefinition } from '@basaltkit/cli';
3
-
4
- /**
5
- * Provider-agnostic AI surface.
6
- *
7
- * The agent never talks to a vendor SDK directly — every piece of it depends on
8
- * this interface. Swapping Anthropic ↔ Ollama ↔ an OpenAI-compatible endpoint is
9
- * therefore an `AI_PROVIDER` config change, not a code change (spec §17).
10
- */
11
- interface AIMessage {
12
- role: 'system' | 'user' | 'assistant';
13
- content: string;
14
- }
15
- interface GenerateOptions {
16
- messages: AIMessage[];
17
- /** Sampling temperature (0 = deterministic). */
18
- temperature?: number;
19
- /** Hard cap on output tokens. */
20
- maxTokens?: number;
21
- }
22
- interface AIProvider {
23
- /** Stable id, e.g. `'anthropic'`. */
24
- readonly name: string;
25
- /** Concrete model id in use, e.g. `'claude-sonnet-5'`. */
26
- readonly model: string;
27
- /** One-shot completion — returns the assistant's full text. */
28
- generate(options: GenerateOptions): Promise<string>;
29
- /** Streaming completion — yields chunks as they arrive. */
30
- stream(options: GenerateOptions): AsyncIterable<string>;
31
- }
32
- /**
33
- * Minimal fetch surface — the global `fetch` (Node 18+) is assignable, and it's
34
- * injectable so providers can be unit-tested without a network. Mirrors the shape
35
- * used by `@basaltkit/search-elasticsearch` to avoid a DOM lib dependency.
36
- */
37
- type FetchLike = (url: string, init?: {
38
- method?: string;
39
- headers?: Record<string, string>;
40
- body?: string;
41
- }) => Promise<{
42
- ok: boolean;
43
- status: number;
44
- text(): Promise<string>;
45
- }>;
46
- /**
47
- * Default `stream()` implementation for providers without real token streaming
48
- * yet: run the one-shot completion and yield it as a single chunk. Real
49
- * server-sent-event streaming lands in a later phase; this keeps the
50
- * {@link AIProvider} contract honest today.
51
- */
52
- declare function singleChunkStream(run: () => Promise<string>): AsyncIterable<string>;
53
-
54
- interface AnthropicProviderOptions {
55
- /** API key → `x-api-key` header. Required. */
56
- apiKey: string;
57
- /** Model id. Defaults to a current Claude model. */
58
- model?: string;
59
- /** API base URL. Defaults to the public Anthropic API. */
60
- baseUrl?: string;
61
- /** Injected fetch. Defaults to the global `fetch`. */
62
- fetch?: FetchLike;
63
- }
64
- /**
65
- * Anthropic Messages API provider — REST directly, no SDK (same approach as the
66
- * Elasticsearch driver). System messages are hoisted into the top-level `system`
67
- * field, as the API requires.
68
- */
69
- declare class AnthropicProvider implements AIProvider {
70
- readonly name = "anthropic";
71
- readonly model: string;
72
- private readonly apiKey;
73
- private readonly baseUrl;
74
- private readonly fetchImpl;
75
- constructor(options: AnthropicProviderOptions);
76
- generate(options: GenerateOptions): Promise<string>;
77
- stream(options: GenerateOptions): AsyncIterable<string>;
78
- }
79
-
80
- interface OllamaProviderOptions {
81
- /** Model id, e.g. `'llama3.1'`. */
82
- model?: string;
83
- /** Ollama base URL. Defaults to the local daemon. */
84
- baseUrl?: string;
85
- /** Injected fetch. Defaults to the global `fetch`. */
86
- fetch?: FetchLike;
87
- }
88
- /**
89
- * Local Ollama provider (`/api/chat`, `stream: false`). Needs no API key, so it's
90
- * the zero-cost default for offline development and CI.
91
- */
92
- declare class OllamaProvider implements AIProvider {
93
- readonly name = "ollama";
94
- readonly model: string;
95
- private readonly baseUrl;
96
- private readonly fetchImpl;
97
- constructor(options?: OllamaProviderOptions);
98
- generate(options: GenerateOptions): Promise<string>;
99
- stream(options: GenerateOptions): AsyncIterable<string>;
100
- }
101
-
102
- /**
103
- * Minimal Server-Sent-Events support for OpenAI-compatible streaming.
104
- *
105
- * Some gateways (e.g. go4ai) return a spurious 500 when they buffer a long
106
- * non-streaming completion, even though the model generated it (and charged for
107
- * it). Streaming sidesteps that: the response is delivered in chunks, so there's
108
- * no large buffered body to choke on.
109
- */
110
- interface SseResponse {
111
- ok: boolean;
112
- status: number;
113
- /** Full body — used for the error message on a non-2xx. */
114
- text(): Promise<string>;
115
- /** Decoded text pieces of the streaming body. */
116
- chunks: AsyncIterable<string>;
117
- }
118
- type SseFetch = (url: string, init: {
119
- method?: string;
120
- headers?: Record<string, string>;
121
- body?: string;
122
- }) => Promise<SseResponse>;
123
- /** Default {@link SseFetch} backed by the global `fetch` + its streaming body. */
124
- declare const globalSseFetch: SseFetch;
125
- /**
126
- * Parse an OpenAI-style SSE stream, yielding each `choices[0].delta.content`
127
- * piece. Tolerant of keep-alive lines, partial frames and the `[DONE]` sentinel.
128
- */
129
- declare function parseSseContent(chunks: AsyncIterable<string>): AsyncIterable<string>;
130
-
131
- interface OpenAICompatibleProviderOptions {
132
- /** API key → `Authorization: Bearer <key>`. Required. */
133
- apiKey: string;
134
- /** Model id, e.g. `'gpt-4o-mini'` or `'us.anthropic.claude-sonnet-4-5-20250929-v1:0'`. */
135
- model?: string;
136
- /**
137
- * API base URL up to and including `/v1`. `/chat/completions` is appended.
138
- * Defaults to the public OpenAI API; point it at any compatible gateway, e.g.
139
- * `https://ai.go4ai.io/v1`.
140
- */
141
- baseUrl?: string;
142
- /**
143
- * Stream the completion via SSE (default `true`). Streaming avoids the spurious
144
- * 500 some gateways return when buffering a long non-streaming response. Set
145
- * `false` for a gateway that doesn't support streaming.
146
- */
147
- stream?: boolean;
148
- /** Injected non-streaming fetch. Defaults to the global `fetch`. */
149
- fetch?: FetchLike;
150
- /** Injected streaming fetch. Defaults to one built on the global `fetch`. */
151
- sseFetch?: SseFetch;
152
- }
153
- /**
154
- * Provider for any OpenAI-compatible Chat Completions endpoint (OpenAI itself,
155
- * Azure-style gateways, LiteLLM, OpenRouter, go4ai, …). Unlike Anthropic's API,
156
- * system messages stay inline in the `messages` array.
157
- *
158
- * Streams by default — the robust path for large generations behind a gateway.
159
- */
160
- declare class OpenAICompatibleProvider implements AIProvider {
161
- readonly name = "openai";
162
- readonly model: string;
163
- private readonly apiKey;
164
- private readonly baseUrl;
165
- private readonly streaming;
166
- private readonly fetchImpl;
167
- private readonly sseFetchImpl;
168
- constructor(options: OpenAICompatibleProviderOptions);
169
- generate(options: GenerateOptions): Promise<string>;
170
- stream(options: GenerateOptions): AsyncIterable<string>;
171
- private get endpoint();
172
- private headers;
173
- private requestBody;
174
- private viaStream;
175
- private viaJson;
176
- }
177
-
178
- interface RetryOptions {
179
- /** Extra attempts after the first (so `2` = up to 3 tries). */
180
- retries?: number;
181
- /** Base backoff in ms; doubles each attempt. */
182
- baseDelayMs?: number;
183
- }
184
- /**
185
- * Fetch with retry on transient 5xx/429 responses and network errors, using
186
- * exponential backoff. Providers hit remote gateways (go4ai, OpenAI, Anthropic)
187
- * that occasionally return a spurious 500 — one retry usually clears it.
188
- *
189
- * Reads the body only on the terminal attempt, so retried responses are simply
190
- * discarded.
191
- */
192
- declare function fetchWithRetry(fetchImpl: FetchLike, url: string, init: {
193
- method?: string;
194
- headers?: Record<string, string>;
195
- body?: string;
196
- }, options?: RetryOptions): Promise<{
197
- ok: boolean;
198
- status: number;
199
- body: string;
200
- }>;
201
-
202
- /** Providers wired in 0.1. `openai` covers any OpenAI-compatible gateway; `google` is stubbed. */
203
- type ProviderName = 'anthropic' | 'ollama' | 'openai' | 'openai-compatible' | 'google';
204
- /** The subset of environment the provider layer reads. */
205
- interface ProviderEnv {
206
- AI_PROVIDER?: string;
207
- AI_MODEL?: string;
208
- AI_API_KEY?: string;
209
- AI_BASE_URL?: string;
210
- /** `'false'` disables SSE streaming for the OpenAI-compatible provider. */
211
- AI_STREAM?: string;
212
- }
213
- interface CreateProviderOptions {
214
- /** Override the injected fetch (tests). */
215
- fetch?: FetchLike;
216
- }
217
- /** Read the provider-relevant vars off `process.env` into a typed {@link ProviderEnv}. */
218
- declare function providerEnvFromProcess(): ProviderEnv;
219
- /**
220
- * Build an {@link AIProvider} from environment. `AI_PROVIDER` selects the vendor
221
- * (default `anthropic`); `AI_MODEL`, `AI_API_KEY`, `AI_BASE_URL` tune it.
222
- *
223
- * The read-only `ai:analyze` / `ai:doctor` commands never call this — their rules
224
- * are deterministic and run offline. A provider is only needed for the optional
225
- * `--explain` narrative and, later, for `ai:plan` / `ai:make`.
226
- */
227
- declare function createProvider(env?: ProviderEnv, options?: CreateProviderOptions): AIProvider;
228
-
229
- /**
230
- * Reads project files relative to a root. Injectable so the whole detection layer
231
- * is unit-testable with an in-memory map (no disk).
232
- */
233
- interface ProjectReader {
234
- read(relPath: string): string | null;
235
- exists(relPath: string): boolean;
236
- }
237
- /** Default reader backed by the filesystem. */
238
- declare function nodeReader(root: string): ProjectReader;
239
- /** In-memory reader for tests: keys are relative paths. */
240
- declare function memoryReader(files: Record<string, string>): ProjectReader;
241
- /** Which capabilities the app actually wires (from its plugin list), not just installs. */
242
- interface DetectedStack {
243
- http: 'fastify' | null;
244
- orm: 'prisma' | null;
245
- database: 'postgresql' | 'mysql' | 'sqlite' | null;
246
- tenancy: boolean;
247
- auth: boolean;
248
- rbac: boolean;
249
- subscriptions: boolean;
250
- payments: boolean;
251
- storage: boolean;
252
- queue: boolean;
253
- search: boolean;
254
- audit: boolean;
255
- events: boolean;
256
- scheduler: boolean;
257
- logger: boolean;
258
- ai: boolean;
259
- }
260
- interface PrismaModel {
261
- name: string;
262
- fields: string[];
263
- /** True when the model carries a `tenantId` column (tenant-scoped). */
264
- tenantScoped: boolean;
265
- }
266
- interface PrismaInfo {
267
- provider: DetectedStack['database'];
268
- models: PrismaModel[];
269
- }
270
- interface AppFileInfo {
271
- path: string;
272
- /** Plugin factories detected in the `plugins:` array, e.g. `'fastifyPlugin'`. */
273
- plugins: string[];
274
- /** `fastifyPlugin({ fastify: { logger … } })` present. */
275
- fastifyLoggerConfigured: boolean;
276
- /** In-memory (non-durable) sources still wired, e.g. `MemoryTenantSource`. */
277
- memorySources: string[];
278
- /** Every `<name>Plugin(` factory call in the app file (unfiltered by capability). */
279
- pluginCalls: string[];
280
- }
281
- interface ServerFileInfo {
282
- path: string;
283
- /** Calls `.$connect()` somewhere in bootstrap (fail-loud DB check). */
284
- connectsAtBoot: boolean;
285
- /** Calls `.listen(` (actually starts a server). */
286
- startsServer: boolean;
287
- }
288
- interface EnvFileInfo {
289
- path: string;
290
- /** Default value baked into `APP_SECRET`, if any. */
291
- appSecretDefault: string | null;
292
- /** Default value baked into `REDIS_URL`, if any. */
293
- redisUrlDefault: string | null;
294
- }
295
- interface ProjectContext {
296
- root: string;
297
- /** All `@basaltkit/*` packages found in package.json deps. */
298
- installed: string[];
299
- stack: DetectedStack;
300
- prisma: PrismaInfo | null;
301
- app: AppFileInfo | null;
302
- server: ServerFileInfo | null;
303
- env: EnvFileInfo | null;
304
- }
305
- /** Detect the project's stack and key files. Pure over the injected reader. */
306
- declare function detectProject(root: string, reader?: ProjectReader): ProjectContext;
307
-
308
- type Severity = 'error' | 'warning' | 'info';
309
- type DiagnosticCategory = 'security' | 'database' | 'observability' | 'tenancy' | 'performance' | 'durability' | 'config';
310
- interface Diagnostic {
311
- /** Stable slug — also the (future) `basalt ai:fix <id>` target. */
312
- id: string;
313
- title: string;
314
- severity: Severity;
315
- category: DiagnosticCategory;
316
- /** What the project currently does. */
317
- detected: string;
318
- /** What it should do instead. */
319
- recommended: string;
320
- /** Why it matters. */
321
- reason: string;
322
- /** Suggested manual fix (code snippet or command). Auto-fix arrives in a later phase. */
323
- fix?: string;
324
- /** Related docs path. */
325
- docs?: string;
326
- }
327
- interface DoctorRule {
328
- id: string;
329
- category: DiagnosticCategory;
330
- /** Returns a diagnostic when the rule fires, else `null`. */
331
- check(ctx: ProjectContext): Diagnostic | null;
332
- }
333
- declare function defineRule(rule: DoctorRule): DoctorRule;
334
-
335
- /**
336
- * Built-in diagnostic rules. Each is pure over a {@link ProjectContext}, so they
337
- * run offline with no API key. New rules are added here (or supplied to
338
- * {@link runDoctor}).
339
- */
340
- declare const DEFAULT_RULES: DoctorRule[];
341
-
342
- /**
343
- * Run every rule against the project context and return the diagnostics that
344
- * fired, most severe first.
345
- */
346
- declare function runDoctor(ctx: ProjectContext, rules?: DoctorRule[]): Diagnostic[];
347
- /** True when any diagnostic is an error — lets CI gate on `ai:doctor`. */
348
- declare function hasErrors(diagnostics: Diagnostic[]): boolean;
349
-
350
- interface AnalysisReport {
351
- root: string;
352
- /** Human-readable capability lines, e.g. `'Fastify detected'`. */
353
- capabilities: string[];
354
- installed: string[];
355
- database: string | null;
356
- models: string[];
357
- tenantScopedModels: string[];
358
- unscopedModels: string[];
359
- /** Diagnostics from the doctor rules, summarised into the report. */
360
- diagnostics: Diagnostic[];
361
- }
362
- /**
363
- * Static, offline analysis of a project: what's wired, the data model, and the
364
- * doctor's findings — the data behind the `ai:analyze` report.
365
- */
366
- declare function analyze(ctx: ProjectContext): AnalysisReport;
367
-
368
- /** The single output method the CLI needs — matches `CommandIo.log`. */
369
- interface LineWriter {
370
- log(message: string): void;
371
- }
372
- /** Render the `ai:analyze` report in the spec's `✓ … detected` style. */
373
- declare function renderAnalysis(report: AnalysisReport, io: LineWriter): void;
374
- /** Render the `ai:doctor` diagnostics in the spec's `AI DIAGNOSTICS` style. */
375
- declare function renderDoctor(diagnostics: Diagnostic[], io: LineWriter): void;
376
-
377
- interface FileEdit {
378
- /** Path relative to the project root. */
379
- path: string;
380
- before: string;
381
- after: string;
382
- }
383
- /** Returns the edits a fix would make, `[]` if there's nothing to change, or `null` if it can't apply. */
384
- type FixApplier = (read: (rel: string) => string | null, ctx: ProjectContext) => FileEdit[] | null;
385
- /**
386
- * Auto-fixers for doctor rules — only the ones with a safe, precise edit. Rules
387
- * that need a judgement call (tenant scoping, choosing a durable store) have no
388
- * entry and report "no auto-fix — apply manually".
389
- */
390
- declare const FIXES: Record<string, FixApplier>;
391
- type FixStatus = 'ready' | 'noop' | 'unfixable';
392
- interface FixOutcome {
393
- id: string;
394
- status: FixStatus;
395
- edits: FileEdit[];
396
- message: string;
397
- }
398
- /** IDs of rules that have an auto-fixer. */
399
- declare function fixableIds(): string[];
400
- /** Compute (but don't write) the edits for a rule's fix. */
401
- declare function planFix(id: string, ctx: ProjectContext, read: (rel: string) => string | null): FixOutcome;
402
- /** Write a fix's edits to disk. */
403
- declare function applyFixEdits(edits: FileEdit[], baseDir: string): Promise<void>;
404
- /** Render fix outcomes with a line-level diff. */
405
- declare function renderFixes(outcomes: FixOutcome[], io: LineWriter): void;
406
-
407
- type PlanStepKind = 'generator' | 'schema' | 'migration' | 'service' | 'routes' | 'permissions' | 'audit' | 'test' | 'docs' | 'other';
408
- interface PlanField {
409
- name: string;
410
- type: string;
411
- /**
412
- * Allowed values — makes this a `z.enum([...])` field (validated on input,
413
- * stored as a String column). E.g. a status: `['pago', 'pendente']`.
414
- */
415
- enum?: string[];
416
- }
417
- /** A belongs-to relation: this entity holds a `<name>Id` FK referencing `model`. */
418
- interface PlanRelation {
419
- /** Relation field name on this model, e.g. `paciente`. */
420
- name: string;
421
- /** Related model, e.g. `Paciente`. */
422
- model: string;
423
- }
424
- interface PlanEntity {
425
- name: string;
426
- fields: PlanField[];
427
- tenantScoped: boolean;
428
- relations?: PlanRelation[];
429
- }
430
- interface PlanStep {
431
- order: number;
432
- title: string;
433
- kind: PlanStepKind;
434
- detail: string;
435
- /** Exact CLI command to run (mainly for `kind: 'generator'`). */
436
- command?: string;
437
- /** Files the step creates or edits. */
438
- files?: string[];
439
- }
440
- interface ArchitecturePlan {
441
- request: string;
442
- summary: string;
443
- entities: PlanEntity[];
444
- steps: PlanStep[];
445
- /** RBAC permissions to register, e.g. `patients.create`. */
446
- permissions: string[];
447
- /** Audit events to emit, e.g. `patient.created`. */
448
- auditEvents: string[];
449
- tenantScoped: boolean;
450
- warnings: string[];
451
- }
452
-
453
- interface CreatePlanOptions {
454
- temperature?: number;
455
- maxTokens?: number;
456
- }
457
- /**
458
- * Turn a natural-language request into a grounded {@link ArchitecturePlan}. The
459
- * project context keeps the plan on-convention and collision-free. Read-only —
460
- * it produces a plan, it changes nothing.
461
- */
462
- declare function createPlan(provider: AIProvider, ctx: ProjectContext, request: string, options?: CreatePlanOptions): Promise<ArchitecturePlan>;
463
- /** Parse + normalize the model's JSON into a validated plan. Tolerant of fences and missing arrays. */
464
- declare function parsePlan(raw: string, request: string): ArchitecturePlan;
465
-
466
- /**
467
- * Compact, relevant-only project summary for the planner (spec §18 Context
468
- * Engineering — never ship the whole project to the model). Surfaces exactly what
469
- * the Architect needs to ground a plan: enabled capabilities, existing models and
470
- * the generator's flags.
471
- */
472
- declare function buildPlanContext(ctx: ProjectContext): string;
473
-
474
- /**
475
- * The Architect agent's system prompt — Basalt's conventions and official APIs,
476
- * encoded so the model plans *with* the framework instead of inventing its own
477
- * architecture (spec §8 Framework Knowledge, §22 "prefer official APIs").
478
- *
479
- * This is deliberately prescriptive: the plan must reuse the generator and the
480
- * platform plugins, never hand-roll parallel abstractions.
481
- */
482
- declare const BASALT_KNOWLEDGE = "You are the Architect agent for Basalt \u2014 a batteries-included SaaS framework for Node.js/TypeScript (Fastify + Prisma + Zod). You turn a developer's natural-language request into a precise, convention-following implementation PLAN. You PLAN only; you never write code here.\n\nABSOLUTE RULES \u2014 always use the framework's official APIs and generators, never invent new ones:\n\n1. SCAFFOLDING. Create a resource with the generator, not by hand:\n `basalt make:resource <Name>` \u2014 generates a full vertical: Zod schema, repository, service, DI plugin, typed routes and a test, all on-convention and auto-wired into src/app.ts.\n Flags: --prisma (Prisma-backed repository + a schema.prisma model with id/createdAt/updatedAt), --soft-delete (adds deletedAt, a restore() method + route), --no-register (skip wiring), --dir=<path>, --force.\n\n2. PERSISTENCE. Prisma via prismaPlugin. Models live in schema.prisma. After editing the schema, run `basalt prisma:sync` to generate the client + migration. Use --prisma so the generator emits the model for you.\n\n3. MULTI-TENANCY. When tenancy is enabled, every tenant-owned model MUST carry a `tenantId` column, and the tenancy layer scopes queries by the current tenant. NEVER plan a query that ignores tenant isolation. Platform-global tables (users, tenants, plans) are the only exceptions.\n\n4. RBAC / PERMISSIONS. For each resource, register `<resource>.view`, `<resource>.create`, `<resource>.update`, `<resource>.delete` and guard the matching routes with the permission check. Use the resource name in plural, lowercase (e.g. `patients.create`).\n\n5. AUDIT. Emit an audit event for each state change: `<resource>.created`, `<resource>.updated`, `<resource>.deleted` (singular resource).\n\n6. VALIDATION + DOCS. Every route carries a Zod schema; OpenAPI is generated from it automatically \u2014 no separate doc wiring.\n\n7. TESTS. Every resource ships a test (the generator creates one; extend it for domain rules).\n\nGROUNDING: Reuse what already exists in the project. Avoid name collisions with existing models. Only add what is missing. Prefer editing the schema + running the generator over hand-writing files.\n\nOUTPUT: Return ONLY a single JSON object (no prose, no markdown fences) matching exactly:\n{\n \"summary\": string, // 1-2 sentences on the approach\n \"entities\": [ // domain entities to create\n { \"name\": string, \"fields\": [{ \"name\": string, \"type\": string, \"enum\"?: string[] }], \"tenantScoped\": boolean,\n \"relations\": [{ \"name\": string, \"model\": string }] } // belongs-to: this model gets a <name>Id FK \u2192 <model>\n ],\n \"steps\": [ // ordered implementation steps\n { \"order\": number, \"title\": string, \"kind\": \"generator\"|\"schema\"|\"migration\"|\"service\"|\"routes\"|\"permissions\"|\"audit\"|\"test\"|\"docs\"|\"other\", \"detail\": string, \"command\": string, \"files\": string[] }\n ],\n \"permissions\": string[], // e.g. [\"patients.view\",\"patients.create\",...]\n \"auditEvents\": string[], // e.g. [\"patient.created\",...]\n \"tenantScoped\": boolean, // does this feature involve tenant-owned data\n \"warnings\": string[] // risks, decisions to confirm, missing info\n}\nFor generator steps, put the exact command in \"command\" (e.g. \"basalt make:resource Patient --prisma --soft-delete\"). Omit \"command\"/\"files\" when not applicable.\n\nENUMS: a field with a fixed set of values (a status/state) MUST set \"enum\" to those values \u2014 e.g. \"estado (pago/pendente)\" \u2192 { \"name\": \"estado\", \"type\": \"String\", \"enum\": [\"pago\", \"pendente\"] }. It becomes a validated z.enum([...]) (stored as a String column), never a free z.string().\n\nRELATIONS: express a belongs-to as an entry in \"relations\" (e.g. an Appointment belongs to a Patient \u2192 { \"name\": \"patient\", \"model\": \"Patient\" }). Do NOT also add the \"<name>Id\" field to \"fields\" \u2014 the FK column, the @relation and the inverse field are generated. Prefer generating both sides of a relation in the same plan.";
483
-
484
- /** Render an {@link ArchitecturePlan} in the spec's `ARCHITECTURE PLAN` style. */
485
- declare function renderPlan(plan: ArchitecturePlan, io: LineWriter): void;
486
-
487
- interface MakeOptions {
488
- /** Skip writing to disk — return what would be generated. */
489
- dryRun?: boolean;
490
- /** Overwrite existing files instead of refusing. */
491
- force?: boolean;
492
- /** Project root. Defaults to process.cwd(). */
493
- baseDir?: string;
494
- /** Force a Prisma-backed resource (default: inferred from the stack). */
495
- prisma?: boolean;
496
- /** Force soft-delete (default: from the plan's generator command). */
497
- softDelete?: boolean;
498
- /** Path to the Prisma schema to merge models into. Default `prisma/schema.prisma`. */
499
- schemaPath?: string;
500
- /** After merging models, run `prisma db push` (creates tables + regenerates the client). */
501
- migrate?: boolean;
502
- }
503
- /** How the generated models were merged into prisma/schema.prisma. */
504
- interface SchemaMerge {
505
- path: string;
506
- /** The schema file was found on disk. */
507
- found: boolean;
508
- /** Models appended. */
509
- merged: string[];
510
- /** Models already present (left as is). */
511
- skipped: string[];
512
- /** The schema file was written (false on dry-run). */
513
- written: boolean;
514
- }
515
- /** Result of an opt-in `prisma db push`. */
516
- interface Migration {
517
- ok: boolean;
518
- /** Tail of the command output. */
519
- output: string;
520
- }
521
- interface ResourceBuild {
522
- name: string;
523
- prisma: boolean;
524
- softDelete: boolean;
525
- /** Generated (and domain-augmented) files. */
526
- files: GeneratedFile[];
527
- /** Domain fields were injected into schema/model. */
528
- augmented: boolean;
529
- /** Permission `meta.can` guards were injected into the routes. */
530
- guarded: boolean;
531
- /** Audit recording was injected into the service + plugin. */
532
- audited: boolean;
533
- /** Files actually written (empty on dry-run or on clash). */
534
- written: string[];
535
- /** Wired into src/app.ts. */
536
- registered: boolean;
537
- /** Why a step didn't complete (e.g. a file clash). */
538
- note?: string;
539
- }
540
- type ReviewStatus = 'pass' | 'warn' | 'fail';
541
- interface ReviewItem {
542
- label: string;
543
- status: ReviewStatus;
544
- detail: string;
545
- }
546
- interface ReviewResult {
547
- items: ReviewItem[];
548
- /** True when no item failed. */
549
- ok: boolean;
550
- }
551
- interface MakeResult {
552
- request: string;
553
- dryRun: boolean;
554
- resources: ResourceBuild[];
555
- /** How models were merged into the Prisma schema (undefined when no Prisma resource). */
556
- schema?: SchemaMerge;
557
- /** Result of `--migrate` (undefined when not requested). */
558
- migration?: Migration;
559
- /** Manual follow-ups the scaffold can't do yet. */
560
- followUps: string[];
561
- review: ReviewResult;
562
- }
563
-
564
- /**
565
- * Execute an {@link ArchitecturePlan}: scaffold each entity via the official
566
- * generator, inject the plan's domain fields, write + auto-wire, then run a
567
- * deterministic review gate. Read-only when `dryRun` is set.
568
- *
569
- * Hybrid by design (the locked decision): the generator produces the
570
- * on-convention vertical; we only augment it with fields the plan already
571
- * decided. No destructive operations — never overwrites without `force`, never
572
- * touches the database, never deletes.
573
- */
574
- declare function runMake(ctx: ProjectContext, plan: ArchitecturePlan, options?: MakeOptions): Promise<MakeResult>;
575
-
576
- /** Render a {@link MakeResult} — what was generated, the review gate and follow-ups. */
577
- declare function renderMakeResult(result: MakeResult, io: LineWriter): void;
578
-
579
- interface VerifyResult {
580
- ok: boolean;
581
- /** Command that was run, e.g. `pnpm typecheck`. */
582
- command: string;
583
- /** Combined stdout/stderr tail on failure. */
584
- output: string;
585
- }
586
- /**
587
- * Run the project's typecheck as a review gate (spec §12/§20). Best-effort: if
588
- * the command is missing or errors, it reports failure rather than throwing.
589
- */
590
- declare function verifyProject(baseDir: string, command?: string, args?: string[]): Promise<VerifyResult>;
591
-
592
- type CanonicalType = 'String' | 'Int' | 'Float' | 'Boolean' | 'DateTime' | 'Json';
593
- /** Map a loose plan type (`'string'`, `'DateTime'`, `'number'`…) to a canonical type. */
594
- declare function canonicalType(raw: string): CanonicalType;
595
- /** Prisma column type — identical to the canonical name. */
596
- declare function prismaType(t: CanonicalType): string;
597
- /**
598
- * Zod validator for a field. On input (`create`) a `DateTime` coerces from a
599
- * date/ISO string to a `Date` (so Prisma accepts it — a bare `z.string()` lets a
600
- * non-ISO value like `"1990-05-01"` through to Prisma and 500). On output it's an
601
- * ISO string, matching the mapper.
602
- */
603
- declare function zodValidator(t: CanonicalType, create: boolean): string;
604
- /** Zod validator for a field, honoring an enum (`z.enum([...])`) over its base type. */
605
- declare function zodForField(field: PlanField, create: boolean): string;
606
- /** The entity's own fields, minus reserved/base columns and duplicates. */
607
- declare function domainFields(fields: PlanField[]): PlanField[];
608
- /**
609
- * Inject domain fields (and `tenantId` + `@@index` when tenant-scoped) into a
610
- * generated Prisma model snippet, right after its `name` column. Returns
611
- * `injected: false` if the expected anchor isn't found — the scaffold still lands
612
- * unchanged and the caller reports it.
613
- */
614
- declare function injectPrismaFields(content: string, fields: PlanField[], tenantScoped: boolean, removeName?: boolean): {
615
- content: string;
616
- injected: boolean;
617
- };
618
- /**
619
- * Inject domain fields into a generated Zod schema file — into both the entity
620
- * schema (`<Name>Schema`) and the create schema (`Create<Name>Schema`).
621
- */
622
- declare function injectZodFields(content: string, fields: PlanField[], removeName?: boolean): {
623
- content: string;
624
- injected: boolean;
625
- };
626
-
627
- interface RepositoryOptions {
628
- softDelete: boolean;
629
- /** Scope every query by tenantId from context (portable — no global extension needed). */
630
- tenantScoped: boolean;
631
- /** Keep the generator's base `name` column. */
632
- keepName: boolean;
633
- }
634
- /**
635
- * Render a complete Prisma repository for a resource — replacing the generator's,
636
- * which (a) only maps id/name/timestamps (dropping domain fields) and (b) relies
637
- * on the global tenancy extension to stamp tenantId. This one maps every domain
638
- * field and, when tenant-scoped, scopes tenantId explicitly from the request
639
- * context, so it works even on a raw client shared with non-tenant models.
640
- */
641
- declare function renderPrismaRepository(name: string, fields: PlanField[], options: RepositoryOptions): string;
642
-
643
- /**
644
- * The RBAC permissions for a resource — the exact `<plural>.<action>` strings its
645
- * route guards check on (F5 derives guards from the entity name, so declaring
646
- * them here keeps a single source of truth that can't drift from the guards).
647
- */
648
- declare function permissionList(name: string): string[];
649
- /**
650
- * Generate `<kebab>.permissions.ts`: the permission constants plus a
651
- * `grant<Name>Permissions(store, role)` helper — so registering RBAC for the
652
- * resource is one call in the app's seed/setup, closing the loop F4 opened
653
- * (guards without a way to grant them).
654
- */
655
- declare function renderPermissionsFile(name: string): string;
656
-
657
- /**
658
- * OpenAPI enrichment (spec §14): add a `summary` and `tags` to each generated
659
- * route's `meta`, merging into an existing `meta` (e.g. the `can` guard) or
660
- * creating one. `@basaltkit/http` renders these into the OpenAPI document.
661
- */
662
- declare function injectOpenApiMeta(content: string, entityName: string): {
663
- content: string;
664
- injected: boolean;
665
- };
666
-
667
- /**
668
- * Real Prisma relations (Fase 6). A belongs-to relation on an entity generates:
669
- * a `<name>Id` FK column, a `<name> <Model> @relation(...)` field, and — on the
670
- * related model — an inverse `<plural> <This>[]` field. Both sides are required
671
- * for Prisma to validate, so a relation to a model outside the plan is surfaced
672
- * as a follow-up (the user adds the inverse field to their existing model).
673
- */
674
- /** FK column name for a relation, e.g. `paciente` → `pacienteId`. */
675
- declare function foreignKey(relation: PlanRelation): string;
676
- /** Synthetic FK fields (String) for an entity's relations — feed the Zod schema, mapper and columns. */
677
- declare function relationForeignKeys(entity: PlanEntity): PlanField[];
678
- /** Prisma relation field lines for the entity's belongs-to relations. */
679
- declare function relationFieldLines(entity: PlanEntity): string[];
680
- /** Inverse `hasMany` lines to add to `modelName`, from every other entity that belongs to it. */
681
- declare function inverseRelationLines(modelName: string, allEntities: PlanEntity[]): string[];
682
- /** Insert relation + inverse lines into a generated Prisma model, before its closing brace. */
683
- declare function injectPrismaRelations(content: string, relationLines: string[], inverseLines: string[]): {
684
- content: string;
685
- injected: boolean;
686
- };
687
- /** Relation targets that aren't generated in this plan (need a manual inverse field). */
688
- declare function externalRelationTargets(entities: PlanEntity[]): string[];
689
-
690
- /**
691
- * Inject `meta: { can: '<prefix>.<action>' }` into every generated route, so the
692
- * permissions guard enforces it. `prefix` is the resource's permission namespace
693
- * (e.g. `patients`) — derived per entity by the caller, so each entity in a
694
- * multi-entity plan gets its own correct permission, not a shared one.
695
- */
696
- declare function injectPermissionGuards(content: string, prefix: string): {
697
- content: string;
698
- injected: boolean;
699
- };
700
- /**
701
- * Inject audit recording into the generated service: the constructor receives an
702
- * `Audit`, and create/update/remove record their event after the repository call.
703
- */
704
- declare function injectAuditService(content: string, resourceName: string, events: string[]): {
705
- content: string;
706
- injected: boolean;
707
- };
708
- /** Inject the `AUDIT` dependency into the generated plugin's service construction. */
709
- declare function injectAuditPlugin(content: string, resourceName: string): {
710
- content: string;
711
- injected: boolean;
712
- };
713
-
714
- interface ModelBlock {
715
- name: string;
716
- block: string;
717
- }
718
- /** Extract the `model <Name> { … }` block from a generated `.prisma` snippet. */
719
- declare function extractModelBlock(snippet: string): ModelBlock | null;
720
- interface MergeOutcome {
721
- content: string;
722
- merged: string[];
723
- skipped: string[];
724
- }
725
- /** Append model blocks to a schema, skipping any model already declared (idempotent). */
726
- declare function mergeModelsIntoSchema(schema: string, blocks: ModelBlock[]): MergeOutcome;
727
- interface PrismaPushResult {
728
- ok: boolean;
729
- /** Tail of stdout/stderr. */
730
- output: string;
731
- }
732
- /** Run `npx prisma db push` — creates the table(s) and regenerates the client. Best-effort. */
733
- declare function runPrismaPush(baseDir: string): Promise<PrismaPushResult>;
734
-
735
- interface ReviewIssue {
736
- /** e.g. `tenancy`, `security`, `rbac`, `validation`, `tests`, `fit`. */
737
- dimension: string;
738
- severity: 'error' | 'warning';
739
- message: string;
740
- }
741
- interface AgentReview {
742
- /** Derived: true only when no issue is error-severity. */
743
- approved: boolean;
744
- summary: string;
745
- issues: ReviewIssue[];
746
- }
747
-
748
- interface ReviewOptions {
749
- temperature?: number;
750
- maxTokens?: number;
751
- }
752
- /**
753
- * Compact, relevant-only context for the Review agent: the request, the plan, the
754
- * deterministic review, and the generated code that carries behaviour.
755
- */
756
- declare function buildReviewContext(plan: ArchitecturePlan, result: MakeResult): string;
757
- /** Ask the Review agent to critique the generated code. Read-only — it judges, it doesn't edit. */
758
- declare function reviewImplementation(provider: AIProvider, plan: ArchitecturePlan, result: MakeResult, options?: ReviewOptions): Promise<AgentReview>;
759
- /** Parse the verdict. `approved` is derived from the issues, not trusted from the model. */
760
- declare function parseReview(raw: string): AgentReview;
761
-
762
- /**
763
- * The Review agent's rubric (spec §20). It reviews the *generated code* against
764
- * Basalt's conventions and the original request, and returns issues by dimension.
765
- * It does not rewrite code — it judges it.
766
- */
767
- declare const REVIEW_KNOWLEDGE = "You are the Review agent for Basalt, a SaaS framework (Fastify + Prisma + Zod). Given a feature request, its plan, the deterministic review results and the generated code, decide whether the implementation is correct and on-convention. You do NOT rewrite code \u2014 you review it, precisely.\n\nSCOPE: `ai:make` generates a BACKEND resource vertical only \u2014 Prisma model, Zod schema, typed routes, service, repository, permissions and a test. It does NOT generate web/UI, React, frontend pages, background jobs, emails or infrastructure; other tools do. Review ONLY the backend vertical, on its own terms. If the request also asks for something outside this scope (a web page, a component, a job\u2026), mention it as a \"warning\" at most \u2014 NEVER an \"error\" \u2014 since `ai:make` is not the tool that produces it.\n\nCheck these dimensions and only raise an issue you can point to in the code:\n\n- tenancy: a tenant-scoped model MUST carry a tenantId column, and every query in its repository MUST be scoped by tenantId (create stamps it; list/find/update/delete filter by it). Raise an ERROR if a tenant-scoped resource can leak or write across tenants.\n- security: no hardcoded secrets; request bodies validated by Zod; no obvious injection.\n- rbac: routes carry `meta: { can: '<resource>.<action>' }` guards, and the permissions are declared. ERROR if a mutating route is unguarded when RBAC is enabled.\n- validation: every route has a Zod schema; the repository maps Prisma rows to the API type (no Date leaking as a Date, no raw row returned).\n- audit: state changes record audit events when audit is enabled (warning if not).\n- tests: a test file exists.\n- fit: the generated backend matches the request's DATA MODEL \u2014 the right entities, fields, relations and tenancy. Do NOT raise an \"error\" for artifacts outside the backend vertical (web UI, frontend, jobs); those are out of scope.\n\nReturn ONLY a single JSON object (no prose, no markdown fences):\n{\n \"summary\": string, // one or two sentences\n \"issues\": [ { \"dimension\": string, \"severity\": \"error\" | \"warning\", \"message\": string } ]\n}\nRaise \"error\" only for a real, blocking convention violation you can cite; use \"warning\" for improvements. An empty issues array means the implementation is approved.";
768
-
769
- /** Render the Review agent's verdict in the spec's `Implementation rejected` style. */
770
- declare function renderAgentReview(review: AgentReview, io: LineWriter): void;
771
-
772
- interface AiCommandsOptions {
773
- /** Project root to analyze. Defaults to the current working directory. */
774
- cwd?: string;
775
- }
776
- /**
777
- * CLI commands for the AI-native DX (foundation phase):
778
- *
779
- * - `basalt ai` — overview: detected stack + what the agent can do
780
- * - `basalt ai:analyze` — static analysis report (read-only, offline)
781
- * - `basalt ai:doctor` — diagnostics with fixes (read-only, offline; exits 1 on errors)
782
- * - `basalt ai:fix` — apply a doctor fix, or all auto-fixable ones (offline; --dry-run/--yes)
783
- * - `basalt ai:plan` — natural language → architecture plan (read-only; needs a provider)
784
- * - `basalt ai:make` — plan then implement: scaffold + domain fields, review gate (writes; needs a provider)
785
- *
786
- * Wire them in like the generator commands:
787
- * `commandsPlugin([...generatorCommands(), ...aiCommands()])`.
788
- */
789
- declare function aiCommands(options?: AiCommandsOptions): CommandDefinition[];
790
-
791
- export { type AIMessage, type AIProvider, type AgentReview, type AiCommandsOptions, type AnalysisReport, AnthropicProvider, type AnthropicProviderOptions, type AppFileInfo, type ArchitecturePlan, BASALT_KNOWLEDGE, type CanonicalType, type CreatePlanOptions, type CreateProviderOptions, DEFAULT_RULES, type DetectedStack, type Diagnostic, type DiagnosticCategory, type DoctorRule, type EnvFileInfo, FIXES, type FetchLike, type FileEdit, type FixApplier, type FixOutcome, type FixStatus, type GenerateOptions, type LineWriter, type MakeOptions, type MakeResult, type Migration, type ModelBlock, OllamaProvider, type OllamaProviderOptions, OpenAICompatibleProvider, type OpenAICompatibleProviderOptions, type PlanEntity, type PlanField, type PlanRelation, type PlanStep, type PlanStepKind, type PrismaInfo, type PrismaModel, type ProjectContext, type ProjectReader, type ProviderEnv, type ProviderName, REVIEW_KNOWLEDGE, type RepositoryOptions, type ResourceBuild, type RetryOptions, type ReviewIssue, type ReviewItem, type ReviewOptions, type ReviewResult, type ReviewStatus, type SchemaMerge, type ServerFileInfo, type Severity, type SseFetch, type SseResponse, type VerifyResult, aiCommands, analyze, applyFixEdits, buildPlanContext, buildReviewContext, canonicalType, createPlan, createProvider, defineRule, detectProject, domainFields, externalRelationTargets, extractModelBlock, fetchWithRetry, fixableIds, foreignKey, globalSseFetch, hasErrors, injectAuditPlugin, injectAuditService, injectOpenApiMeta, injectPermissionGuards, injectPrismaFields, injectPrismaRelations, injectZodFields, inverseRelationLines, memoryReader, mergeModelsIntoSchema, nodeReader, parsePlan, parseReview, parseSseContent, permissionList, planFix, prismaType, providerEnvFromProcess, relationFieldLines, relationForeignKeys, renderAgentReview, renderAnalysis, renderDoctor, renderFixes, renderMakeResult, renderPermissionsFile, renderPlan, renderPrismaRepository, reviewImplementation, runDoctor, runMake, runPrismaPush, singleChunkStream, verifyProject, zodForField, zodValidator };
1
+ export { singleChunkStream, type AIMessage, type AIProvider, type FetchLike, type GenerateOptions, } from './provider/types.js';
2
+ export { AnthropicProvider, type AnthropicProviderOptions } from './provider/anthropic.js';
3
+ export { OllamaProvider, type OllamaProviderOptions } from './provider/ollama.js';
4
+ export { OpenAICompatibleProvider, type OpenAICompatibleProviderOptions, } from './provider/openai.js';
5
+ export { fetchWithRetry, type RetryOptions } from './provider/http.js';
6
+ export { globalSseFetch, parseSseContent, type SseFetch, type SseResponse } from './provider/sse.js';
7
+ export { createProvider, providerEnvFromProcess, type CreateProviderOptions, type ProviderEnv, type ProviderName, } from './provider/factory.js';
8
+ export { detectProject, nodeReader, memoryReader, type AppFileInfo, type DetectedStack, type EnvFileInfo, type PrismaInfo, type PrismaModel, type ProjectContext, type ProjectReader, type ServerFileInfo, } from './context/project.js';
9
+ export { defineRule, type Diagnostic, type DiagnosticCategory, type DoctorRule, type Severity, } from './doctor/types.js';
10
+ export { DEFAULT_RULES } from './doctor/rules.js';
11
+ export { hasErrors, runDoctor } from './doctor/run.js';
12
+ export { applyFixEdits, fixableIds, planFix, renderFixes, FIXES, type FileEdit, type FixApplier, type FixOutcome, type FixStatus, } from './doctor/fixes.js';
13
+ export { analyze, type AnalysisReport } from './analyze/run.js';
14
+ export { createPlan, parsePlan, type CreatePlanOptions } from './plan/plan.js';
15
+ export { buildPlanContext } from './plan/context.js';
16
+ export { BASALT_KNOWLEDGE } from './plan/knowledge.js';
17
+ export { renderPlan } from './plan/render.js';
18
+ export type { ArchitecturePlan, PlanEntity, PlanField, PlanRelation, PlanStep, PlanStepKind, } from './plan/types.js';
19
+ export { runMake } from './make/make.js';
20
+ export { renderMakeResult } from './make/render.js';
21
+ export { verifyProject, type VerifyResult } from './make/verify.js';
22
+ export { canonicalType, domainFields, injectPrismaFields, injectZodFields, prismaType, zodForField, zodValidator, type CanonicalType, } from './make/fields.js';
23
+ export { renderPrismaRepository, type RepositoryOptions } from './make/repository.js';
24
+ export { permissionList, renderPermissionsFile } from './make/permissions.js';
25
+ export { injectOpenApiMeta } from './make/openapi.js';
26
+ export { externalRelationTargets, foreignKey, injectPrismaRelations, inverseRelationLines, relationFieldLines, relationForeignKeys, } from './make/relations.js';
27
+ export { injectAuditPlugin, injectAuditService, injectPermissionGuards } from './make/wire.js';
28
+ export { extractModelBlock, mergeModelsIntoSchema, runPrismaPush, type ModelBlock, } from './make/schema.js';
29
+ export type { MakeOptions, MakeResult, Migration, ResourceBuild, ReviewItem, ReviewResult, ReviewStatus, SchemaMerge, } from './make/types.js';
30
+ export { buildReviewContext, parseReview, reviewImplementation, type ReviewOptions } from './review/review.js';
31
+ export { REVIEW_KNOWLEDGE } from './review/knowledge.js';
32
+ export { renderAgentReview } from './review/render.js';
33
+ export type { AgentReview, ReviewIssue } from './review/types.js';
34
+ export { renderAnalysis, renderDoctor, type LineWriter } from './render.js';
35
+ export { aiCommands, type AiCommandsOptions } from './commands.js';