@usesidekick/react 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/index.d.mts +3 -1
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.js +77 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +76 -0
- package/dist/server/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/server/index.ts +1 -0
- package/src/server/json-storage.ts +88 -0
package/dist/server/index.d.mts
CHANGED
|
@@ -222,6 +222,8 @@ type DrizzleDb = {
|
|
|
222
222
|
};
|
|
223
223
|
declare function createDrizzleStorage(db: DrizzleDb, overridesTable: typeof sidekickOverrides): SidekickStorage;
|
|
224
224
|
|
|
225
|
+
declare function createJsonFileStorage(filePath?: string): SidekickStorage;
|
|
226
|
+
|
|
225
227
|
declare function formatDesignSystem(schema: Record<string, unknown>): string;
|
|
226
228
|
declare function buildSystemPrompt(schema: Record<string, unknown>): string;
|
|
227
229
|
declare function buildUserPrompt(request: string, previousErrors?: string[], existingCode?: string): string;
|
|
@@ -232,4 +234,4 @@ declare function validateCode(code: string): {
|
|
|
232
234
|
errors: string[];
|
|
233
235
|
};
|
|
234
236
|
|
|
235
|
-
export { type GeneratedOverride, type NewOverrideRecord, type NewSidekickOverride, type OverrideManifest, type OverrideRecord, type SidekickHandlerOptions, type SidekickOverride, type SidekickStorage, buildSystemPrompt, buildUserPrompt, callAI, createDrizzleStorage, createSidekickHandler, formatDesignSystem, parseAIResponse, sidekickOverrides, validateCode };
|
|
237
|
+
export { type GeneratedOverride, type NewOverrideRecord, type NewSidekickOverride, type OverrideManifest, type OverrideRecord, type SidekickHandlerOptions, type SidekickOverride, type SidekickStorage, buildSystemPrompt, buildUserPrompt, callAI, createDrizzleStorage, createJsonFileStorage, createSidekickHandler, formatDesignSystem, parseAIResponse, sidekickOverrides, validateCode };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -222,6 +222,8 @@ type DrizzleDb = {
|
|
|
222
222
|
};
|
|
223
223
|
declare function createDrizzleStorage(db: DrizzleDb, overridesTable: typeof sidekickOverrides): SidekickStorage;
|
|
224
224
|
|
|
225
|
+
declare function createJsonFileStorage(filePath?: string): SidekickStorage;
|
|
226
|
+
|
|
225
227
|
declare function formatDesignSystem(schema: Record<string, unknown>): string;
|
|
226
228
|
declare function buildSystemPrompt(schema: Record<string, unknown>): string;
|
|
227
229
|
declare function buildUserPrompt(request: string, previousErrors?: string[], existingCode?: string): string;
|
|
@@ -232,4 +234,4 @@ declare function validateCode(code: string): {
|
|
|
232
234
|
errors: string[];
|
|
233
235
|
};
|
|
234
236
|
|
|
235
|
-
export { type GeneratedOverride, type NewOverrideRecord, type NewSidekickOverride, type OverrideManifest, type OverrideRecord, type SidekickHandlerOptions, type SidekickOverride, type SidekickStorage, buildSystemPrompt, buildUserPrompt, callAI, createDrizzleStorage, createSidekickHandler, formatDesignSystem, parseAIResponse, sidekickOverrides, validateCode };
|
|
237
|
+
export { type GeneratedOverride, type NewOverrideRecord, type NewSidekickOverride, type OverrideManifest, type OverrideRecord, type SidekickHandlerOptions, type SidekickOverride, type SidekickStorage, buildSystemPrompt, buildUserPrompt, callAI, createDrizzleStorage, createJsonFileStorage, createSidekickHandler, formatDesignSystem, parseAIResponse, sidekickOverrides, validateCode };
|
package/dist/server/index.js
CHANGED
|
@@ -34,6 +34,7 @@ __export(server_exports, {
|
|
|
34
34
|
buildUserPrompt: () => buildUserPrompt,
|
|
35
35
|
callAI: () => callAI,
|
|
36
36
|
createDrizzleStorage: () => createDrizzleStorage,
|
|
37
|
+
createJsonFileStorage: () => createJsonFileStorage,
|
|
37
38
|
createSidekickHandler: () => createSidekickHandler,
|
|
38
39
|
formatDesignSystem: () => formatDesignSystem,
|
|
39
40
|
parseAIResponse: () => parseAIResponse,
|
|
@@ -613,6 +614,81 @@ function createDrizzleStorage(db, overridesTable) {
|
|
|
613
614
|
};
|
|
614
615
|
}
|
|
615
616
|
|
|
617
|
+
// src/server/json-storage.ts
|
|
618
|
+
var fs2 = __toESM(require("fs/promises"));
|
|
619
|
+
var path2 = __toESM(require("path"));
|
|
620
|
+
function deserializeDates(record) {
|
|
621
|
+
return {
|
|
622
|
+
...record,
|
|
623
|
+
createdAt: new Date(record.createdAt),
|
|
624
|
+
updatedAt: new Date(record.updatedAt)
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
async function readData(filePath) {
|
|
628
|
+
try {
|
|
629
|
+
const raw = await fs2.readFile(filePath, "utf-8");
|
|
630
|
+
const parsed = JSON.parse(raw);
|
|
631
|
+
return {
|
|
632
|
+
overrides: (parsed.overrides || []).map(deserializeDates)
|
|
633
|
+
};
|
|
634
|
+
} catch (err) {
|
|
635
|
+
if (err.code === "ENOENT") {
|
|
636
|
+
const empty = { overrides: [] };
|
|
637
|
+
await fs2.mkdir(path2.dirname(filePath), { recursive: true });
|
|
638
|
+
await fs2.writeFile(filePath, JSON.stringify(empty, null, 2) + "\n");
|
|
639
|
+
return empty;
|
|
640
|
+
}
|
|
641
|
+
throw err;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
async function writeData(filePath, data) {
|
|
645
|
+
const tmpPath = filePath + ".tmp";
|
|
646
|
+
await fs2.mkdir(path2.dirname(filePath), { recursive: true });
|
|
647
|
+
await fs2.writeFile(tmpPath, JSON.stringify(data, null, 2) + "\n");
|
|
648
|
+
await fs2.rename(tmpPath, filePath);
|
|
649
|
+
}
|
|
650
|
+
function createJsonFileStorage(filePath) {
|
|
651
|
+
const resolvedPath = filePath || path2.join(process.cwd(), ".sidekick", "overrides.json");
|
|
652
|
+
return {
|
|
653
|
+
async listOverrides() {
|
|
654
|
+
const data = await readData(resolvedPath);
|
|
655
|
+
return data.overrides;
|
|
656
|
+
},
|
|
657
|
+
async getOverride(id) {
|
|
658
|
+
const data = await readData(resolvedPath);
|
|
659
|
+
return data.overrides.find((o) => o.id === id) ?? null;
|
|
660
|
+
},
|
|
661
|
+
async createOverride(record) {
|
|
662
|
+
const data = await readData(resolvedPath);
|
|
663
|
+
data.overrides.push({
|
|
664
|
+
id: record.id,
|
|
665
|
+
name: record.name,
|
|
666
|
+
description: record.description,
|
|
667
|
+
version: record.version,
|
|
668
|
+
primitives: record.primitives,
|
|
669
|
+
code: record.code,
|
|
670
|
+
enabled: record.enabled ?? true,
|
|
671
|
+
createdAt: record.createdAt ?? /* @__PURE__ */ new Date(),
|
|
672
|
+
updatedAt: record.updatedAt ?? /* @__PURE__ */ new Date()
|
|
673
|
+
});
|
|
674
|
+
await writeData(resolvedPath, data);
|
|
675
|
+
},
|
|
676
|
+
async updateOverride(id, updates) {
|
|
677
|
+
const data = await readData(resolvedPath);
|
|
678
|
+
const idx = data.overrides.findIndex((o) => o.id === id);
|
|
679
|
+
if (idx === -1) return;
|
|
680
|
+
const { id: _id, createdAt: _ca, ...rest } = updates;
|
|
681
|
+
data.overrides[idx] = { ...data.overrides[idx], ...rest, updatedAt: /* @__PURE__ */ new Date() };
|
|
682
|
+
await writeData(resolvedPath, data);
|
|
683
|
+
},
|
|
684
|
+
async deleteOverride(id) {
|
|
685
|
+
const data = await readData(resolvedPath);
|
|
686
|
+
data.overrides = data.overrides.filter((o) => o.id !== id);
|
|
687
|
+
await writeData(resolvedPath, data);
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
|
|
616
692
|
// src/server/drizzle-schema.ts
|
|
617
693
|
var import_pg_core = require("drizzle-orm/pg-core");
|
|
618
694
|
var sidekickOverrides = (0, import_pg_core.pgTable)("overrides", {
|
|
@@ -633,6 +709,7 @@ var sidekickOverrides = (0, import_pg_core.pgTable)("overrides", {
|
|
|
633
709
|
buildUserPrompt,
|
|
634
710
|
callAI,
|
|
635
711
|
createDrizzleStorage,
|
|
712
|
+
createJsonFileStorage,
|
|
636
713
|
createSidekickHandler,
|
|
637
714
|
formatDesignSystem,
|
|
638
715
|
parseAIResponse,
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/index.ts","../../src/server/handler.ts","../../src/server/generate.ts","../../src/server/drizzle-adapter.ts","../../src/server/drizzle-schema.ts"],"sourcesContent":["export { createSidekickHandler } from './handler';\nexport { createDrizzleStorage } from './drizzle-adapter';\nexport { sidekickOverrides } from './drizzle-schema';\nexport type { SidekickOverride, NewSidekickOverride } from './drizzle-schema';\nexport type {\n SidekickStorage,\n SidekickHandlerOptions,\n OverrideRecord,\n NewOverrideRecord,\n OverrideManifest,\n GeneratedOverride,\n} from './types';\nexport {\n buildSystemPrompt,\n buildUserPrompt,\n callAI,\n parseAIResponse,\n validateCode,\n formatDesignSystem,\n} from './generate';\n","import * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { NextRequest } from 'next/server';\nimport { NextResponse } from 'next/server';\nimport type { SidekickHandlerOptions, SidekickStorage } from './types';\nimport { callAI, validateCode } from './generate';\n\ninterface GenerateRequestBody {\n request: string;\n overrideId?: string;\n}\n\ninterface ToggleRequestBody {\n overrideId: string;\n enabled: boolean;\n}\n\ninterface DeleteRequestBody {\n overrideId: string;\n}\n\nfunction getAction(req: NextRequest): string {\n const url = new URL(req.url);\n const segments = url.pathname.split('/').filter(Boolean);\n // Last segment is the action: overrides, generate, toggle, delete\n return segments[segments.length - 1] || '';\n}\n\nasync function readSchema(schemaPath?: string): Promise<Record<string, unknown>> {\n const candidates = schemaPath\n ? [schemaPath]\n : [\n path.join(process.cwd(), 'src/sidekick/schema.json'),\n path.join(process.cwd(), 'sidekick/schema.json'),\n ];\n\n for (const candidate of candidates) {\n try {\n const content = await fs.readFile(candidate, 'utf-8');\n return JSON.parse(content);\n } catch {\n continue;\n }\n }\n throw new Error('Failed to read schema.json');\n}\n\nfunction safeParsePrimitives(primitives: string): string[] {\n try {\n const parsed = JSON.parse(primitives);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\nasync function handleListOverrides(storage: SidekickStorage) {\n const overrides = await storage.listOverrides();\n return NextResponse.json(\n {\n success: true,\n overrides: overrides.map((o) => ({\n id: o.id,\n name: o.name,\n description: o.description,\n version: o.version,\n primitives: safeParsePrimitives(o.primitives),\n code: o.code,\n enabled: o.enabled,\n createdAt: o.createdAt,\n updatedAt: o.updatedAt,\n })),\n },\n {\n headers: {\n 'Cache-Control': 'no-store, no-cache, must-revalidate',\n },\n }\n );\n}\n\nasync function safeParseBody<T>(req: NextRequest): Promise<T | null> {\n try {\n return (await req.json()) as T;\n } catch {\n return null;\n }\n}\n\nasync function handleGenerate(\n req: NextRequest,\n storage: SidekickStorage,\n schemaPath?: string\n) {\n const body = await safeParseBody<GenerateRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { request, overrideId } = body;\n\n if (!request || typeof request !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing request parameter' },\n { status: 400 }\n );\n }\n\n let existingCode: string | undefined;\n if (overrideId) {\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: `Override \"${overrideId}\" not found` },\n { status: 404 }\n );\n }\n existingCode = existing.code;\n }\n\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n return NextResponse.json(\n { success: false, error: 'ANTHROPIC_API_KEY not configured' },\n { status: 500 }\n );\n }\n\n let schema: Record<string, unknown>;\n try {\n schema = await readSchema(schemaPath);\n } catch {\n return NextResponse.json(\n { success: false, error: 'Failed to read schema.json' },\n { status: 500 }\n );\n }\n\n const MAX_RETRIES = 3;\n let lastError: string | undefined;\n let lastValidationErrors: string[] | undefined;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n const generated = await callAI(request, schema, apiKey, lastValidationErrors, existingCode);\n\n if (overrideId) {\n generated.manifest.id = overrideId;\n }\n\n const validation = validateCode(generated.code);\n\n if (!validation.valid) {\n lastValidationErrors = validation.errors;\n lastError = `Validation failed: ${validation.errors.join(', ')}`;\n\n if (attempt < MAX_RETRIES) {\n console.log(`[Sidekick] Attempt ${attempt} failed validation, retrying...`);\n continue;\n }\n\n return NextResponse.json({\n success: false,\n error: lastError,\n validationErrors: lastValidationErrors,\n });\n }\n\n const existing = await storage.getOverride(generated.manifest.id);\n\n if (existing) {\n await storage.updateOverride(generated.manifest.id, {\n name: generated.manifest.name,\n description: generated.manifest.description,\n version: generated.manifest.version,\n primitives: JSON.stringify(generated.manifest.primitives),\n code: generated.code,\n });\n } else {\n await storage.createOverride({\n id: generated.manifest.id,\n name: generated.manifest.name,\n description: generated.manifest.description,\n version: generated.manifest.version,\n primitives: JSON.stringify(generated.manifest.primitives),\n code: generated.code,\n enabled: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n }\n\n return NextResponse.json({\n success: true,\n overrideId: generated.manifest.id,\n name: generated.manifest.name,\n description: generated.manifest.description,\n });\n } catch (e) {\n lastError = e instanceof Error ? e.message : String(e);\n if (attempt === MAX_RETRIES) {\n return NextResponse.json({\n success: false,\n error: `Failed after ${MAX_RETRIES} attempts: ${lastError}`,\n });\n }\n }\n }\n\n return NextResponse.json({\n success: false,\n error: lastError || 'Unknown error',\n });\n}\n\nasync function handleToggle(req: NextRequest, storage: SidekickStorage) {\n const body = await safeParseBody<ToggleRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { overrideId, enabled } = body;\n\n if (!overrideId || typeof overrideId !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing overrideId parameter' },\n { status: 400 }\n );\n }\n\n if (typeof enabled !== 'boolean') {\n return NextResponse.json(\n { success: false, error: 'Missing or invalid enabled parameter' },\n { status: 400 }\n );\n }\n\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: 'Override not found' },\n { status: 404 }\n );\n }\n\n await storage.updateOverride(overrideId, { enabled });\n\n // Re-read to confirm persisted value\n const updated = await storage.getOverride(overrideId);\n\n return NextResponse.json({\n success: true,\n overrideId,\n enabled: updated?.enabled ?? enabled,\n });\n}\n\nasync function handleDelete(req: NextRequest, storage: SidekickStorage) {\n const body = await safeParseBody<DeleteRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { overrideId } = body;\n\n if (!overrideId || typeof overrideId !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing overrideId parameter' },\n { status: 400 }\n );\n }\n\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: 'Override not found' },\n { status: 404 }\n );\n }\n\n await storage.deleteOverride(overrideId);\n\n return NextResponse.json({\n success: true,\n message: `Override \"${overrideId}\" deleted successfully`,\n });\n}\n\nexport function createSidekickHandler(options: SidekickHandlerOptions) {\n const { storage, schemaPath } = options;\n\n return {\n async GET(req: NextRequest): Promise<NextResponse> {\n try {\n const action = getAction(req);\n if (action === 'overrides') {\n return handleListOverrides(storage);\n }\n return NextResponse.json(\n { success: false, error: `Unknown GET action: ${action}` },\n { status: 404 }\n );\n } catch (e) {\n console.error('[Sidekick] Handler error:', e);\n return NextResponse.json(\n { success: false, error: e instanceof Error ? e.message : 'Unknown error' },\n { status: 500 }\n );\n }\n },\n\n async POST(req: NextRequest): Promise<NextResponse> {\n try {\n const action = getAction(req);\n switch (action) {\n case 'generate':\n return handleGenerate(req, storage, schemaPath);\n case 'toggle':\n return handleToggle(req, storage);\n case 'delete':\n return handleDelete(req, storage);\n default:\n return NextResponse.json(\n { success: false, error: `Unknown POST action: ${action}` },\n { status: 404 }\n );\n }\n } catch (e) {\n console.error('[Sidekick] Handler error:', e);\n return NextResponse.json(\n { success: false, error: e instanceof Error ? e.message : 'Unknown error' },\n { status: 500 }\n );\n }\n },\n };\n}\n","import type { GeneratedOverride, OverrideManifest } from './types';\n\nconst ALLOWED_IMPORTS = ['@usesidekick/react', 'react'];\n\nconst FORBIDDEN_PATTERNS = [\n /\\bfetch\\s*\\(/,\n /\\beval\\s*\\(/,\n /\\bFunction\\s*\\(/,\n /\\bdocument\\./,\n /\\bwindow\\./,\n /\\blocalStorage\\./,\n /\\bsessionStorage\\./,\n /\\bXMLHttpRequest/,\n /\\bWebSocket/,\n /\\bimport\\s*\\(/,\n /require\\s*\\(/,\n];\n\ninterface DesignSystemSchema {\n framework?: string | null;\n fontFamily?: string | null;\n globalCss?: string | null;\n componentStyles?: Array<{\n component: string;\n file: string;\n classNames: string[];\n }>;\n}\n\nexport function formatDesignSystem(schema: Record<string, unknown>): string {\n const ds = (schema as { designSystem?: DesignSystemSchema }).designSystem;\n if (!ds) {\n return 'No design system information available. Use clean, minimal inline styles with neutral colors and standard spacing.';\n }\n\n const parts: string[] = [];\n parts.push('All generated UI MUST look like it was built by the same team that built the host app. Study the style samples below and match the exact same patterns — colors, spacing, typography, border radii, hover states, and dark mode support.');\n\n if (ds.framework) {\n parts.push(`\\n**CSS Framework:** ${ds.framework}`);\n }\n if (ds.fontFamily) {\n parts.push(`**Font:** ${ds.fontFamily}`);\n }\n if (ds.globalCss) {\n parts.push('**Global CSS:**\\n```css\\n' + ds.globalCss + '\\n```');\n }\n\n if (ds.componentStyles && ds.componentStyles.length > 0) {\n parts.push('\\n**Component Style Samples (actual className strings from the app — use these as your reference):**');\n for (const cs of ds.componentStyles) {\n parts.push(`\\n${cs.component} (${cs.file}):`);\n for (const cn of cs.classNames) {\n parts.push(` \"${cn}\"`);\n }\n }\n parts.push('\\nWhen adding new UI elements to an existing component, copy the exact className patterns from that component above. When creating standalone elements, pick the closest matching component pattern and follow it.');\n }\n\n return parts.join('\\n');\n}\n\nfunction formatComponents(schema: Record<string, unknown>): string {\n const components = (schema as { components?: Array<{ name: string; props: string[]; renderStructure?: string }> }).components || [];\n return components.map((c) => {\n let entry = '- ' + c.name + ' (props: ' + (c.props.join(', ') || 'none') + ')';\n if (c.renderStructure) {\n entry += '\\n Renders: ' + c.renderStructure;\n }\n return entry;\n }).join('\\n');\n}\n\nexport function buildSystemPrompt(schema: Record<string, unknown>): string {\n const tick = '`';\n const ticks = '```';\n const dollar = '$';\n return 'You are a code generator for Sidekick, an SDK that allows users to customize web applications.\\n' +\n'Your task is to generate override modules based on user requests.\\n' +\n'\\n' +\n'## SDK Primitives Available\\n' +\n'\\n' +\n'UI Primitives:\\n' +\n'- sdk.ui.wrap(componentName, wrapperFn, options?) - Wrap any component by name to add behavior around it. options: { priority?, where?: (props) => boolean }\\n' +\n'- sdk.ui.replace(componentName, Component) - Replace any component entirely by name\\n' +\n'- sdk.ui.addColumn(tableId, config) - Add a column to a table (config: { header, accessor, render? })\\n' +\n'- sdk.ui.renameColumn(tableId, originalHeader, newHeader) - Rename a column header\\n' +\n'- sdk.ui.reorderColumns(tableId, headerOrder) - Reorder columns by header name (array of header strings)\\n' +\n'- sdk.ui.hideColumn(tableId, header) - Hide a column by header name\\n' +\n'- sdk.ui.filterRows(tableId, filterFn) - Filter table rows (filterFn receives a row object, returns true to keep)\\n' +\n'- sdk.ui.addStyles(css) - Add CSS styles globally\\n' +\n'- sdk.ui.setText(selector, text) - Change text content of elements matching CSS selector\\n' +\n'- sdk.ui.setAttribute(selector, attr, value) - Set an attribute on matching elements\\n' +\n'- sdk.ui.setStyle(selector, styles) - Apply inline styles to matching elements (styles is an object like { color: \\'red\\' })\\n' +\n'- sdk.ui.addClass(selector, className) - Add a CSS class to matching elements\\n' +\n'- sdk.ui.removeClass(selector, className) - Remove a CSS class from matching elements\\n' +\n'- sdk.ui.inject(selector, Component, position?) - Inject a React component before/after/prepend/append an element (default: \\'after\\')\\n' +\n'\\n' +\n'Data Primitives:\\n' +\n'- sdk.data.computed(fieldName, computeFn) - Add a computed field\\n' +\n'- sdk.data.addFilter(name, filterFn) - Add a filter preset\\n' +\n'- sdk.data.transform(dataKey, transformFn) - Transform data\\n' +\n'- sdk.data.intercept(pathPattern, handler) - Intercept and modify API responses (handler receives response JSON and { path, method })\\n' +\n'\\n' +\n'Behavior Primitives:\\n' +\n'- sdk.behavior.addKeyboardShortcut(keys, action, description?) - Register a keyboard shortcut (e.g., \"ctrl+k\", \"shift+?\")\\n' +\n'- sdk.behavior.onDOMEvent(selector, eventType, handler) - Listen for DOM events on elements matching a CSS selector\\n' +\n'- sdk.behavior.modifyRoute(pathPattern, handler) - Intercept navigation and optionally redirect\\n' +\n'\\n' +\n'## App Design System\\n' +\n'\\n' +\nformatDesignSystem(schema) + '\\n' +\n'\\n' +\n'## App Schema\\n' +\n'\\n' +\n'Components (targetable by name with wrap/replace):\\n' +\nformatComponents(schema) + '\\n' +\n'\\n' +\n'Data Models: ' + JSON.stringify((schema as { dataModels?: unknown[] }).dataModels || []) + '\\n' +\n'\\n' +\n'## Example Overrides\\n' +\n'\\n' +\n'### Example 1: Wrap a component to add a badge (RECOMMENDED APPROACH)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'import { ComponentType } from \\'react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'task-card-badge\\',\\n' +\n' name: \\'Task Card Badge\\',\\n' +\n' description: \\'Adds a \"New\" badge to task cards\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.wrap\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.wrap(\\'TaskCard\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function WrappedTaskCard(props: Record<string, unknown>) {\\n' +\n' return (\\n' +\n' <div style={{ position: \\'relative\\' }}>\\n' +\n' <span style={{\\n' +\n' position: \\'absolute\\',\\n' +\n' top: \\'-8px\\',\\n' +\n' right: \\'-8px\\',\\n' +\n' backgroundColor: \\'#EF4444\\',\\n' +\n' color: \\'white\\',\\n' +\n' fontSize: \\'10px\\',\\n' +\n' fontWeight: \\'bold\\',\\n' +\n' padding: \\'2px 6px\\',\\n' +\n' borderRadius: \\'9999px\\',\\n' +\n' }}>\\n' +\n' NEW\\n' +\n' </span>\\n' +\n' <Original {...props} />\\n' +\n' </div>\\n' +\n' );\\n' +\n' };\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 2: Replace a component entirely\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'const CustomTaskModal = (props: Record<string, unknown>) => {\\n' +\n' const task = props.task as { title?: string; description?: string } | undefined;\\n' +\n' const onClose = props.onClose as () => void;\\n' +\n'\\n' +\n' return (\\n' +\n' <div style={{\\n' +\n' position: \\'fixed\\',\\n' +\n' inset: 0,\\n' +\n' backgroundColor: \\'rgba(0,0,0,0.5)\\',\\n' +\n' display: \\'flex\\',\\n' +\n' alignItems: \\'center\\',\\n' +\n' justifyContent: \\'center\\',\\n' +\n' }}>\\n' +\n' <div style={{\\n' +\n' backgroundColor: \\'white\\',\\n' +\n' borderRadius: \\'16px\\',\\n' +\n' padding: \\'24px\\',\\n' +\n' maxWidth: \\'500px\\',\\n' +\n' width: \\'100%\\',\\n' +\n' }}>\\n' +\n' <h2 style={{ fontSize: \\'24px\\', fontWeight: \\'bold\\', marginBottom: \\'16px\\' }}>\\n' +\n' {task?.title || \\'Task Details\\'}\\n' +\n' </h2>\\n' +\n' <p>{task?.description || \\'No description\\'}</p>\\n' +\n' <button\\n' +\n' onClick={onClose}\\n' +\n' style={{\\n' +\n' marginTop: \\'16px\\',\\n' +\n' backgroundColor: \\'#3B82F6\\',\\n' +\n' color: \\'white\\',\\n' +\n' padding: \\'8px 16px\\',\\n' +\n' borderRadius: \\'8px\\',\\n' +\n' border: \\'none\\',\\n' +\n' cursor: \\'pointer\\',\\n' +\n' }}\\n' +\n' >\\n' +\n' Close\\n' +\n' </button>\\n' +\n' </div>\\n' +\n' </div>\\n' +\n' );\\n' +\n'};\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-task-modal\\',\\n' +\n' name: \\'Custom Task Modal\\',\\n' +\n' description: \\'Replaces the task modal with a custom design\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.replace\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.replace(\\'TaskModal\\', CustomTaskModal);\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 3: Add a table column\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'days-left-column\\',\\n' +\n' name: \\'Days Left Column\\',\\n' +\n' description: \\'Shows days until due date\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.addColumn\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.addColumn(\\'task-table\\', {\\n' +\n' header: \\'Days Left\\',\\n' +\n' accessor: (row: Record<string, unknown>) => {\\n' +\n' const dueDate = row.dueDate as string | undefined;\\n' +\n' if (!dueDate) return null;\\n' +\n' const days = Math.ceil((new Date(dueDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24));\\n' +\n' return days;\\n' +\n' },\\n' +\n' render: (value: unknown) => {\\n' +\n' if (value === null) return <span>-</span>;\\n' +\n' const days = value as number;\\n' +\n' const color = days < 0 ? \\'red\\' : days <= 3 ? \\'orange\\' : \\'green\\';\\n' +\n' return <span style={{ color }}>{days}d</span>;\\n' +\n' },\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 4: Add global styles\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-theme\\',\\n' +\n' name: \\'Custom Theme\\',\\n' +\n' description: \\'Adds custom styling to the app\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.addStyles\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.addStyles(' + tick + '\\n' +\n' .task-card {\\n' +\n' border-radius: 12px;\\n' +\n' box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\\n' +\n' }\\n' +\n' .task-card:hover {\\n' +\n' transform: translateY(-2px);\\n' +\n' transition: transform 0.2s ease;\\n' +\n' }\\n' +\n' ' + tick + ');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 5: Column operations (rename, reorder, hide)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'reorder-columns\\',\\n' +\n' name: \\'Reorder Table Columns\\',\\n' +\n' description: \\'Reorders Status and Importance columns and hides Assignee\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.reorderColumns\\', \\'ui.hideColumn\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.reorderColumns(\\'task-table\\', [\\'Title\\', \\'Importance\\', \\'Status\\', \\'Project\\', \\'Due Date\\']);\\n' +\n' sdk.ui.hideColumn(\\'task-table\\', \\'Assignee\\');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 6: Interactive filter with sidebar nav button\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'import { ComponentType, useState, useEffect } from \\'react\\';\\n' +\n'\\n' +\n'let _filterActive = false;\\n' +\n'const _listeners = new Set<(active: boolean) => void>();\\n' +\n'\\n' +\n'function setFilterActive(active: boolean) {\\n' +\n' _filterActive = active;\\n' +\n' _listeners.forEach(fn => fn(active));\\n' +\n'}\\n' +\n'\\n' +\n'function useFilterActive(): [boolean, (active: boolean) => void] {\\n' +\n' const [active, setActive] = useState(_filterActive);\\n' +\n' useEffect(() => {\\n' +\n' const handler = (val: boolean) => setActive(val);\\n' +\n' _listeners.add(handler);\\n' +\n' return () => { _listeners.delete(handler); };\\n' +\n' }, []);\\n' +\n' return [active, setFilterActive];\\n' +\n'}\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'high-importance-filter\\',\\n' +\n' name: \\'High Importance Filter\\',\\n' +\n' description: \\'Adds a sidebar button to filter high importance tasks\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.wrap\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.wrap(\\'Sidebar\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function SidebarWithFilter(props: Record<string, unknown>) {\\n' +\n' const [isActive, setActive] = useFilterActive();\\n' +\n' return (\\n' +\n' <div style={{ display: \\'contents\\' }}>\\n' +\n' <Original {...props} />\\n' +\n' <div style={{ padding: \\'0 16px\\', marginTop: \\'8px\\' }}>\\n' +\n' <button\\n' +\n' onClick={() => setActive(!isActive)}\\n' +\n' style={{\\n' +\n' backgroundColor: isActive ? \\'#EFF6FF\\' : \\'transparent\\',\\n' +\n' color: isActive ? \\'#1D4ED8\\' : \\'#374151\\',\\n' +\n' }}\\n' +\n' >\\n' +\n' High Importance\\n' +\n' </button>\\n' +\n' </div>\\n' +\n' </div>\\n' +\n' );\\n' +\n' };\\n' +\n' });\\n' +\n'\\n' +\n' sdk.ui.wrap(\\'TaskTable\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function FilteredTaskTable(props: Record<string, unknown>) {\\n' +\n' const [isActive] = useFilterActive();\\n' +\n' if (!isActive) return <Original {...props} />;\\n' +\n' const tasks = (props.tasks || []) as Array<Record<string, unknown>>;\\n' +\n' const filtered = tasks.filter(t => t.importance === \\'high\\');\\n' +\n' return <Original {...props} tasks={filtered} />;\\n' +\n' };\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 7: Permanent row filter (always active when override is ON)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'hide-done-tasks\\',\\n' +\n' name: \\'Hide Done Tasks\\',\\n' +\n' description: \\'Hides completed tasks from the table\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.filterRows\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.filterRows(\\'task-table\\', (row: Record<string, unknown>) => {\\n' +\n' return row.status !== \\'done\\';\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 8: Keyboard shortcut\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'quick-search-shortcut\\',\\n' +\n' name: \\'Quick Search Shortcut\\',\\n' +\n' description: \\'Adds Ctrl+K shortcut to focus search\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'behavior.addKeyboardShortcut\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.behavior.addKeyboardShortcut(\\'ctrl+k\\', () => {\\n' +\n' // Toggle search or custom action\\n' +\n' }, \\'Open quick search\\');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 9: DOM modification\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-dom-mods\\',\\n' +\n' name: \\'Custom DOM Modifications\\',\\n' +\n' description: \\'Changes sidebar title and styles task cards\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.setText\\', \\'ui.setStyle\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.setText(\\'.sidebar-title\\', \\'My Custom App\\');\\n' +\n' sdk.ui.setStyle(\\'.task-card\\', { borderLeft: \\'4px solid #3B82F6\\' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'## Rules\\n' +\n'\\n' +\n'1. ONLY import from \\'@usesidekick/react\\' or \\'react\\' - NO other imports allowed\\n' +\n'2. DO NOT use fetch, eval, document.*, window.*, localStorage, etc.\\n' +\n'3. Use the createOverride function from @usesidekick/react\\n' +\n'4. The activate function receives an SDK object with ui and data primitives\\n' +\n'5. Generate React components using JSX with inline styles\\n' +\n'6. Keep code simple and focused on the user\\'s request\\n' +\n'7. Use sdk.ui.wrap() to add behavior around existing components, use sdk.ui.replace() to fully swap components\\n' +\n'8. When wrapping, always pass through all props to the Original component\\n' +\n'9. ALWAYS prefer SDK primitives over raw wrapping for table operations\\n' +\n'10. When using sdk.ui.wrap(), ALWAYS render <Original {...props} /> as-is\\n' +\n'11. A wrapper\\'s ONLY job is to add content BEFORE or AFTER <Original {...props} />, or to modify the props passed to it\\n' +\n'12. For permanent row filtering, use sdk.ui.filterRows(tableId, filterFn)\\n' +\n'13. Overrides MUST always act on the UI\\n' +\n'14. For interactive/togglable behavior, use module-level shared state with a listener pattern\\n' +\n'15. VISUAL INTEGRATION IS MANDATORY\\n' +\n'16. Study the component style samples in the design system carefully\\n' +\n'17. NEVER use absolute/fixed positioning to place elements inside existing components\\n' +\n'18. New elements MUST be visually indistinguishable from existing elements\\n' +\n'19. All interactive elements MUST have hover states\\n' +\n'20. BEFORE writing any wrap() code, read the target component\\'s \"Renders:\" tree\\n' +\n'21. Structure wrappers as: optional-new-content-above + <Original {...props} /> + optional-new-content-below\\n' +\n'22. CSS selectors MUST be valid CSS selectors supported by document.querySelectorAll()\\n' +\n'\\n' +\n'## Output Format\\n' +\n'\\n' +\n'Respond with ONLY a JSON object (no markdown, no explanation):\\n' +\n'{\\n' +\n' \"manifest\": {\\n' +\n' \"id\": \"override-id-kebab-case\",\\n' +\n' \"name\": \"Human Readable Name\",\\n' +\n' \"description\": \"What this override does\",\\n' +\n' \"version\": \"1.0.0\",\\n' +\n' \"primitives\": [\"list\", \"of\", \"primitives\", \"used\"]\\n' +\n' },\\n' +\n' \"code\": \"// Full TypeScript/TSX code here\"\\n' +\n'}';\n}\n\nexport function buildUserPrompt(\n request: string,\n previousErrors?: string[],\n existingCode?: string\n): string {\n let prompt: string;\n\n if (existingCode) {\n prompt = 'You are MODIFYING an existing override. Here is the current code:\\n```tsx\\n' + existingCode + '\\n```\\n\\nApply this change: \"' + request + '\"\\n\\nIMPORTANT: Keep the same override ID. Preserve all existing functionality unless the user explicitly asks to change it. Return the complete modified code.';\n } else {\n prompt = 'Generate an override module for this request: \"' + request + '\"';\n }\n\n if (previousErrors && previousErrors.length > 0) {\n prompt += '\\n\\nIMPORTANT: Previous generation attempt failed validation with these errors:\\n';\n prompt += previousErrors.map((e) => '- ' + e).join('\\n');\n prompt += '\\n\\nPlease fix these issues in your response.';\n }\n\n return prompt;\n}\n\nexport async function callAI(\n request: string,\n schema: Record<string, unknown>,\n apiKey: string,\n previousErrors?: string[],\n existingCode?: string\n): Promise<GeneratedOverride> {\n const systemPrompt = buildSystemPrompt(schema);\n const userPrompt = buildUserPrompt(request, previousErrors, existingCode);\n\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: 'claude-sonnet-4-20250514',\n max_tokens: 4096,\n system: systemPrompt,\n messages: [{ role: 'user', content: userPrompt }],\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error('API request failed: ' + response.status + ' ' + error);\n }\n\n const result = (await response.json()) as {\n content: Array<{ type: string; text?: string }>;\n };\n\n const textContent = result.content.find((c) => c.type === 'text');\n if (!textContent || !textContent.text) {\n throw new Error('No text content in API response');\n }\n\n return parseAIResponse(textContent.text);\n}\n\nexport function parseAIResponse(text: string): GeneratedOverride {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (!jsonMatch) {\n throw new Error('No JSON found in AI response');\n }\n\n try {\n const parsed = JSON.parse(jsonMatch[0]) as {\n manifest: OverrideManifest;\n code: string;\n };\n\n if (!parsed.manifest || !parsed.code) {\n throw new Error('Invalid response structure');\n }\n\n return {\n manifest: parsed.manifest,\n code: parsed.code,\n };\n } catch (e) {\n throw new Error('Failed to parse AI response: ' + (e instanceof Error ? e.message : String(e)));\n }\n}\n\nexport function validateCode(code: string): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n const importMatches = code.matchAll(/import\\s+.*?\\s+from\\s+['\"]([^'\"]+)['\"]/g);\n for (const match of importMatches) {\n const moduleName = match[1];\n const isAllowed = ALLOWED_IMPORTS.some(\n (allowed) => moduleName === allowed || moduleName.startsWith(allowed + '/')\n );\n if (!isAllowed) {\n errors.push('Forbidden import: ' + moduleName);\n }\n }\n\n for (const pattern of FORBIDDEN_PATTERNS) {\n if (pattern.test(code)) {\n errors.push('Forbidden pattern: ' + pattern.source);\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n","import { eq } from 'drizzle-orm';\nimport type { SidekickStorage, OverrideRecord, NewOverrideRecord } from './types';\nimport type { sidekickOverrides as OverridesTable } from './drizzle-schema';\n\ntype DrizzleDb = {\n select(): any;\n insert(table: any): any;\n update(table: any): any;\n delete(table: any): any;\n};\n\nexport function createDrizzleStorage(\n db: DrizzleDb,\n overridesTable: typeof OverridesTable\n): SidekickStorage {\n return {\n async listOverrides(): Promise<OverrideRecord[]> {\n const rows = await db.select().from(overridesTable).orderBy(overridesTable.createdAt);\n return rows as OverrideRecord[];\n },\n\n async getOverride(id: string): Promise<OverrideRecord | null> {\n const rows = await db.select().from(overridesTable).where(eq(overridesTable.id, id));\n return (rows[0] as OverrideRecord) ?? null;\n },\n\n async createOverride(data: NewOverrideRecord): Promise<void> {\n await db.insert(overridesTable).values({\n id: data.id,\n name: data.name,\n description: data.description,\n version: data.version,\n primitives: data.primitives,\n code: data.code,\n enabled: data.enabled ?? true,\n createdAt: data.createdAt ?? new Date(),\n updatedAt: data.updatedAt ?? new Date(),\n });\n },\n\n async updateOverride(id: string, data: Partial<OverrideRecord>): Promise<void> {\n // Strip id and createdAt to prevent accidental PK/immutable field updates\n const { id: _id, createdAt: _ca, ...updateData } = data;\n await db.update(overridesTable)\n .set({ ...updateData, updatedAt: new Date() })\n .where(eq(overridesTable.id, id));\n },\n\n async deleteOverride(id: string): Promise<void> {\n await db.delete(overridesTable).where(eq(overridesTable.id, id));\n },\n };\n}\n","import { pgTable, text, timestamp, boolean } from 'drizzle-orm/pg-core';\n\nexport const sidekickOverrides = pgTable('overrides', {\n id: text('id').primaryKey(),\n name: text('name').notNull(),\n description: text('description').notNull(),\n version: text('version').notNull().default('1.0.0'),\n primitives: text('primitives').notNull(), // JSON array stored as text\n code: text('code').notNull(),\n enabled: boolean('enabled').notNull().default(true),\n createdAt: timestamp('created_at').defaultNow().notNull(),\n updatedAt: timestamp('updated_at').defaultNow().notNull(),\n});\n\nexport type SidekickOverride = typeof sidekickOverrides.$inferSelect;\nexport type NewSidekickOverride = typeof sidekickOverrides.$inferInsert;\n"],"mappings":"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","names":["text"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/index.ts","../../src/server/handler.ts","../../src/server/generate.ts","../../src/server/drizzle-adapter.ts","../../src/server/json-storage.ts","../../src/server/drizzle-schema.ts"],"sourcesContent":["export { createSidekickHandler } from './handler';\nexport { createDrizzleStorage } from './drizzle-adapter';\nexport { createJsonFileStorage } from './json-storage';\nexport { sidekickOverrides } from './drizzle-schema';\nexport type { SidekickOverride, NewSidekickOverride } from './drizzle-schema';\nexport type {\n SidekickStorage,\n SidekickHandlerOptions,\n OverrideRecord,\n NewOverrideRecord,\n OverrideManifest,\n GeneratedOverride,\n} from './types';\nexport {\n buildSystemPrompt,\n buildUserPrompt,\n callAI,\n parseAIResponse,\n validateCode,\n formatDesignSystem,\n} from './generate';\n","import * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { NextRequest } from 'next/server';\nimport { NextResponse } from 'next/server';\nimport type { SidekickHandlerOptions, SidekickStorage } from './types';\nimport { callAI, validateCode } from './generate';\n\ninterface GenerateRequestBody {\n request: string;\n overrideId?: string;\n}\n\ninterface ToggleRequestBody {\n overrideId: string;\n enabled: boolean;\n}\n\ninterface DeleteRequestBody {\n overrideId: string;\n}\n\nfunction getAction(req: NextRequest): string {\n const url = new URL(req.url);\n const segments = url.pathname.split('/').filter(Boolean);\n // Last segment is the action: overrides, generate, toggle, delete\n return segments[segments.length - 1] || '';\n}\n\nasync function readSchema(schemaPath?: string): Promise<Record<string, unknown>> {\n const candidates = schemaPath\n ? [schemaPath]\n : [\n path.join(process.cwd(), 'src/sidekick/schema.json'),\n path.join(process.cwd(), 'sidekick/schema.json'),\n ];\n\n for (const candidate of candidates) {\n try {\n const content = await fs.readFile(candidate, 'utf-8');\n return JSON.parse(content);\n } catch {\n continue;\n }\n }\n throw new Error('Failed to read schema.json');\n}\n\nfunction safeParsePrimitives(primitives: string): string[] {\n try {\n const parsed = JSON.parse(primitives);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\nasync function handleListOverrides(storage: SidekickStorage) {\n const overrides = await storage.listOverrides();\n return NextResponse.json(\n {\n success: true,\n overrides: overrides.map((o) => ({\n id: o.id,\n name: o.name,\n description: o.description,\n version: o.version,\n primitives: safeParsePrimitives(o.primitives),\n code: o.code,\n enabled: o.enabled,\n createdAt: o.createdAt,\n updatedAt: o.updatedAt,\n })),\n },\n {\n headers: {\n 'Cache-Control': 'no-store, no-cache, must-revalidate',\n },\n }\n );\n}\n\nasync function safeParseBody<T>(req: NextRequest): Promise<T | null> {\n try {\n return (await req.json()) as T;\n } catch {\n return null;\n }\n}\n\nasync function handleGenerate(\n req: NextRequest,\n storage: SidekickStorage,\n schemaPath?: string\n) {\n const body = await safeParseBody<GenerateRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { request, overrideId } = body;\n\n if (!request || typeof request !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing request parameter' },\n { status: 400 }\n );\n }\n\n let existingCode: string | undefined;\n if (overrideId) {\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: `Override \"${overrideId}\" not found` },\n { status: 404 }\n );\n }\n existingCode = existing.code;\n }\n\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n return NextResponse.json(\n { success: false, error: 'ANTHROPIC_API_KEY not configured' },\n { status: 500 }\n );\n }\n\n let schema: Record<string, unknown>;\n try {\n schema = await readSchema(schemaPath);\n } catch {\n return NextResponse.json(\n { success: false, error: 'Failed to read schema.json' },\n { status: 500 }\n );\n }\n\n const MAX_RETRIES = 3;\n let lastError: string | undefined;\n let lastValidationErrors: string[] | undefined;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n const generated = await callAI(request, schema, apiKey, lastValidationErrors, existingCode);\n\n if (overrideId) {\n generated.manifest.id = overrideId;\n }\n\n const validation = validateCode(generated.code);\n\n if (!validation.valid) {\n lastValidationErrors = validation.errors;\n lastError = `Validation failed: ${validation.errors.join(', ')}`;\n\n if (attempt < MAX_RETRIES) {\n console.log(`[Sidekick] Attempt ${attempt} failed validation, retrying...`);\n continue;\n }\n\n return NextResponse.json({\n success: false,\n error: lastError,\n validationErrors: lastValidationErrors,\n });\n }\n\n const existing = await storage.getOverride(generated.manifest.id);\n\n if (existing) {\n await storage.updateOverride(generated.manifest.id, {\n name: generated.manifest.name,\n description: generated.manifest.description,\n version: generated.manifest.version,\n primitives: JSON.stringify(generated.manifest.primitives),\n code: generated.code,\n });\n } else {\n await storage.createOverride({\n id: generated.manifest.id,\n name: generated.manifest.name,\n description: generated.manifest.description,\n version: generated.manifest.version,\n primitives: JSON.stringify(generated.manifest.primitives),\n code: generated.code,\n enabled: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n }\n\n return NextResponse.json({\n success: true,\n overrideId: generated.manifest.id,\n name: generated.manifest.name,\n description: generated.manifest.description,\n });\n } catch (e) {\n lastError = e instanceof Error ? e.message : String(e);\n if (attempt === MAX_RETRIES) {\n return NextResponse.json({\n success: false,\n error: `Failed after ${MAX_RETRIES} attempts: ${lastError}`,\n });\n }\n }\n }\n\n return NextResponse.json({\n success: false,\n error: lastError || 'Unknown error',\n });\n}\n\nasync function handleToggle(req: NextRequest, storage: SidekickStorage) {\n const body = await safeParseBody<ToggleRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { overrideId, enabled } = body;\n\n if (!overrideId || typeof overrideId !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing overrideId parameter' },\n { status: 400 }\n );\n }\n\n if (typeof enabled !== 'boolean') {\n return NextResponse.json(\n { success: false, error: 'Missing or invalid enabled parameter' },\n { status: 400 }\n );\n }\n\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: 'Override not found' },\n { status: 404 }\n );\n }\n\n await storage.updateOverride(overrideId, { enabled });\n\n // Re-read to confirm persisted value\n const updated = await storage.getOverride(overrideId);\n\n return NextResponse.json({\n success: true,\n overrideId,\n enabled: updated?.enabled ?? enabled,\n });\n}\n\nasync function handleDelete(req: NextRequest, storage: SidekickStorage) {\n const body = await safeParseBody<DeleteRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { overrideId } = body;\n\n if (!overrideId || typeof overrideId !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing overrideId parameter' },\n { status: 400 }\n );\n }\n\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: 'Override not found' },\n { status: 404 }\n );\n }\n\n await storage.deleteOverride(overrideId);\n\n return NextResponse.json({\n success: true,\n message: `Override \"${overrideId}\" deleted successfully`,\n });\n}\n\nexport function createSidekickHandler(options: SidekickHandlerOptions) {\n const { storage, schemaPath } = options;\n\n return {\n async GET(req: NextRequest): Promise<NextResponse> {\n try {\n const action = getAction(req);\n if (action === 'overrides') {\n return handleListOverrides(storage);\n }\n return NextResponse.json(\n { success: false, error: `Unknown GET action: ${action}` },\n { status: 404 }\n );\n } catch (e) {\n console.error('[Sidekick] Handler error:', e);\n return NextResponse.json(\n { success: false, error: e instanceof Error ? e.message : 'Unknown error' },\n { status: 500 }\n );\n }\n },\n\n async POST(req: NextRequest): Promise<NextResponse> {\n try {\n const action = getAction(req);\n switch (action) {\n case 'generate':\n return handleGenerate(req, storage, schemaPath);\n case 'toggle':\n return handleToggle(req, storage);\n case 'delete':\n return handleDelete(req, storage);\n default:\n return NextResponse.json(\n { success: false, error: `Unknown POST action: ${action}` },\n { status: 404 }\n );\n }\n } catch (e) {\n console.error('[Sidekick] Handler error:', e);\n return NextResponse.json(\n { success: false, error: e instanceof Error ? e.message : 'Unknown error' },\n { status: 500 }\n );\n }\n },\n };\n}\n","import type { GeneratedOverride, OverrideManifest } from './types';\n\nconst ALLOWED_IMPORTS = ['@usesidekick/react', 'react'];\n\nconst FORBIDDEN_PATTERNS = [\n /\\bfetch\\s*\\(/,\n /\\beval\\s*\\(/,\n /\\bFunction\\s*\\(/,\n /\\bdocument\\./,\n /\\bwindow\\./,\n /\\blocalStorage\\./,\n /\\bsessionStorage\\./,\n /\\bXMLHttpRequest/,\n /\\bWebSocket/,\n /\\bimport\\s*\\(/,\n /require\\s*\\(/,\n];\n\ninterface DesignSystemSchema {\n framework?: string | null;\n fontFamily?: string | null;\n globalCss?: string | null;\n componentStyles?: Array<{\n component: string;\n file: string;\n classNames: string[];\n }>;\n}\n\nexport function formatDesignSystem(schema: Record<string, unknown>): string {\n const ds = (schema as { designSystem?: DesignSystemSchema }).designSystem;\n if (!ds) {\n return 'No design system information available. Use clean, minimal inline styles with neutral colors and standard spacing.';\n }\n\n const parts: string[] = [];\n parts.push('All generated UI MUST look like it was built by the same team that built the host app. Study the style samples below and match the exact same patterns — colors, spacing, typography, border radii, hover states, and dark mode support.');\n\n if (ds.framework) {\n parts.push(`\\n**CSS Framework:** ${ds.framework}`);\n }\n if (ds.fontFamily) {\n parts.push(`**Font:** ${ds.fontFamily}`);\n }\n if (ds.globalCss) {\n parts.push('**Global CSS:**\\n```css\\n' + ds.globalCss + '\\n```');\n }\n\n if (ds.componentStyles && ds.componentStyles.length > 0) {\n parts.push('\\n**Component Style Samples (actual className strings from the app — use these as your reference):**');\n for (const cs of ds.componentStyles) {\n parts.push(`\\n${cs.component} (${cs.file}):`);\n for (const cn of cs.classNames) {\n parts.push(` \"${cn}\"`);\n }\n }\n parts.push('\\nWhen adding new UI elements to an existing component, copy the exact className patterns from that component above. When creating standalone elements, pick the closest matching component pattern and follow it.');\n }\n\n return parts.join('\\n');\n}\n\nfunction formatComponents(schema: Record<string, unknown>): string {\n const components = (schema as { components?: Array<{ name: string; props: string[]; renderStructure?: string }> }).components || [];\n return components.map((c) => {\n let entry = '- ' + c.name + ' (props: ' + (c.props.join(', ') || 'none') + ')';\n if (c.renderStructure) {\n entry += '\\n Renders: ' + c.renderStructure;\n }\n return entry;\n }).join('\\n');\n}\n\nexport function buildSystemPrompt(schema: Record<string, unknown>): string {\n const tick = '`';\n const ticks = '```';\n const dollar = '$';\n return 'You are a code generator for Sidekick, an SDK that allows users to customize web applications.\\n' +\n'Your task is to generate override modules based on user requests.\\n' +\n'\\n' +\n'## SDK Primitives Available\\n' +\n'\\n' +\n'UI Primitives:\\n' +\n'- sdk.ui.wrap(componentName, wrapperFn, options?) - Wrap any component by name to add behavior around it. options: { priority?, where?: (props) => boolean }\\n' +\n'- sdk.ui.replace(componentName, Component) - Replace any component entirely by name\\n' +\n'- sdk.ui.addColumn(tableId, config) - Add a column to a table (config: { header, accessor, render? })\\n' +\n'- sdk.ui.renameColumn(tableId, originalHeader, newHeader) - Rename a column header\\n' +\n'- sdk.ui.reorderColumns(tableId, headerOrder) - Reorder columns by header name (array of header strings)\\n' +\n'- sdk.ui.hideColumn(tableId, header) - Hide a column by header name\\n' +\n'- sdk.ui.filterRows(tableId, filterFn) - Filter table rows (filterFn receives a row object, returns true to keep)\\n' +\n'- sdk.ui.addStyles(css) - Add CSS styles globally\\n' +\n'- sdk.ui.setText(selector, text) - Change text content of elements matching CSS selector\\n' +\n'- sdk.ui.setAttribute(selector, attr, value) - Set an attribute on matching elements\\n' +\n'- sdk.ui.setStyle(selector, styles) - Apply inline styles to matching elements (styles is an object like { color: \\'red\\' })\\n' +\n'- sdk.ui.addClass(selector, className) - Add a CSS class to matching elements\\n' +\n'- sdk.ui.removeClass(selector, className) - Remove a CSS class from matching elements\\n' +\n'- sdk.ui.inject(selector, Component, position?) - Inject a React component before/after/prepend/append an element (default: \\'after\\')\\n' +\n'\\n' +\n'Data Primitives:\\n' +\n'- sdk.data.computed(fieldName, computeFn) - Add a computed field\\n' +\n'- sdk.data.addFilter(name, filterFn) - Add a filter preset\\n' +\n'- sdk.data.transform(dataKey, transformFn) - Transform data\\n' +\n'- sdk.data.intercept(pathPattern, handler) - Intercept and modify API responses (handler receives response JSON and { path, method })\\n' +\n'\\n' +\n'Behavior Primitives:\\n' +\n'- sdk.behavior.addKeyboardShortcut(keys, action, description?) - Register a keyboard shortcut (e.g., \"ctrl+k\", \"shift+?\")\\n' +\n'- sdk.behavior.onDOMEvent(selector, eventType, handler) - Listen for DOM events on elements matching a CSS selector\\n' +\n'- sdk.behavior.modifyRoute(pathPattern, handler) - Intercept navigation and optionally redirect\\n' +\n'\\n' +\n'## App Design System\\n' +\n'\\n' +\nformatDesignSystem(schema) + '\\n' +\n'\\n' +\n'## App Schema\\n' +\n'\\n' +\n'Components (targetable by name with wrap/replace):\\n' +\nformatComponents(schema) + '\\n' +\n'\\n' +\n'Data Models: ' + JSON.stringify((schema as { dataModels?: unknown[] }).dataModels || []) + '\\n' +\n'\\n' +\n'## Example Overrides\\n' +\n'\\n' +\n'### Example 1: Wrap a component to add a badge (RECOMMENDED APPROACH)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'import { ComponentType } from \\'react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'task-card-badge\\',\\n' +\n' name: \\'Task Card Badge\\',\\n' +\n' description: \\'Adds a \"New\" badge to task cards\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.wrap\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.wrap(\\'TaskCard\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function WrappedTaskCard(props: Record<string, unknown>) {\\n' +\n' return (\\n' +\n' <div style={{ position: \\'relative\\' }}>\\n' +\n' <span style={{\\n' +\n' position: \\'absolute\\',\\n' +\n' top: \\'-8px\\',\\n' +\n' right: \\'-8px\\',\\n' +\n' backgroundColor: \\'#EF4444\\',\\n' +\n' color: \\'white\\',\\n' +\n' fontSize: \\'10px\\',\\n' +\n' fontWeight: \\'bold\\',\\n' +\n' padding: \\'2px 6px\\',\\n' +\n' borderRadius: \\'9999px\\',\\n' +\n' }}>\\n' +\n' NEW\\n' +\n' </span>\\n' +\n' <Original {...props} />\\n' +\n' </div>\\n' +\n' );\\n' +\n' };\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 2: Replace a component entirely\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'const CustomTaskModal = (props: Record<string, unknown>) => {\\n' +\n' const task = props.task as { title?: string; description?: string } | undefined;\\n' +\n' const onClose = props.onClose as () => void;\\n' +\n'\\n' +\n' return (\\n' +\n' <div style={{\\n' +\n' position: \\'fixed\\',\\n' +\n' inset: 0,\\n' +\n' backgroundColor: \\'rgba(0,0,0,0.5)\\',\\n' +\n' display: \\'flex\\',\\n' +\n' alignItems: \\'center\\',\\n' +\n' justifyContent: \\'center\\',\\n' +\n' }}>\\n' +\n' <div style={{\\n' +\n' backgroundColor: \\'white\\',\\n' +\n' borderRadius: \\'16px\\',\\n' +\n' padding: \\'24px\\',\\n' +\n' maxWidth: \\'500px\\',\\n' +\n' width: \\'100%\\',\\n' +\n' }}>\\n' +\n' <h2 style={{ fontSize: \\'24px\\', fontWeight: \\'bold\\', marginBottom: \\'16px\\' }}>\\n' +\n' {task?.title || \\'Task Details\\'}\\n' +\n' </h2>\\n' +\n' <p>{task?.description || \\'No description\\'}</p>\\n' +\n' <button\\n' +\n' onClick={onClose}\\n' +\n' style={{\\n' +\n' marginTop: \\'16px\\',\\n' +\n' backgroundColor: \\'#3B82F6\\',\\n' +\n' color: \\'white\\',\\n' +\n' padding: \\'8px 16px\\',\\n' +\n' borderRadius: \\'8px\\',\\n' +\n' border: \\'none\\',\\n' +\n' cursor: \\'pointer\\',\\n' +\n' }}\\n' +\n' >\\n' +\n' Close\\n' +\n' </button>\\n' +\n' </div>\\n' +\n' </div>\\n' +\n' );\\n' +\n'};\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-task-modal\\',\\n' +\n' name: \\'Custom Task Modal\\',\\n' +\n' description: \\'Replaces the task modal with a custom design\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.replace\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.replace(\\'TaskModal\\', CustomTaskModal);\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 3: Add a table column\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'days-left-column\\',\\n' +\n' name: \\'Days Left Column\\',\\n' +\n' description: \\'Shows days until due date\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.addColumn\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.addColumn(\\'task-table\\', {\\n' +\n' header: \\'Days Left\\',\\n' +\n' accessor: (row: Record<string, unknown>) => {\\n' +\n' const dueDate = row.dueDate as string | undefined;\\n' +\n' if (!dueDate) return null;\\n' +\n' const days = Math.ceil((new Date(dueDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24));\\n' +\n' return days;\\n' +\n' },\\n' +\n' render: (value: unknown) => {\\n' +\n' if (value === null) return <span>-</span>;\\n' +\n' const days = value as number;\\n' +\n' const color = days < 0 ? \\'red\\' : days <= 3 ? \\'orange\\' : \\'green\\';\\n' +\n' return <span style={{ color }}>{days}d</span>;\\n' +\n' },\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 4: Add global styles\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-theme\\',\\n' +\n' name: \\'Custom Theme\\',\\n' +\n' description: \\'Adds custom styling to the app\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.addStyles\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.addStyles(' + tick + '\\n' +\n' .task-card {\\n' +\n' border-radius: 12px;\\n' +\n' box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\\n' +\n' }\\n' +\n' .task-card:hover {\\n' +\n' transform: translateY(-2px);\\n' +\n' transition: transform 0.2s ease;\\n' +\n' }\\n' +\n' ' + tick + ');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 5: Column operations (rename, reorder, hide)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'reorder-columns\\',\\n' +\n' name: \\'Reorder Table Columns\\',\\n' +\n' description: \\'Reorders Status and Importance columns and hides Assignee\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.reorderColumns\\', \\'ui.hideColumn\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.reorderColumns(\\'task-table\\', [\\'Title\\', \\'Importance\\', \\'Status\\', \\'Project\\', \\'Due Date\\']);\\n' +\n' sdk.ui.hideColumn(\\'task-table\\', \\'Assignee\\');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 6: Interactive filter with sidebar nav button\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'import { ComponentType, useState, useEffect } from \\'react\\';\\n' +\n'\\n' +\n'let _filterActive = false;\\n' +\n'const _listeners = new Set<(active: boolean) => void>();\\n' +\n'\\n' +\n'function setFilterActive(active: boolean) {\\n' +\n' _filterActive = active;\\n' +\n' _listeners.forEach(fn => fn(active));\\n' +\n'}\\n' +\n'\\n' +\n'function useFilterActive(): [boolean, (active: boolean) => void] {\\n' +\n' const [active, setActive] = useState(_filterActive);\\n' +\n' useEffect(() => {\\n' +\n' const handler = (val: boolean) => setActive(val);\\n' +\n' _listeners.add(handler);\\n' +\n' return () => { _listeners.delete(handler); };\\n' +\n' }, []);\\n' +\n' return [active, setFilterActive];\\n' +\n'}\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'high-importance-filter\\',\\n' +\n' name: \\'High Importance Filter\\',\\n' +\n' description: \\'Adds a sidebar button to filter high importance tasks\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.wrap\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.wrap(\\'Sidebar\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function SidebarWithFilter(props: Record<string, unknown>) {\\n' +\n' const [isActive, setActive] = useFilterActive();\\n' +\n' return (\\n' +\n' <div style={{ display: \\'contents\\' }}>\\n' +\n' <Original {...props} />\\n' +\n' <div style={{ padding: \\'0 16px\\', marginTop: \\'8px\\' }}>\\n' +\n' <button\\n' +\n' onClick={() => setActive(!isActive)}\\n' +\n' style={{\\n' +\n' backgroundColor: isActive ? \\'#EFF6FF\\' : \\'transparent\\',\\n' +\n' color: isActive ? \\'#1D4ED8\\' : \\'#374151\\',\\n' +\n' }}\\n' +\n' >\\n' +\n' High Importance\\n' +\n' </button>\\n' +\n' </div>\\n' +\n' </div>\\n' +\n' );\\n' +\n' };\\n' +\n' });\\n' +\n'\\n' +\n' sdk.ui.wrap(\\'TaskTable\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function FilteredTaskTable(props: Record<string, unknown>) {\\n' +\n' const [isActive] = useFilterActive();\\n' +\n' if (!isActive) return <Original {...props} />;\\n' +\n' const tasks = (props.tasks || []) as Array<Record<string, unknown>>;\\n' +\n' const filtered = tasks.filter(t => t.importance === \\'high\\');\\n' +\n' return <Original {...props} tasks={filtered} />;\\n' +\n' };\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 7: Permanent row filter (always active when override is ON)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'hide-done-tasks\\',\\n' +\n' name: \\'Hide Done Tasks\\',\\n' +\n' description: \\'Hides completed tasks from the table\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.filterRows\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.filterRows(\\'task-table\\', (row: Record<string, unknown>) => {\\n' +\n' return row.status !== \\'done\\';\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 8: Keyboard shortcut\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'quick-search-shortcut\\',\\n' +\n' name: \\'Quick Search Shortcut\\',\\n' +\n' description: \\'Adds Ctrl+K shortcut to focus search\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'behavior.addKeyboardShortcut\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.behavior.addKeyboardShortcut(\\'ctrl+k\\', () => {\\n' +\n' // Toggle search or custom action\\n' +\n' }, \\'Open quick search\\');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 9: DOM modification\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-dom-mods\\',\\n' +\n' name: \\'Custom DOM Modifications\\',\\n' +\n' description: \\'Changes sidebar title and styles task cards\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.setText\\', \\'ui.setStyle\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.setText(\\'.sidebar-title\\', \\'My Custom App\\');\\n' +\n' sdk.ui.setStyle(\\'.task-card\\', { borderLeft: \\'4px solid #3B82F6\\' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'## Rules\\n' +\n'\\n' +\n'1. ONLY import from \\'@usesidekick/react\\' or \\'react\\' - NO other imports allowed\\n' +\n'2. DO NOT use fetch, eval, document.*, window.*, localStorage, etc.\\n' +\n'3. Use the createOverride function from @usesidekick/react\\n' +\n'4. The activate function receives an SDK object with ui and data primitives\\n' +\n'5. Generate React components using JSX with inline styles\\n' +\n'6. Keep code simple and focused on the user\\'s request\\n' +\n'7. Use sdk.ui.wrap() to add behavior around existing components, use sdk.ui.replace() to fully swap components\\n' +\n'8. When wrapping, always pass through all props to the Original component\\n' +\n'9. ALWAYS prefer SDK primitives over raw wrapping for table operations\\n' +\n'10. When using sdk.ui.wrap(), ALWAYS render <Original {...props} /> as-is\\n' +\n'11. A wrapper\\'s ONLY job is to add content BEFORE or AFTER <Original {...props} />, or to modify the props passed to it\\n' +\n'12. For permanent row filtering, use sdk.ui.filterRows(tableId, filterFn)\\n' +\n'13. Overrides MUST always act on the UI\\n' +\n'14. For interactive/togglable behavior, use module-level shared state with a listener pattern\\n' +\n'15. VISUAL INTEGRATION IS MANDATORY\\n' +\n'16. Study the component style samples in the design system carefully\\n' +\n'17. NEVER use absolute/fixed positioning to place elements inside existing components\\n' +\n'18. New elements MUST be visually indistinguishable from existing elements\\n' +\n'19. All interactive elements MUST have hover states\\n' +\n'20. BEFORE writing any wrap() code, read the target component\\'s \"Renders:\" tree\\n' +\n'21. Structure wrappers as: optional-new-content-above + <Original {...props} /> + optional-new-content-below\\n' +\n'22. CSS selectors MUST be valid CSS selectors supported by document.querySelectorAll()\\n' +\n'\\n' +\n'## Output Format\\n' +\n'\\n' +\n'Respond with ONLY a JSON object (no markdown, no explanation):\\n' +\n'{\\n' +\n' \"manifest\": {\\n' +\n' \"id\": \"override-id-kebab-case\",\\n' +\n' \"name\": \"Human Readable Name\",\\n' +\n' \"description\": \"What this override does\",\\n' +\n' \"version\": \"1.0.0\",\\n' +\n' \"primitives\": [\"list\", \"of\", \"primitives\", \"used\"]\\n' +\n' },\\n' +\n' \"code\": \"// Full TypeScript/TSX code here\"\\n' +\n'}';\n}\n\nexport function buildUserPrompt(\n request: string,\n previousErrors?: string[],\n existingCode?: string\n): string {\n let prompt: string;\n\n if (existingCode) {\n prompt = 'You are MODIFYING an existing override. Here is the current code:\\n```tsx\\n' + existingCode + '\\n```\\n\\nApply this change: \"' + request + '\"\\n\\nIMPORTANT: Keep the same override ID. Preserve all existing functionality unless the user explicitly asks to change it. Return the complete modified code.';\n } else {\n prompt = 'Generate an override module for this request: \"' + request + '\"';\n }\n\n if (previousErrors && previousErrors.length > 0) {\n prompt += '\\n\\nIMPORTANT: Previous generation attempt failed validation with these errors:\\n';\n prompt += previousErrors.map((e) => '- ' + e).join('\\n');\n prompt += '\\n\\nPlease fix these issues in your response.';\n }\n\n return prompt;\n}\n\nexport async function callAI(\n request: string,\n schema: Record<string, unknown>,\n apiKey: string,\n previousErrors?: string[],\n existingCode?: string\n): Promise<GeneratedOverride> {\n const systemPrompt = buildSystemPrompt(schema);\n const userPrompt = buildUserPrompt(request, previousErrors, existingCode);\n\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: 'claude-sonnet-4-20250514',\n max_tokens: 4096,\n system: systemPrompt,\n messages: [{ role: 'user', content: userPrompt }],\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error('API request failed: ' + response.status + ' ' + error);\n }\n\n const result = (await response.json()) as {\n content: Array<{ type: string; text?: string }>;\n };\n\n const textContent = result.content.find((c) => c.type === 'text');\n if (!textContent || !textContent.text) {\n throw new Error('No text content in API response');\n }\n\n return parseAIResponse(textContent.text);\n}\n\nexport function parseAIResponse(text: string): GeneratedOverride {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (!jsonMatch) {\n throw new Error('No JSON found in AI response');\n }\n\n try {\n const parsed = JSON.parse(jsonMatch[0]) as {\n manifest: OverrideManifest;\n code: string;\n };\n\n if (!parsed.manifest || !parsed.code) {\n throw new Error('Invalid response structure');\n }\n\n return {\n manifest: parsed.manifest,\n code: parsed.code,\n };\n } catch (e) {\n throw new Error('Failed to parse AI response: ' + (e instanceof Error ? e.message : String(e)));\n }\n}\n\nexport function validateCode(code: string): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n const importMatches = code.matchAll(/import\\s+.*?\\s+from\\s+['\"]([^'\"]+)['\"]/g);\n for (const match of importMatches) {\n const moduleName = match[1];\n const isAllowed = ALLOWED_IMPORTS.some(\n (allowed) => moduleName === allowed || moduleName.startsWith(allowed + '/')\n );\n if (!isAllowed) {\n errors.push('Forbidden import: ' + moduleName);\n }\n }\n\n for (const pattern of FORBIDDEN_PATTERNS) {\n if (pattern.test(code)) {\n errors.push('Forbidden pattern: ' + pattern.source);\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n","import { eq } from 'drizzle-orm';\nimport type { SidekickStorage, OverrideRecord, NewOverrideRecord } from './types';\nimport type { sidekickOverrides as OverridesTable } from './drizzle-schema';\n\ntype DrizzleDb = {\n select(): any;\n insert(table: any): any;\n update(table: any): any;\n delete(table: any): any;\n};\n\nexport function createDrizzleStorage(\n db: DrizzleDb,\n overridesTable: typeof OverridesTable\n): SidekickStorage {\n return {\n async listOverrides(): Promise<OverrideRecord[]> {\n const rows = await db.select().from(overridesTable).orderBy(overridesTable.createdAt);\n return rows as OverrideRecord[];\n },\n\n async getOverride(id: string): Promise<OverrideRecord | null> {\n const rows = await db.select().from(overridesTable).where(eq(overridesTable.id, id));\n return (rows[0] as OverrideRecord) ?? null;\n },\n\n async createOverride(data: NewOverrideRecord): Promise<void> {\n await db.insert(overridesTable).values({\n id: data.id,\n name: data.name,\n description: data.description,\n version: data.version,\n primitives: data.primitives,\n code: data.code,\n enabled: data.enabled ?? true,\n createdAt: data.createdAt ?? new Date(),\n updatedAt: data.updatedAt ?? new Date(),\n });\n },\n\n async updateOverride(id: string, data: Partial<OverrideRecord>): Promise<void> {\n // Strip id and createdAt to prevent accidental PK/immutable field updates\n const { id: _id, createdAt: _ca, ...updateData } = data;\n await db.update(overridesTable)\n .set({ ...updateData, updatedAt: new Date() })\n .where(eq(overridesTable.id, id));\n },\n\n async deleteOverride(id: string): Promise<void> {\n await db.delete(overridesTable).where(eq(overridesTable.id, id));\n },\n };\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { SidekickStorage, OverrideRecord, NewOverrideRecord } from './types';\n\ninterface StorageData {\n overrides: OverrideRecord[];\n}\n\nfunction deserializeDates(record: any): OverrideRecord {\n return {\n ...record,\n createdAt: new Date(record.createdAt),\n updatedAt: new Date(record.updatedAt),\n };\n}\n\nasync function readData(filePath: string): Promise<StorageData> {\n try {\n const raw = await fs.readFile(filePath, 'utf-8');\n const parsed = JSON.parse(raw) as StorageData;\n return {\n overrides: (parsed.overrides || []).map(deserializeDates),\n };\n } catch (err: any) {\n if (err.code === 'ENOENT') {\n // Auto-create file with empty data\n const empty: StorageData = { overrides: [] };\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, JSON.stringify(empty, null, 2) + '\\n');\n return empty;\n }\n throw err;\n }\n}\n\nasync function writeData(filePath: string, data: StorageData): Promise<void> {\n const tmpPath = filePath + '.tmp';\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(tmpPath, JSON.stringify(data, null, 2) + '\\n');\n await fs.rename(tmpPath, filePath);\n}\n\nexport function createJsonFileStorage(filePath?: string): SidekickStorage {\n const resolvedPath = filePath || path.join(process.cwd(), '.sidekick', 'overrides.json');\n\n return {\n async listOverrides(): Promise<OverrideRecord[]> {\n const data = await readData(resolvedPath);\n return data.overrides;\n },\n\n async getOverride(id: string): Promise<OverrideRecord | null> {\n const data = await readData(resolvedPath);\n return data.overrides.find((o) => o.id === id) ?? null;\n },\n\n async createOverride(record: NewOverrideRecord): Promise<void> {\n const data = await readData(resolvedPath);\n data.overrides.push({\n id: record.id,\n name: record.name,\n description: record.description,\n version: record.version,\n primitives: record.primitives,\n code: record.code,\n enabled: record.enabled ?? true,\n createdAt: record.createdAt ?? new Date(),\n updatedAt: record.updatedAt ?? new Date(),\n });\n await writeData(resolvedPath, data);\n },\n\n async updateOverride(id: string, updates: Partial<OverrideRecord>): Promise<void> {\n const data = await readData(resolvedPath);\n const idx = data.overrides.findIndex((o) => o.id === id);\n if (idx === -1) return;\n const { id: _id, createdAt: _ca, ...rest } = updates;\n data.overrides[idx] = { ...data.overrides[idx], ...rest, updatedAt: new Date() };\n await writeData(resolvedPath, data);\n },\n\n async deleteOverride(id: string): Promise<void> {\n const data = await readData(resolvedPath);\n data.overrides = data.overrides.filter((o) => o.id !== id);\n await writeData(resolvedPath, data);\n },\n };\n}\n","import { pgTable, text, timestamp, boolean } from 'drizzle-orm/pg-core';\n\nexport const sidekickOverrides = pgTable('overrides', {\n id: text('id').primaryKey(),\n name: text('name').notNull(),\n description: text('description').notNull(),\n version: text('version').notNull().default('1.0.0'),\n primitives: text('primitives').notNull(), // JSON array stored as text\n code: text('code').notNull(),\n enabled: boolean('enabled').notNull().default(true),\n createdAt: timestamp('created_at').defaultNow().notNull(),\n updatedAt: timestamp('updated_at').defaultNow().notNull(),\n});\n\nexport type SidekickOverride = typeof sidekickOverrides.$inferSelect;\nexport type NewSidekickOverride = typeof sidekickOverrides.$inferInsert;\n"],"mappings":"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","names":["text","fs","path"]}
|
package/dist/server/index.mjs
CHANGED
|
@@ -569,6 +569,81 @@ function createDrizzleStorage(db, overridesTable) {
|
|
|
569
569
|
};
|
|
570
570
|
}
|
|
571
571
|
|
|
572
|
+
// src/server/json-storage.ts
|
|
573
|
+
import * as fs2 from "fs/promises";
|
|
574
|
+
import * as path2 from "path";
|
|
575
|
+
function deserializeDates(record) {
|
|
576
|
+
return {
|
|
577
|
+
...record,
|
|
578
|
+
createdAt: new Date(record.createdAt),
|
|
579
|
+
updatedAt: new Date(record.updatedAt)
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
async function readData(filePath) {
|
|
583
|
+
try {
|
|
584
|
+
const raw = await fs2.readFile(filePath, "utf-8");
|
|
585
|
+
const parsed = JSON.parse(raw);
|
|
586
|
+
return {
|
|
587
|
+
overrides: (parsed.overrides || []).map(deserializeDates)
|
|
588
|
+
};
|
|
589
|
+
} catch (err) {
|
|
590
|
+
if (err.code === "ENOENT") {
|
|
591
|
+
const empty = { overrides: [] };
|
|
592
|
+
await fs2.mkdir(path2.dirname(filePath), { recursive: true });
|
|
593
|
+
await fs2.writeFile(filePath, JSON.stringify(empty, null, 2) + "\n");
|
|
594
|
+
return empty;
|
|
595
|
+
}
|
|
596
|
+
throw err;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
async function writeData(filePath, data) {
|
|
600
|
+
const tmpPath = filePath + ".tmp";
|
|
601
|
+
await fs2.mkdir(path2.dirname(filePath), { recursive: true });
|
|
602
|
+
await fs2.writeFile(tmpPath, JSON.stringify(data, null, 2) + "\n");
|
|
603
|
+
await fs2.rename(tmpPath, filePath);
|
|
604
|
+
}
|
|
605
|
+
function createJsonFileStorage(filePath) {
|
|
606
|
+
const resolvedPath = filePath || path2.join(process.cwd(), ".sidekick", "overrides.json");
|
|
607
|
+
return {
|
|
608
|
+
async listOverrides() {
|
|
609
|
+
const data = await readData(resolvedPath);
|
|
610
|
+
return data.overrides;
|
|
611
|
+
},
|
|
612
|
+
async getOverride(id) {
|
|
613
|
+
const data = await readData(resolvedPath);
|
|
614
|
+
return data.overrides.find((o) => o.id === id) ?? null;
|
|
615
|
+
},
|
|
616
|
+
async createOverride(record) {
|
|
617
|
+
const data = await readData(resolvedPath);
|
|
618
|
+
data.overrides.push({
|
|
619
|
+
id: record.id,
|
|
620
|
+
name: record.name,
|
|
621
|
+
description: record.description,
|
|
622
|
+
version: record.version,
|
|
623
|
+
primitives: record.primitives,
|
|
624
|
+
code: record.code,
|
|
625
|
+
enabled: record.enabled ?? true,
|
|
626
|
+
createdAt: record.createdAt ?? /* @__PURE__ */ new Date(),
|
|
627
|
+
updatedAt: record.updatedAt ?? /* @__PURE__ */ new Date()
|
|
628
|
+
});
|
|
629
|
+
await writeData(resolvedPath, data);
|
|
630
|
+
},
|
|
631
|
+
async updateOverride(id, updates) {
|
|
632
|
+
const data = await readData(resolvedPath);
|
|
633
|
+
const idx = data.overrides.findIndex((o) => o.id === id);
|
|
634
|
+
if (idx === -1) return;
|
|
635
|
+
const { id: _id, createdAt: _ca, ...rest } = updates;
|
|
636
|
+
data.overrides[idx] = { ...data.overrides[idx], ...rest, updatedAt: /* @__PURE__ */ new Date() };
|
|
637
|
+
await writeData(resolvedPath, data);
|
|
638
|
+
},
|
|
639
|
+
async deleteOverride(id) {
|
|
640
|
+
const data = await readData(resolvedPath);
|
|
641
|
+
data.overrides = data.overrides.filter((o) => o.id !== id);
|
|
642
|
+
await writeData(resolvedPath, data);
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
572
647
|
// src/server/drizzle-schema.ts
|
|
573
648
|
import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";
|
|
574
649
|
var sidekickOverrides = pgTable("overrides", {
|
|
@@ -588,6 +663,7 @@ export {
|
|
|
588
663
|
buildUserPrompt,
|
|
589
664
|
callAI,
|
|
590
665
|
createDrizzleStorage,
|
|
666
|
+
createJsonFileStorage,
|
|
591
667
|
createSidekickHandler,
|
|
592
668
|
formatDesignSystem,
|
|
593
669
|
parseAIResponse,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/handler.ts","../../src/server/generate.ts","../../src/server/drizzle-adapter.ts","../../src/server/drizzle-schema.ts"],"sourcesContent":["import * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { NextRequest } from 'next/server';\nimport { NextResponse } from 'next/server';\nimport type { SidekickHandlerOptions, SidekickStorage } from './types';\nimport { callAI, validateCode } from './generate';\n\ninterface GenerateRequestBody {\n request: string;\n overrideId?: string;\n}\n\ninterface ToggleRequestBody {\n overrideId: string;\n enabled: boolean;\n}\n\ninterface DeleteRequestBody {\n overrideId: string;\n}\n\nfunction getAction(req: NextRequest): string {\n const url = new URL(req.url);\n const segments = url.pathname.split('/').filter(Boolean);\n // Last segment is the action: overrides, generate, toggle, delete\n return segments[segments.length - 1] || '';\n}\n\nasync function readSchema(schemaPath?: string): Promise<Record<string, unknown>> {\n const candidates = schemaPath\n ? [schemaPath]\n : [\n path.join(process.cwd(), 'src/sidekick/schema.json'),\n path.join(process.cwd(), 'sidekick/schema.json'),\n ];\n\n for (const candidate of candidates) {\n try {\n const content = await fs.readFile(candidate, 'utf-8');\n return JSON.parse(content);\n } catch {\n continue;\n }\n }\n throw new Error('Failed to read schema.json');\n}\n\nfunction safeParsePrimitives(primitives: string): string[] {\n try {\n const parsed = JSON.parse(primitives);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\nasync function handleListOverrides(storage: SidekickStorage) {\n const overrides = await storage.listOverrides();\n return NextResponse.json(\n {\n success: true,\n overrides: overrides.map((o) => ({\n id: o.id,\n name: o.name,\n description: o.description,\n version: o.version,\n primitives: safeParsePrimitives(o.primitives),\n code: o.code,\n enabled: o.enabled,\n createdAt: o.createdAt,\n updatedAt: o.updatedAt,\n })),\n },\n {\n headers: {\n 'Cache-Control': 'no-store, no-cache, must-revalidate',\n },\n }\n );\n}\n\nasync function safeParseBody<T>(req: NextRequest): Promise<T | null> {\n try {\n return (await req.json()) as T;\n } catch {\n return null;\n }\n}\n\nasync function handleGenerate(\n req: NextRequest,\n storage: SidekickStorage,\n schemaPath?: string\n) {\n const body = await safeParseBody<GenerateRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { request, overrideId } = body;\n\n if (!request || typeof request !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing request parameter' },\n { status: 400 }\n );\n }\n\n let existingCode: string | undefined;\n if (overrideId) {\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: `Override \"${overrideId}\" not found` },\n { status: 404 }\n );\n }\n existingCode = existing.code;\n }\n\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n return NextResponse.json(\n { success: false, error: 'ANTHROPIC_API_KEY not configured' },\n { status: 500 }\n );\n }\n\n let schema: Record<string, unknown>;\n try {\n schema = await readSchema(schemaPath);\n } catch {\n return NextResponse.json(\n { success: false, error: 'Failed to read schema.json' },\n { status: 500 }\n );\n }\n\n const MAX_RETRIES = 3;\n let lastError: string | undefined;\n let lastValidationErrors: string[] | undefined;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n const generated = await callAI(request, schema, apiKey, lastValidationErrors, existingCode);\n\n if (overrideId) {\n generated.manifest.id = overrideId;\n }\n\n const validation = validateCode(generated.code);\n\n if (!validation.valid) {\n lastValidationErrors = validation.errors;\n lastError = `Validation failed: ${validation.errors.join(', ')}`;\n\n if (attempt < MAX_RETRIES) {\n console.log(`[Sidekick] Attempt ${attempt} failed validation, retrying...`);\n continue;\n }\n\n return NextResponse.json({\n success: false,\n error: lastError,\n validationErrors: lastValidationErrors,\n });\n }\n\n const existing = await storage.getOverride(generated.manifest.id);\n\n if (existing) {\n await storage.updateOverride(generated.manifest.id, {\n name: generated.manifest.name,\n description: generated.manifest.description,\n version: generated.manifest.version,\n primitives: JSON.stringify(generated.manifest.primitives),\n code: generated.code,\n });\n } else {\n await storage.createOverride({\n id: generated.manifest.id,\n name: generated.manifest.name,\n description: generated.manifest.description,\n version: generated.manifest.version,\n primitives: JSON.stringify(generated.manifest.primitives),\n code: generated.code,\n enabled: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n }\n\n return NextResponse.json({\n success: true,\n overrideId: generated.manifest.id,\n name: generated.manifest.name,\n description: generated.manifest.description,\n });\n } catch (e) {\n lastError = e instanceof Error ? e.message : String(e);\n if (attempt === MAX_RETRIES) {\n return NextResponse.json({\n success: false,\n error: `Failed after ${MAX_RETRIES} attempts: ${lastError}`,\n });\n }\n }\n }\n\n return NextResponse.json({\n success: false,\n error: lastError || 'Unknown error',\n });\n}\n\nasync function handleToggle(req: NextRequest, storage: SidekickStorage) {\n const body = await safeParseBody<ToggleRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { overrideId, enabled } = body;\n\n if (!overrideId || typeof overrideId !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing overrideId parameter' },\n { status: 400 }\n );\n }\n\n if (typeof enabled !== 'boolean') {\n return NextResponse.json(\n { success: false, error: 'Missing or invalid enabled parameter' },\n { status: 400 }\n );\n }\n\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: 'Override not found' },\n { status: 404 }\n );\n }\n\n await storage.updateOverride(overrideId, { enabled });\n\n // Re-read to confirm persisted value\n const updated = await storage.getOverride(overrideId);\n\n return NextResponse.json({\n success: true,\n overrideId,\n enabled: updated?.enabled ?? enabled,\n });\n}\n\nasync function handleDelete(req: NextRequest, storage: SidekickStorage) {\n const body = await safeParseBody<DeleteRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { overrideId } = body;\n\n if (!overrideId || typeof overrideId !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing overrideId parameter' },\n { status: 400 }\n );\n }\n\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: 'Override not found' },\n { status: 404 }\n );\n }\n\n await storage.deleteOverride(overrideId);\n\n return NextResponse.json({\n success: true,\n message: `Override \"${overrideId}\" deleted successfully`,\n });\n}\n\nexport function createSidekickHandler(options: SidekickHandlerOptions) {\n const { storage, schemaPath } = options;\n\n return {\n async GET(req: NextRequest): Promise<NextResponse> {\n try {\n const action = getAction(req);\n if (action === 'overrides') {\n return handleListOverrides(storage);\n }\n return NextResponse.json(\n { success: false, error: `Unknown GET action: ${action}` },\n { status: 404 }\n );\n } catch (e) {\n console.error('[Sidekick] Handler error:', e);\n return NextResponse.json(\n { success: false, error: e instanceof Error ? e.message : 'Unknown error' },\n { status: 500 }\n );\n }\n },\n\n async POST(req: NextRequest): Promise<NextResponse> {\n try {\n const action = getAction(req);\n switch (action) {\n case 'generate':\n return handleGenerate(req, storage, schemaPath);\n case 'toggle':\n return handleToggle(req, storage);\n case 'delete':\n return handleDelete(req, storage);\n default:\n return NextResponse.json(\n { success: false, error: `Unknown POST action: ${action}` },\n { status: 404 }\n );\n }\n } catch (e) {\n console.error('[Sidekick] Handler error:', e);\n return NextResponse.json(\n { success: false, error: e instanceof Error ? e.message : 'Unknown error' },\n { status: 500 }\n );\n }\n },\n };\n}\n","import type { GeneratedOverride, OverrideManifest } from './types';\n\nconst ALLOWED_IMPORTS = ['@usesidekick/react', 'react'];\n\nconst FORBIDDEN_PATTERNS = [\n /\\bfetch\\s*\\(/,\n /\\beval\\s*\\(/,\n /\\bFunction\\s*\\(/,\n /\\bdocument\\./,\n /\\bwindow\\./,\n /\\blocalStorage\\./,\n /\\bsessionStorage\\./,\n /\\bXMLHttpRequest/,\n /\\bWebSocket/,\n /\\bimport\\s*\\(/,\n /require\\s*\\(/,\n];\n\ninterface DesignSystemSchema {\n framework?: string | null;\n fontFamily?: string | null;\n globalCss?: string | null;\n componentStyles?: Array<{\n component: string;\n file: string;\n classNames: string[];\n }>;\n}\n\nexport function formatDesignSystem(schema: Record<string, unknown>): string {\n const ds = (schema as { designSystem?: DesignSystemSchema }).designSystem;\n if (!ds) {\n return 'No design system information available. Use clean, minimal inline styles with neutral colors and standard spacing.';\n }\n\n const parts: string[] = [];\n parts.push('All generated UI MUST look like it was built by the same team that built the host app. Study the style samples below and match the exact same patterns — colors, spacing, typography, border radii, hover states, and dark mode support.');\n\n if (ds.framework) {\n parts.push(`\\n**CSS Framework:** ${ds.framework}`);\n }\n if (ds.fontFamily) {\n parts.push(`**Font:** ${ds.fontFamily}`);\n }\n if (ds.globalCss) {\n parts.push('**Global CSS:**\\n```css\\n' + ds.globalCss + '\\n```');\n }\n\n if (ds.componentStyles && ds.componentStyles.length > 0) {\n parts.push('\\n**Component Style Samples (actual className strings from the app — use these as your reference):**');\n for (const cs of ds.componentStyles) {\n parts.push(`\\n${cs.component} (${cs.file}):`);\n for (const cn of cs.classNames) {\n parts.push(` \"${cn}\"`);\n }\n }\n parts.push('\\nWhen adding new UI elements to an existing component, copy the exact className patterns from that component above. When creating standalone elements, pick the closest matching component pattern and follow it.');\n }\n\n return parts.join('\\n');\n}\n\nfunction formatComponents(schema: Record<string, unknown>): string {\n const components = (schema as { components?: Array<{ name: string; props: string[]; renderStructure?: string }> }).components || [];\n return components.map((c) => {\n let entry = '- ' + c.name + ' (props: ' + (c.props.join(', ') || 'none') + ')';\n if (c.renderStructure) {\n entry += '\\n Renders: ' + c.renderStructure;\n }\n return entry;\n }).join('\\n');\n}\n\nexport function buildSystemPrompt(schema: Record<string, unknown>): string {\n const tick = '`';\n const ticks = '```';\n const dollar = '$';\n return 'You are a code generator for Sidekick, an SDK that allows users to customize web applications.\\n' +\n'Your task is to generate override modules based on user requests.\\n' +\n'\\n' +\n'## SDK Primitives Available\\n' +\n'\\n' +\n'UI Primitives:\\n' +\n'- sdk.ui.wrap(componentName, wrapperFn, options?) - Wrap any component by name to add behavior around it. options: { priority?, where?: (props) => boolean }\\n' +\n'- sdk.ui.replace(componentName, Component) - Replace any component entirely by name\\n' +\n'- sdk.ui.addColumn(tableId, config) - Add a column to a table (config: { header, accessor, render? })\\n' +\n'- sdk.ui.renameColumn(tableId, originalHeader, newHeader) - Rename a column header\\n' +\n'- sdk.ui.reorderColumns(tableId, headerOrder) - Reorder columns by header name (array of header strings)\\n' +\n'- sdk.ui.hideColumn(tableId, header) - Hide a column by header name\\n' +\n'- sdk.ui.filterRows(tableId, filterFn) - Filter table rows (filterFn receives a row object, returns true to keep)\\n' +\n'- sdk.ui.addStyles(css) - Add CSS styles globally\\n' +\n'- sdk.ui.setText(selector, text) - Change text content of elements matching CSS selector\\n' +\n'- sdk.ui.setAttribute(selector, attr, value) - Set an attribute on matching elements\\n' +\n'- sdk.ui.setStyle(selector, styles) - Apply inline styles to matching elements (styles is an object like { color: \\'red\\' })\\n' +\n'- sdk.ui.addClass(selector, className) - Add a CSS class to matching elements\\n' +\n'- sdk.ui.removeClass(selector, className) - Remove a CSS class from matching elements\\n' +\n'- sdk.ui.inject(selector, Component, position?) - Inject a React component before/after/prepend/append an element (default: \\'after\\')\\n' +\n'\\n' +\n'Data Primitives:\\n' +\n'- sdk.data.computed(fieldName, computeFn) - Add a computed field\\n' +\n'- sdk.data.addFilter(name, filterFn) - Add a filter preset\\n' +\n'- sdk.data.transform(dataKey, transformFn) - Transform data\\n' +\n'- sdk.data.intercept(pathPattern, handler) - Intercept and modify API responses (handler receives response JSON and { path, method })\\n' +\n'\\n' +\n'Behavior Primitives:\\n' +\n'- sdk.behavior.addKeyboardShortcut(keys, action, description?) - Register a keyboard shortcut (e.g., \"ctrl+k\", \"shift+?\")\\n' +\n'- sdk.behavior.onDOMEvent(selector, eventType, handler) - Listen for DOM events on elements matching a CSS selector\\n' +\n'- sdk.behavior.modifyRoute(pathPattern, handler) - Intercept navigation and optionally redirect\\n' +\n'\\n' +\n'## App Design System\\n' +\n'\\n' +\nformatDesignSystem(schema) + '\\n' +\n'\\n' +\n'## App Schema\\n' +\n'\\n' +\n'Components (targetable by name with wrap/replace):\\n' +\nformatComponents(schema) + '\\n' +\n'\\n' +\n'Data Models: ' + JSON.stringify((schema as { dataModels?: unknown[] }).dataModels || []) + '\\n' +\n'\\n' +\n'## Example Overrides\\n' +\n'\\n' +\n'### Example 1: Wrap a component to add a badge (RECOMMENDED APPROACH)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'import { ComponentType } from \\'react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'task-card-badge\\',\\n' +\n' name: \\'Task Card Badge\\',\\n' +\n' description: \\'Adds a \"New\" badge to task cards\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.wrap\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.wrap(\\'TaskCard\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function WrappedTaskCard(props: Record<string, unknown>) {\\n' +\n' return (\\n' +\n' <div style={{ position: \\'relative\\' }}>\\n' +\n' <span style={{\\n' +\n' position: \\'absolute\\',\\n' +\n' top: \\'-8px\\',\\n' +\n' right: \\'-8px\\',\\n' +\n' backgroundColor: \\'#EF4444\\',\\n' +\n' color: \\'white\\',\\n' +\n' fontSize: \\'10px\\',\\n' +\n' fontWeight: \\'bold\\',\\n' +\n' padding: \\'2px 6px\\',\\n' +\n' borderRadius: \\'9999px\\',\\n' +\n' }}>\\n' +\n' NEW\\n' +\n' </span>\\n' +\n' <Original {...props} />\\n' +\n' </div>\\n' +\n' );\\n' +\n' };\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 2: Replace a component entirely\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'const CustomTaskModal = (props: Record<string, unknown>) => {\\n' +\n' const task = props.task as { title?: string; description?: string } | undefined;\\n' +\n' const onClose = props.onClose as () => void;\\n' +\n'\\n' +\n' return (\\n' +\n' <div style={{\\n' +\n' position: \\'fixed\\',\\n' +\n' inset: 0,\\n' +\n' backgroundColor: \\'rgba(0,0,0,0.5)\\',\\n' +\n' display: \\'flex\\',\\n' +\n' alignItems: \\'center\\',\\n' +\n' justifyContent: \\'center\\',\\n' +\n' }}>\\n' +\n' <div style={{\\n' +\n' backgroundColor: \\'white\\',\\n' +\n' borderRadius: \\'16px\\',\\n' +\n' padding: \\'24px\\',\\n' +\n' maxWidth: \\'500px\\',\\n' +\n' width: \\'100%\\',\\n' +\n' }}>\\n' +\n' <h2 style={{ fontSize: \\'24px\\', fontWeight: \\'bold\\', marginBottom: \\'16px\\' }}>\\n' +\n' {task?.title || \\'Task Details\\'}\\n' +\n' </h2>\\n' +\n' <p>{task?.description || \\'No description\\'}</p>\\n' +\n' <button\\n' +\n' onClick={onClose}\\n' +\n' style={{\\n' +\n' marginTop: \\'16px\\',\\n' +\n' backgroundColor: \\'#3B82F6\\',\\n' +\n' color: \\'white\\',\\n' +\n' padding: \\'8px 16px\\',\\n' +\n' borderRadius: \\'8px\\',\\n' +\n' border: \\'none\\',\\n' +\n' cursor: \\'pointer\\',\\n' +\n' }}\\n' +\n' >\\n' +\n' Close\\n' +\n' </button>\\n' +\n' </div>\\n' +\n' </div>\\n' +\n' );\\n' +\n'};\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-task-modal\\',\\n' +\n' name: \\'Custom Task Modal\\',\\n' +\n' description: \\'Replaces the task modal with a custom design\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.replace\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.replace(\\'TaskModal\\', CustomTaskModal);\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 3: Add a table column\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'days-left-column\\',\\n' +\n' name: \\'Days Left Column\\',\\n' +\n' description: \\'Shows days until due date\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.addColumn\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.addColumn(\\'task-table\\', {\\n' +\n' header: \\'Days Left\\',\\n' +\n' accessor: (row: Record<string, unknown>) => {\\n' +\n' const dueDate = row.dueDate as string | undefined;\\n' +\n' if (!dueDate) return null;\\n' +\n' const days = Math.ceil((new Date(dueDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24));\\n' +\n' return days;\\n' +\n' },\\n' +\n' render: (value: unknown) => {\\n' +\n' if (value === null) return <span>-</span>;\\n' +\n' const days = value as number;\\n' +\n' const color = days < 0 ? \\'red\\' : days <= 3 ? \\'orange\\' : \\'green\\';\\n' +\n' return <span style={{ color }}>{days}d</span>;\\n' +\n' },\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 4: Add global styles\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-theme\\',\\n' +\n' name: \\'Custom Theme\\',\\n' +\n' description: \\'Adds custom styling to the app\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.addStyles\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.addStyles(' + tick + '\\n' +\n' .task-card {\\n' +\n' border-radius: 12px;\\n' +\n' box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\\n' +\n' }\\n' +\n' .task-card:hover {\\n' +\n' transform: translateY(-2px);\\n' +\n' transition: transform 0.2s ease;\\n' +\n' }\\n' +\n' ' + tick + ');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 5: Column operations (rename, reorder, hide)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'reorder-columns\\',\\n' +\n' name: \\'Reorder Table Columns\\',\\n' +\n' description: \\'Reorders Status and Importance columns and hides Assignee\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.reorderColumns\\', \\'ui.hideColumn\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.reorderColumns(\\'task-table\\', [\\'Title\\', \\'Importance\\', \\'Status\\', \\'Project\\', \\'Due Date\\']);\\n' +\n' sdk.ui.hideColumn(\\'task-table\\', \\'Assignee\\');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 6: Interactive filter with sidebar nav button\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'import { ComponentType, useState, useEffect } from \\'react\\';\\n' +\n'\\n' +\n'let _filterActive = false;\\n' +\n'const _listeners = new Set<(active: boolean) => void>();\\n' +\n'\\n' +\n'function setFilterActive(active: boolean) {\\n' +\n' _filterActive = active;\\n' +\n' _listeners.forEach(fn => fn(active));\\n' +\n'}\\n' +\n'\\n' +\n'function useFilterActive(): [boolean, (active: boolean) => void] {\\n' +\n' const [active, setActive] = useState(_filterActive);\\n' +\n' useEffect(() => {\\n' +\n' const handler = (val: boolean) => setActive(val);\\n' +\n' _listeners.add(handler);\\n' +\n' return () => { _listeners.delete(handler); };\\n' +\n' }, []);\\n' +\n' return [active, setFilterActive];\\n' +\n'}\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'high-importance-filter\\',\\n' +\n' name: \\'High Importance Filter\\',\\n' +\n' description: \\'Adds a sidebar button to filter high importance tasks\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.wrap\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.wrap(\\'Sidebar\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function SidebarWithFilter(props: Record<string, unknown>) {\\n' +\n' const [isActive, setActive] = useFilterActive();\\n' +\n' return (\\n' +\n' <div style={{ display: \\'contents\\' }}>\\n' +\n' <Original {...props} />\\n' +\n' <div style={{ padding: \\'0 16px\\', marginTop: \\'8px\\' }}>\\n' +\n' <button\\n' +\n' onClick={() => setActive(!isActive)}\\n' +\n' style={{\\n' +\n' backgroundColor: isActive ? \\'#EFF6FF\\' : \\'transparent\\',\\n' +\n' color: isActive ? \\'#1D4ED8\\' : \\'#374151\\',\\n' +\n' }}\\n' +\n' >\\n' +\n' High Importance\\n' +\n' </button>\\n' +\n' </div>\\n' +\n' </div>\\n' +\n' );\\n' +\n' };\\n' +\n' });\\n' +\n'\\n' +\n' sdk.ui.wrap(\\'TaskTable\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function FilteredTaskTable(props: Record<string, unknown>) {\\n' +\n' const [isActive] = useFilterActive();\\n' +\n' if (!isActive) return <Original {...props} />;\\n' +\n' const tasks = (props.tasks || []) as Array<Record<string, unknown>>;\\n' +\n' const filtered = tasks.filter(t => t.importance === \\'high\\');\\n' +\n' return <Original {...props} tasks={filtered} />;\\n' +\n' };\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 7: Permanent row filter (always active when override is ON)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'hide-done-tasks\\',\\n' +\n' name: \\'Hide Done Tasks\\',\\n' +\n' description: \\'Hides completed tasks from the table\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.filterRows\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.filterRows(\\'task-table\\', (row: Record<string, unknown>) => {\\n' +\n' return row.status !== \\'done\\';\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 8: Keyboard shortcut\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'quick-search-shortcut\\',\\n' +\n' name: \\'Quick Search Shortcut\\',\\n' +\n' description: \\'Adds Ctrl+K shortcut to focus search\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'behavior.addKeyboardShortcut\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.behavior.addKeyboardShortcut(\\'ctrl+k\\', () => {\\n' +\n' // Toggle search or custom action\\n' +\n' }, \\'Open quick search\\');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 9: DOM modification\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-dom-mods\\',\\n' +\n' name: \\'Custom DOM Modifications\\',\\n' +\n' description: \\'Changes sidebar title and styles task cards\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.setText\\', \\'ui.setStyle\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.setText(\\'.sidebar-title\\', \\'My Custom App\\');\\n' +\n' sdk.ui.setStyle(\\'.task-card\\', { borderLeft: \\'4px solid #3B82F6\\' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'## Rules\\n' +\n'\\n' +\n'1. ONLY import from \\'@usesidekick/react\\' or \\'react\\' - NO other imports allowed\\n' +\n'2. DO NOT use fetch, eval, document.*, window.*, localStorage, etc.\\n' +\n'3. Use the createOverride function from @usesidekick/react\\n' +\n'4. The activate function receives an SDK object with ui and data primitives\\n' +\n'5. Generate React components using JSX with inline styles\\n' +\n'6. Keep code simple and focused on the user\\'s request\\n' +\n'7. Use sdk.ui.wrap() to add behavior around existing components, use sdk.ui.replace() to fully swap components\\n' +\n'8. When wrapping, always pass through all props to the Original component\\n' +\n'9. ALWAYS prefer SDK primitives over raw wrapping for table operations\\n' +\n'10. When using sdk.ui.wrap(), ALWAYS render <Original {...props} /> as-is\\n' +\n'11. A wrapper\\'s ONLY job is to add content BEFORE or AFTER <Original {...props} />, or to modify the props passed to it\\n' +\n'12. For permanent row filtering, use sdk.ui.filterRows(tableId, filterFn)\\n' +\n'13. Overrides MUST always act on the UI\\n' +\n'14. For interactive/togglable behavior, use module-level shared state with a listener pattern\\n' +\n'15. VISUAL INTEGRATION IS MANDATORY\\n' +\n'16. Study the component style samples in the design system carefully\\n' +\n'17. NEVER use absolute/fixed positioning to place elements inside existing components\\n' +\n'18. New elements MUST be visually indistinguishable from existing elements\\n' +\n'19. All interactive elements MUST have hover states\\n' +\n'20. BEFORE writing any wrap() code, read the target component\\'s \"Renders:\" tree\\n' +\n'21. Structure wrappers as: optional-new-content-above + <Original {...props} /> + optional-new-content-below\\n' +\n'22. CSS selectors MUST be valid CSS selectors supported by document.querySelectorAll()\\n' +\n'\\n' +\n'## Output Format\\n' +\n'\\n' +\n'Respond with ONLY a JSON object (no markdown, no explanation):\\n' +\n'{\\n' +\n' \"manifest\": {\\n' +\n' \"id\": \"override-id-kebab-case\",\\n' +\n' \"name\": \"Human Readable Name\",\\n' +\n' \"description\": \"What this override does\",\\n' +\n' \"version\": \"1.0.0\",\\n' +\n' \"primitives\": [\"list\", \"of\", \"primitives\", \"used\"]\\n' +\n' },\\n' +\n' \"code\": \"// Full TypeScript/TSX code here\"\\n' +\n'}';\n}\n\nexport function buildUserPrompt(\n request: string,\n previousErrors?: string[],\n existingCode?: string\n): string {\n let prompt: string;\n\n if (existingCode) {\n prompt = 'You are MODIFYING an existing override. Here is the current code:\\n```tsx\\n' + existingCode + '\\n```\\n\\nApply this change: \"' + request + '\"\\n\\nIMPORTANT: Keep the same override ID. Preserve all existing functionality unless the user explicitly asks to change it. Return the complete modified code.';\n } else {\n prompt = 'Generate an override module for this request: \"' + request + '\"';\n }\n\n if (previousErrors && previousErrors.length > 0) {\n prompt += '\\n\\nIMPORTANT: Previous generation attempt failed validation with these errors:\\n';\n prompt += previousErrors.map((e) => '- ' + e).join('\\n');\n prompt += '\\n\\nPlease fix these issues in your response.';\n }\n\n return prompt;\n}\n\nexport async function callAI(\n request: string,\n schema: Record<string, unknown>,\n apiKey: string,\n previousErrors?: string[],\n existingCode?: string\n): Promise<GeneratedOverride> {\n const systemPrompt = buildSystemPrompt(schema);\n const userPrompt = buildUserPrompt(request, previousErrors, existingCode);\n\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: 'claude-sonnet-4-20250514',\n max_tokens: 4096,\n system: systemPrompt,\n messages: [{ role: 'user', content: userPrompt }],\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error('API request failed: ' + response.status + ' ' + error);\n }\n\n const result = (await response.json()) as {\n content: Array<{ type: string; text?: string }>;\n };\n\n const textContent = result.content.find((c) => c.type === 'text');\n if (!textContent || !textContent.text) {\n throw new Error('No text content in API response');\n }\n\n return parseAIResponse(textContent.text);\n}\n\nexport function parseAIResponse(text: string): GeneratedOverride {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (!jsonMatch) {\n throw new Error('No JSON found in AI response');\n }\n\n try {\n const parsed = JSON.parse(jsonMatch[0]) as {\n manifest: OverrideManifest;\n code: string;\n };\n\n if (!parsed.manifest || !parsed.code) {\n throw new Error('Invalid response structure');\n }\n\n return {\n manifest: parsed.manifest,\n code: parsed.code,\n };\n } catch (e) {\n throw new Error('Failed to parse AI response: ' + (e instanceof Error ? e.message : String(e)));\n }\n}\n\nexport function validateCode(code: string): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n const importMatches = code.matchAll(/import\\s+.*?\\s+from\\s+['\"]([^'\"]+)['\"]/g);\n for (const match of importMatches) {\n const moduleName = match[1];\n const isAllowed = ALLOWED_IMPORTS.some(\n (allowed) => moduleName === allowed || moduleName.startsWith(allowed + '/')\n );\n if (!isAllowed) {\n errors.push('Forbidden import: ' + moduleName);\n }\n }\n\n for (const pattern of FORBIDDEN_PATTERNS) {\n if (pattern.test(code)) {\n errors.push('Forbidden pattern: ' + pattern.source);\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n","import { eq } from 'drizzle-orm';\nimport type { SidekickStorage, OverrideRecord, NewOverrideRecord } from './types';\nimport type { sidekickOverrides as OverridesTable } from './drizzle-schema';\n\ntype DrizzleDb = {\n select(): any;\n insert(table: any): any;\n update(table: any): any;\n delete(table: any): any;\n};\n\nexport function createDrizzleStorage(\n db: DrizzleDb,\n overridesTable: typeof OverridesTable\n): SidekickStorage {\n return {\n async listOverrides(): Promise<OverrideRecord[]> {\n const rows = await db.select().from(overridesTable).orderBy(overridesTable.createdAt);\n return rows as OverrideRecord[];\n },\n\n async getOverride(id: string): Promise<OverrideRecord | null> {\n const rows = await db.select().from(overridesTable).where(eq(overridesTable.id, id));\n return (rows[0] as OverrideRecord) ?? null;\n },\n\n async createOverride(data: NewOverrideRecord): Promise<void> {\n await db.insert(overridesTable).values({\n id: data.id,\n name: data.name,\n description: data.description,\n version: data.version,\n primitives: data.primitives,\n code: data.code,\n enabled: data.enabled ?? true,\n createdAt: data.createdAt ?? new Date(),\n updatedAt: data.updatedAt ?? new Date(),\n });\n },\n\n async updateOverride(id: string, data: Partial<OverrideRecord>): Promise<void> {\n // Strip id and createdAt to prevent accidental PK/immutable field updates\n const { id: _id, createdAt: _ca, ...updateData } = data;\n await db.update(overridesTable)\n .set({ ...updateData, updatedAt: new Date() })\n .where(eq(overridesTable.id, id));\n },\n\n async deleteOverride(id: string): Promise<void> {\n await db.delete(overridesTable).where(eq(overridesTable.id, id));\n },\n };\n}\n","import { pgTable, text, timestamp, boolean } from 'drizzle-orm/pg-core';\n\nexport const sidekickOverrides = pgTable('overrides', {\n id: text('id').primaryKey(),\n name: text('name').notNull(),\n description: text('description').notNull(),\n version: text('version').notNull().default('1.0.0'),\n primitives: text('primitives').notNull(), // JSON array stored as text\n code: text('code').notNull(),\n enabled: boolean('enabled').notNull().default(true),\n createdAt: timestamp('created_at').defaultNow().notNull(),\n updatedAt: timestamp('updated_at').defaultNow().notNull(),\n});\n\nexport type SidekickOverride = typeof sidekickOverrides.$inferSelect;\nexport type NewSidekickOverride = typeof sidekickOverrides.$inferInsert;\n"],"mappings":"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","names":["text"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/handler.ts","../../src/server/generate.ts","../../src/server/drizzle-adapter.ts","../../src/server/json-storage.ts","../../src/server/drizzle-schema.ts"],"sourcesContent":["import * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { NextRequest } from 'next/server';\nimport { NextResponse } from 'next/server';\nimport type { SidekickHandlerOptions, SidekickStorage } from './types';\nimport { callAI, validateCode } from './generate';\n\ninterface GenerateRequestBody {\n request: string;\n overrideId?: string;\n}\n\ninterface ToggleRequestBody {\n overrideId: string;\n enabled: boolean;\n}\n\ninterface DeleteRequestBody {\n overrideId: string;\n}\n\nfunction getAction(req: NextRequest): string {\n const url = new URL(req.url);\n const segments = url.pathname.split('/').filter(Boolean);\n // Last segment is the action: overrides, generate, toggle, delete\n return segments[segments.length - 1] || '';\n}\n\nasync function readSchema(schemaPath?: string): Promise<Record<string, unknown>> {\n const candidates = schemaPath\n ? [schemaPath]\n : [\n path.join(process.cwd(), 'src/sidekick/schema.json'),\n path.join(process.cwd(), 'sidekick/schema.json'),\n ];\n\n for (const candidate of candidates) {\n try {\n const content = await fs.readFile(candidate, 'utf-8');\n return JSON.parse(content);\n } catch {\n continue;\n }\n }\n throw new Error('Failed to read schema.json');\n}\n\nfunction safeParsePrimitives(primitives: string): string[] {\n try {\n const parsed = JSON.parse(primitives);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\nasync function handleListOverrides(storage: SidekickStorage) {\n const overrides = await storage.listOverrides();\n return NextResponse.json(\n {\n success: true,\n overrides: overrides.map((o) => ({\n id: o.id,\n name: o.name,\n description: o.description,\n version: o.version,\n primitives: safeParsePrimitives(o.primitives),\n code: o.code,\n enabled: o.enabled,\n createdAt: o.createdAt,\n updatedAt: o.updatedAt,\n })),\n },\n {\n headers: {\n 'Cache-Control': 'no-store, no-cache, must-revalidate',\n },\n }\n );\n}\n\nasync function safeParseBody<T>(req: NextRequest): Promise<T | null> {\n try {\n return (await req.json()) as T;\n } catch {\n return null;\n }\n}\n\nasync function handleGenerate(\n req: NextRequest,\n storage: SidekickStorage,\n schemaPath?: string\n) {\n const body = await safeParseBody<GenerateRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { request, overrideId } = body;\n\n if (!request || typeof request !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing request parameter' },\n { status: 400 }\n );\n }\n\n let existingCode: string | undefined;\n if (overrideId) {\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: `Override \"${overrideId}\" not found` },\n { status: 404 }\n );\n }\n existingCode = existing.code;\n }\n\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n return NextResponse.json(\n { success: false, error: 'ANTHROPIC_API_KEY not configured' },\n { status: 500 }\n );\n }\n\n let schema: Record<string, unknown>;\n try {\n schema = await readSchema(schemaPath);\n } catch {\n return NextResponse.json(\n { success: false, error: 'Failed to read schema.json' },\n { status: 500 }\n );\n }\n\n const MAX_RETRIES = 3;\n let lastError: string | undefined;\n let lastValidationErrors: string[] | undefined;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n const generated = await callAI(request, schema, apiKey, lastValidationErrors, existingCode);\n\n if (overrideId) {\n generated.manifest.id = overrideId;\n }\n\n const validation = validateCode(generated.code);\n\n if (!validation.valid) {\n lastValidationErrors = validation.errors;\n lastError = `Validation failed: ${validation.errors.join(', ')}`;\n\n if (attempt < MAX_RETRIES) {\n console.log(`[Sidekick] Attempt ${attempt} failed validation, retrying...`);\n continue;\n }\n\n return NextResponse.json({\n success: false,\n error: lastError,\n validationErrors: lastValidationErrors,\n });\n }\n\n const existing = await storage.getOverride(generated.manifest.id);\n\n if (existing) {\n await storage.updateOverride(generated.manifest.id, {\n name: generated.manifest.name,\n description: generated.manifest.description,\n version: generated.manifest.version,\n primitives: JSON.stringify(generated.manifest.primitives),\n code: generated.code,\n });\n } else {\n await storage.createOverride({\n id: generated.manifest.id,\n name: generated.manifest.name,\n description: generated.manifest.description,\n version: generated.manifest.version,\n primitives: JSON.stringify(generated.manifest.primitives),\n code: generated.code,\n enabled: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n }\n\n return NextResponse.json({\n success: true,\n overrideId: generated.manifest.id,\n name: generated.manifest.name,\n description: generated.manifest.description,\n });\n } catch (e) {\n lastError = e instanceof Error ? e.message : String(e);\n if (attempt === MAX_RETRIES) {\n return NextResponse.json({\n success: false,\n error: `Failed after ${MAX_RETRIES} attempts: ${lastError}`,\n });\n }\n }\n }\n\n return NextResponse.json({\n success: false,\n error: lastError || 'Unknown error',\n });\n}\n\nasync function handleToggle(req: NextRequest, storage: SidekickStorage) {\n const body = await safeParseBody<ToggleRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { overrideId, enabled } = body;\n\n if (!overrideId || typeof overrideId !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing overrideId parameter' },\n { status: 400 }\n );\n }\n\n if (typeof enabled !== 'boolean') {\n return NextResponse.json(\n { success: false, error: 'Missing or invalid enabled parameter' },\n { status: 400 }\n );\n }\n\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: 'Override not found' },\n { status: 404 }\n );\n }\n\n await storage.updateOverride(overrideId, { enabled });\n\n // Re-read to confirm persisted value\n const updated = await storage.getOverride(overrideId);\n\n return NextResponse.json({\n success: true,\n overrideId,\n enabled: updated?.enabled ?? enabled,\n });\n}\n\nasync function handleDelete(req: NextRequest, storage: SidekickStorage) {\n const body = await safeParseBody<DeleteRequestBody>(req);\n if (!body) {\n return NextResponse.json(\n { success: false, error: 'Invalid JSON in request body' },\n { status: 400 }\n );\n }\n const { overrideId } = body;\n\n if (!overrideId || typeof overrideId !== 'string') {\n return NextResponse.json(\n { success: false, error: 'Missing overrideId parameter' },\n { status: 400 }\n );\n }\n\n const existing = await storage.getOverride(overrideId);\n if (!existing) {\n return NextResponse.json(\n { success: false, error: 'Override not found' },\n { status: 404 }\n );\n }\n\n await storage.deleteOverride(overrideId);\n\n return NextResponse.json({\n success: true,\n message: `Override \"${overrideId}\" deleted successfully`,\n });\n}\n\nexport function createSidekickHandler(options: SidekickHandlerOptions) {\n const { storage, schemaPath } = options;\n\n return {\n async GET(req: NextRequest): Promise<NextResponse> {\n try {\n const action = getAction(req);\n if (action === 'overrides') {\n return handleListOverrides(storage);\n }\n return NextResponse.json(\n { success: false, error: `Unknown GET action: ${action}` },\n { status: 404 }\n );\n } catch (e) {\n console.error('[Sidekick] Handler error:', e);\n return NextResponse.json(\n { success: false, error: e instanceof Error ? e.message : 'Unknown error' },\n { status: 500 }\n );\n }\n },\n\n async POST(req: NextRequest): Promise<NextResponse> {\n try {\n const action = getAction(req);\n switch (action) {\n case 'generate':\n return handleGenerate(req, storage, schemaPath);\n case 'toggle':\n return handleToggle(req, storage);\n case 'delete':\n return handleDelete(req, storage);\n default:\n return NextResponse.json(\n { success: false, error: `Unknown POST action: ${action}` },\n { status: 404 }\n );\n }\n } catch (e) {\n console.error('[Sidekick] Handler error:', e);\n return NextResponse.json(\n { success: false, error: e instanceof Error ? e.message : 'Unknown error' },\n { status: 500 }\n );\n }\n },\n };\n}\n","import type { GeneratedOverride, OverrideManifest } from './types';\n\nconst ALLOWED_IMPORTS = ['@usesidekick/react', 'react'];\n\nconst FORBIDDEN_PATTERNS = [\n /\\bfetch\\s*\\(/,\n /\\beval\\s*\\(/,\n /\\bFunction\\s*\\(/,\n /\\bdocument\\./,\n /\\bwindow\\./,\n /\\blocalStorage\\./,\n /\\bsessionStorage\\./,\n /\\bXMLHttpRequest/,\n /\\bWebSocket/,\n /\\bimport\\s*\\(/,\n /require\\s*\\(/,\n];\n\ninterface DesignSystemSchema {\n framework?: string | null;\n fontFamily?: string | null;\n globalCss?: string | null;\n componentStyles?: Array<{\n component: string;\n file: string;\n classNames: string[];\n }>;\n}\n\nexport function formatDesignSystem(schema: Record<string, unknown>): string {\n const ds = (schema as { designSystem?: DesignSystemSchema }).designSystem;\n if (!ds) {\n return 'No design system information available. Use clean, minimal inline styles with neutral colors and standard spacing.';\n }\n\n const parts: string[] = [];\n parts.push('All generated UI MUST look like it was built by the same team that built the host app. Study the style samples below and match the exact same patterns — colors, spacing, typography, border radii, hover states, and dark mode support.');\n\n if (ds.framework) {\n parts.push(`\\n**CSS Framework:** ${ds.framework}`);\n }\n if (ds.fontFamily) {\n parts.push(`**Font:** ${ds.fontFamily}`);\n }\n if (ds.globalCss) {\n parts.push('**Global CSS:**\\n```css\\n' + ds.globalCss + '\\n```');\n }\n\n if (ds.componentStyles && ds.componentStyles.length > 0) {\n parts.push('\\n**Component Style Samples (actual className strings from the app — use these as your reference):**');\n for (const cs of ds.componentStyles) {\n parts.push(`\\n${cs.component} (${cs.file}):`);\n for (const cn of cs.classNames) {\n parts.push(` \"${cn}\"`);\n }\n }\n parts.push('\\nWhen adding new UI elements to an existing component, copy the exact className patterns from that component above. When creating standalone elements, pick the closest matching component pattern and follow it.');\n }\n\n return parts.join('\\n');\n}\n\nfunction formatComponents(schema: Record<string, unknown>): string {\n const components = (schema as { components?: Array<{ name: string; props: string[]; renderStructure?: string }> }).components || [];\n return components.map((c) => {\n let entry = '- ' + c.name + ' (props: ' + (c.props.join(', ') || 'none') + ')';\n if (c.renderStructure) {\n entry += '\\n Renders: ' + c.renderStructure;\n }\n return entry;\n }).join('\\n');\n}\n\nexport function buildSystemPrompt(schema: Record<string, unknown>): string {\n const tick = '`';\n const ticks = '```';\n const dollar = '$';\n return 'You are a code generator for Sidekick, an SDK that allows users to customize web applications.\\n' +\n'Your task is to generate override modules based on user requests.\\n' +\n'\\n' +\n'## SDK Primitives Available\\n' +\n'\\n' +\n'UI Primitives:\\n' +\n'- sdk.ui.wrap(componentName, wrapperFn, options?) - Wrap any component by name to add behavior around it. options: { priority?, where?: (props) => boolean }\\n' +\n'- sdk.ui.replace(componentName, Component) - Replace any component entirely by name\\n' +\n'- sdk.ui.addColumn(tableId, config) - Add a column to a table (config: { header, accessor, render? })\\n' +\n'- sdk.ui.renameColumn(tableId, originalHeader, newHeader) - Rename a column header\\n' +\n'- sdk.ui.reorderColumns(tableId, headerOrder) - Reorder columns by header name (array of header strings)\\n' +\n'- sdk.ui.hideColumn(tableId, header) - Hide a column by header name\\n' +\n'- sdk.ui.filterRows(tableId, filterFn) - Filter table rows (filterFn receives a row object, returns true to keep)\\n' +\n'- sdk.ui.addStyles(css) - Add CSS styles globally\\n' +\n'- sdk.ui.setText(selector, text) - Change text content of elements matching CSS selector\\n' +\n'- sdk.ui.setAttribute(selector, attr, value) - Set an attribute on matching elements\\n' +\n'- sdk.ui.setStyle(selector, styles) - Apply inline styles to matching elements (styles is an object like { color: \\'red\\' })\\n' +\n'- sdk.ui.addClass(selector, className) - Add a CSS class to matching elements\\n' +\n'- sdk.ui.removeClass(selector, className) - Remove a CSS class from matching elements\\n' +\n'- sdk.ui.inject(selector, Component, position?) - Inject a React component before/after/prepend/append an element (default: \\'after\\')\\n' +\n'\\n' +\n'Data Primitives:\\n' +\n'- sdk.data.computed(fieldName, computeFn) - Add a computed field\\n' +\n'- sdk.data.addFilter(name, filterFn) - Add a filter preset\\n' +\n'- sdk.data.transform(dataKey, transformFn) - Transform data\\n' +\n'- sdk.data.intercept(pathPattern, handler) - Intercept and modify API responses (handler receives response JSON and { path, method })\\n' +\n'\\n' +\n'Behavior Primitives:\\n' +\n'- sdk.behavior.addKeyboardShortcut(keys, action, description?) - Register a keyboard shortcut (e.g., \"ctrl+k\", \"shift+?\")\\n' +\n'- sdk.behavior.onDOMEvent(selector, eventType, handler) - Listen for DOM events on elements matching a CSS selector\\n' +\n'- sdk.behavior.modifyRoute(pathPattern, handler) - Intercept navigation and optionally redirect\\n' +\n'\\n' +\n'## App Design System\\n' +\n'\\n' +\nformatDesignSystem(schema) + '\\n' +\n'\\n' +\n'## App Schema\\n' +\n'\\n' +\n'Components (targetable by name with wrap/replace):\\n' +\nformatComponents(schema) + '\\n' +\n'\\n' +\n'Data Models: ' + JSON.stringify((schema as { dataModels?: unknown[] }).dataModels || []) + '\\n' +\n'\\n' +\n'## Example Overrides\\n' +\n'\\n' +\n'### Example 1: Wrap a component to add a badge (RECOMMENDED APPROACH)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'import { ComponentType } from \\'react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'task-card-badge\\',\\n' +\n' name: \\'Task Card Badge\\',\\n' +\n' description: \\'Adds a \"New\" badge to task cards\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.wrap\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.wrap(\\'TaskCard\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function WrappedTaskCard(props: Record<string, unknown>) {\\n' +\n' return (\\n' +\n' <div style={{ position: \\'relative\\' }}>\\n' +\n' <span style={{\\n' +\n' position: \\'absolute\\',\\n' +\n' top: \\'-8px\\',\\n' +\n' right: \\'-8px\\',\\n' +\n' backgroundColor: \\'#EF4444\\',\\n' +\n' color: \\'white\\',\\n' +\n' fontSize: \\'10px\\',\\n' +\n' fontWeight: \\'bold\\',\\n' +\n' padding: \\'2px 6px\\',\\n' +\n' borderRadius: \\'9999px\\',\\n' +\n' }}>\\n' +\n' NEW\\n' +\n' </span>\\n' +\n' <Original {...props} />\\n' +\n' </div>\\n' +\n' );\\n' +\n' };\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 2: Replace a component entirely\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'const CustomTaskModal = (props: Record<string, unknown>) => {\\n' +\n' const task = props.task as { title?: string; description?: string } | undefined;\\n' +\n' const onClose = props.onClose as () => void;\\n' +\n'\\n' +\n' return (\\n' +\n' <div style={{\\n' +\n' position: \\'fixed\\',\\n' +\n' inset: 0,\\n' +\n' backgroundColor: \\'rgba(0,0,0,0.5)\\',\\n' +\n' display: \\'flex\\',\\n' +\n' alignItems: \\'center\\',\\n' +\n' justifyContent: \\'center\\',\\n' +\n' }}>\\n' +\n' <div style={{\\n' +\n' backgroundColor: \\'white\\',\\n' +\n' borderRadius: \\'16px\\',\\n' +\n' padding: \\'24px\\',\\n' +\n' maxWidth: \\'500px\\',\\n' +\n' width: \\'100%\\',\\n' +\n' }}>\\n' +\n' <h2 style={{ fontSize: \\'24px\\', fontWeight: \\'bold\\', marginBottom: \\'16px\\' }}>\\n' +\n' {task?.title || \\'Task Details\\'}\\n' +\n' </h2>\\n' +\n' <p>{task?.description || \\'No description\\'}</p>\\n' +\n' <button\\n' +\n' onClick={onClose}\\n' +\n' style={{\\n' +\n' marginTop: \\'16px\\',\\n' +\n' backgroundColor: \\'#3B82F6\\',\\n' +\n' color: \\'white\\',\\n' +\n' padding: \\'8px 16px\\',\\n' +\n' borderRadius: \\'8px\\',\\n' +\n' border: \\'none\\',\\n' +\n' cursor: \\'pointer\\',\\n' +\n' }}\\n' +\n' >\\n' +\n' Close\\n' +\n' </button>\\n' +\n' </div>\\n' +\n' </div>\\n' +\n' );\\n' +\n'};\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-task-modal\\',\\n' +\n' name: \\'Custom Task Modal\\',\\n' +\n' description: \\'Replaces the task modal with a custom design\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.replace\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.replace(\\'TaskModal\\', CustomTaskModal);\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 3: Add a table column\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'days-left-column\\',\\n' +\n' name: \\'Days Left Column\\',\\n' +\n' description: \\'Shows days until due date\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.addColumn\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.addColumn(\\'task-table\\', {\\n' +\n' header: \\'Days Left\\',\\n' +\n' accessor: (row: Record<string, unknown>) => {\\n' +\n' const dueDate = row.dueDate as string | undefined;\\n' +\n' if (!dueDate) return null;\\n' +\n' const days = Math.ceil((new Date(dueDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24));\\n' +\n' return days;\\n' +\n' },\\n' +\n' render: (value: unknown) => {\\n' +\n' if (value === null) return <span>-</span>;\\n' +\n' const days = value as number;\\n' +\n' const color = days < 0 ? \\'red\\' : days <= 3 ? \\'orange\\' : \\'green\\';\\n' +\n' return <span style={{ color }}>{days}d</span>;\\n' +\n' },\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 4: Add global styles\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-theme\\',\\n' +\n' name: \\'Custom Theme\\',\\n' +\n' description: \\'Adds custom styling to the app\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.addStyles\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.addStyles(' + tick + '\\n' +\n' .task-card {\\n' +\n' border-radius: 12px;\\n' +\n' box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\\n' +\n' }\\n' +\n' .task-card:hover {\\n' +\n' transform: translateY(-2px);\\n' +\n' transition: transform 0.2s ease;\\n' +\n' }\\n' +\n' ' + tick + ');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 5: Column operations (rename, reorder, hide)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'reorder-columns\\',\\n' +\n' name: \\'Reorder Table Columns\\',\\n' +\n' description: \\'Reorders Status and Importance columns and hides Assignee\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.reorderColumns\\', \\'ui.hideColumn\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.reorderColumns(\\'task-table\\', [\\'Title\\', \\'Importance\\', \\'Status\\', \\'Project\\', \\'Due Date\\']);\\n' +\n' sdk.ui.hideColumn(\\'task-table\\', \\'Assignee\\');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 6: Interactive filter with sidebar nav button\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'import { ComponentType, useState, useEffect } from \\'react\\';\\n' +\n'\\n' +\n'let _filterActive = false;\\n' +\n'const _listeners = new Set<(active: boolean) => void>();\\n' +\n'\\n' +\n'function setFilterActive(active: boolean) {\\n' +\n' _filterActive = active;\\n' +\n' _listeners.forEach(fn => fn(active));\\n' +\n'}\\n' +\n'\\n' +\n'function useFilterActive(): [boolean, (active: boolean) => void] {\\n' +\n' const [active, setActive] = useState(_filterActive);\\n' +\n' useEffect(() => {\\n' +\n' const handler = (val: boolean) => setActive(val);\\n' +\n' _listeners.add(handler);\\n' +\n' return () => { _listeners.delete(handler); };\\n' +\n' }, []);\\n' +\n' return [active, setFilterActive];\\n' +\n'}\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'high-importance-filter\\',\\n' +\n' name: \\'High Importance Filter\\',\\n' +\n' description: \\'Adds a sidebar button to filter high importance tasks\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.wrap\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.wrap(\\'Sidebar\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function SidebarWithFilter(props: Record<string, unknown>) {\\n' +\n' const [isActive, setActive] = useFilterActive();\\n' +\n' return (\\n' +\n' <div style={{ display: \\'contents\\' }}>\\n' +\n' <Original {...props} />\\n' +\n' <div style={{ padding: \\'0 16px\\', marginTop: \\'8px\\' }}>\\n' +\n' <button\\n' +\n' onClick={() => setActive(!isActive)}\\n' +\n' style={{\\n' +\n' backgroundColor: isActive ? \\'#EFF6FF\\' : \\'transparent\\',\\n' +\n' color: isActive ? \\'#1D4ED8\\' : \\'#374151\\',\\n' +\n' }}\\n' +\n' >\\n' +\n' High Importance\\n' +\n' </button>\\n' +\n' </div>\\n' +\n' </div>\\n' +\n' );\\n' +\n' };\\n' +\n' });\\n' +\n'\\n' +\n' sdk.ui.wrap(\\'TaskTable\\', (Original: ComponentType<Record<string, unknown>>) => {\\n' +\n' return function FilteredTaskTable(props: Record<string, unknown>) {\\n' +\n' const [isActive] = useFilterActive();\\n' +\n' if (!isActive) return <Original {...props} />;\\n' +\n' const tasks = (props.tasks || []) as Array<Record<string, unknown>>;\\n' +\n' const filtered = tasks.filter(t => t.importance === \\'high\\');\\n' +\n' return <Original {...props} tasks={filtered} />;\\n' +\n' };\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 7: Permanent row filter (always active when override is ON)\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'hide-done-tasks\\',\\n' +\n' name: \\'Hide Done Tasks\\',\\n' +\n' description: \\'Hides completed tasks from the table\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.filterRows\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.filterRows(\\'task-table\\', (row: Record<string, unknown>) => {\\n' +\n' return row.status !== \\'done\\';\\n' +\n' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 8: Keyboard shortcut\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'quick-search-shortcut\\',\\n' +\n' name: \\'Quick Search Shortcut\\',\\n' +\n' description: \\'Adds Ctrl+K shortcut to focus search\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'behavior.addKeyboardShortcut\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.behavior.addKeyboardShortcut(\\'ctrl+k\\', () => {\\n' +\n' // Toggle search or custom action\\n' +\n' }, \\'Open quick search\\');\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'### Example 9: DOM modification\\n' +\nticks + 'tsx\\n' +\n'import { createOverride, SDK } from \\'@usesidekick/react\\';\\n' +\n'\\n' +\n'export default createOverride(\\n' +\n' {\\n' +\n' id: \\'custom-dom-mods\\',\\n' +\n' name: \\'Custom DOM Modifications\\',\\n' +\n' description: \\'Changes sidebar title and styles task cards\\',\\n' +\n' version: \\'1.0.0\\',\\n' +\n' primitives: [\\'ui.setText\\', \\'ui.setStyle\\'],\\n' +\n' },\\n' +\n' (sdk: SDK) => {\\n' +\n' sdk.ui.setText(\\'.sidebar-title\\', \\'My Custom App\\');\\n' +\n' sdk.ui.setStyle(\\'.task-card\\', { borderLeft: \\'4px solid #3B82F6\\' });\\n' +\n' }\\n' +\n');\\n' +\nticks + '\\n' +\n'\\n' +\n'## Rules\\n' +\n'\\n' +\n'1. ONLY import from \\'@usesidekick/react\\' or \\'react\\' - NO other imports allowed\\n' +\n'2. DO NOT use fetch, eval, document.*, window.*, localStorage, etc.\\n' +\n'3. Use the createOverride function from @usesidekick/react\\n' +\n'4. The activate function receives an SDK object with ui and data primitives\\n' +\n'5. Generate React components using JSX with inline styles\\n' +\n'6. Keep code simple and focused on the user\\'s request\\n' +\n'7. Use sdk.ui.wrap() to add behavior around existing components, use sdk.ui.replace() to fully swap components\\n' +\n'8. When wrapping, always pass through all props to the Original component\\n' +\n'9. ALWAYS prefer SDK primitives over raw wrapping for table operations\\n' +\n'10. When using sdk.ui.wrap(), ALWAYS render <Original {...props} /> as-is\\n' +\n'11. A wrapper\\'s ONLY job is to add content BEFORE or AFTER <Original {...props} />, or to modify the props passed to it\\n' +\n'12. For permanent row filtering, use sdk.ui.filterRows(tableId, filterFn)\\n' +\n'13. Overrides MUST always act on the UI\\n' +\n'14. For interactive/togglable behavior, use module-level shared state with a listener pattern\\n' +\n'15. VISUAL INTEGRATION IS MANDATORY\\n' +\n'16. Study the component style samples in the design system carefully\\n' +\n'17. NEVER use absolute/fixed positioning to place elements inside existing components\\n' +\n'18. New elements MUST be visually indistinguishable from existing elements\\n' +\n'19. All interactive elements MUST have hover states\\n' +\n'20. BEFORE writing any wrap() code, read the target component\\'s \"Renders:\" tree\\n' +\n'21. Structure wrappers as: optional-new-content-above + <Original {...props} /> + optional-new-content-below\\n' +\n'22. CSS selectors MUST be valid CSS selectors supported by document.querySelectorAll()\\n' +\n'\\n' +\n'## Output Format\\n' +\n'\\n' +\n'Respond with ONLY a JSON object (no markdown, no explanation):\\n' +\n'{\\n' +\n' \"manifest\": {\\n' +\n' \"id\": \"override-id-kebab-case\",\\n' +\n' \"name\": \"Human Readable Name\",\\n' +\n' \"description\": \"What this override does\",\\n' +\n' \"version\": \"1.0.0\",\\n' +\n' \"primitives\": [\"list\", \"of\", \"primitives\", \"used\"]\\n' +\n' },\\n' +\n' \"code\": \"// Full TypeScript/TSX code here\"\\n' +\n'}';\n}\n\nexport function buildUserPrompt(\n request: string,\n previousErrors?: string[],\n existingCode?: string\n): string {\n let prompt: string;\n\n if (existingCode) {\n prompt = 'You are MODIFYING an existing override. Here is the current code:\\n```tsx\\n' + existingCode + '\\n```\\n\\nApply this change: \"' + request + '\"\\n\\nIMPORTANT: Keep the same override ID. Preserve all existing functionality unless the user explicitly asks to change it. Return the complete modified code.';\n } else {\n prompt = 'Generate an override module for this request: \"' + request + '\"';\n }\n\n if (previousErrors && previousErrors.length > 0) {\n prompt += '\\n\\nIMPORTANT: Previous generation attempt failed validation with these errors:\\n';\n prompt += previousErrors.map((e) => '- ' + e).join('\\n');\n prompt += '\\n\\nPlease fix these issues in your response.';\n }\n\n return prompt;\n}\n\nexport async function callAI(\n request: string,\n schema: Record<string, unknown>,\n apiKey: string,\n previousErrors?: string[],\n existingCode?: string\n): Promise<GeneratedOverride> {\n const systemPrompt = buildSystemPrompt(schema);\n const userPrompt = buildUserPrompt(request, previousErrors, existingCode);\n\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: 'claude-sonnet-4-20250514',\n max_tokens: 4096,\n system: systemPrompt,\n messages: [{ role: 'user', content: userPrompt }],\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error('API request failed: ' + response.status + ' ' + error);\n }\n\n const result = (await response.json()) as {\n content: Array<{ type: string; text?: string }>;\n };\n\n const textContent = result.content.find((c) => c.type === 'text');\n if (!textContent || !textContent.text) {\n throw new Error('No text content in API response');\n }\n\n return parseAIResponse(textContent.text);\n}\n\nexport function parseAIResponse(text: string): GeneratedOverride {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (!jsonMatch) {\n throw new Error('No JSON found in AI response');\n }\n\n try {\n const parsed = JSON.parse(jsonMatch[0]) as {\n manifest: OverrideManifest;\n code: string;\n };\n\n if (!parsed.manifest || !parsed.code) {\n throw new Error('Invalid response structure');\n }\n\n return {\n manifest: parsed.manifest,\n code: parsed.code,\n };\n } catch (e) {\n throw new Error('Failed to parse AI response: ' + (e instanceof Error ? e.message : String(e)));\n }\n}\n\nexport function validateCode(code: string): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n const importMatches = code.matchAll(/import\\s+.*?\\s+from\\s+['\"]([^'\"]+)['\"]/g);\n for (const match of importMatches) {\n const moduleName = match[1];\n const isAllowed = ALLOWED_IMPORTS.some(\n (allowed) => moduleName === allowed || moduleName.startsWith(allowed + '/')\n );\n if (!isAllowed) {\n errors.push('Forbidden import: ' + moduleName);\n }\n }\n\n for (const pattern of FORBIDDEN_PATTERNS) {\n if (pattern.test(code)) {\n errors.push('Forbidden pattern: ' + pattern.source);\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n","import { eq } from 'drizzle-orm';\nimport type { SidekickStorage, OverrideRecord, NewOverrideRecord } from './types';\nimport type { sidekickOverrides as OverridesTable } from './drizzle-schema';\n\ntype DrizzleDb = {\n select(): any;\n insert(table: any): any;\n update(table: any): any;\n delete(table: any): any;\n};\n\nexport function createDrizzleStorage(\n db: DrizzleDb,\n overridesTable: typeof OverridesTable\n): SidekickStorage {\n return {\n async listOverrides(): Promise<OverrideRecord[]> {\n const rows = await db.select().from(overridesTable).orderBy(overridesTable.createdAt);\n return rows as OverrideRecord[];\n },\n\n async getOverride(id: string): Promise<OverrideRecord | null> {\n const rows = await db.select().from(overridesTable).where(eq(overridesTable.id, id));\n return (rows[0] as OverrideRecord) ?? null;\n },\n\n async createOverride(data: NewOverrideRecord): Promise<void> {\n await db.insert(overridesTable).values({\n id: data.id,\n name: data.name,\n description: data.description,\n version: data.version,\n primitives: data.primitives,\n code: data.code,\n enabled: data.enabled ?? true,\n createdAt: data.createdAt ?? new Date(),\n updatedAt: data.updatedAt ?? new Date(),\n });\n },\n\n async updateOverride(id: string, data: Partial<OverrideRecord>): Promise<void> {\n // Strip id and createdAt to prevent accidental PK/immutable field updates\n const { id: _id, createdAt: _ca, ...updateData } = data;\n await db.update(overridesTable)\n .set({ ...updateData, updatedAt: new Date() })\n .where(eq(overridesTable.id, id));\n },\n\n async deleteOverride(id: string): Promise<void> {\n await db.delete(overridesTable).where(eq(overridesTable.id, id));\n },\n };\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { SidekickStorage, OverrideRecord, NewOverrideRecord } from './types';\n\ninterface StorageData {\n overrides: OverrideRecord[];\n}\n\nfunction deserializeDates(record: any): OverrideRecord {\n return {\n ...record,\n createdAt: new Date(record.createdAt),\n updatedAt: new Date(record.updatedAt),\n };\n}\n\nasync function readData(filePath: string): Promise<StorageData> {\n try {\n const raw = await fs.readFile(filePath, 'utf-8');\n const parsed = JSON.parse(raw) as StorageData;\n return {\n overrides: (parsed.overrides || []).map(deserializeDates),\n };\n } catch (err: any) {\n if (err.code === 'ENOENT') {\n // Auto-create file with empty data\n const empty: StorageData = { overrides: [] };\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, JSON.stringify(empty, null, 2) + '\\n');\n return empty;\n }\n throw err;\n }\n}\n\nasync function writeData(filePath: string, data: StorageData): Promise<void> {\n const tmpPath = filePath + '.tmp';\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(tmpPath, JSON.stringify(data, null, 2) + '\\n');\n await fs.rename(tmpPath, filePath);\n}\n\nexport function createJsonFileStorage(filePath?: string): SidekickStorage {\n const resolvedPath = filePath || path.join(process.cwd(), '.sidekick', 'overrides.json');\n\n return {\n async listOverrides(): Promise<OverrideRecord[]> {\n const data = await readData(resolvedPath);\n return data.overrides;\n },\n\n async getOverride(id: string): Promise<OverrideRecord | null> {\n const data = await readData(resolvedPath);\n return data.overrides.find((o) => o.id === id) ?? null;\n },\n\n async createOverride(record: NewOverrideRecord): Promise<void> {\n const data = await readData(resolvedPath);\n data.overrides.push({\n id: record.id,\n name: record.name,\n description: record.description,\n version: record.version,\n primitives: record.primitives,\n code: record.code,\n enabled: record.enabled ?? true,\n createdAt: record.createdAt ?? new Date(),\n updatedAt: record.updatedAt ?? new Date(),\n });\n await writeData(resolvedPath, data);\n },\n\n async updateOverride(id: string, updates: Partial<OverrideRecord>): Promise<void> {\n const data = await readData(resolvedPath);\n const idx = data.overrides.findIndex((o) => o.id === id);\n if (idx === -1) return;\n const { id: _id, createdAt: _ca, ...rest } = updates;\n data.overrides[idx] = { ...data.overrides[idx], ...rest, updatedAt: new Date() };\n await writeData(resolvedPath, data);\n },\n\n async deleteOverride(id: string): Promise<void> {\n const data = await readData(resolvedPath);\n data.overrides = data.overrides.filter((o) => o.id !== id);\n await writeData(resolvedPath, data);\n },\n };\n}\n","import { pgTable, text, timestamp, boolean } from 'drizzle-orm/pg-core';\n\nexport const sidekickOverrides = pgTable('overrides', {\n id: text('id').primaryKey(),\n name: text('name').notNull(),\n description: text('description').notNull(),\n version: text('version').notNull().default('1.0.0'),\n primitives: text('primitives').notNull(), // JSON array stored as text\n code: text('code').notNull(),\n enabled: boolean('enabled').notNull().default(true),\n createdAt: timestamp('created_at').defaultNow().notNull(),\n updatedAt: timestamp('updated_at').defaultNow().notNull(),\n});\n\nexport type SidekickOverride = typeof sidekickOverrides.$inferSelect;\nexport type NewSidekickOverride = typeof sidekickOverrides.$inferInsert;\n"],"mappings":"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","names":["text","fs","path"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usesidekick/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"types": "./dist/server/index.d.ts",
|
|
28
28
|
"import": "./dist/server/index.mjs",
|
|
29
29
|
"require": "./dist/server/index.js"
|
|
30
|
-
}
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
31
32
|
},
|
|
32
33
|
"files": [
|
|
33
34
|
"dist",
|
package/src/server/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { createSidekickHandler } from './handler';
|
|
2
2
|
export { createDrizzleStorage } from './drizzle-adapter';
|
|
3
|
+
export { createJsonFileStorage } from './json-storage';
|
|
3
4
|
export { sidekickOverrides } from './drizzle-schema';
|
|
4
5
|
export type { SidekickOverride, NewSidekickOverride } from './drizzle-schema';
|
|
5
6
|
export type {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import type { SidekickStorage, OverrideRecord, NewOverrideRecord } from './types';
|
|
4
|
+
|
|
5
|
+
interface StorageData {
|
|
6
|
+
overrides: OverrideRecord[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function deserializeDates(record: any): OverrideRecord {
|
|
10
|
+
return {
|
|
11
|
+
...record,
|
|
12
|
+
createdAt: new Date(record.createdAt),
|
|
13
|
+
updatedAt: new Date(record.updatedAt),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function readData(filePath: string): Promise<StorageData> {
|
|
18
|
+
try {
|
|
19
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
20
|
+
const parsed = JSON.parse(raw) as StorageData;
|
|
21
|
+
return {
|
|
22
|
+
overrides: (parsed.overrides || []).map(deserializeDates),
|
|
23
|
+
};
|
|
24
|
+
} catch (err: any) {
|
|
25
|
+
if (err.code === 'ENOENT') {
|
|
26
|
+
// Auto-create file with empty data
|
|
27
|
+
const empty: StorageData = { overrides: [] };
|
|
28
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
29
|
+
await fs.writeFile(filePath, JSON.stringify(empty, null, 2) + '\n');
|
|
30
|
+
return empty;
|
|
31
|
+
}
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function writeData(filePath: string, data: StorageData): Promise<void> {
|
|
37
|
+
const tmpPath = filePath + '.tmp';
|
|
38
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
39
|
+
await fs.writeFile(tmpPath, JSON.stringify(data, null, 2) + '\n');
|
|
40
|
+
await fs.rename(tmpPath, filePath);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createJsonFileStorage(filePath?: string): SidekickStorage {
|
|
44
|
+
const resolvedPath = filePath || path.join(process.cwd(), '.sidekick', 'overrides.json');
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
async listOverrides(): Promise<OverrideRecord[]> {
|
|
48
|
+
const data = await readData(resolvedPath);
|
|
49
|
+
return data.overrides;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
async getOverride(id: string): Promise<OverrideRecord | null> {
|
|
53
|
+
const data = await readData(resolvedPath);
|
|
54
|
+
return data.overrides.find((o) => o.id === id) ?? null;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
async createOverride(record: NewOverrideRecord): Promise<void> {
|
|
58
|
+
const data = await readData(resolvedPath);
|
|
59
|
+
data.overrides.push({
|
|
60
|
+
id: record.id,
|
|
61
|
+
name: record.name,
|
|
62
|
+
description: record.description,
|
|
63
|
+
version: record.version,
|
|
64
|
+
primitives: record.primitives,
|
|
65
|
+
code: record.code,
|
|
66
|
+
enabled: record.enabled ?? true,
|
|
67
|
+
createdAt: record.createdAt ?? new Date(),
|
|
68
|
+
updatedAt: record.updatedAt ?? new Date(),
|
|
69
|
+
});
|
|
70
|
+
await writeData(resolvedPath, data);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async updateOverride(id: string, updates: Partial<OverrideRecord>): Promise<void> {
|
|
74
|
+
const data = await readData(resolvedPath);
|
|
75
|
+
const idx = data.overrides.findIndex((o) => o.id === id);
|
|
76
|
+
if (idx === -1) return;
|
|
77
|
+
const { id: _id, createdAt: _ca, ...rest } = updates;
|
|
78
|
+
data.overrides[idx] = { ...data.overrides[idx], ...rest, updatedAt: new Date() };
|
|
79
|
+
await writeData(resolvedPath, data);
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
async deleteOverride(id: string): Promise<void> {
|
|
83
|
+
const data = await readData(resolvedPath);
|
|
84
|
+
data.overrides = data.overrides.filter((o) => o.id !== id);
|
|
85
|
+
await writeData(resolvedPath, data);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|