voidforge-build 23.9.1 → 23.10.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 (48) hide show
  1. package/dist/.claude/agents/kusanagi-devops.md +8 -0
  2. package/dist/.claude/agents/leia-secrets.md +10 -0
  3. package/dist/.claude/agents/picard-architecture.md +8 -0
  4. package/dist/.claude/agents/silver-surfer-herald.md +14 -0
  5. package/dist/.claude/agents/thufir-protocol-parsing.md +10 -0
  6. package/dist/.claude/commands/architect.md +18 -0
  7. package/dist/.claude/commands/campaign.md +24 -1
  8. package/dist/.claude/commands/deploy.md +31 -0
  9. package/dist/.claude/commands/prd.md +8 -0
  10. package/dist/CHANGELOG.md +64 -0
  11. package/dist/VERSION.md +3 -1
  12. package/dist/docs/methods/BUILD_PROTOCOL.md +19 -0
  13. package/dist/docs/methods/CAMPAIGN.md +8 -0
  14. package/dist/docs/methods/DEVOPS_ENGINEER.md +64 -0
  15. package/dist/docs/methods/FORGE_KEEPER.md +62 -3
  16. package/dist/docs/methods/PRD_GENERATOR.md +15 -0
  17. package/dist/docs/methods/SPEC_HANDOFF.md +53 -0
  18. package/dist/docs/methods/SYSTEMS_ARCHITECT.md +13 -0
  19. package/dist/docs/methods/TROUBLESHOOTING.md +27 -0
  20. package/dist/docs/patterns/deploy-preflight.ts +195 -0
  21. package/dist/scripts/voidforge.js +0 -0
  22. package/package.json +1 -1
  23. package/dist/wizard/lib/anomaly-detection.d.ts +0 -59
  24. package/dist/wizard/lib/anomaly-detection.js +0 -122
  25. package/dist/wizard/lib/asset-scanner.d.ts +0 -23
  26. package/dist/wizard/lib/asset-scanner.js +0 -107
  27. package/dist/wizard/lib/build-analytics.d.ts +0 -39
  28. package/dist/wizard/lib/build-analytics.js +0 -91
  29. package/dist/wizard/lib/codegen/erd-gen.d.ts +0 -16
  30. package/dist/wizard/lib/codegen/erd-gen.js +0 -98
  31. package/dist/wizard/lib/codegen/openapi-gen.d.ts +0 -15
  32. package/dist/wizard/lib/codegen/openapi-gen.js +0 -79
  33. package/dist/wizard/lib/codegen/prisma-types.d.ts +0 -15
  34. package/dist/wizard/lib/codegen/prisma-types.js +0 -44
  35. package/dist/wizard/lib/codegen/seed-gen.d.ts +0 -16
  36. package/dist/wizard/lib/codegen/seed-gen.js +0 -128
  37. package/dist/wizard/lib/correlation-engine.d.ts +0 -59
  38. package/dist/wizard/lib/correlation-engine.js +0 -152
  39. package/dist/wizard/lib/desktop-notify.d.ts +0 -27
  40. package/dist/wizard/lib/desktop-notify.js +0 -98
  41. package/dist/wizard/lib/image-gen.d.ts +0 -56
  42. package/dist/wizard/lib/image-gen.js +0 -159
  43. package/dist/wizard/lib/natural-language-deploy.d.ts +0 -30
  44. package/dist/wizard/lib/natural-language-deploy.js +0 -186
  45. package/dist/wizard/lib/route-optimizer.d.ts +0 -28
  46. package/dist/wizard/lib/route-optimizer.js +0 -93
  47. package/dist/wizard/lib/service-install.d.ts +0 -18
  48. package/dist/wizard/lib/service-install.js +0 -182
@@ -1,152 +0,0 @@
1
- /**
2
- * Chakotay's Correlation Engine — connects product changes to metric outcomes (v12.2).
3
- *
4
- * Maintains an event log of product changes (campaigns, deploys) and metric
5
- * observations (traffic, conversions, revenue, performance scores). Computes
6
- * before/after comparisons to identify which changes drove which outcomes.
7
- *
8
- * PRD Reference: ROADMAP v12.2, DEEP_CURRENT.md LEARN step
9
- */
10
- import { appendFile, readFile, mkdir } from 'node:fs/promises';
11
- import { existsSync } from 'node:fs';
12
- import { correlationsPath, predictionsPath, deepCurrentDir } from './deep-current.js';
13
- import { findProjectRoot } from './marker.js';
14
- // ── Event Logging ─────────────────────────────────────
15
- async function appendEvent(entry) {
16
- await mkdir(deepCurrentDir(findProjectRoot() ?? process.cwd()), { recursive: true });
17
- await appendFile(correlationsPath(findProjectRoot() ?? process.cwd()), JSON.stringify(entry) + '\n', 'utf-8');
18
- }
19
- export async function logCampaignComplete(name, missions, dimension) {
20
- await appendEvent({
21
- type: 'campaign_complete',
22
- date: new Date().toISOString(),
23
- campaign: name,
24
- missions,
25
- dimension,
26
- description: `Campaign "${name}" completed (${missions} missions, targeting ${dimension})`,
27
- });
28
- }
29
- export async function logMetric(metric, value, source) {
30
- await appendEvent({
31
- type: 'metric',
32
- date: new Date().toISOString(),
33
- metric,
34
- value,
35
- source,
36
- });
37
- }
38
- // ── Correlation Detection ─────────────────────────────
39
- /**
40
- * Detect correlations between recent campaigns and metric changes.
41
- * Uses before/after comparison with configurable lag windows.
42
- */
43
- export async function detectCorrelations(lagDays = 7) {
44
- if (!existsSync(correlationsPath(findProjectRoot() ?? process.cwd())))
45
- return [];
46
- const content = await readFile(correlationsPath(findProjectRoot() ?? process.cwd()), 'utf-8');
47
- const lines = content.trim().split('\n').filter(Boolean);
48
- const events = lines.map(l => { try {
49
- return JSON.parse(l);
50
- }
51
- catch {
52
- return null;
53
- } }).filter(Boolean);
54
- const changes = events.filter(e => e.type === 'campaign_complete');
55
- const metrics = events.filter(e => e.type === 'metric');
56
- const correlations = [];
57
- for (const change of changes) {
58
- const changeDate = new Date(change.date).getTime();
59
- // Find metrics of the same type before and after the change
60
- const metricNames = [...new Set(metrics.map(m => m.metric))];
61
- for (const metricName of metricNames) {
62
- const metricsOfType = metrics.filter(m => m.metric === metricName);
63
- // Before: last metric reading before the change
64
- const before = metricsOfType
65
- .filter(m => new Date(m.date).getTime() < changeDate)
66
- .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())[0];
67
- // After: first metric reading lagDays after the change
68
- const afterCutoff = changeDate + lagDays * 24 * 60 * 60 * 1000;
69
- const after = metricsOfType
70
- .filter(m => {
71
- const t = new Date(m.date).getTime();
72
- return t > changeDate && t <= afterCutoff;
73
- })
74
- .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())[0];
75
- if (before && after && before.value !== after.value) {
76
- const delta = after.value - before.value;
77
- const pctChange = before.value !== 0 ? Math.round((delta / before.value) * 100) : 0;
78
- const significance = Math.abs(pctChange);
79
- // Only record meaningful changes (>5%)
80
- if (significance > 5) {
81
- const correlation = {
82
- type: 'correlation',
83
- date: new Date().toISOString(),
84
- productChange: change.campaign || change.description,
85
- metric: metricName,
86
- valueBefore: before.value,
87
- valueAfter: after.value,
88
- delta: `${delta > 0 ? '+' : ''}${pctChange}%`,
89
- confidence: significance > 30 ? 'high' : significance > 15 ? 'medium' : 'low',
90
- lagDays,
91
- };
92
- correlations.push(correlation);
93
- await appendEvent(correlation); // Record the correlation in the log
94
- }
95
- }
96
- }
97
- }
98
- return correlations;
99
- }
100
- export async function recordPrediction(proposal) {
101
- await mkdir(deepCurrentDir(findProjectRoot() ?? process.cwd()), { recursive: true });
102
- const record = {
103
- proposalId: proposal.id,
104
- proposalName: proposal.name,
105
- predictedImpact: proposal.expectedImpact,
106
- recordedAt: new Date().toISOString(),
107
- };
108
- await appendFile(predictionsPath(findProjectRoot() ?? process.cwd()), JSON.stringify(record) + '\n', 'utf-8');
109
- }
110
- export async function evaluatePrediction(proposalId, actualImpact, accuracy) {
111
- // Read existing predictions, find the matching one, update it
112
- if (!existsSync(predictionsPath(findProjectRoot() ?? process.cwd())))
113
- return;
114
- const content = await readFile(predictionsPath(findProjectRoot() ?? process.cwd()), 'utf-8');
115
- const lines = content.trim().split('\n').filter(Boolean);
116
- const updated = lines.map(line => {
117
- try {
118
- const record = JSON.parse(line);
119
- if (record.proposalId === proposalId) {
120
- record.actualImpact = actualImpact;
121
- record.accuracy = accuracy;
122
- record.evaluatedAt = new Date().toISOString();
123
- }
124
- return JSON.stringify(record);
125
- }
126
- catch {
127
- return line;
128
- }
129
- });
130
- const { writeFile: wf } = await import('node:fs/promises');
131
- await wf(predictionsPath(findProjectRoot() ?? process.cwd()), updated.join('\n') + '\n');
132
- }
133
- /**
134
- * Calculate average prediction accuracy across all evaluated predictions.
135
- */
136
- export async function getAveragePredictionAccuracy() {
137
- if (!existsSync(predictionsPath(findProjectRoot() ?? process.cwd())))
138
- return 0;
139
- const content = await readFile(predictionsPath(findProjectRoot() ?? process.cwd()), 'utf-8');
140
- const records = content.trim().split('\n').filter(Boolean)
141
- .map(l => { try {
142
- return JSON.parse(l);
143
- }
144
- catch {
145
- return null;
146
- } })
147
- .filter(Boolean);
148
- const evaluated = records.filter(r => r.accuracy !== undefined);
149
- if (evaluated.length === 0)
150
- return 0;
151
- return evaluated.reduce((sum, r) => sum + (r.accuracy || 0), 0) / evaluated.length;
152
- }
@@ -1,27 +0,0 @@
1
- /**
2
- * Desktop Notifications — macOS/Linux native notifications for daemon events.
3
- *
4
- * Uses osascript on macOS and notify-send on Linux. No dependencies.
5
- * Notifications are non-blocking and failure-tolerant (notification failure
6
- * should never crash the daemon).
7
- *
8
- * PRD Reference: §9.7 (Danger Room shows warning), v11.3 deliverables
9
- */
10
- type NotificationUrgency = 'low' | 'normal' | 'critical';
11
- interface NotificationOptions {
12
- title: string;
13
- message: string;
14
- urgency?: NotificationUrgency;
15
- sound?: boolean;
16
- }
17
- /**
18
- * Send a desktop notification. Fails silently — never throws.
19
- */
20
- export declare function notify(opts: NotificationOptions): void;
21
- export declare function notifySpendSpike(platform: string, amount: string): void;
22
- export declare function notifyCampaignKilled(name: string, reason: string): void;
23
- export declare function notifyTokenExpiring(platform: string, hoursLeft: number): void;
24
- export declare function notifyReconciliationDiscrepancy(platform: string, amount: string): void;
25
- export declare function notifyVaultExpiring(hoursLeft: number): void;
26
- export declare function notifyRevenueMilestone(amount: string): void;
27
- export {};
@@ -1,98 +0,0 @@
1
- /**
2
- * Desktop Notifications — macOS/Linux native notifications for daemon events.
3
- *
4
- * Uses osascript on macOS and notify-send on Linux. No dependencies.
5
- * Notifications are non-blocking and failure-tolerant (notification failure
6
- * should never crash the daemon).
7
- *
8
- * PRD Reference: §9.7 (Danger Room shows warning), v11.3 deliverables
9
- */
10
- import { execFileSync } from 'node:child_process';
11
- import { platform } from 'node:os';
12
- /**
13
- * Send a desktop notification. Fails silently — never throws.
14
- */
15
- export function notify(opts) {
16
- try {
17
- if (platform() === 'darwin') {
18
- notifyMacOS(opts);
19
- }
20
- else if (platform() === 'linux') {
21
- notifyLinux(opts);
22
- }
23
- // Windows: notifications deferred — WSL2 path recommended
24
- }
25
- catch {
26
- // Notification failure is never fatal
27
- }
28
- }
29
- function notifyMacOS(opts) {
30
- // SEC-005: Use execFileSync with args array to prevent shell injection
31
- const sound = opts.sound !== false ? ' sound name "Submarine"' : '';
32
- const script = `display notification "${sanitize(opts.message)}" with title "VoidForge"${sound} subtitle "${sanitize(opts.title)}"`;
33
- try {
34
- execFileSync('osascript', ['-e', script], { timeout: 5000, stdio: 'ignore' });
35
- }
36
- catch { /* notification failure is never fatal */ }
37
- }
38
- function notifyLinux(opts) {
39
- // SEC-006: Use execFileSync with args array, validate urgency enum
40
- const validUrgencies = ['low', 'normal', 'critical'];
41
- const urgency = validUrgencies.includes(opts.urgency || '') ? opts.urgency : 'normal';
42
- try {
43
- execFileSync('notify-send', ['-u', urgency, '-a', 'VoidForge', sanitize(opts.title), sanitize(opts.message)], { timeout: 5000, stdio: 'ignore' });
44
- }
45
- catch { /* notification failure is never fatal */ }
46
- }
47
- /** Strip characters that could be dangerous in shell/AppleScript contexts */
48
- function sanitize(input) {
49
- return input.replace(/[`$\\"\n\r\0]/g, '').slice(0, 200);
50
- }
51
- // ── Daemon Event Notifications ────────────────────────
52
- // Pre-built notifications for common daemon events (§9.20.7 agent voice)
53
- export function notifySpendSpike(platform, amount) {
54
- notify({
55
- title: `Spend Spike — ${platform}`,
56
- message: `Wax reports: ${platform} spend is ${amount} above average this hour.`,
57
- urgency: 'critical',
58
- sound: true,
59
- });
60
- }
61
- export function notifyCampaignKilled(name, reason) {
62
- notify({
63
- title: 'Campaign Paused',
64
- message: `Wax pulled the trigger on "${name}" — ${reason}.`,
65
- urgency: 'normal',
66
- });
67
- }
68
- export function notifyTokenExpiring(platform, hoursLeft) {
69
- notify({
70
- title: `Token Expiring — ${platform}`,
71
- message: `Breeze warns: ${platform} token expires in ${hoursLeft} hours. Refresh needed.`,
72
- urgency: hoursLeft < 2 ? 'critical' : 'normal',
73
- sound: hoursLeft < 2,
74
- });
75
- }
76
- export function notifyReconciliationDiscrepancy(platform, amount) {
77
- notify({
78
- title: 'Reconciliation Alert',
79
- message: `Dockson: Numbers don't match on ${platform} — ${amount} discrepancy.`,
80
- urgency: 'critical',
81
- sound: true,
82
- });
83
- }
84
- export function notifyVaultExpiring(hoursLeft) {
85
- notify({
86
- title: 'Vault Session Expiring',
87
- message: `Vault session expires in ${hoursLeft} hour(s). Run \`voidforge heartbeat unlock\` to extend.`,
88
- urgency: 'critical',
89
- sound: true,
90
- });
91
- }
92
- export function notifyRevenueMilestone(amount) {
93
- notify({
94
- title: 'Revenue Milestone!',
95
- message: `Dockson: ${amount} total revenue. Every coin has a story — this one's a good chapter.`,
96
- urgency: 'low',
97
- });
98
- }
@@ -1,56 +0,0 @@
1
- /**
2
- * Image generation provider abstraction — Celebrimbor's forge tools.
3
- * Default: OpenAI (gpt-image-1). Extensible to other providers.
4
- * Uses the same vault system as other VoidForge credentials.
5
- */
6
- import type { ProvisionEmitter } from './provisioners/types.js';
7
- export interface ImageGenerationOptions {
8
- prompt: string;
9
- width: number;
10
- height: number;
11
- model?: string;
12
- quality?: 'low' | 'medium' | 'high';
13
- }
14
- export interface GeneratedAsset {
15
- name: string;
16
- filename: string;
17
- prompt: string;
18
- size: string;
19
- generatedAt: string;
20
- hash: string;
21
- }
22
- export interface AssetManifest {
23
- generated: string;
24
- model: string;
25
- style: string;
26
- assets: GeneratedAsset[];
27
- }
28
- /**
29
- * Generate an image via OpenAI's API.
30
- * Returns the raw image bytes as a Buffer.
31
- */
32
- export declare function generateImage(apiKey: string, options: ImageGenerationOptions, emit: ProvisionEmitter): Promise<Buffer | null>;
33
- /**
34
- * Validate an OpenAI API key by making a lightweight models list request.
35
- */
36
- export declare function validateOpenAIKey(apiKey: string): Promise<boolean>;
37
- /**
38
- * Estimate the cost of generating N images.
39
- */
40
- export declare function estimateImageCost(count: number, model?: string): number;
41
- /**
42
- * Read the asset manifest from disk.
43
- */
44
- export declare function readManifest(imagesDir: string): Promise<AssetManifest | null>;
45
- /**
46
- * Write the asset manifest to disk.
47
- */
48
- export declare function writeManifest(imagesDir: string, manifest: AssetManifest): Promise<void>;
49
- /**
50
- * Save a generated image to disk and update the manifest.
51
- */
52
- export declare function saveGeneratedImage(imagesDir: string, category: string, name: string, imageBuffer: Buffer, prompt: string, size: string, manifest: AssetManifest): Promise<string>;
53
- /**
54
- * Check if an asset already exists on disk.
55
- */
56
- export declare function assetExists(imagesDir: string, category: string, name: string): boolean;
@@ -1,159 +0,0 @@
1
- /**
2
- * Image generation provider abstraction — Celebrimbor's forge tools.
3
- * Default: OpenAI (gpt-image-1). Extensible to other providers.
4
- * Uses the same vault system as other VoidForge credentials.
5
- */
6
- import { writeFile, readFile, mkdir } from 'node:fs/promises';
7
- import { existsSync } from 'node:fs';
8
- import { join } from 'node:path';
9
- import { createHash } from 'node:crypto';
10
- import { httpsPost, httpsGet, safeJsonParse } from './provisioners/http-client.js';
11
- // ── OpenAI Provider ──────────────────────────────────────
12
- const OPENAI_API = 'api.openai.com';
13
- /**
14
- * Generate an image via OpenAI's API.
15
- * Returns the raw image bytes as a Buffer.
16
- */
17
- export async function generateImage(apiKey, options, emit) {
18
- const model = options.model || 'gpt-image-1';
19
- const size = `${options.width}x${options.height}`;
20
- // OpenAI only supports specific sizes — map to nearest and warn
21
- const validSizes = ['1024x1024', '1792x1024', '1024x1792'];
22
- const actualSize = validSizes.includes(size) ? size : '1024x1024';
23
- if (actualSize !== size) {
24
- emit({ step: 'image-gen', status: 'started', message: `Requested ${size} → using ${actualSize} (API constraint)` });
25
- }
26
- const body = JSON.stringify({
27
- model,
28
- prompt: options.prompt,
29
- n: 1,
30
- size: actualSize,
31
- quality: options.quality || 'medium',
32
- response_format: 'b64_json',
33
- });
34
- // Retry logic: 3 attempts with exponential backoff (1s, 3s, 9s)
35
- // DALL-E 3 returns 500 errors on ~15% of requests (field report #1)
36
- const MAX_RETRIES = 3;
37
- const BACKOFF_BASE_MS = 1000;
38
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
39
- try {
40
- const res = await httpsPost(OPENAI_API, '/v1/images/generations', {
41
- 'Authorization': `Bearer ${apiKey}`,
42
- 'Content-Type': 'application/json',
43
- }, body, 120_000); // 2 min timeout for image generation
44
- if (res.status === 500 || res.status === 502 || res.status === 503) {
45
- // Server error — retry with backoff
46
- if (attempt < MAX_RETRIES) {
47
- const delay = BACKOFF_BASE_MS * Math.pow(3, attempt - 1);
48
- emit({ step: 'image-gen', status: 'started', message: `Server error (${res.status}), retrying in ${delay / 1000}s (attempt ${attempt}/${MAX_RETRIES})` });
49
- await new Promise((resolve) => setTimeout(resolve, delay));
50
- continue;
51
- }
52
- emit({ step: 'image-gen', status: 'error', message: `Server error (${res.status}) after ${MAX_RETRIES} attempts` });
53
- return null;
54
- }
55
- if (res.status !== 200) {
56
- const errData = safeJsonParse(res.body);
57
- const errMsg = errData?.error?.message || `API returned ${res.status}`;
58
- emit({ step: 'image-gen', status: 'error', message: `Generation failed: ${errMsg}` });
59
- return null;
60
- }
61
- const data = safeJsonParse(res.body);
62
- const b64 = data?.data?.[0]?.b64_json;
63
- if (!b64) {
64
- emit({ step: 'image-gen', status: 'error', message: 'No image data in API response' });
65
- return null;
66
- }
67
- if (attempt > 1) {
68
- emit({ step: 'image-gen', status: 'done', message: `Succeeded on attempt ${attempt}` });
69
- }
70
- return Buffer.from(b64, 'base64');
71
- }
72
- catch (err) {
73
- if (attempt < MAX_RETRIES) {
74
- const delay = BACKOFF_BASE_MS * Math.pow(3, attempt - 1);
75
- emit({ step: 'image-gen', status: 'started', message: `Request failed, retrying in ${delay / 1000}s (attempt ${attempt}/${MAX_RETRIES})`, detail: err.message });
76
- await new Promise((resolve) => setTimeout(resolve, delay));
77
- continue;
78
- }
79
- emit({ step: 'image-gen', status: 'error', message: `Image generation failed after ${MAX_RETRIES} attempts`, detail: err.message });
80
- return null;
81
- }
82
- }
83
- return null;
84
- }
85
- /**
86
- * Validate an OpenAI API key by making a lightweight models list request.
87
- */
88
- export async function validateOpenAIKey(apiKey) {
89
- try {
90
- const res = await httpsGet(OPENAI_API, '/v1/models', {
91
- 'Authorization': `Bearer ${apiKey}`,
92
- }, 10_000);
93
- return res.status === 200;
94
- }
95
- catch {
96
- return false;
97
- }
98
- }
99
- /**
100
- * Estimate the cost of generating N images.
101
- */
102
- export function estimateImageCost(count, model = 'gpt-image-1') {
103
- const costPerImage = {
104
- 'gpt-image-1': 0.04,
105
- 'dall-e-3': 0.08,
106
- };
107
- return count * (costPerImage[model] || 0.04);
108
- }
109
- // ── Asset Manifest ──────────────────────────────────────
110
- const MANIFEST_FILENAME = 'manifest.json';
111
- /**
112
- * Read the asset manifest from disk.
113
- */
114
- export async function readManifest(imagesDir) {
115
- const manifestPath = join(imagesDir, MANIFEST_FILENAME);
116
- try {
117
- const content = await readFile(manifestPath, 'utf-8');
118
- return JSON.parse(content);
119
- }
120
- catch {
121
- return null;
122
- }
123
- }
124
- /**
125
- * Write the asset manifest to disk.
126
- */
127
- export async function writeManifest(imagesDir, manifest) {
128
- await mkdir(imagesDir, { recursive: true });
129
- await writeFile(join(imagesDir, MANIFEST_FILENAME), JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
130
- }
131
- /**
132
- * Save a generated image to disk and update the manifest.
133
- */
134
- export async function saveGeneratedImage(imagesDir, category, name, imageBuffer, prompt, size, manifest) {
135
- const categoryDir = join(imagesDir, category);
136
- await mkdir(categoryDir, { recursive: true });
137
- const filename = `${category}/${name}.png`;
138
- const filepath = join(imagesDir, filename);
139
- await writeFile(filepath, imageBuffer);
140
- const hash = createHash('sha256').update(imageBuffer).digest('hex');
141
- // Remove any existing entry with the same filename (dedup for --regen)
142
- manifest.assets = manifest.assets.filter(a => a.filename !== filename);
143
- manifest.assets.push({
144
- name,
145
- filename,
146
- prompt,
147
- size,
148
- generatedAt: new Date().toISOString(),
149
- hash: `sha256:${hash}`,
150
- });
151
- await writeManifest(imagesDir, manifest);
152
- return filepath;
153
- }
154
- /**
155
- * Check if an asset already exists on disk.
156
- */
157
- export function assetExists(imagesDir, category, name) {
158
- return existsSync(join(imagesDir, category, `${name}.png`));
159
- }
@@ -1,30 +0,0 @@
1
- /**
2
- * Natural Language Deploy — resolve prose deployment descriptions to YAML frontmatter.
3
- *
4
- * Parse: "I want a $20/month server with SSL and daily backups"
5
- * → { deploy: 'vps', instanceType: 't3.small', hostname: '', resilience: { backups: 'daily', ... } }
6
- *
7
- * Uses keyword matching and heuristics — no AI API call required.
8
- */
9
- export interface DeployConfig {
10
- deploy: 'vps' | 'vercel' | 'railway' | 'cloudflare' | 'static' | 'docker';
11
- instanceType: string;
12
- hostname: string;
13
- estimatedMonthlyCost: string;
14
- resilience: {
15
- multiEnv: boolean;
16
- previewDeploys: boolean;
17
- rollback: boolean;
18
- migrations: 'auto' | 'manual' | 'no';
19
- backups: 'daily' | 'weekly' | 'no';
20
- healthCheck: boolean;
21
- gracefulShutdown: boolean;
22
- errorBoundaries: boolean;
23
- rateLimiting: boolean;
24
- deadLetterQueue: boolean;
25
- };
26
- reasoning: string[];
27
- }
28
- export declare function resolveDeployConfig(prose: string): DeployConfig | null;
29
- /** Convert a DeployConfig to YAML frontmatter fragment. */
30
- export declare function toFrontmatter(config: DeployConfig): string;