@tdsoft-tech/aikit 0.1.2

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.
@@ -0,0 +1,3496 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // node_modules/tsup/assets/esm_shims.js
13
+ import path from "path";
14
+ import { fileURLToPath } from "url";
15
+ var init_esm_shims = __esm({
16
+ "node_modules/tsup/assets/esm_shims.js"() {
17
+ "use strict";
18
+ }
19
+ });
20
+
21
+ // src/utils/paths.ts
22
+ import { homedir } from "os";
23
+ import { join } from "path";
24
+ import { existsSync } from "fs";
25
+ var paths;
26
+ var init_paths = __esm({
27
+ "src/utils/paths.ts"() {
28
+ "use strict";
29
+ init_esm_shims();
30
+ paths = {
31
+ /**
32
+ * Get the global AIKit configuration directory
33
+ * ~/.config/aikit/ on Unix, %APPDATA%/aikit/ on Windows
34
+ */
35
+ globalConfig() {
36
+ const base = process.platform === "win32" ? process.env.APPDATA || join(homedir(), "AppData", "Roaming") : join(homedir(), ".config");
37
+ return join(base, "aikit");
38
+ },
39
+ /**
40
+ * Get the project-level AIKit configuration directory
41
+ */
42
+ projectConfig(projectPath) {
43
+ const base = projectPath || process.cwd();
44
+ return join(base, ".aikit");
45
+ },
46
+ /**
47
+ * Get the OpenCode configuration directory
48
+ */
49
+ opencodeConfig() {
50
+ const base = process.platform === "win32" ? process.env.APPDATA || join(homedir(), "AppData", "Roaming") : join(homedir(), ".config");
51
+ return join(base, "opencode");
52
+ },
53
+ /**
54
+ * Get the Beads directory for the current project
55
+ */
56
+ beadsDir(projectPath) {
57
+ const base = projectPath || process.cwd();
58
+ return join(base, ".beads");
59
+ },
60
+ /**
61
+ * Check if a project has AIKit initialized
62
+ */
63
+ hasProjectConfig(projectPath) {
64
+ return existsSync(this.projectConfig(projectPath));
65
+ },
66
+ /**
67
+ * Check if global AIKit is initialized
68
+ */
69
+ hasGlobalConfig() {
70
+ return existsSync(this.globalConfig());
71
+ },
72
+ /**
73
+ * Get effective config path (project takes precedence over global)
74
+ */
75
+ effectiveConfig(projectPath) {
76
+ if (this.hasProjectConfig(projectPath)) {
77
+ return this.projectConfig(projectPath);
78
+ }
79
+ if (this.hasGlobalConfig()) {
80
+ return this.globalConfig();
81
+ }
82
+ return null;
83
+ },
84
+ /**
85
+ * Get skills directory
86
+ */
87
+ skills(configPath) {
88
+ return join(configPath, "skills");
89
+ },
90
+ /**
91
+ * Get agents directory
92
+ */
93
+ agents(configPath) {
94
+ return join(configPath, "agents");
95
+ },
96
+ /**
97
+ * Get commands directory
98
+ */
99
+ commands(configPath) {
100
+ return join(configPath, "commands");
101
+ },
102
+ /**
103
+ * Get tools directory
104
+ */
105
+ tools(configPath) {
106
+ return join(configPath, "tools");
107
+ },
108
+ /**
109
+ * Get plugins directory
110
+ */
111
+ plugins(configPath) {
112
+ return join(configPath, "plugins");
113
+ },
114
+ /**
115
+ * Get memory directory
116
+ */
117
+ memory(configPath) {
118
+ return join(configPath, "memory");
119
+ }
120
+ };
121
+ }
122
+ });
123
+
124
+ // src/utils/logger.ts
125
+ import chalk from "chalk";
126
+ var logger;
127
+ var init_logger = __esm({
128
+ "src/utils/logger.ts"() {
129
+ "use strict";
130
+ init_esm_shims();
131
+ logger = {
132
+ info(...args) {
133
+ console.log(chalk.blue("\u2139"), ...args);
134
+ },
135
+ success(...args) {
136
+ console.log(chalk.green("\u2713"), ...args);
137
+ },
138
+ warn(...args) {
139
+ console.log(chalk.yellow("\u26A0"), ...args);
140
+ },
141
+ error(...args) {
142
+ console.error(chalk.red("\u2716"), ...args);
143
+ },
144
+ debug(...args) {
145
+ if (process.env.DEBUG || process.env.AIKIT_DEBUG) {
146
+ console.log(chalk.gray("\u22EF"), ...args);
147
+ }
148
+ },
149
+ step(step, total, message) {
150
+ console.log(chalk.cyan(`[${step}/${total}]`), message);
151
+ },
152
+ header(message) {
153
+ console.log(chalk.bold.underline(`
154
+ ${message}
155
+ `));
156
+ },
157
+ list(items, prefix = "\u2022") {
158
+ for (const item of items) {
159
+ console.log(` ${prefix} ${item}`);
160
+ }
161
+ }
162
+ };
163
+ }
164
+ });
165
+
166
+ // src/core/tools/figma-mcp.ts
167
+ var figma_mcp_exports = {};
168
+ __export(figma_mcp_exports, {
169
+ FigmaMcpClient: () => FigmaMcpClient
170
+ });
171
+ import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
172
+ import { join as join5 } from "path";
173
+ import { existsSync as existsSync2 } from "fs";
174
+ var FigmaMcpClient;
175
+ var init_figma_mcp = __esm({
176
+ "src/core/tools/figma-mcp.ts"() {
177
+ "use strict";
178
+ init_esm_shims();
179
+ init_logger();
180
+ FigmaMcpClient = class {
181
+ apiKey;
182
+ configManager;
183
+ constructor(apiKey, configManager) {
184
+ this.apiKey = apiKey;
185
+ this.configManager = configManager;
186
+ }
187
+ /**
188
+ * Fetch helper with simple retry/backoff for 429/5xx
189
+ */
190
+ async fetchWithRetry(url, options, label, retries = 3, backoffMs = 1500) {
191
+ let attempt = 0;
192
+ let lastError;
193
+ while (attempt <= retries) {
194
+ try {
195
+ const res = await fetch(url, options);
196
+ if (res.ok) return res;
197
+ if (res.status === 429 || res.status >= 500) {
198
+ const retryAfter = Number(res.headers.get("retry-after")) || 0;
199
+ const delay = retryAfter > 0 ? retryAfter * 1e3 : backoffMs * (attempt + 1);
200
+ logger.warn(`${label} failed (status ${res.status}), retrying in ${Math.round(delay / 1e3)}s...`);
201
+ await new Promise((r) => setTimeout(r, delay));
202
+ attempt += 1;
203
+ continue;
204
+ }
205
+ const text = await res.text();
206
+ throw new Error(`${label} error: ${res.status} ${res.statusText}
207
+ ${text}`);
208
+ } catch (err) {
209
+ lastError = err;
210
+ logger.warn(`${label} network error, attempt ${attempt + 1}/${retries + 1}: ${err instanceof Error ? err.message : String(err)}`);
211
+ if (attempt >= retries) break;
212
+ await new Promise((r) => setTimeout(r, backoffMs * (attempt + 1)));
213
+ attempt += 1;
214
+ }
215
+ }
216
+ throw lastError instanceof Error ? lastError : new Error(`${label} failed after retries`);
217
+ }
218
+ /**
219
+ * Extract file key from Figma URL
220
+ */
221
+ extractFileKey(url) {
222
+ const match = url.match(/figma\.com\/design\/([a-zA-Z0-9]+)/);
223
+ return match ? match[1] : null;
224
+ }
225
+ /**
226
+ * Extract node ID from Figma URL
227
+ */
228
+ extractNodeId(url) {
229
+ const match = url.match(/[?&]node-id=([^&]+)/);
230
+ if (!match) return null;
231
+ let nodeId = decodeURIComponent(match[1]);
232
+ if (nodeId.includes("-") && !nodeId.includes(":")) {
233
+ nodeId = nodeId.replace(/-/g, ":");
234
+ }
235
+ return nodeId;
236
+ }
237
+ /**
238
+ * Get Figma file data using API
239
+ */
240
+ async getFileData(url) {
241
+ const fileKey = this.extractFileKey(url);
242
+ if (!fileKey) {
243
+ throw new Error(`Invalid Figma URL: ${url}`);
244
+ }
245
+ const nodeId = this.extractNodeId(url);
246
+ const apiUrl = nodeId ? `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${encodeURIComponent(nodeId)}` : `https://api.figma.com/v1/files/${fileKey}`;
247
+ const response = await this.fetchWithRetry(apiUrl, {
248
+ headers: {
249
+ "X-Figma-Token": this.apiKey
250
+ }
251
+ }, "Figma file fetch");
252
+ const data = await response.json();
253
+ if (nodeId) {
254
+ const nodes = data.nodes;
255
+ const nodeData = Object.values(nodes)[0];
256
+ if (!nodeData) {
257
+ throw new Error(`Node not found: ${nodeId}`);
258
+ }
259
+ return {
260
+ document: nodeData.document,
261
+ components: {},
262
+ styles: {}
263
+ };
264
+ }
265
+ return data;
266
+ }
267
+ /**
268
+ * Extract design tokens from Figma file
269
+ */
270
+ async extractDesignTokens(url, downloadAssets = true, assetsDir) {
271
+ const fileData = await this.getFileData(url);
272
+ const fileKey = this.extractFileKey(url);
273
+ if (!fileKey) {
274
+ throw new Error(`Invalid Figma URL: ${url}`);
275
+ }
276
+ const tokens = {
277
+ colors: [],
278
+ typography: [],
279
+ spacing: {
280
+ unit: 8,
281
+ // Default 8px grid
282
+ scale: []
283
+ },
284
+ components: [],
285
+ screens: [],
286
+ breakpoints: [375, 768, 1024, 1280, 1920]
287
+ // Common breakpoints
288
+ };
289
+ const colorMap = /* @__PURE__ */ new Map();
290
+ this.extractColors(fileData.document, colorMap);
291
+ tokens.colors = Array.from(colorMap.entries()).map(([name, hex]) => ({
292
+ name,
293
+ hex,
294
+ rgba: hex
295
+ // Simplified
296
+ }));
297
+ const typographyMap = /* @__PURE__ */ new Map();
298
+ this.extractTypography(fileData.document, typographyMap);
299
+ tokens.typography = Array.from(typographyMap.values());
300
+ Object.values(fileData.components).forEach((component) => {
301
+ tokens.components.push({
302
+ name: component.name,
303
+ type: "component",
304
+ description: component.description
305
+ });
306
+ });
307
+ this.extractScreens(fileData.document, tokens.screens);
308
+ tokens.structure = this.extractStructure(fileData.document);
309
+ if (downloadAssets && tokens.structure) {
310
+ try {
311
+ tokens.assets = await this.downloadAssets(
312
+ fileKey,
313
+ fileData.document,
314
+ assetsDir || "./assets/images"
315
+ );
316
+ } catch (error) {
317
+ logger.warn(`Failed to download assets: ${error instanceof Error ? error.message : String(error)}`);
318
+ }
319
+ }
320
+ return tokens;
321
+ }
322
+ /**
323
+ * Recursively extract colors from nodes
324
+ */
325
+ extractColors(node, colorMap) {
326
+ if (node.fills && Array.isArray(node.fills)) {
327
+ node.fills.forEach((fill) => {
328
+ if (fill.type === "SOLID" && fill.color) {
329
+ const { r, g, b, a } = fill.color;
330
+ const hex = this.rgbaToHex(r, g, b, a);
331
+ const name = node.name || "Color";
332
+ if (!colorMap.has(hex)) {
333
+ colorMap.set(hex, hex);
334
+ }
335
+ }
336
+ });
337
+ }
338
+ if (node.strokes && Array.isArray(node.strokes)) {
339
+ node.strokes.forEach((stroke) => {
340
+ if (stroke.type === "SOLID" && stroke.color) {
341
+ const { r, g, b, a } = stroke.color;
342
+ const hex = this.rgbaToHex(r, g, b, a);
343
+ if (!colorMap.has(hex)) {
344
+ colorMap.set(hex, hex);
345
+ }
346
+ }
347
+ });
348
+ }
349
+ if (node.children) {
350
+ node.children.forEach((child) => this.extractColors(child, colorMap));
351
+ }
352
+ }
353
+ /**
354
+ * Recursively extract typography from nodes
355
+ */
356
+ extractTypography(node, typographyMap) {
357
+ if (node.type === "TEXT" && node.style) {
358
+ const key = `${node.style.fontFamily}-${node.style.fontSize}-${node.style.fontWeight}`;
359
+ if (!typographyMap.has(key)) {
360
+ typographyMap.set(key, {
361
+ name: `${node.style.fontSize}px ${node.style.fontFamily}`,
362
+ fontFamily: node.style.fontFamily || "Inter",
363
+ fontSize: node.style.fontSize || 16,
364
+ fontWeight: node.style.fontWeight || 400,
365
+ lineHeight: node.style.lineHeightPx || node.style.fontSize || 16,
366
+ letterSpacing: node.style.letterSpacing
367
+ });
368
+ }
369
+ }
370
+ if (node.children) {
371
+ node.children.forEach((child) => this.extractTypography(child, typographyMap));
372
+ }
373
+ }
374
+ /**
375
+ * Extract screens/frames with detailed information
376
+ */
377
+ extractScreens(node, screens) {
378
+ if (node.type === "FRAME" || node.type === "COMPONENT") {
379
+ if (node.absoluteBoundingBox) {
380
+ const isMainScreen = node.absoluteBoundingBox.width >= 800 && node.absoluteBoundingBox.height >= 400;
381
+ if (isMainScreen) {
382
+ screens.push({
383
+ id: node.id,
384
+ name: node.name,
385
+ width: node.absoluteBoundingBox.width,
386
+ height: node.absoluteBoundingBox.height,
387
+ type: node.type,
388
+ childrenCount: node.children?.length || 0
389
+ });
390
+ }
391
+ }
392
+ }
393
+ if (node.children) {
394
+ node.children.forEach((child) => this.extractScreens(child, screens));
395
+ }
396
+ }
397
+ /**
398
+ * Extract structure, content, and layout from nodes
399
+ */
400
+ extractStructure(node, depth = 0) {
401
+ const nodes = [];
402
+ const hierarchyLines = [];
403
+ const processNode = (n, level = 0) => {
404
+ const indent = " ".repeat(level);
405
+ const childIds = [];
406
+ const nodeData = {
407
+ id: n.id,
408
+ name: n.name || "Unnamed",
409
+ type: n.type
410
+ };
411
+ if (n.absoluteBoundingBox) {
412
+ nodeData.position = {
413
+ x: n.absoluteBoundingBox.x,
414
+ y: n.absoluteBoundingBox.y,
415
+ width: n.absoluteBoundingBox.width,
416
+ height: n.absoluteBoundingBox.height
417
+ };
418
+ }
419
+ if (n.type === "TEXT" && n.characters) {
420
+ nodeData.content = n.characters;
421
+ }
422
+ const styles = {};
423
+ if (n.fills && Array.isArray(n.fills)) {
424
+ const solidFill = n.fills.find((f) => f.type === "SOLID" && f.color);
425
+ if (solidFill && solidFill.color) {
426
+ const { r, g, b, a } = solidFill.color;
427
+ styles.backgroundColor = this.rgbaToHex(r, g, b, a);
428
+ }
429
+ }
430
+ if (n.style) {
431
+ if (n.style.fontFamily) styles.fontFamily = n.style.fontFamily;
432
+ if (n.style.fontSize) styles.fontSize = n.style.fontSize;
433
+ if (n.style.fontWeight) styles.fontWeight = n.style.fontWeight;
434
+ if (n.style.lineHeightPx) styles.lineHeight = n.style.lineHeightPx;
435
+ if (n.fills && Array.isArray(n.fills)) {
436
+ const textFill = n.fills.find((f) => f.type === "SOLID" && f.color);
437
+ if (textFill && textFill.color) {
438
+ const { r, g, b, a } = textFill.color;
439
+ styles.color = this.rgbaToHex(r, g, b, a);
440
+ }
441
+ }
442
+ }
443
+ if (n.layoutMode) {
444
+ styles.layout = n.layoutMode;
445
+ }
446
+ if (n.itemSpacing !== void 0) {
447
+ styles.gap = n.itemSpacing;
448
+ }
449
+ if (n.paddingLeft || n.paddingRight || n.paddingTop || n.paddingBottom) {
450
+ styles.padding = {
451
+ top: n.paddingTop || 0,
452
+ right: n.paddingRight || 0,
453
+ bottom: n.paddingBottom || 0,
454
+ left: n.paddingLeft || 0
455
+ };
456
+ }
457
+ if (Object.keys(styles).length > 0) {
458
+ nodeData.styles = styles;
459
+ }
460
+ if (n.children && n.children.length > 0) {
461
+ n.children.forEach((child) => {
462
+ const childNodeIds = processNode(child, level + 1);
463
+ childIds.push(child.id);
464
+ childIds.push(...childNodeIds);
465
+ });
466
+ nodeData.children = n.children.map((c) => c.id);
467
+ }
468
+ nodes.push(nodeData);
469
+ const typeLabel = n.type.toLowerCase();
470
+ const nameLabel = n.name || "Unnamed";
471
+ const contentPreview = n.type === "TEXT" && n.characters ? `: "${n.characters.substring(0, 50)}${n.characters.length > 50 ? "..." : ""}"` : "";
472
+ const sizeLabel = n.absoluteBoundingBox ? ` [${Math.round(n.absoluteBoundingBox.width)}\xD7${Math.round(n.absoluteBoundingBox.height)}]` : "";
473
+ hierarchyLines.push(`${indent}${typeLabel} "${nameLabel}"${contentPreview}${sizeLabel}`);
474
+ return [n.id, ...childIds];
475
+ };
476
+ processNode(node, depth);
477
+ return {
478
+ nodes,
479
+ hierarchy: hierarchyLines.join("\n")
480
+ };
481
+ }
482
+ /**
483
+ * Find all nodes that can be exported as images (optionally filtered by screen)
484
+ */
485
+ findImageNodes(node, imageNodes = [], screenId, isWithinScreen = false) {
486
+ const currentIsScreen = node.id === screenId;
487
+ const nowWithinScreen = isWithinScreen || currentIsScreen;
488
+ if (screenId && !nowWithinScreen && node.type !== "PAGE") {
489
+ if (node.children) {
490
+ node.children.forEach((child) => this.findImageNodes(child, imageNodes, screenId, false));
491
+ }
492
+ return;
493
+ }
494
+ const exportableTypes = ["VECTOR", "COMPONENT", "INSTANCE", "FRAME", "GROUP", "RECTANGLE", "ELLIPSE"];
495
+ const hasImageFill = node.fills?.some((fill) => fill.type === "IMAGE" || fill.imageRef);
496
+ const isExportable = exportableTypes.includes(node.type) || hasImageFill;
497
+ if (isExportable && node.absoluteBoundingBox) {
498
+ const minSize = 16;
499
+ if (node.absoluteBoundingBox.width >= minSize && node.absoluteBoundingBox.height >= minSize) {
500
+ imageNodes.push({
501
+ id: node.id,
502
+ name: node.name || "Unnamed",
503
+ type: node.type,
504
+ width: node.absoluteBoundingBox.width,
505
+ height: node.absoluteBoundingBox.height
506
+ });
507
+ }
508
+ }
509
+ if (node.children) {
510
+ node.children.forEach((child) => this.findImageNodes(child, imageNodes, screenId, nowWithinScreen));
511
+ }
512
+ }
513
+ /**
514
+ * Download images/assets from Figma (optionally filtered by screen)
515
+ */
516
+ async downloadAssets(fileKey, rootNode, assetsDir, screenId) {
517
+ const imageNodes = [];
518
+ this.findImageNodes(rootNode, imageNodes, screenId);
519
+ if (imageNodes.length === 0) {
520
+ logger.info("No image nodes found to download");
521
+ return [];
522
+ }
523
+ logger.info(`Found ${imageNodes.length} image nodes to download`);
524
+ const nodesToDownload = imageNodes.slice(0, 50);
525
+ const nodeIds = nodesToDownload.map((n) => n.id).join(",");
526
+ const imageUrl = `https://api.figma.com/v1/images/${fileKey}?ids=${encodeURIComponent(nodeIds)}&format=png&scale=2`;
527
+ const response = await this.fetchWithRetry(imageUrl, {
528
+ headers: {
529
+ "X-Figma-Token": this.apiKey
530
+ }
531
+ }, "Figma images listing");
532
+ const imageData = await response.json();
533
+ const images = imageData.images;
534
+ const fullAssetsDir = assetsDir.startsWith("/") ? assetsDir : join5(process.cwd(), assetsDir);
535
+ if (!existsSync2(fullAssetsDir)) {
536
+ await mkdir3(fullAssetsDir, { recursive: true });
537
+ }
538
+ const downloadedAssets = [];
539
+ for (const node of nodesToDownload) {
540
+ const imageUrl2 = images[node.id];
541
+ if (!imageUrl2) {
542
+ logger.warn(`No image URL returned for node ${node.id} (${node.name})`);
543
+ continue;
544
+ }
545
+ try {
546
+ const imageResponse = await this.fetchWithRetry(
547
+ imageUrl2,
548
+ {},
549
+ `Download image ${node.id}`
550
+ );
551
+ const imageBuffer = await imageResponse.arrayBuffer();
552
+ const safeName = node.name.replace(/[^a-z0-9]/gi, "_").toLowerCase().substring(0, 50);
553
+ const extension = "png";
554
+ const filename = `${safeName}_${node.id.substring(0, 8)}.${extension}`;
555
+ const filePath = join5(fullAssetsDir, filename);
556
+ await writeFile3(filePath, Buffer.from(imageBuffer));
557
+ downloadedAssets.push({
558
+ nodeId: node.id,
559
+ nodeName: node.name,
560
+ nodeType: node.type,
561
+ format: extension,
562
+ path: filePath,
563
+ url: imageUrl2,
564
+ width: node.width,
565
+ height: node.height
566
+ });
567
+ logger.info(`Downloaded: ${filename} (${node.name})`);
568
+ } catch (error) {
569
+ logger.warn(`Error downloading image for node ${node.id}: ${error instanceof Error ? error.message : String(error)}`);
570
+ }
571
+ }
572
+ logger.info(`Downloaded ${downloadedAssets.length} assets to ${fullAssetsDir}`);
573
+ return downloadedAssets;
574
+ }
575
+ /**
576
+ * Convert RGBA to hex
577
+ */
578
+ rgbaToHex(r, g, b, a = 1) {
579
+ const toHex = (n) => {
580
+ const hex = Math.round(n * 255).toString(16);
581
+ return hex.length === 1 ? "0" + hex : hex;
582
+ };
583
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}${a < 1 ? toHex(a) : ""}`;
584
+ }
585
+ };
586
+ }
587
+ });
588
+
589
+ // src/core/tools/figma-screen-developer.ts
590
+ var figma_screen_developer_exports = {};
591
+ __export(figma_screen_developer_exports, {
592
+ checkCurrentCodeStatus: () => checkCurrentCodeStatus,
593
+ compareCodeWithFigma: () => compareCodeWithFigma
594
+ });
595
+ import { readFile as readFile4, readdir as readdir3 } from "fs/promises";
596
+ import { join as join6 } from "path";
597
+ import { existsSync as existsSync3 } from "fs";
598
+ async function checkCurrentCodeStatus(projectPath = process.cwd()) {
599
+ const status = {
600
+ hasHTML: false,
601
+ htmlFile: void 0,
602
+ hasCSS: false,
603
+ cssFiles: [],
604
+ hasAssets: false,
605
+ assetCount: 0,
606
+ sections: []
607
+ };
608
+ try {
609
+ const htmlFiles = ["index.html", "index.htm", "main.html"].filter(
610
+ (file) => existsSync3(join6(projectPath, file))
611
+ );
612
+ if (htmlFiles.length > 0) {
613
+ status.hasHTML = true;
614
+ status.htmlFile = htmlFiles[0];
615
+ try {
616
+ const htmlContent = await readFile4(join6(projectPath, htmlFiles[0]), "utf-8");
617
+ const sectionMatches = htmlContent.match(/<(section|div|header|footer|main|article|aside)[^>]*(?:id|class)=["']([^"']+)["']/gi);
618
+ if (sectionMatches) {
619
+ status.sections = sectionMatches.map((match) => {
620
+ const idMatch = match.match(/id=["']([^"']+)["']/i);
621
+ const classMatch = match.match(/class=["']([^"']+)["']/i);
622
+ return idMatch ? idMatch[1] : classMatch ? classMatch[1].split(" ")[0] : "";
623
+ }).filter(Boolean);
624
+ }
625
+ } catch (e) {
626
+ }
627
+ }
628
+ const stylesDir = join6(projectPath, "styles");
629
+ if (existsSync3(stylesDir)) {
630
+ try {
631
+ const files = await readdir3(stylesDir);
632
+ const cssFiles = files.filter((f) => f.endsWith(".css"));
633
+ if (cssFiles.length > 0) {
634
+ status.hasCSS = true;
635
+ status.cssFiles = cssFiles.map((f) => join6(stylesDir, f));
636
+ }
637
+ } catch (e) {
638
+ }
639
+ }
640
+ const assetsDir = join6(projectPath, "assets", "images");
641
+ if (existsSync3(assetsDir)) {
642
+ try {
643
+ const files = await readdir3(assetsDir);
644
+ const imageFiles = files.filter((f) => /\.(png|jpg|jpeg|svg|webp)$/i.test(f));
645
+ if (imageFiles.length > 0) {
646
+ status.hasAssets = true;
647
+ status.assetCount = imageFiles.length;
648
+ }
649
+ } catch (e) {
650
+ }
651
+ }
652
+ } catch (error) {
653
+ logger.warn(`Error checking code status: ${error instanceof Error ? error.message : String(error)}`);
654
+ }
655
+ return status;
656
+ }
657
+ async function compareCodeWithFigma(figmaTokens, selectedScreenId, projectPath = process.cwd()) {
658
+ const codeStatus = await checkCurrentCodeStatus(projectPath);
659
+ const result = {
660
+ missingSections: [],
661
+ missingAssets: [],
662
+ needsUpdate: false,
663
+ recommendations: []
664
+ };
665
+ const selectedScreen = figmaTokens.screens?.find((s) => s.id === selectedScreenId);
666
+ if (!selectedScreen) {
667
+ result.recommendations.push("Selected screen not found in Figma design");
668
+ return result;
669
+ }
670
+ const figmaSections = [];
671
+ if (figmaTokens.structure?.nodes) {
672
+ const screenNode = figmaTokens.structure.nodes.find((n) => n.id === selectedScreenId);
673
+ if (screenNode?.children) {
674
+ screenNode.children.forEach((childId) => {
675
+ const childNode = figmaTokens.structure.nodes.find((n) => n.id === childId);
676
+ if (childNode && (childNode.type === "FRAME" || childNode.type === "COMPONENT")) {
677
+ const sectionName = childNode.name.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-");
678
+ figmaSections.push(sectionName);
679
+ }
680
+ });
681
+ }
682
+ }
683
+ const existingSections = codeStatus.sections.map((s) => s.toLowerCase());
684
+ result.missingSections = figmaSections.filter(
685
+ (s) => !existingSections.some((existing) => existing.includes(s) || s.includes(existing))
686
+ );
687
+ if (codeStatus.assetCount === 0) {
688
+ result.missingAssets.push("All assets need to be downloaded");
689
+ result.needsUpdate = true;
690
+ }
691
+ if (!codeStatus.hasHTML) {
692
+ result.recommendations.push("Create index.html with HTML5 structure");
693
+ }
694
+ if (!codeStatus.hasCSS) {
695
+ result.recommendations.push("Create CSS files (variables.css, base.css, components.css)");
696
+ }
697
+ if (result.missingSections.length > 0) {
698
+ result.recommendations.push(`Implement missing sections: ${result.missingSections.join(", ")}`);
699
+ }
700
+ if (result.missingAssets.length > 0) {
701
+ result.recommendations.push("Download required assets from Figma");
702
+ }
703
+ return result;
704
+ }
705
+ var init_figma_screen_developer = __esm({
706
+ "src/core/tools/figma-screen-developer.ts"() {
707
+ "use strict";
708
+ init_esm_shims();
709
+ init_logger();
710
+ }
711
+ });
712
+
713
+ // src/core/memory.ts
714
+ var memory_exports = {};
715
+ __export(memory_exports, {
716
+ MemoryManager: () => MemoryManager
717
+ });
718
+ import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, access as access2, constants as constants2 } from "fs/promises";
719
+ import { join as join7 } from "path";
720
+ var MemoryManager;
721
+ var init_memory = __esm({
722
+ "src/core/memory.ts"() {
723
+ "use strict";
724
+ init_esm_shims();
725
+ init_paths();
726
+ MemoryManager = class {
727
+ config;
728
+ constructor(config) {
729
+ this.config = config;
730
+ }
731
+ /**
732
+ * List all memory entries
733
+ */
734
+ async list() {
735
+ const memories = [];
736
+ const memoryPath = paths.memory(this.config.configPath);
737
+ const subDirs = ["observations", "handoffs", "research"];
738
+ for (const subDir of subDirs) {
739
+ const dirPath = join7(memoryPath, subDir);
740
+ try {
741
+ const { readdir: readdir5 } = await import("fs/promises");
742
+ const files = await readdir5(dirPath);
743
+ for (const file of files) {
744
+ if (!file.endsWith(".md")) continue;
745
+ const content = await readFile5(join7(dirPath, file), "utf-8");
746
+ const summary = this.extractSummary(content);
747
+ memories.push({
748
+ key: `${subDir}/${file.replace(".md", "")}`,
749
+ content,
750
+ summary,
751
+ createdAt: /* @__PURE__ */ new Date(),
752
+ // Would get from file stats
753
+ updatedAt: /* @__PURE__ */ new Date(),
754
+ type: subDir
755
+ });
756
+ }
757
+ } catch {
758
+ }
759
+ }
760
+ return memories;
761
+ }
762
+ /**
763
+ * Read a memory entry
764
+ */
765
+ async read(key) {
766
+ const memoryPath = paths.memory(this.config.configPath);
767
+ let filePath;
768
+ if (key.includes("/")) {
769
+ filePath = join7(memoryPath, `${key}.md`);
770
+ } else {
771
+ const subDirs = ["observations", "handoffs", "research", "_templates"];
772
+ for (const subDir of subDirs) {
773
+ const testPath = join7(memoryPath, subDir, `${key}.md`);
774
+ try {
775
+ await access2(testPath, constants2.R_OK);
776
+ filePath = testPath;
777
+ break;
778
+ } catch {
779
+ continue;
780
+ }
781
+ }
782
+ filePath = filePath || join7(memoryPath, `${key}.md`);
783
+ }
784
+ try {
785
+ return await readFile5(filePath, "utf-8");
786
+ } catch {
787
+ return null;
788
+ }
789
+ }
790
+ /**
791
+ * Update a memory entry
792
+ */
793
+ async update(key, content, options) {
794
+ const memoryPath = paths.memory(this.config.configPath);
795
+ const type = options?.type || "custom";
796
+ let filePath;
797
+ if (key.includes("/")) {
798
+ filePath = join7(memoryPath, `${key}.md`);
799
+ } else {
800
+ const subDir = type === "observation" ? "observations" : type === "handoff" ? "handoffs" : type === "research" ? "research" : "";
801
+ filePath = join7(memoryPath, subDir, `${key}.md`);
802
+ }
803
+ const { dirname: dirname2 } = await import("path");
804
+ await mkdir4(dirname2(filePath), { recursive: true });
805
+ if (options?.append) {
806
+ try {
807
+ const existing = await readFile5(filePath, "utf-8");
808
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
809
+ content = `${existing}
810
+
811
+ ---
812
+ _Updated: ${timestamp}_
813
+
814
+ ${content}`;
815
+ } catch {
816
+ }
817
+ }
818
+ await writeFile4(filePath, content);
819
+ }
820
+ /**
821
+ * Create a handoff bundle
822
+ */
823
+ async createHandoff(summary) {
824
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
825
+ const key = `handoffs/${timestamp}`;
826
+ const content = `# Handoff: ${(/* @__PURE__ */ new Date()).toLocaleString()}
827
+
828
+ ## Completed
829
+ ${summary.completed.map((item) => `- [x] ${item}`).join("\n") || "- None"}
830
+
831
+ ## In Progress
832
+ ${summary.inProgress.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
833
+
834
+ ## Remaining
835
+ ${summary.remaining.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
836
+
837
+ ## Context
838
+ ${summary.context || "No additional context."}
839
+
840
+ ## Next Steps
841
+ ${summary.nextSteps.map((step, i) => `${i + 1}. ${step}`).join("\n") || "- Continue from where left off"}
842
+ `;
843
+ await this.update(key, content, { type: "handoff" });
844
+ return key;
845
+ }
846
+ /**
847
+ * Get the latest handoff
848
+ */
849
+ async getLatestHandoff() {
850
+ const memories = await this.list();
851
+ const handoffs = memories.filter((m) => m.type === "handoff");
852
+ if (handoffs.length === 0) return null;
853
+ handoffs.sort((a, b) => b.key.localeCompare(a.key));
854
+ return handoffs[0];
855
+ }
856
+ /**
857
+ * Create an observation
858
+ */
859
+ async createObservation(title, observation) {
860
+ const slug = title.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
861
+ const key = `observations/${slug}`;
862
+ const content = `# ${title}
863
+
864
+ ## What
865
+ ${observation.what}
866
+
867
+ ## Why
868
+ ${observation.why}
869
+
870
+ ## Impact
871
+ ${observation.impact}
872
+
873
+ ${observation.tags?.length ? `## Tags
874
+ ${observation.tags.map((t) => `- ${t}`).join("\n")}` : ""}
875
+
876
+ ---
877
+ _Created: ${(/* @__PURE__ */ new Date()).toISOString()}_
878
+ `;
879
+ await this.update(key, content, { type: "observation" });
880
+ return key;
881
+ }
882
+ /**
883
+ * Save research findings
884
+ */
885
+ async saveResearch(topic, findings) {
886
+ const slug = topic.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
887
+ const key = `research/${slug}`;
888
+ const content = `# Research: ${topic}
889
+
890
+ ## Summary
891
+ ${findings.summary}
892
+
893
+ ## Sources
894
+ ${findings.sources.map((s) => `- ${s}`).join("\n")}
895
+
896
+ ## Recommendations
897
+ ${findings.recommendations.map((r, i) => `${i + 1}. ${r}`).join("\n")}
898
+
899
+ ${findings.codeExamples ? `## Code Examples
900
+ \`\`\`
901
+ ${findings.codeExamples}
902
+ \`\`\`` : ""}
903
+
904
+ ---
905
+ _Researched: ${(/* @__PURE__ */ new Date()).toISOString()}_
906
+ `;
907
+ await this.update(key, content, { type: "research" });
908
+ return key;
909
+ }
910
+ /**
911
+ * Extract a summary from content (first paragraph or heading)
912
+ */
913
+ extractSummary(content) {
914
+ const lines = content.split("\n").filter((l) => l.trim());
915
+ for (const line of lines) {
916
+ if (!line.startsWith("#") && line.trim().length > 0) {
917
+ return line.slice(0, 100) + (line.length > 100 ? "..." : "");
918
+ }
919
+ }
920
+ if (lines[0]?.startsWith("#")) {
921
+ return lines[0].replace(/^#+\s*/, "");
922
+ }
923
+ return "No summary available";
924
+ }
925
+ };
926
+ }
927
+ });
928
+
929
+ // src/mcp-server.ts
930
+ init_esm_shims();
931
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
932
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
933
+ import {
934
+ CallToolRequestSchema,
935
+ ListToolsRequestSchema
936
+ } from "@modelcontextprotocol/sdk/types.js";
937
+
938
+ // src/core/config.ts
939
+ init_esm_shims();
940
+ init_paths();
941
+ import { readFile, access, constants } from "fs/promises";
942
+ import { join as join2 } from "path";
943
+ import { z } from "zod";
944
+ var ConfigSchema = z.object({
945
+ version: z.string(),
946
+ skills: z.object({
947
+ enabled: z.boolean().default(true),
948
+ directory: z.string().optional()
949
+ }).default({}),
950
+ agents: z.object({
951
+ enabled: z.boolean().default(true),
952
+ default: z.string().default("build")
953
+ }).default({}),
954
+ commands: z.object({
955
+ enabled: z.boolean().default(true)
956
+ }).default({}),
957
+ tools: z.object({
958
+ enabled: z.boolean().default(true)
959
+ }).default({}),
960
+ plugins: z.object({
961
+ enabled: z.boolean().default(true),
962
+ autoload: z.array(z.string()).optional()
963
+ }).default({}),
964
+ memory: z.object({
965
+ enabled: z.boolean().default(true),
966
+ maxSize: z.number().optional()
967
+ }).default({}),
968
+ beads: z.object({
969
+ enabled: z.boolean().default(true),
970
+ autoInit: z.boolean().default(false)
971
+ }).default({}),
972
+ antiHallucination: z.object({
973
+ enabled: z.boolean().default(true),
974
+ specFile: z.string().default("spec.md"),
975
+ reviewFile: z.string().default("review.md")
976
+ }).default({}),
977
+ mcp: z.object({
978
+ context7: z.boolean().default(false),
979
+ githubGrep: z.boolean().default(false),
980
+ gkg: z.boolean().default(false)
981
+ }).optional()
982
+ });
983
+ var Config = class {
984
+ config;
985
+ constructor(config) {
986
+ this.config = config;
987
+ }
988
+ get() {
989
+ return this.config;
990
+ }
991
+ get skills() {
992
+ return this.config.skills;
993
+ }
994
+ get agents() {
995
+ return this.config.agents;
996
+ }
997
+ get commands() {
998
+ return this.config.commands;
999
+ }
1000
+ get tools() {
1001
+ return this.config.tools;
1002
+ }
1003
+ get plugins() {
1004
+ return this.config.plugins;
1005
+ }
1006
+ get memory() {
1007
+ return this.config.memory;
1008
+ }
1009
+ get beads() {
1010
+ return this.config.beads;
1011
+ }
1012
+ get antiHallucination() {
1013
+ return this.config.antiHallucination;
1014
+ }
1015
+ get configPath() {
1016
+ return this.config.configPath;
1017
+ }
1018
+ get projectPath() {
1019
+ return this.config.projectPath;
1020
+ }
1021
+ /**
1022
+ * Get path to a specific resource
1023
+ */
1024
+ getPath(resource) {
1025
+ return paths[resource](this.configPath);
1026
+ }
1027
+ };
1028
+ async function loadConfig(projectPath) {
1029
+ const project = projectPath || process.cwd();
1030
+ const projectConfigPath = paths.projectConfig(project);
1031
+ const globalConfigPath = paths.globalConfig();
1032
+ let configPath;
1033
+ let configData = {};
1034
+ try {
1035
+ await access(join2(globalConfigPath, "aikit.json"), constants.R_OK);
1036
+ const globalContent = await readFile(join2(globalConfigPath, "aikit.json"), "utf-8");
1037
+ configData = JSON.parse(globalContent);
1038
+ configPath = globalConfigPath;
1039
+ } catch {
1040
+ configPath = projectConfigPath;
1041
+ }
1042
+ try {
1043
+ await access(join2(projectConfigPath, "aikit.json"), constants.R_OK);
1044
+ const projectContent = await readFile(join2(projectConfigPath, "aikit.json"), "utf-8");
1045
+ const projectData = JSON.parse(projectContent);
1046
+ configData = deepMerge(configData, projectData);
1047
+ configPath = projectConfigPath;
1048
+ } catch {
1049
+ }
1050
+ if (!configData.version) {
1051
+ configData.version = "0.1.0";
1052
+ }
1053
+ const parsed = ConfigSchema.parse(configData);
1054
+ return new Config({
1055
+ ...parsed,
1056
+ configPath,
1057
+ projectPath: project
1058
+ });
1059
+ }
1060
+ function deepMerge(base, override) {
1061
+ const result = { ...base };
1062
+ for (const key in override) {
1063
+ const baseValue = base[key];
1064
+ const overrideValue = override[key];
1065
+ if (typeof baseValue === "object" && baseValue !== null && typeof overrideValue === "object" && overrideValue !== null && !Array.isArray(baseValue) && !Array.isArray(overrideValue)) {
1066
+ result[key] = deepMerge(
1067
+ baseValue,
1068
+ overrideValue
1069
+ );
1070
+ } else if (overrideValue !== void 0) {
1071
+ result[key] = overrideValue;
1072
+ }
1073
+ }
1074
+ return result;
1075
+ }
1076
+
1077
+ // src/core/skills.ts
1078
+ init_esm_shims();
1079
+ init_paths();
1080
+ import { readFile as readFile2, readdir, writeFile, mkdir } from "fs/promises";
1081
+ import { join as join3, basename, extname } from "path";
1082
+ import matter from "gray-matter";
1083
+ var SkillEngine = class {
1084
+ config;
1085
+ skillsCache = /* @__PURE__ */ new Map();
1086
+ constructor(config) {
1087
+ this.config = config;
1088
+ }
1089
+ /**
1090
+ * List all available skills
1091
+ */
1092
+ async listSkills() {
1093
+ const skills = [];
1094
+ const globalSkillsPath = paths.skills(paths.globalConfig());
1095
+ try {
1096
+ const globalSkills = await this.loadSkillsFromDir(globalSkillsPath);
1097
+ skills.push(...globalSkills);
1098
+ } catch {
1099
+ }
1100
+ const projectSkillsPath = paths.skills(this.config.configPath);
1101
+ if (projectSkillsPath !== globalSkillsPath) {
1102
+ try {
1103
+ const projectSkills = await this.loadSkillsFromDir(projectSkillsPath);
1104
+ for (const skill of projectSkills) {
1105
+ const existingIndex = skills.findIndex((s) => s.name === skill.name);
1106
+ if (existingIndex >= 0) {
1107
+ skills[existingIndex] = skill;
1108
+ } else {
1109
+ skills.push(skill);
1110
+ }
1111
+ }
1112
+ } catch {
1113
+ }
1114
+ }
1115
+ return skills.sort((a, b) => a.name.localeCompare(b.name));
1116
+ }
1117
+ /**
1118
+ * Get a specific skill by name
1119
+ */
1120
+ async getSkill(name) {
1121
+ if (this.skillsCache.has(name)) {
1122
+ return this.skillsCache.get(name);
1123
+ }
1124
+ const skills = await this.listSkills();
1125
+ const skill = skills.find((s) => s.name === name);
1126
+ if (skill) {
1127
+ this.skillsCache.set(name, skill);
1128
+ }
1129
+ return skill || null;
1130
+ }
1131
+ /**
1132
+ * Find skills matching a query
1133
+ */
1134
+ async findSkills(query) {
1135
+ const skills = await this.listSkills();
1136
+ if (!query) {
1137
+ return skills;
1138
+ }
1139
+ const lowerQuery = query.toLowerCase();
1140
+ return skills.filter(
1141
+ (skill) => skill.name.toLowerCase().includes(lowerQuery) || skill.description.toLowerCase().includes(lowerQuery) || skill.tags.some((tag) => tag.toLowerCase().includes(lowerQuery)) || skill.category.toLowerCase().includes(lowerQuery)
1142
+ );
1143
+ }
1144
+ /**
1145
+ * Create a new skill
1146
+ */
1147
+ async createSkill(name, options) {
1148
+ const configPath = options?.global ? paths.globalConfig() : this.config.configPath;
1149
+ const skillsDir = paths.skills(configPath);
1150
+ await mkdir(skillsDir, { recursive: true });
1151
+ const fileName = `${name.replace(/\s+/g, "-").toLowerCase()}.md`;
1152
+ const filePath = join3(skillsDir, fileName);
1153
+ const frontmatter = {
1154
+ name,
1155
+ description: options?.description || `Use when you need to ${name}`,
1156
+ useWhen: options?.useWhen || `The user asks you to ${name}`,
1157
+ category: options?.category || "custom",
1158
+ tags: options?.tags || ["custom"]
1159
+ };
1160
+ const content = options?.content || `## ${name}
1161
+
1162
+ ### Overview
1163
+ Describe what this skill does.
1164
+
1165
+ ### Workflow
1166
+
1167
+ #### Step 1: Understand the Task
1168
+ - Gather context
1169
+ - Clarify requirements
1170
+
1171
+ #### Step 2: Plan the Approach
1172
+ - Break down into sub-tasks
1173
+ - Identify dependencies
1174
+
1175
+ #### Step 3: Execute
1176
+ - Follow TDD principles
1177
+ - Write tests first
1178
+
1179
+ #### Step 4: Verify
1180
+ - Run all tests
1181
+ - Check for regressions
1182
+
1183
+ ### Checklist
1184
+ - [ ] Requirements understood
1185
+ - [ ] Tests written
1186
+ - [ ] Implementation complete
1187
+ - [ ] All tests passing
1188
+ - [ ] Code reviewed
1189
+ `;
1190
+ const fileContent = matter.stringify(content, frontmatter);
1191
+ await writeFile(filePath, fileContent);
1192
+ const skill = {
1193
+ name,
1194
+ description: frontmatter.description,
1195
+ useWhen: frontmatter.useWhen,
1196
+ category: frontmatter.category,
1197
+ tags: frontmatter.tags,
1198
+ content,
1199
+ filePath
1200
+ };
1201
+ this.skillsCache.set(name, skill);
1202
+ return skill;
1203
+ }
1204
+ /**
1205
+ * Sync global skills to project directory
1206
+ */
1207
+ async syncSkillsToProject() {
1208
+ const globalSkillsPath = paths.skills(paths.globalConfig());
1209
+ const projectSkillsPath = paths.skills(this.config.configPath);
1210
+ if (globalSkillsPath === projectSkillsPath) {
1211
+ return { count: 0, synced: [] };
1212
+ }
1213
+ const globalSkills = await this.loadSkillsFromDir(globalSkillsPath);
1214
+ await mkdir(projectSkillsPath, { recursive: true });
1215
+ const synced = [];
1216
+ for (const skill of globalSkills) {
1217
+ const fileName = `${skill.name.replace(/\s+/g, "-").toLowerCase()}.md`;
1218
+ const destPath = join3(projectSkillsPath, fileName);
1219
+ const srcContent = await readFile2(skill.filePath, "utf-8");
1220
+ await writeFile(destPath, srcContent);
1221
+ synced.push(skill.name);
1222
+ }
1223
+ return { count: synced.length, synced };
1224
+ }
1225
+ /**
1226
+ * Format skill for agent consumption
1227
+ */
1228
+ formatForAgent(skill) {
1229
+ return `# Skill: ${skill.name}
1230
+
1231
+ ## When to Use
1232
+ ${skill.useWhen}
1233
+
1234
+ ## Description
1235
+ ${skill.description}
1236
+
1237
+ ## Workflow
1238
+ ${skill.content}
1239
+
1240
+ ---
1241
+ **IMPORTANT**: Follow this workflow step by step. Do not skip steps.
1242
+ `;
1243
+ }
1244
+ /**
1245
+ * Load skills from a directory
1246
+ */
1247
+ async loadSkillsFromDir(dir) {
1248
+ const files = await readdir(dir);
1249
+ const skills = [];
1250
+ for (const file of files) {
1251
+ if (extname(file) !== ".md") continue;
1252
+ const filePath = join3(dir, file);
1253
+ const content = await readFile2(filePath, "utf-8");
1254
+ const { data, content: body } = matter(content);
1255
+ const frontmatter = data;
1256
+ const name = frontmatter.name || basename(file, ".md");
1257
+ skills.push({
1258
+ name,
1259
+ description: frontmatter.description || "",
1260
+ useWhen: frontmatter.useWhen || "",
1261
+ category: frontmatter.category || "general",
1262
+ tags: frontmatter.tags || [],
1263
+ content: body.trim(),
1264
+ filePath
1265
+ });
1266
+ }
1267
+ return skills;
1268
+ }
1269
+ };
1270
+
1271
+ // src/core/agents.ts
1272
+ init_esm_shims();
1273
+ var DEFAULT_AGENTS = [
1274
+ {
1275
+ name: "planner",
1276
+ displayName: "@planner",
1277
+ description: "Strategic planning and multi-agent coordination",
1278
+ useWhen: "Complex tasks requiring architecture decisions, multi-step planning, or coordination between specialists",
1279
+ capabilities: [
1280
+ "Break down complex tasks into sub-tasks",
1281
+ "Coordinate between specialist agents",
1282
+ "Make architecture decisions",
1283
+ "Create implementation plans"
1284
+ ],
1285
+ systemPrompt: `You are a strategic planner agent. Your role is to:
1286
+
1287
+ 1. ANALYZE the task and understand the full scope
1288
+ 2. BREAK DOWN complex tasks into smaller, manageable sub-tasks
1289
+ 3. DELEGATE to appropriate specialist agents
1290
+ 4. COORDINATE the overall workflow
1291
+ 5. VERIFY completion of the overall goal
1292
+
1293
+ When delegating:
1294
+ - Use @build for implementation tasks
1295
+ - Use @scout for external research
1296
+ - Use @review for code review and security audits
1297
+ - Use @explore for codebase navigation
1298
+ - Use @vision for visual analysis
1299
+
1300
+ Always create a clear plan before delegating. Track progress and ensure all sub-tasks complete successfully.`,
1301
+ delegatesTo: ["build", "scout", "review", "explore", "vision"]
1302
+ },
1303
+ {
1304
+ name: "build",
1305
+ displayName: "@build",
1306
+ description: "Primary execution agent for implementing features",
1307
+ useWhen: "Implementing features, writing code, making changes to the codebase",
1308
+ capabilities: [
1309
+ "Write production code",
1310
+ "Write tests",
1311
+ "Refactor code",
1312
+ "Fix bugs",
1313
+ "Implement features"
1314
+ ],
1315
+ systemPrompt: `You are the build agent. Your role is to implement code changes.
1316
+
1317
+ Follow these principles:
1318
+ 1. TEST-DRIVEN DEVELOPMENT: Write tests before implementation
1319
+ 2. INCREMENTAL CHANGES: Make small, focused commits
1320
+ 3. VERIFY: Run tests and type checks after each change
1321
+ 4. DOCUMENT: Add comments for complex logic
1322
+
1323
+ Before starting:
1324
+ - Understand the requirements fully
1325
+ - Check existing patterns in the codebase
1326
+ - Plan the implementation approach
1327
+
1328
+ After completing:
1329
+ - Ensure all tests pass
1330
+ - Run linting and type checks
1331
+ - Create a clear commit message`,
1332
+ delegatesTo: ["review", "explore"]
1333
+ },
1334
+ {
1335
+ name: "rush",
1336
+ displayName: "@rush",
1337
+ description: "Fast execution with minimal planning",
1338
+ useWhen: "Quick fixes, hotfixes, simple edits that need minimal planning",
1339
+ capabilities: [
1340
+ "Quick bug fixes",
1341
+ "Simple refactoring",
1342
+ "Minor changes",
1343
+ "Hotfixes"
1344
+ ],
1345
+ systemPrompt: `You are the rush agent. Your role is to make quick, focused changes.
1346
+
1347
+ Guidelines:
1348
+ 1. ACT FAST: Minimal planning, direct execution
1349
+ 2. FOCUS: One change at a time
1350
+ 3. VERIFY: Quick sanity check after change
1351
+ 4. MINIMAL SCOPE: Don't expand beyond the immediate task
1352
+
1353
+ Use for:
1354
+ - Typo fixes
1355
+ - Simple bug fixes
1356
+ - Minor adjustments
1357
+ - Urgent hotfixes`,
1358
+ delegatesTo: []
1359
+ },
1360
+ {
1361
+ name: "review",
1362
+ displayName: "@review",
1363
+ description: "Code review, debugging, and security audits",
1364
+ useWhen: "Reviewing code quality, finding bugs, security review, debugging issues",
1365
+ capabilities: [
1366
+ "Code review",
1367
+ "Security audit",
1368
+ "Performance analysis",
1369
+ "Bug finding",
1370
+ "Best practices enforcement"
1371
+ ],
1372
+ systemPrompt: `You are the review agent. Your role is to review and improve code quality.
1373
+
1374
+ Review checklist:
1375
+ 1. CORRECTNESS: Does the code do what it's supposed to?
1376
+ 2. SECURITY: Are there any security vulnerabilities?
1377
+ 3. PERFORMANCE: Are there performance issues?
1378
+ 4. MAINTAINABILITY: Is the code clean and readable?
1379
+ 5. TESTS: Are there adequate tests?
1380
+ 6. PATTERNS: Does it follow project conventions?
1381
+
1382
+ When reviewing:
1383
+ - Be specific about issues
1384
+ - Suggest fixes, not just problems
1385
+ - Prioritize by severity
1386
+ - Check for edge cases`,
1387
+ delegatesTo: []
1388
+ },
1389
+ {
1390
+ name: "scout",
1391
+ displayName: "@scout",
1392
+ description: "External research - library docs, GitHub patterns, frameworks",
1393
+ useWhen: "Researching external libraries, finding code patterns on GitHub, learning about frameworks",
1394
+ capabilities: [
1395
+ "Web research",
1396
+ "GitHub code search",
1397
+ "Documentation lookup",
1398
+ "Framework exploration",
1399
+ "Best practices research"
1400
+ ],
1401
+ systemPrompt: `You are the scout agent. Your role is to research external resources.
1402
+
1403
+ Research strategy:
1404
+ 1. UNDERSTAND what information is needed
1405
+ 2. SEARCH appropriate sources (docs, GitHub, web)
1406
+ 3. EVALUATE quality and relevance of findings
1407
+ 4. SUMMARIZE key findings concisely
1408
+ 5. PROVIDE actionable recommendations
1409
+
1410
+ Sources to use:
1411
+ - Official documentation
1412
+ - GitHub code examples
1413
+ - Stack Overflow (verified answers)
1414
+ - Framework guides
1415
+ - Community best practices
1416
+
1417
+ Always cite your sources and verify information accuracy.`,
1418
+ delegatesTo: []
1419
+ },
1420
+ {
1421
+ name: "explore",
1422
+ displayName: "@explore",
1423
+ description: "Fast codebase navigation and pattern search",
1424
+ useWhen: "Finding files, understanding codebase structure, searching for patterns in code",
1425
+ capabilities: [
1426
+ "File discovery",
1427
+ "Pattern search",
1428
+ "Codebase navigation",
1429
+ "Structure analysis",
1430
+ "Dependency mapping"
1431
+ ],
1432
+ systemPrompt: `You are the explore agent. Your role is to navigate and understand the codebase.
1433
+
1434
+ Exploration techniques:
1435
+ 1. FILE STRUCTURE: Understand project organization
1436
+ 2. GREP SEARCH: Find specific patterns or usages
1437
+ 3. DEPENDENCY ANALYSIS: Map relationships between modules
1438
+ 4. PATTERN DISCOVERY: Find existing patterns to follow
1439
+ 5. QUICK CONTEXT: Gather just enough context for the task
1440
+
1441
+ Focus on speed and accuracy. Provide concise summaries of findings.`,
1442
+ delegatesTo: []
1443
+ },
1444
+ {
1445
+ name: "vision",
1446
+ displayName: "@vision",
1447
+ description: "Multimodal analysis - mockups, PDFs, diagrams",
1448
+ useWhen: "Analyzing images, mockups, screenshots, PDFs, or diagrams",
1449
+ capabilities: [
1450
+ "Image analysis",
1451
+ "Mockup interpretation",
1452
+ "PDF extraction",
1453
+ "Diagram understanding",
1454
+ "UI/UX analysis"
1455
+ ],
1456
+ systemPrompt: `You are the vision agent. Your role is to analyze visual content.
1457
+
1458
+ Analysis approach:
1459
+ 1. OBSERVE: Carefully examine the visual content
1460
+ 2. EXTRACT: Identify key information, text, structure
1461
+ 3. INTERPRET: Understand the intent and requirements
1462
+ 4. TRANSLATE: Convert visual specs to actionable tasks
1463
+ 5. VALIDATE: Ensure accurate interpretation
1464
+
1465
+ For mockups:
1466
+ - Identify components and layout
1467
+ - Note colors, spacing, typography
1468
+ - Extract interaction patterns
1469
+
1470
+ For diagrams:
1471
+ - Understand relationships
1472
+ - Map to code structure
1473
+ - Note data flow`,
1474
+ delegatesTo: []
1475
+ }
1476
+ ];
1477
+ var AgentManager = class {
1478
+ config;
1479
+ agents;
1480
+ constructor(config) {
1481
+ this.config = config;
1482
+ this.agents = /* @__PURE__ */ new Map();
1483
+ for (const agent of DEFAULT_AGENTS) {
1484
+ this.agents.set(agent.name, agent);
1485
+ }
1486
+ }
1487
+ /**
1488
+ * List all available agents
1489
+ */
1490
+ listAgents() {
1491
+ return Array.from(this.agents.values());
1492
+ }
1493
+ /**
1494
+ * Get a specific agent
1495
+ */
1496
+ getAgent(name) {
1497
+ return this.agents.get(name);
1498
+ }
1499
+ /**
1500
+ * Get the default agent
1501
+ */
1502
+ getDefaultAgent() {
1503
+ const defaultName = this.config.agents.default;
1504
+ return this.agents.get(defaultName) || this.agents.get("build");
1505
+ }
1506
+ /**
1507
+ * Decide which agent should handle a task
1508
+ */
1509
+ decideAgent(task, _context) {
1510
+ const lowerTask = task.toLowerCase();
1511
+ for (const [name, agent] of this.agents) {
1512
+ if (lowerTask.includes(`@${name}`)) {
1513
+ return {
1514
+ agent,
1515
+ reason: `Explicitly requested @${name}`,
1516
+ shouldDelegate: false
1517
+ };
1518
+ }
1519
+ }
1520
+ if (this.matchesKeywords(lowerTask, ["plan", "architect", "design", "coordinate", "complex"])) {
1521
+ return {
1522
+ agent: this.agents.get("planner"),
1523
+ reason: "Task requires planning and coordination",
1524
+ shouldDelegate: true
1525
+ };
1526
+ }
1527
+ if (this.matchesKeywords(lowerTask, ["research", "docs", "documentation", "library", "framework", "how to"])) {
1528
+ return {
1529
+ agent: this.agents.get("scout"),
1530
+ reason: "Task requires external research",
1531
+ shouldDelegate: false
1532
+ };
1533
+ }
1534
+ if (this.matchesKeywords(lowerTask, ["review", "audit", "security", "check", "debug"])) {
1535
+ return {
1536
+ agent: this.agents.get("review"),
1537
+ reason: "Task requires code review or debugging",
1538
+ shouldDelegate: false
1539
+ };
1540
+ }
1541
+ if (this.matchesKeywords(lowerTask, ["find", "search", "where", "locate", "explore"])) {
1542
+ return {
1543
+ agent: this.agents.get("explore"),
1544
+ reason: "Task requires codebase exploration",
1545
+ shouldDelegate: false
1546
+ };
1547
+ }
1548
+ if (this.matchesKeywords(lowerTask, ["image", "mockup", "screenshot", "pdf", "diagram", "visual"])) {
1549
+ return {
1550
+ agent: this.agents.get("vision"),
1551
+ reason: "Task requires visual analysis",
1552
+ shouldDelegate: false
1553
+ };
1554
+ }
1555
+ if (this.matchesKeywords(lowerTask, ["fix quickly", "hotfix", "urgent", "quick fix", "typo"])) {
1556
+ return {
1557
+ agent: this.agents.get("rush"),
1558
+ reason: "Task is a quick fix",
1559
+ shouldDelegate: false
1560
+ };
1561
+ }
1562
+ return {
1563
+ agent: this.agents.get("build"),
1564
+ reason: "Default to build agent for implementation",
1565
+ shouldDelegate: false
1566
+ };
1567
+ }
1568
+ /**
1569
+ * Format agent instructions for prompt
1570
+ */
1571
+ formatAgentPrompt(agent) {
1572
+ return `# Agent: ${agent.displayName}
1573
+
1574
+ ${agent.systemPrompt}
1575
+
1576
+ ## Capabilities
1577
+ ${agent.capabilities.map((c) => `- ${c}`).join("\n")}
1578
+
1579
+ ${agent.delegatesTo.length > 0 ? `## Can Delegate To
1580
+ ${agent.delegatesTo.map((a) => `- @${a}`).join("\n")}` : ""}
1581
+ `;
1582
+ }
1583
+ matchesKeywords(text, keywords) {
1584
+ return keywords.some((kw) => text.includes(kw));
1585
+ }
1586
+ };
1587
+
1588
+ // src/core/commands.ts
1589
+ init_esm_shims();
1590
+ init_paths();
1591
+ import { readFile as readFile3, readdir as readdir2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1592
+ import { join as join4, basename as basename2, extname as extname2 } from "path";
1593
+ import matter2 from "gray-matter";
1594
+ var DEFAULT_COMMANDS = [
1595
+ // Core Workflow Commands (Beads integration)
1596
+ {
1597
+ name: "create",
1598
+ description: "Create a new Beads task for tracking",
1599
+ category: "core",
1600
+ usage: "/create <task description>",
1601
+ examples: ["/create Add user authentication", "/create Fix navigation bug"],
1602
+ content: `Create a new task in the Beads system (.beads/ directory).
1603
+
1604
+ ## Workflow
1605
+ 1. Create a new bead file with unique ID
1606
+ 2. Add task description, status, and notes
1607
+ 3. Set status to "in-progress"
1608
+ 4. Initialize working notes section
1609
+
1610
+ ## Required Output
1611
+ - Task ID for reference
1612
+ - Confirmation of task creation
1613
+ - Next steps`
1614
+ },
1615
+ {
1616
+ name: "plan",
1617
+ description: "Create a detailed implementation plan",
1618
+ category: "core",
1619
+ usage: "/plan <feature or task>",
1620
+ examples: ["/plan user authentication system", "/plan refactor database layer"],
1621
+ content: `Create a comprehensive plan before implementation.
1622
+
1623
+ ## Workflow
1624
+ 1. UNDERSTAND: Clarify requirements through Socratic questioning
1625
+ 2. RESEARCH: Check existing patterns and dependencies
1626
+ 3. BREAK DOWN: Create 2-5 minute sub-tasks with:
1627
+ - Exact file paths
1628
+ - Expected changes
1629
+ - Verification steps
1630
+ 4. DOCUMENT: Write plan to memory/plans/
1631
+
1632
+ ## Output Format
1633
+ \`\`\`markdown
1634
+ # Plan: [Feature Name]
1635
+
1636
+ ## Overview
1637
+ Brief description of the goal.
1638
+
1639
+ ## Tasks
1640
+ 1. [ ] Task 1 - file.ts
1641
+ 2. [ ] Task 2 - component.tsx
1642
+ ...
1643
+
1644
+ ## Dependencies
1645
+ - List dependencies
1646
+
1647
+ ## Risks
1648
+ - Potential issues
1649
+
1650
+ ## Verification
1651
+ - How to verify completion
1652
+ \`\`\``
1653
+ },
1654
+ {
1655
+ name: "implement",
1656
+ description: "Implement a planned task with TDD",
1657
+ category: "core",
1658
+ usage: "/implement <task reference>",
1659
+ examples: ["/implement task-001", '/implement "add login form"'],
1660
+ content: `Implement a task following TDD principles.
1661
+
1662
+ ## Workflow
1663
+ 1. LOAD: Get task details from .beads/ or plan
1664
+ 2. TEST: Write failing tests first (RED)
1665
+ 3. IMPLEMENT: Write minimal code to pass (GREEN)
1666
+ 4. REFACTOR: Clean up while keeping tests green
1667
+ 5. VERIFY: Run full test suite
1668
+
1669
+ ## Hard Gates
1670
+ Before marking complete:
1671
+ - [ ] All new tests pass
1672
+ - [ ] No regressions
1673
+ - [ ] Type check passes
1674
+ - [ ] Linting passes`
1675
+ },
1676
+ {
1677
+ name: "finish",
1678
+ description: "Complete a task with quality gates",
1679
+ category: "core",
1680
+ usage: "/finish [task-id]",
1681
+ examples: ["/finish", "/finish task-001"],
1682
+ content: `Complete the current task with mandatory quality checks.
1683
+
1684
+ ## Hard Gates (Must ALL Pass)
1685
+ 1. \`npm run typecheck\` - No type errors
1686
+ 2. \`npm run test\` - All tests pass
1687
+ 3. \`npm run lint\` - No linting errors
1688
+ 4. \`npm run build\` - Build succeeds
1689
+
1690
+ ## Workflow
1691
+ 1. Run all quality gates
1692
+ 2. If any fail, report issues and stop
1693
+ 3. If all pass, update task status to "completed"
1694
+ 4. Create summary of changes
1695
+ 5. Suggest commit message`
1696
+ },
1697
+ {
1698
+ name: "handoff",
1699
+ description: "Create handoff bundle for session continuity",
1700
+ category: "core",
1701
+ usage: "/handoff",
1702
+ examples: ["/handoff"],
1703
+ content: `Create a handoff bundle for context transfer to next session.
1704
+
1705
+ ## Workflow
1706
+ 1. Summarize current progress
1707
+ 2. Document:
1708
+ - What was completed
1709
+ - What remains
1710
+ - Current blockers
1711
+ - Key decisions made
1712
+ 3. Save to memory/handoffs/[timestamp].md
1713
+
1714
+ ## Output Format
1715
+ \`\`\`markdown
1716
+ # Handoff: [Date/Time]
1717
+
1718
+ ## Completed
1719
+ - List of completed items
1720
+
1721
+ ## In Progress
1722
+ - Current work state
1723
+
1724
+ ## Remaining
1725
+ - What still needs to be done
1726
+
1727
+ ## Context
1728
+ - Important context for next session
1729
+
1730
+ ## Next Steps
1731
+ - Recommended actions
1732
+ \`\`\``
1733
+ },
1734
+ {
1735
+ name: "resume",
1736
+ description: "Resume from last handoff",
1737
+ category: "core",
1738
+ usage: "/resume",
1739
+ examples: ["/resume"],
1740
+ content: `Resume work from the most recent handoff.
1741
+
1742
+ ## Workflow
1743
+ 1. Load latest handoff from memory/handoffs/
1744
+ 2. Display summary to user
1745
+ 3. Propose next actions
1746
+ 4. Continue from where left off`
1747
+ },
1748
+ // Quick Actions
1749
+ {
1750
+ name: "fix",
1751
+ description: "Quick fix for an issue",
1752
+ category: "quick",
1753
+ usage: "/fix <issue description>",
1754
+ examples: ["/fix button not clickable", "/fix type error in auth.ts"],
1755
+ content: `Quick fix with minimal ceremony.
1756
+
1757
+ ## Workflow
1758
+ 1. Identify the issue
1759
+ 2. Make minimal change to fix
1760
+ 3. Verify fix works
1761
+ 4. Run affected tests`
1762
+ },
1763
+ {
1764
+ name: "fix-types",
1765
+ description: "Fix TypeScript type errors",
1766
+ category: "quick",
1767
+ usage: "/fix-types [file]",
1768
+ examples: ["/fix-types", "/fix-types src/auth.ts"],
1769
+ content: `Fix TypeScript type errors systematically.
1770
+
1771
+ ## Workflow
1772
+ 1. Run \`npm run typecheck\`
1773
+ 2. Parse error output
1774
+ 3. Fix each error in dependency order
1775
+ 4. Verify all types pass`
1776
+ },
1777
+ {
1778
+ name: "fix-ci",
1779
+ description: "Fix CI/CD pipeline failures",
1780
+ category: "quick",
1781
+ usage: "/fix-ci",
1782
+ examples: ["/fix-ci"],
1783
+ content: `Diagnose and fix CI failures.
1784
+
1785
+ ## Workflow
1786
+ 1. Check CI logs for errors
1787
+ 2. Reproduce locally if possible
1788
+ 3. Fix issues in order:
1789
+ - Type errors
1790
+ - Test failures
1791
+ - Lint errors
1792
+ - Build errors
1793
+ 4. Verify full CI pipeline locally`
1794
+ },
1795
+ {
1796
+ name: "commit",
1797
+ description: "Create a well-formatted commit",
1798
+ category: "quick",
1799
+ usage: "/commit [message]",
1800
+ examples: ["/commit", '/commit "feat: add login"'],
1801
+ content: `Create a conventional commit.
1802
+
1803
+ ## Workflow
1804
+ 1. Stage changes: \`git add -A\`
1805
+ 2. Generate commit message following conventional commits:
1806
+ - feat: New feature
1807
+ - fix: Bug fix
1808
+ - docs: Documentation
1809
+ - refactor: Code refactoring
1810
+ - test: Adding tests
1811
+ - chore: Maintenance
1812
+ 3. Commit with message`
1813
+ },
1814
+ {
1815
+ name: "pr",
1816
+ description: "Create a pull request",
1817
+ category: "quick",
1818
+ usage: "/pr [title]",
1819
+ examples: ["/pr", '/pr "Add user authentication"'],
1820
+ content: `Create a pull request with proper description.
1821
+
1822
+ ## Workflow
1823
+ 1. Push current branch
1824
+ 2. Generate PR description:
1825
+ - Summary of changes
1826
+ - Related issues
1827
+ - Testing done
1828
+ - Screenshots if UI changes
1829
+ 3. Create PR via GitHub CLI or provide URL`
1830
+ },
1831
+ // Research & Analysis
1832
+ {
1833
+ name: "research",
1834
+ description: "Deep research on a topic",
1835
+ category: "research",
1836
+ usage: "/research <topic>",
1837
+ examples: ["/research React Server Components", "/research OAuth 2.0 best practices"],
1838
+ content: `Conduct thorough research and document findings.
1839
+
1840
+ ## Workflow
1841
+ 1. Search documentation and resources
1842
+ 2. Find code examples and patterns
1843
+ 3. Evaluate options and trade-offs
1844
+ 4. Document findings in memory/research/
1845
+
1846
+ ## Output
1847
+ - Summary of findings
1848
+ - Recommended approach
1849
+ - Code examples
1850
+ - Links to resources`
1851
+ },
1852
+ {
1853
+ name: "analyze-project",
1854
+ description: "Analyze project structure and patterns",
1855
+ category: "research",
1856
+ usage: "/analyze-project",
1857
+ examples: ["/analyze-project"],
1858
+ content: `Comprehensive project analysis.
1859
+
1860
+ ## Workflow
1861
+ 1. Scan directory structure
1862
+ 2. Identify:
1863
+ - Tech stack
1864
+ - Architecture patterns
1865
+ - Key dependencies
1866
+ - Coding conventions
1867
+ 3. Document findings in AGENTS.md`
1868
+ },
1869
+ {
1870
+ name: "review-codebase",
1871
+ description: "Review codebase quality",
1872
+ category: "research",
1873
+ usage: "/review-codebase [path]",
1874
+ examples: ["/review-codebase", "/review-codebase src/"],
1875
+ content: `Review codebase for quality issues.
1876
+
1877
+ ## Workflow
1878
+ 1. Check code quality metrics
1879
+ 2. Identify:
1880
+ - Code smells
1881
+ - Security issues
1882
+ - Performance concerns
1883
+ - Test coverage gaps
1884
+ 3. Prioritize findings
1885
+ 4. Suggest improvements`
1886
+ },
1887
+ // Design & Planning
1888
+ {
1889
+ name: "design",
1890
+ description: "Design a feature or system",
1891
+ category: "design",
1892
+ usage: "/design <feature>",
1893
+ examples: ["/design notification system", "/design API gateway"],
1894
+ content: `Design a feature with thorough planning.
1895
+
1896
+ ## Workflow
1897
+ 1. Requirements gathering (Socratic questioning)
1898
+ 2. Research existing solutions
1899
+ 3. Design options with trade-offs
1900
+ 4. Choose approach
1901
+ 5. Document design in memory/
1902
+
1903
+ ## Output
1904
+ - Design document
1905
+ - Architecture diagrams (described)
1906
+ - API contracts
1907
+ - Data models`
1908
+ },
1909
+ {
1910
+ name: "brainstorm",
1911
+ description: "Brainstorm ideas for a problem",
1912
+ category: "design",
1913
+ usage: "/brainstorm <problem>",
1914
+ examples: ["/brainstorm user retention", "/brainstorm performance optimization"],
1915
+ content: `Collaborative brainstorming session.
1916
+
1917
+ ## Workflow
1918
+ 1. Define the problem clearly
1919
+ 2. Generate diverse ideas (no judgement)
1920
+ 3. Group related ideas
1921
+ 4. Evaluate feasibility
1922
+ 5. Select top candidates`
1923
+ },
1924
+ // Git & Version Control
1925
+ {
1926
+ name: "branch",
1927
+ description: "Create a new feature branch",
1928
+ category: "git",
1929
+ usage: "/branch <name>",
1930
+ examples: ["/branch feat/auth", "/branch fix/navigation-bug"],
1931
+ content: `Create and switch to a new branch.
1932
+
1933
+ ## Workflow
1934
+ 1. Ensure clean working directory
1935
+ 2. Pull latest main/master
1936
+ 3. Create branch with naming convention:
1937
+ - feat/* for features
1938
+ - fix/* for bug fixes
1939
+ - refactor/* for refactoring
1940
+ - docs/* for documentation
1941
+ 4. Switch to new branch`
1942
+ },
1943
+ {
1944
+ name: "merge",
1945
+ description: "Merge current branch to target",
1946
+ category: "git",
1947
+ usage: "/merge [target]",
1948
+ examples: ["/merge", "/merge main"],
1949
+ content: `Merge current branch to target.
1950
+
1951
+ ## Workflow
1952
+ 1. Run quality gates first
1953
+ 2. Commit any pending changes
1954
+ 3. Switch to target branch
1955
+ 4. Pull latest
1956
+ 5. Merge feature branch
1957
+ 6. Resolve conflicts if any
1958
+ 7. Push`
1959
+ },
1960
+ // Utilities
1961
+ {
1962
+ name: "status",
1963
+ description: "Show current status overview",
1964
+ category: "utility",
1965
+ usage: "/status",
1966
+ examples: ["/status"],
1967
+ content: `Display comprehensive status.
1968
+
1969
+ ## Shows
1970
+ - Current task (from Beads)
1971
+ - Git status
1972
+ - Active branch
1973
+ - Pending changes
1974
+ - Test status
1975
+ - Recent activity`
1976
+ },
1977
+ {
1978
+ name: "help",
1979
+ description: "Show available commands",
1980
+ category: "utility",
1981
+ usage: "/help [command]",
1982
+ examples: ["/help", "/help plan"],
1983
+ content: `Display help information.
1984
+
1985
+ If no command specified, list all available commands.
1986
+ If command specified, show detailed help for that command.`
1987
+ },
1988
+ {
1989
+ name: "analyze-figma",
1990
+ description: "Analyze Figma design and extract design tokens using Figma API",
1991
+ category: "design",
1992
+ usage: "/analyze-figma <figma-url>",
1993
+ examples: [
1994
+ "/analyze-figma https://www.figma.com/design/...",
1995
+ "/analyze-figma [figma-url]"
1996
+ ],
1997
+ content: `Analyze a Figma design and extract all design tokens automatically using Figma API.
1998
+
1999
+ ## Workflow
2000
+
2001
+ **Step 1: Extract URL from User Input**
2002
+
2003
+ The Figma URL is provided in the SAME message as the command. Extract it:
2004
+ - Check the full user input message
2005
+ - Look for URL pattern: \`https://www.figma.com/design/...\` or \`http://www.figma.com/design/...\`
2006
+ - Extract the ENTIRE URL including all query parameters
2007
+ - If URL not found in current message, check previous messages
2008
+
2009
+ **Step 2: Check Tool Configuration**
2010
+
2011
+ Before calling the tool, verify that Figma tool is configured:
2012
+ - If not configured, inform user to run: \`aikit skills figma-analysis config\`
2013
+ - The tool requires a Figma Personal Access Token
2014
+
2015
+ **Step 3: Call MCP Tool**
2016
+
2017
+ **CRITICAL**: You MUST use the MCP tool \`tool_read_figma_design\`, NOT web fetch!
2018
+
2019
+ **The correct tool name is**: \`tool_read_figma_design\` (exposed via MCP)
2020
+
2021
+ **DO NOT use**:
2022
+ - \u274C \`read_figma_design\` (wrong - missing "tool_" prefix)
2023
+ - \u274C \`figma-analysis/read_figma_design\` (wrong format)
2024
+ - \u274C Web fetch (file requires authentication)
2025
+
2026
+ **DO use**:
2027
+ - \u2705 \`tool_read_figma_design\` (correct MCP tool name)
2028
+
2029
+ Use the MCP tool:
2030
+ \`\`\`
2031
+ Use MCP tool: tool_read_figma_design
2032
+ Arguments: { "url": "[extracted URL]" }
2033
+ \`\`\`
2034
+
2035
+ The tool has the Figma API token configured and will authenticate automatically.
2036
+
2037
+ This tool will:
2038
+ 1. Validate the Figma URL format
2039
+ 2. Check if Figma tool is configured
2040
+ 3. Call Figma API to fetch design data
2041
+ 4. Extract design tokens:
2042
+ - Colors (from fills and strokes)
2043
+ - Typography (font families, sizes, weights, line heights)
2044
+ - Spacing system (8px grid detection)
2045
+ - Components (from Figma components)
2046
+ - Screens/Frames (dimensions and names)
2047
+ - Breakpoints (common responsive breakpoints)
2048
+ 5. Return formatted markdown with all extracted tokens
2049
+
2050
+ **Step 4: Format and Save**
2051
+
2052
+ Format extracted tokens as structured markdown:
2053
+ \`\`\`markdown
2054
+ # Figma Design Analysis
2055
+
2056
+ **Source**: [Figma URL]
2057
+ **Analyzed**: [Date]
2058
+
2059
+ ## Screens/Pages
2060
+ - [List all screens]
2061
+
2062
+ ## Color Palette
2063
+ ### Primary Colors
2064
+ - Color Name: #hexcode
2065
+ [Continue for all colors]
2066
+
2067
+ ## Typography
2068
+ ### Font Families
2069
+ - Primary: Font Name
2070
+ [Continue]
2071
+
2072
+ ### Font Sizes
2073
+ - Heading 1: 48px
2074
+ [Continue for all sizes]
2075
+
2076
+ ## Spacing System
2077
+ - Base unit: 8px
2078
+ - Values used: [list]
2079
+
2080
+ ## Component Structure
2081
+ - Header: [description]
2082
+ [Continue for all components]
2083
+
2084
+ ## Layout Grid
2085
+ - Container max-width: [value]
2086
+ - Columns: [value]
2087
+
2088
+ ## Responsive Breakpoints
2089
+ - Mobile: [range]
2090
+ - Tablet: [range]
2091
+ - Desktop: [range]
2092
+
2093
+ ## Assets Needed
2094
+ ### Images
2095
+ - [List]
2096
+
2097
+ ### Icons
2098
+ - [List]
2099
+
2100
+ ### Fonts
2101
+ - [List]
2102
+ \`\`\`
2103
+
2104
+ Save to memory using memory-update tool:
2105
+ \`\`\`
2106
+ Use tool: memory-update
2107
+ Arguments: {
2108
+ "key": "research/figma-analysis",
2109
+ "content": "[formatted markdown]"
2110
+ }
2111
+ \`\`\`
2112
+
2113
+ **Step 5: Report Results**
2114
+
2115
+ Summarize what was extracted:
2116
+ - Number of colors found
2117
+ - Number of typography styles
2118
+ - Number of components
2119
+ - Number of screens/frames
2120
+ - Confirm save location: \`memory/research/figma-analysis.md\`
2121
+
2122
+ ## Important Notes
2123
+
2124
+ - **DO NOT** ask user to provide URL again - extract it from input
2125
+ - **DO NOT** wait - start immediately after extracting URL
2126
+ - The URL is in the SAME message as the command
2127
+ - The tool uses Figma API, so the file must be accessible with your API token
2128
+ - If the tool returns an error about configuration, guide user to run: \`aikit skills figma-analysis config\`
2129
+ - If the tool returns an error about access, verify the file is accessible with your token
2130
+
2131
+ The analysis will be saved automatically for later reference.`
2132
+ },
2133
+ {
2134
+ name: "refactor",
2135
+ description: "Refactor code to improve structure without changing behavior",
2136
+ category: "quick",
2137
+ usage: "/refactor [file or pattern]",
2138
+ examples: ["/refactor src/utils.ts", "/refactor duplicate code"],
2139
+ content: `Refactor code following best practices.
2140
+
2141
+ ## Workflow
2142
+ 1. Ensure tests are in place
2143
+ 2. Identify refactoring opportunities
2144
+ 3. Apply refactoring incrementally
2145
+ 4. Run tests after each change
2146
+ 5. Verify no behavior changes`
2147
+ },
2148
+ {
2149
+ name: "test",
2150
+ description: "Run tests and show results",
2151
+ category: "utility",
2152
+ usage: "/test [pattern]",
2153
+ examples: ["/test", "/test auth", "/test --watch"],
2154
+ content: `Run test suite and display results.
2155
+
2156
+ ## Workflow
2157
+ 1. Run test command: \`npm run test\`
2158
+ 2. Parse and display results
2159
+ 3. Show coverage if available
2160
+ 4. Highlight failures
2161
+ 5. Suggest fixes for failures`
2162
+ },
2163
+ {
2164
+ name: "lint",
2165
+ description: "Run linter and fix issues",
2166
+ category: "quick",
2167
+ usage: "/lint [--fix]",
2168
+ examples: ["/lint", "/lint --fix"],
2169
+ content: `Run linter and optionally fix issues.
2170
+
2171
+ ## Workflow
2172
+ 1. Run linter: \`npm run lint\`
2173
+ 2. Parse errors and warnings
2174
+ 3. If --fix flag, run auto-fix
2175
+ 4. Report remaining issues
2176
+ 5. Suggest manual fixes if needed`
2177
+ },
2178
+ {
2179
+ name: "deploy",
2180
+ description: "Deploy application to production",
2181
+ category: "utility",
2182
+ usage: "/deploy [environment]",
2183
+ examples: ["/deploy", "/deploy staging", "/deploy production"],
2184
+ content: `Deploy application with quality checks.
2185
+
2186
+ ## Workflow
2187
+ 1. Run quality gates (test, lint, build)
2188
+ 2. Check for uncommitted changes
2189
+ 3. Build production bundle
2190
+ 4. Deploy to target environment
2191
+ 5. Verify deployment success`
2192
+ },
2193
+ {
2194
+ name: "rollback",
2195
+ description: "Rollback to previous deployment",
2196
+ category: "utility",
2197
+ usage: "/rollback [version]",
2198
+ examples: ["/rollback", "/rollback v1.2.3"],
2199
+ content: `Rollback to previous version.
2200
+
2201
+ ## Workflow
2202
+ 1. Identify current version
2203
+ 2. List available versions
2204
+ 3. Confirm rollback target
2205
+ 4. Execute rollback
2206
+ 5. Verify rollback success`
2207
+ },
2208
+ {
2209
+ name: "logs",
2210
+ description: "View application logs",
2211
+ category: "utility",
2212
+ usage: "/logs [--tail] [--follow]",
2213
+ examples: ["/logs", "/logs --tail 100", "/logs --follow"],
2214
+ content: `View and filter application logs.
2215
+
2216
+ ## Workflow
2217
+ 1. Determine log location
2218
+ 2. Apply filters if specified
2219
+ 3. Display logs
2220
+ 4. If --follow, stream updates
2221
+ 5. Format for readability`
2222
+ }
2223
+ ];
2224
+ var CommandRunner = class {
2225
+ config;
2226
+ commandsCache = /* @__PURE__ */ new Map();
2227
+ constructor(config) {
2228
+ this.config = config;
2229
+ }
2230
+ /**
2231
+ * List all available commands
2232
+ */
2233
+ async listCommands() {
2234
+ const commands = [];
2235
+ for (const cmd of DEFAULT_COMMANDS) {
2236
+ commands.push({
2237
+ ...cmd,
2238
+ filePath: "built-in"
2239
+ });
2240
+ }
2241
+ const globalCommandsPath = paths.commands(paths.globalConfig());
2242
+ try {
2243
+ const globalCommands = await this.loadCommandsFromDir(globalCommandsPath);
2244
+ commands.push(...globalCommands);
2245
+ } catch {
2246
+ }
2247
+ const projectCommandsPath = paths.commands(this.config.configPath);
2248
+ if (projectCommandsPath !== globalCommandsPath) {
2249
+ try {
2250
+ const projectCommands = await this.loadCommandsFromDir(projectCommandsPath);
2251
+ for (const cmd of projectCommands) {
2252
+ const existingIndex = commands.findIndex((c) => c.name === cmd.name);
2253
+ if (existingIndex >= 0) {
2254
+ commands[existingIndex] = cmd;
2255
+ } else {
2256
+ commands.push(cmd);
2257
+ }
2258
+ }
2259
+ } catch {
2260
+ }
2261
+ }
2262
+ return commands.sort((a, b) => a.name.localeCompare(b.name));
2263
+ }
2264
+ /**
2265
+ * Get a specific command
2266
+ */
2267
+ async getCommand(name) {
2268
+ if (this.commandsCache.has(name)) {
2269
+ return this.commandsCache.get(name);
2270
+ }
2271
+ const commands = await this.listCommands();
2272
+ const command = commands.find((c) => c.name === name);
2273
+ if (command) {
2274
+ this.commandsCache.set(name, command);
2275
+ }
2276
+ return command || null;
2277
+ }
2278
+ /**
2279
+ * Create a custom command
2280
+ */
2281
+ async createCommand(name, options) {
2282
+ const configPath = options?.global ? paths.globalConfig() : this.config.configPath;
2283
+ const category = options?.category || "utility";
2284
+ const commandsDir = join4(paths.commands(configPath), category);
2285
+ await mkdir2(commandsDir, { recursive: true });
2286
+ const fileName = `${name}.md`;
2287
+ const filePath = join4(commandsDir, fileName);
2288
+ const frontmatter = {
2289
+ name,
2290
+ description: options?.description || `Custom command: ${name}`,
2291
+ category,
2292
+ usage: options?.usage || `/${name}`,
2293
+ examples: options?.examples || [`/${name}`]
2294
+ };
2295
+ const content = options?.content || `## /${name}
2296
+
2297
+ Describe what this command does.
2298
+
2299
+ ## Workflow
2300
+ 1. Step 1
2301
+ 2. Step 2
2302
+ 3. Step 3
2303
+ `;
2304
+ const fileContent = matter2.stringify(content, frontmatter);
2305
+ await writeFile2(filePath, fileContent);
2306
+ const command = {
2307
+ name,
2308
+ description: frontmatter.description,
2309
+ category,
2310
+ usage: frontmatter.usage,
2311
+ examples: frontmatter.examples,
2312
+ content,
2313
+ filePath
2314
+ };
2315
+ this.commandsCache.set(name, command);
2316
+ return command;
2317
+ }
2318
+ /**
2319
+ * Format command for agent consumption
2320
+ */
2321
+ formatForAgent(command) {
2322
+ return `# Command: /${command.name}
2323
+
2324
+ ## Usage
2325
+ \`${command.usage}\`
2326
+
2327
+ ## Description
2328
+ ${command.description}
2329
+
2330
+ ## Examples
2331
+ ${command.examples.map((e) => `- \`${e}\``).join("\n")}
2332
+
2333
+ ## Workflow
2334
+ ${command.content}
2335
+ `;
2336
+ }
2337
+ /**
2338
+ * Load commands from directory (recursively)
2339
+ */
2340
+ async loadCommandsFromDir(dir) {
2341
+ const commands = [];
2342
+ const processDir = async (currentDir, category) => {
2343
+ try {
2344
+ const entries = await readdir2(currentDir, { withFileTypes: true });
2345
+ for (const entry of entries) {
2346
+ const fullPath = join4(currentDir, entry.name);
2347
+ if (entry.isDirectory()) {
2348
+ await processDir(fullPath, entry.name);
2349
+ } else if (extname2(entry.name) === ".md") {
2350
+ const content = await readFile3(fullPath, "utf-8");
2351
+ const { data, content: body } = matter2(content);
2352
+ const frontmatter = data;
2353
+ const name = frontmatter.name || basename2(entry.name, ".md");
2354
+ commands.push({
2355
+ name,
2356
+ description: frontmatter.description || "",
2357
+ category: frontmatter.category || category,
2358
+ usage: frontmatter.usage || `/${name}`,
2359
+ examples: frontmatter.examples || [],
2360
+ content: body.trim(),
2361
+ filePath: fullPath
2362
+ });
2363
+ }
2364
+ }
2365
+ } catch {
2366
+ return;
2367
+ }
2368
+ };
2369
+ await processDir(dir, "custom");
2370
+ return commands;
2371
+ }
2372
+ };
2373
+
2374
+ // src/core/tools.ts
2375
+ init_esm_shims();
2376
+ init_paths();
2377
+ init_logger();
2378
+ import { readdir as readdir4, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
2379
+ import { join as join8, extname as extname3 } from "path";
2380
+ import { z as z2 } from "zod";
2381
+ var ToolArgSchema = z2.object({
2382
+ type: z2.enum(["string", "number", "boolean", "array", "object"]),
2383
+ description: z2.string(),
2384
+ required: z2.boolean().optional().default(true),
2385
+ default: z2.any().optional()
2386
+ });
2387
+ var BUILT_IN_TOOLS = [
2388
+ {
2389
+ name: "websearch",
2390
+ description: "Search the web for documentation, articles, and current information",
2391
+ args: {
2392
+ query: {
2393
+ type: "string",
2394
+ description: "The search query",
2395
+ required: true
2396
+ },
2397
+ numResults: {
2398
+ type: "number",
2399
+ description: "Number of results to return (default: 5)",
2400
+ required: false,
2401
+ default: 5
2402
+ }
2403
+ },
2404
+ async execute({ query }) {
2405
+ return `Web search results for: "${query}"
2406
+
2407
+ Note: Configure a search provider in AIKit settings for actual results.`;
2408
+ }
2409
+ },
2410
+ {
2411
+ name: "codesearch",
2412
+ description: "Search GitHub for code patterns and examples across millions of repositories",
2413
+ args: {
2414
+ query: {
2415
+ type: "string",
2416
+ description: "The code pattern or search query",
2417
+ required: true
2418
+ },
2419
+ language: {
2420
+ type: "string",
2421
+ description: "Programming language to filter by",
2422
+ required: false
2423
+ }
2424
+ },
2425
+ async execute({ query, language }) {
2426
+ const langFilter = language ? ` in ${language}` : "";
2427
+ return `GitHub code search for: "${query}"${langFilter}
2428
+
2429
+ Note: Configure GitHub integration in AIKit settings for actual results.`;
2430
+ }
2431
+ },
2432
+ {
2433
+ name: "memory-read",
2434
+ description: "Read from persistent memory (project or global)",
2435
+ args: {
2436
+ key: {
2437
+ type: "string",
2438
+ description: "The memory key to read",
2439
+ required: true
2440
+ }
2441
+ },
2442
+ async execute({ key }) {
2443
+ return `Memory read: ${key}`;
2444
+ }
2445
+ },
2446
+ {
2447
+ name: "memory-update",
2448
+ description: "Update persistent memory with new information",
2449
+ args: {
2450
+ key: {
2451
+ type: "string",
2452
+ description: "The memory key to update",
2453
+ required: true
2454
+ },
2455
+ content: {
2456
+ type: "string",
2457
+ description: "The content to write",
2458
+ required: true
2459
+ },
2460
+ append: {
2461
+ type: "boolean",
2462
+ description: "Whether to append to existing content (default: true)",
2463
+ required: false,
2464
+ default: true
2465
+ }
2466
+ },
2467
+ async execute({ key, append = true }) {
2468
+ return `Memory updated: ${key} (append: ${append})`;
2469
+ }
2470
+ },
2471
+ {
2472
+ name: "find_skills",
2473
+ description: "Find available workflow skills",
2474
+ args: {
2475
+ query: {
2476
+ type: "string",
2477
+ description: "Optional search query to filter skills",
2478
+ required: false
2479
+ }
2480
+ },
2481
+ async execute({ query }) {
2482
+ return `Skills matching: ${query || "all"}`;
2483
+ }
2484
+ },
2485
+ {
2486
+ name: "use_skill",
2487
+ description: "Load and use a specific skill workflow",
2488
+ args: {
2489
+ name: {
2490
+ type: "string",
2491
+ description: "Name of the skill to use",
2492
+ required: true
2493
+ }
2494
+ },
2495
+ async execute({ name }) {
2496
+ return `Loading skill: ${name}`;
2497
+ }
2498
+ },
2499
+ {
2500
+ name: "read_figma_design",
2501
+ description: "Read and analyze a Figma design using Figma API. Extracts design tokens including colors, typography, spacing, components, and layout.",
2502
+ args: {
2503
+ url: {
2504
+ type: "string",
2505
+ description: "Figma design URL to analyze (must start with https://www.figma.com/design/)",
2506
+ required: true
2507
+ }
2508
+ },
2509
+ async execute({ url }, context) {
2510
+ if (!url || typeof url !== "string") {
2511
+ return "Error: Invalid URL provided";
2512
+ }
2513
+ if (!url.startsWith("https://www.figma.com/design/") && !url.startsWith("http://www.figma.com/design/")) {
2514
+ return `Error: Invalid Figma URL format. URL must start with https://www.figma.com/design/
2515
+
2516
+ Provided URL: ${url}`;
2517
+ }
2518
+ const configManager = context?.toolConfigManager;
2519
+ if (!configManager) {
2520
+ return `Error: Tool configuration manager not available. This usually means the MCP server isn't properly initialized. Please restart OpenCode.
2521
+
2522
+ If the issue persists, configure Figma tool manually: aikit skills figma-analysis config`;
2523
+ }
2524
+ const isReady = await configManager.isToolReady("figma-analysis");
2525
+ if (!isReady) {
2526
+ const toolConfig = await configManager.getToolConfig("figma-analysis");
2527
+ if (toolConfig?.status === "error") {
2528
+ return `Error: Figma tool configuration error: ${toolConfig.errorMessage || "Unknown error"}
2529
+
2530
+ Please reconfigure: aikit skills figma-analysis config`;
2531
+ }
2532
+ return `Error: Figma tool is not configured. Please run: aikit skills figma-analysis config
2533
+
2534
+ This will guide you through setting up your Figma Personal Access Token.`;
2535
+ }
2536
+ const apiKey = await configManager.getApiKey("figma-analysis");
2537
+ if (!apiKey) {
2538
+ return `Error: Figma API key not found. Please run: aikit skills figma-analysis config`;
2539
+ }
2540
+ try {
2541
+ const { FigmaMcpClient: FigmaMcpClient2 } = await Promise.resolve().then(() => (init_figma_mcp(), figma_mcp_exports));
2542
+ const client = new FigmaMcpClient2(apiKey, configManager);
2543
+ const assetsDir = "./assets/images";
2544
+ const tokens = await client.extractDesignTokens(url, false, assetsDir);
2545
+ let result = `# Figma Design Analysis
2546
+
2547
+ `;
2548
+ result += `**URL**: ${url}
2549
+
2550
+ `;
2551
+ result += `## Design Structure & Content
2552
+
2553
+ `;
2554
+ if (tokens.structure) {
2555
+ result += `### Node Hierarchy (${tokens.structure.nodes.length} nodes)
2556
+
2557
+ `;
2558
+ result += `\`\`\`
2559
+ ${tokens.structure.hierarchy}
2560
+ \`\`\`
2561
+
2562
+ `;
2563
+ const textNodes = tokens.structure.nodes.filter((n) => n.type === "TEXT" && n.content);
2564
+ if (textNodes.length > 0) {
2565
+ result += `### Text Content (${textNodes.length} text elements)
2566
+
2567
+ `;
2568
+ textNodes.slice(0, 20).forEach((node) => {
2569
+ const preview = node.content && node.content.length > 100 ? node.content.substring(0, 100) + "..." : node.content;
2570
+ result += `- **${node.name}**: "${preview}"
2571
+ `;
2572
+ if (node.styles) {
2573
+ result += ` - Style: ${node.styles.fontFamily || "N/A"} ${node.styles.fontSize || "N/A"}px, weight ${node.styles.fontWeight || "N/A"}
2574
+ `;
2575
+ }
2576
+ });
2577
+ if (textNodes.length > 20) {
2578
+ result += `
2579
+ ... and ${textNodes.length - 20} more text elements
2580
+ `;
2581
+ }
2582
+ result += `
2583
+ `;
2584
+ }
2585
+ const frameNodes = tokens.structure.nodes.filter((n) => n.type === "FRAME" || n.type === "COMPONENT");
2586
+ if (frameNodes.length > 0) {
2587
+ result += `### Layout Structure (${frameNodes.length} frames/components)
2588
+
2589
+ `;
2590
+ frameNodes.slice(0, 15).forEach((node) => {
2591
+ result += `- **${node.name}** (${node.type})
2592
+ `;
2593
+ if (node.position) {
2594
+ result += ` - Position: x=${Math.round(node.position.x)}, y=${Math.round(node.position.y)}
2595
+ `;
2596
+ result += ` - Size: ${Math.round(node.position.width)}\xD7${Math.round(node.position.height)}px
2597
+ `;
2598
+ }
2599
+ if (node.styles?.layout) {
2600
+ result += ` - Layout: ${node.styles.layout}${node.styles.gap ? `, gap: ${node.styles.gap}px` : ""}
2601
+ `;
2602
+ }
2603
+ if (node.children && node.children.length > 0) {
2604
+ result += ` - Children: ${node.children.length}
2605
+ `;
2606
+ }
2607
+ });
2608
+ if (frameNodes.length > 15) {
2609
+ result += `
2610
+ ... and ${frameNodes.length - 15} more frames/components
2611
+ `;
2612
+ }
2613
+ result += `
2614
+ `;
2615
+ }
2616
+ }
2617
+ result += `## Design Tokens
2618
+
2619
+ `;
2620
+ if (tokens.colors.length > 0) {
2621
+ result += `### Colors (${tokens.colors.length} found)
2622
+
2623
+ `;
2624
+ tokens.colors.slice(0, 30).forEach((color) => {
2625
+ result += `- \`${color.hex}\`
2626
+ `;
2627
+ });
2628
+ if (tokens.colors.length > 30) {
2629
+ result += `
2630
+ ... and ${tokens.colors.length - 30} more colors
2631
+ `;
2632
+ }
2633
+ result += `
2634
+ `;
2635
+ }
2636
+ if (tokens.typography.length > 0) {
2637
+ result += `### Typography (${tokens.typography.length} styles)
2638
+
2639
+ `;
2640
+ tokens.typography.forEach((typography) => {
2641
+ result += `- **${typography.name}**: ${typography.fontFamily}, ${typography.fontSize}px, weight ${typography.fontWeight}, line-height ${typography.lineHeight}px
2642
+ `;
2643
+ });
2644
+ result += `
2645
+ `;
2646
+ }
2647
+ result += `### Spacing System
2648
+
2649
+ `;
2650
+ result += `- Base unit: ${tokens.spacing.unit}px
2651
+ `;
2652
+ result += `- Scale: ${tokens.spacing.scale.length > 0 ? tokens.spacing.scale.join(", ") : "Not detected"}
2653
+
2654
+ `;
2655
+ if (tokens.components.length > 0) {
2656
+ result += `### Components (${tokens.components.length} found)
2657
+
2658
+ `;
2659
+ tokens.components.forEach((component) => {
2660
+ result += `- **${component.name}**: ${component.type}${component.description ? ` - ${component.description}` : ""}
2661
+ `;
2662
+ });
2663
+ result += `
2664
+ `;
2665
+ }
2666
+ if (tokens.screens.length > 0) {
2667
+ result += `## Available Screens/Frames (${tokens.screens.length} found)
2668
+
2669
+ `;
2670
+ result += `**Please confirm which screen(s) you want to develop:**
2671
+
2672
+ `;
2673
+ tokens.screens.forEach((screen, index) => {
2674
+ result += `${index + 1}. **${screen.name}**
2675
+ `;
2676
+ result += ` - Size: ${screen.width}\xD7${screen.height}px
2677
+ `;
2678
+ result += ` - Type: ${screen.type}
2679
+ `;
2680
+ if (screen.childrenCount) {
2681
+ result += ` - Components: ${screen.childrenCount}
2682
+ `;
2683
+ }
2684
+ result += ` - ID: \`${screen.id}\`
2685
+
2686
+ `;
2687
+ });
2688
+ result += `
2689
+ **To proceed, simply reply with the screen number(s) or name(s) you want to develop.**
2690
+ `;
2691
+ result += `Example: "1" or "Main Page" or "1, 2, 3"
2692
+
2693
+ `;
2694
+ }
2695
+ result += `### Responsive Breakpoints
2696
+
2697
+ `;
2698
+ result += `- ${tokens.breakpoints.join("px, ")}px
2699
+
2700
+ `;
2701
+ if (tokens.assets && tokens.assets.length > 0) {
2702
+ result += `## Downloaded Assets (${tokens.assets.length} files)
2703
+
2704
+ `;
2705
+ result += `All assets have been downloaded to: \`${tokens.assets[0].path.split("/").slice(0, -1).join("/")}\`
2706
+
2707
+ `;
2708
+ result += `### Image Files
2709
+
2710
+ `;
2711
+ tokens.assets.forEach((asset) => {
2712
+ const relativePath = asset.path.replace(process.cwd() + "/", "");
2713
+ result += `- **${asset.nodeName}** (${asset.nodeType})
2714
+ `;
2715
+ result += ` - File: \`${relativePath}\`
2716
+ `;
2717
+ if (asset.width && asset.height) {
2718
+ result += ` - Size: ${Math.round(asset.width)}\xD7${Math.round(asset.height)}px
2719
+ `;
2720
+ }
2721
+ result += ` - Format: ${asset.format.toUpperCase()}
2722
+ `;
2723
+ result += ` - Usage: \`<img src="${relativePath}" alt="${asset.nodeName}" />\`
2724
+
2725
+ `;
2726
+ });
2727
+ }
2728
+ result += `## Implementation Guide
2729
+
2730
+ `;
2731
+ result += `### Structure Analysis
2732
+ `;
2733
+ result += `The design contains ${tokens.structure?.nodes.length || 0} nodes organized in a hierarchical structure.
2734
+ `;
2735
+ result += `Use the node hierarchy above to understand:
2736
+ `;
2737
+ result += `1. **Component structure** - How elements are organized
2738
+ `;
2739
+ result += `2. **Text content** - All text content from TEXT nodes
2740
+ `;
2741
+ result += `3. **Layout properties** - Flex direction, gaps, padding
2742
+ `;
2743
+ result += `4. **Positioning** - Exact x, y, width, height values
2744
+
2745
+ `;
2746
+ result += `### Next Steps
2747
+
2748
+ `;
2749
+ result += `1. Review the structure hierarchy to understand component organization
2750
+ `;
2751
+ result += `2. Extract text content from TEXT nodes for HTML content
2752
+ `;
2753
+ result += `3. Use position and size data for pixel-perfect CSS
2754
+ `;
2755
+ result += `4. Use layout properties (HORIZONTAL/VERTICAL) for flexbox/grid
2756
+ `;
2757
+ result += `5. Use extracted design tokens (colors, typography) for styling
2758
+ `;
2759
+ result += `6. Save this analysis: \`memory-update("research/figma-analysis", "[this analysis]")\`
2760
+ `;
2761
+ return result;
2762
+ } catch (error) {
2763
+ const errorMessage = error instanceof Error ? error.message : String(error);
2764
+ return `Error analyzing Figma design: ${errorMessage}
2765
+
2766
+ Please check:
2767
+ 1. The Figma URL is correct and accessible
2768
+ 2. Your Figma API token has proper permissions
2769
+ 3. The design file is shared with your account`;
2770
+ }
2771
+ }
2772
+ },
2773
+ {
2774
+ name: "analyze_figma",
2775
+ description: "Analyze a Figma design URL and extract all design tokens automatically. The URL should be provided in the user input after the command.",
2776
+ args: {
2777
+ url: {
2778
+ type: "string",
2779
+ description: "Figma design URL to analyze",
2780
+ required: true
2781
+ }
2782
+ },
2783
+ async execute({ url }) {
2784
+ return `Figma analysis tool called for: ${url}
2785
+
2786
+ Next steps:
2787
+ 1. Use @vision agent to analyze the design
2788
+ 2. Extract all design tokens
2789
+ 3. Save to memory/research/figma-analysis.md`;
2790
+ }
2791
+ },
2792
+ {
2793
+ name: "develop_figma_screen",
2794
+ description: "Smart workflow to develop a specific Figma screen: check current code, pull needed assets, plan, and develop. User just needs to confirm the screen number/name.",
2795
+ args: {
2796
+ figmaUrl: {
2797
+ type: "string",
2798
+ description: "Figma design URL",
2799
+ required: true
2800
+ },
2801
+ screenId: {
2802
+ type: "string",
2803
+ description: "Screen ID or name to develop (from read_figma_design output)",
2804
+ required: true
2805
+ }
2806
+ },
2807
+ async execute({ figmaUrl, screenId }, context) {
2808
+ const configManager = context?.toolConfigManager;
2809
+ if (!configManager) {
2810
+ return "Error: Tool configuration manager not available.";
2811
+ }
2812
+ const isReady = await configManager.isToolReady("figma-analysis");
2813
+ if (!isReady) {
2814
+ return "Error: Figma tool is not configured. Please run: aikit skills figma-analysis config";
2815
+ }
2816
+ const apiKey = await configManager.getApiKey("figma-analysis");
2817
+ if (!apiKey) {
2818
+ return "Error: Figma API key not found.";
2819
+ }
2820
+ try {
2821
+ const { FigmaMcpClient: FigmaMcpClient2 } = await Promise.resolve().then(() => (init_figma_mcp(), figma_mcp_exports));
2822
+ const { checkCurrentCodeStatus: checkCurrentCodeStatus2, compareCodeWithFigma: compareCodeWithFigma2 } = await Promise.resolve().then(() => (init_figma_screen_developer(), figma_screen_developer_exports));
2823
+ const client = new FigmaMcpClient2(apiKey, configManager);
2824
+ const tokens = await client.extractDesignTokens(figmaUrl, false, "./assets/images");
2825
+ const screenIdStr = String(screenId);
2826
+ const selectedScreen = tokens.screens?.find(
2827
+ (s) => s.id === screenIdStr || s.name.toLowerCase() === screenIdStr.toLowerCase()
2828
+ );
2829
+ if (!selectedScreen) {
2830
+ return `Error: Screen "${screenId}" not found. Available screens:
2831
+ ${tokens.screens?.map((s, i) => `${i + 1}. ${s.name} (ID: ${s.id})`).join("\n") || "None"}`;
2832
+ }
2833
+ const codeStatus = await checkCurrentCodeStatus2();
2834
+ const comparison = await compareCodeWithFigma2(tokens, selectedScreen.id);
2835
+ let downloadedAssets = [];
2836
+ const fileKey = client.extractFileKey(figmaUrl);
2837
+ if (fileKey) {
2838
+ try {
2839
+ const fileData = await client.getFileData(figmaUrl);
2840
+ const projectPath = process.cwd();
2841
+ const assetsDir = join8(projectPath, "assets", "images");
2842
+ const assets = await client.downloadAssets(fileKey, fileData.document, assetsDir, selectedScreen.id);
2843
+ downloadedAssets = assets || [];
2844
+ logger.info(`Downloaded ${downloadedAssets.length} assets for screen ${selectedScreen.name}`);
2845
+ } catch (error) {
2846
+ logger.warn(`Failed to download assets: ${error instanceof Error ? error.message : String(error)}`);
2847
+ downloadedAssets = [];
2848
+ }
2849
+ }
2850
+ let result = `# Development Plan for Screen: ${selectedScreen.name}
2851
+
2852
+ `;
2853
+ result += `## Current Code Status
2854
+
2855
+ `;
2856
+ result += `- HTML: ${codeStatus.hasHTML ? `\u2705 ${codeStatus.htmlFile}` : "\u274C Not found"}
2857
+ `;
2858
+ result += `- CSS: ${codeStatus.hasCSS ? `\u2705 ${codeStatus.cssFiles.length} files` : "\u274C Not found"}
2859
+ `;
2860
+ result += `- Assets: ${codeStatus.hasAssets ? `\u2705 ${codeStatus.assetCount} files` : "\u274C Not found"}
2861
+ `;
2862
+ result += `- Existing Sections: ${codeStatus.sections.length > 0 ? codeStatus.sections.join(", ") : "None"}
2863
+
2864
+ `;
2865
+ result += `## Comparison with Figma Design
2866
+
2867
+ `;
2868
+ result += `- Missing Sections: ${comparison.missingSections.length > 0 ? comparison.missingSections.join(", ") : "None"}
2869
+ `;
2870
+ result += `- Missing Assets: ${comparison.missingAssets.length > 0 ? comparison.missingAssets.join(", ") : "None"}
2871
+ `;
2872
+ result += `- Needs Update: ${comparison.needsUpdate ? "Yes" : "No"}
2873
+
2874
+ `;
2875
+ result += `## Recommendations
2876
+
2877
+ `;
2878
+ comparison.recommendations.forEach((rec, i) => {
2879
+ result += `${i + 1}. ${rec}
2880
+ `;
2881
+ });
2882
+ result += `
2883
+ `;
2884
+ result += `## Downloaded Assets (${downloadedAssets.length})
2885
+
2886
+ `;
2887
+ if (downloadedAssets.length > 0) {
2888
+ downloadedAssets.forEach((asset) => {
2889
+ const relativePath = asset.path.replace(process.cwd() + "/", "");
2890
+ result += `- **${asset.nodeName}** (${asset.nodeType})
2891
+ `;
2892
+ result += ` - File: \`${relativePath}\`
2893
+ `;
2894
+ if (asset.width && asset.height) {
2895
+ result += ` - Size: ${Math.round(asset.width)}\xD7${Math.round(asset.height)}px
2896
+ `;
2897
+ }
2898
+ result += ` - Usage: \`<img src="${relativePath}" alt="${asset.nodeName}" />\`
2899
+
2900
+ `;
2901
+ });
2902
+ result += `
2903
+ **\u26A0\uFE0F IMPORTANT: Use downloaded assets above instead of placeholder images!**
2904
+ `;
2905
+ result += `Replace all Unsplash/placeholder image URLs with the downloaded asset paths.
2906
+
2907
+ `;
2908
+ } else {
2909
+ result += `**No assets downloaded.** This may indicate:
2910
+ `;
2911
+ result += `1. The screen doesn't contain exportable image nodes
2912
+ `;
2913
+ result += `2. Assets download failed (check logs)
2914
+ `;
2915
+ result += `3. Figma API permissions issue
2916
+
2917
+ `;
2918
+ }
2919
+ result += `
2920
+ ## Next Steps
2921
+
2922
+ `;
2923
+ result += `1. Review the plan above
2924
+ `;
2925
+ result += `2. Use @build agent to implement missing sections
2926
+ `;
2927
+ result += `3. Use extracted design tokens for CSS variables
2928
+ `;
2929
+ result += `4. Use downloaded assets in HTML
2930
+ `;
2931
+ result += `5. Verify pixel-perfect match with Figma design
2932
+ `;
2933
+ return result;
2934
+ } catch (error) {
2935
+ return `Error: ${error instanceof Error ? error.message : String(error)}`;
2936
+ }
2937
+ }
2938
+ },
2939
+ {
2940
+ name: "list_session",
2941
+ description: "List previous sessions to discover what happened and when. Use this before read_session to find the right context.",
2942
+ args: {
2943
+ limit: {
2944
+ type: "number",
2945
+ description: "Maximum number of sessions to return (default: 10)",
2946
+ required: false,
2947
+ default: 10
2948
+ }
2949
+ },
2950
+ async execute({ limit = 10 }, context) {
2951
+ const config = context?.config;
2952
+ if (!config) {
2953
+ return "Error: Configuration not available";
2954
+ }
2955
+ const limitNum = typeof limit === "number" ? limit : 10;
2956
+ try {
2957
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
2958
+ const memory = new MemoryManager2(config);
2959
+ const memories = await memory.list();
2960
+ const handoffs = memories.filter((m) => m.type === "handoff").sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()).slice(0, limitNum);
2961
+ if (handoffs.length === 0) {
2962
+ return "No previous sessions found. Use /handoff to create a session handoff.";
2963
+ }
2964
+ let result = `# Previous Sessions (${handoffs.length})
2965
+
2966
+ `;
2967
+ handoffs.forEach((handoff, index) => {
2968
+ const sessionId = handoff.key.replace("handoffs/", "");
2969
+ result += `${index + 1}. **${sessionId}**
2970
+ `;
2971
+ result += ` - Updated: ${handoff.updatedAt.toLocaleString()}
2972
+ `;
2973
+ result += ` - Summary: ${handoff.summary}
2974
+
2975
+ `;
2976
+ });
2977
+ result += `
2978
+ Use \`read_session\` with a session ID to load full context.`;
2979
+ return result;
2980
+ } catch (error) {
2981
+ return `Error listing sessions: ${error instanceof Error ? error.message : String(error)}`;
2982
+ }
2983
+ }
2984
+ },
2985
+ {
2986
+ name: "read_session",
2987
+ description: "Load context from a previous session. Returns session summary, user tasks, and file changes.",
2988
+ args: {
2989
+ sessionId: {
2990
+ type: "string",
2991
+ description: 'Session ID from list_session (e.g., "2024-01-15T10-30-00")',
2992
+ required: true
2993
+ }
2994
+ },
2995
+ async execute({ sessionId }, context) {
2996
+ const config = context?.config;
2997
+ if (!config) {
2998
+ return "Error: Configuration not available";
2999
+ }
3000
+ try {
3001
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3002
+ const memory = new MemoryManager2(config);
3003
+ const content = await memory.read(`handoffs/${sessionId}`);
3004
+ if (!content) {
3005
+ return `Session not found: ${sessionId}
3006
+
3007
+ Use \`list_session\` to see available sessions.`;
3008
+ }
3009
+ return `# Session Context: ${sessionId}
3010
+
3011
+ ${content}
3012
+
3013
+ ---
3014
+
3015
+ This context has been loaded. Use /resume to continue from this point.`;
3016
+ } catch (error) {
3017
+ return `Error reading session: ${error instanceof Error ? error.message : String(error)}`;
3018
+ }
3019
+ }
3020
+ }
3021
+ ];
3022
+ var ToolRegistry = class {
3023
+ config;
3024
+ tools = /* @__PURE__ */ new Map();
3025
+ toolConfigManager;
3026
+ constructor(config) {
3027
+ this.config = config;
3028
+ for (const tool of BUILT_IN_TOOLS) {
3029
+ this.tools.set(tool.name, tool);
3030
+ }
3031
+ }
3032
+ /**
3033
+ * Set tool config manager (for tools that need configuration)
3034
+ */
3035
+ setToolConfigManager(manager) {
3036
+ this.toolConfigManager = manager;
3037
+ }
3038
+ /**
3039
+ * List all available tools
3040
+ */
3041
+ async listTools() {
3042
+ await this.loadCustomTools();
3043
+ return Array.from(this.tools.values());
3044
+ }
3045
+ /**
3046
+ * Get a specific tool
3047
+ */
3048
+ getTool(name) {
3049
+ return this.tools.get(name);
3050
+ }
3051
+ /**
3052
+ * Register a new tool
3053
+ */
3054
+ registerTool(tool) {
3055
+ this.tools.set(tool.name, tool);
3056
+ }
3057
+ /**
3058
+ * Execute a tool
3059
+ */
3060
+ async executeTool(name, args, context) {
3061
+ const tool = this.tools.get(name);
3062
+ if (!tool) {
3063
+ throw new Error(`Tool not found: ${name}`);
3064
+ }
3065
+ for (const [argName, argDef] of Object.entries(tool.args)) {
3066
+ if (argDef.required && args[argName] === void 0) {
3067
+ throw new Error(`Missing required argument: ${argName}`);
3068
+ }
3069
+ }
3070
+ const mergedContext = {
3071
+ ...context,
3072
+ toolConfigManager: context?.toolConfigManager || this.toolConfigManager
3073
+ };
3074
+ if (tool.execute.length === 2) {
3075
+ return await tool.execute(args, mergedContext);
3076
+ }
3077
+ return await tool.execute(args);
3078
+ }
3079
+ /**
3080
+ * Create a custom tool
3081
+ */
3082
+ async createTool(name, options) {
3083
+ const configPath = options.global ? paths.globalConfig() : this.config.configPath;
3084
+ const toolsDir = paths.tools(configPath);
3085
+ await mkdir5(toolsDir, { recursive: true });
3086
+ const fileName = `${name}.ts`;
3087
+ const filePath = join8(toolsDir, fileName);
3088
+ const argsSchema = Object.entries(options.args).map(([argName, arg]) => ` ${argName}: {
3089
+ type: '${arg.type}',
3090
+ description: '${arg.description}',
3091
+ required: ${arg.required ?? true},
3092
+ }`).join(",\n");
3093
+ const content = `import { defineTool } from 'aikit';
3094
+
3095
+ export default defineTool({
3096
+ name: '${name}',
3097
+ description: '${options.description}',
3098
+ args: {
3099
+ ${argsSchema}
3100
+ },
3101
+ async execute(args) {
3102
+ ${options.code}
3103
+ }
3104
+ });
3105
+ `;
3106
+ await writeFile5(filePath, content);
3107
+ }
3108
+ /**
3109
+ * Format tool for agent consumption
3110
+ */
3111
+ formatForAgent(tool) {
3112
+ const argsDesc = Object.entries(tool.args).map(([name, arg]) => ` - ${name} (${arg.type}${arg.required ? ", required" : ""}): ${arg.description}`).join("\n");
3113
+ return `## Tool: ${tool.name}
3114
+
3115
+ ${tool.description}
3116
+
3117
+ ### Arguments
3118
+ ${argsDesc}
3119
+ `;
3120
+ }
3121
+ /**
3122
+ * Load custom tools from disk
3123
+ */
3124
+ async loadCustomTools() {
3125
+ const globalToolsPath = paths.tools(paths.globalConfig());
3126
+ await this.loadToolsFromDir(globalToolsPath);
3127
+ const projectToolsPath = paths.tools(this.config.configPath);
3128
+ if (projectToolsPath !== globalToolsPath) {
3129
+ await this.loadToolsFromDir(projectToolsPath);
3130
+ }
3131
+ }
3132
+ async loadToolsFromDir(dir) {
3133
+ let files;
3134
+ try {
3135
+ files = await readdir4(dir);
3136
+ } catch {
3137
+ return;
3138
+ }
3139
+ for (const file of files) {
3140
+ if (extname3(file) !== ".ts" && extname3(file) !== ".js") continue;
3141
+ const filePath = join8(dir, file);
3142
+ try {
3143
+ const toolModule = await import(`file://${filePath}`);
3144
+ const tool = toolModule.default;
3145
+ if (tool?.name && typeof tool.execute === "function") {
3146
+ tool.filePath = filePath;
3147
+ this.tools.set(tool.name, tool);
3148
+ }
3149
+ } catch (error) {
3150
+ logger.warn(`Failed to load tool from ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
3151
+ }
3152
+ }
3153
+ }
3154
+ };
3155
+
3156
+ // src/core/tool-config.ts
3157
+ init_esm_shims();
3158
+ import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir6, access as access3, constants as constants3 } from "fs/promises";
3159
+ import { join as join9 } from "path";
3160
+ import { z as z3 } from "zod";
3161
+ var ToolConfigSchema = z3.object({
3162
+ name: z3.string(),
3163
+ status: z3.enum(["ready", "needs_config", "error"]),
3164
+ description: z3.string(),
3165
+ configMethod: z3.enum(["oauth", "manual", "none"]),
3166
+ config: z3.record(z3.unknown()).optional(),
3167
+ errorMessage: z3.string().optional()
3168
+ });
3169
+ var REGISTERED_TOOLS = [
3170
+ {
3171
+ name: "figma-analysis",
3172
+ description: "Analyze Figma designs and extract design tokens using Figma API",
3173
+ configMethod: "oauth"
3174
+ }
3175
+ // Add more tools here as needed
3176
+ ];
3177
+ var ToolConfigManager = class {
3178
+ config;
3179
+ toolsConfigPath;
3180
+ constructor(config) {
3181
+ this.config = config;
3182
+ this.toolsConfigPath = join9(this.config.configPath, "config", "tools.json");
3183
+ }
3184
+ /**
3185
+ * Get all registered tools with their current status
3186
+ */
3187
+ async listTools() {
3188
+ const savedConfigs = await this.loadConfigs();
3189
+ const tools = [];
3190
+ for (const tool of REGISTERED_TOOLS) {
3191
+ const saved = savedConfigs[tool.name];
3192
+ const toolConfig = {
3193
+ ...tool,
3194
+ status: this.determineStatus(tool, saved),
3195
+ config: saved?.config,
3196
+ errorMessage: saved?.errorMessage
3197
+ };
3198
+ tools.push(toolConfig);
3199
+ }
3200
+ return tools;
3201
+ }
3202
+ /**
3203
+ * Get configuration for a specific tool
3204
+ */
3205
+ async getToolConfig(toolName) {
3206
+ const tools = await this.listTools();
3207
+ return tools.find((t) => t.name === toolName) || null;
3208
+ }
3209
+ /**
3210
+ * Update tool configuration
3211
+ */
3212
+ async updateToolConfig(toolName, updates) {
3213
+ const savedConfigs = await this.loadConfigs();
3214
+ const tool = REGISTERED_TOOLS.find((t) => t.name === toolName);
3215
+ if (!tool) {
3216
+ throw new Error(`Tool not found: ${toolName}`);
3217
+ }
3218
+ const existing = savedConfigs[toolName] || {};
3219
+ savedConfigs[toolName] = {
3220
+ ...existing,
3221
+ ...updates
3222
+ };
3223
+ await this.saveConfigs(savedConfigs);
3224
+ }
3225
+ /**
3226
+ * Check if a tool is ready to use
3227
+ */
3228
+ async isToolReady(toolName) {
3229
+ const toolConfig = await this.getToolConfig(toolName);
3230
+ return toolConfig?.status === "ready";
3231
+ }
3232
+ /**
3233
+ * Get API key for a tool (if configured)
3234
+ */
3235
+ async getApiKey(toolName) {
3236
+ const toolConfig = await this.getToolConfig(toolName);
3237
+ if (toolConfig?.config?.apiKey && typeof toolConfig.config.apiKey === "string") {
3238
+ return toolConfig.config.apiKey;
3239
+ }
3240
+ return null;
3241
+ }
3242
+ /**
3243
+ * Determine tool status based on configuration
3244
+ */
3245
+ determineStatus(tool, saved) {
3246
+ if (tool.configMethod === "none") {
3247
+ return "ready";
3248
+ }
3249
+ if (saved?.errorMessage) {
3250
+ return "error";
3251
+ }
3252
+ if (tool.configMethod === "oauth" || tool.configMethod === "manual") {
3253
+ if (saved?.config?.apiKey && typeof saved.config.apiKey === "string" && saved.config.apiKey.length > 0) {
3254
+ return "ready";
3255
+ }
3256
+ return "needs_config";
3257
+ }
3258
+ return "needs_config";
3259
+ }
3260
+ /**
3261
+ * Load saved configurations
3262
+ */
3263
+ async loadConfigs() {
3264
+ try {
3265
+ await access3(this.toolsConfigPath, constants3.R_OK);
3266
+ const content = await readFile6(this.toolsConfigPath, "utf-8");
3267
+ return JSON.parse(content);
3268
+ } catch {
3269
+ return {};
3270
+ }
3271
+ }
3272
+ /**
3273
+ * Save configurations
3274
+ */
3275
+ async saveConfigs(configs) {
3276
+ const configDir = join9(this.config.configPath, "config");
3277
+ await mkdir6(configDir, { recursive: true });
3278
+ await writeFile6(this.toolsConfigPath, JSON.stringify(configs, null, 2));
3279
+ }
3280
+ };
3281
+
3282
+ // src/mcp-server.ts
3283
+ init_logger();
3284
+ var AiKitMcpServer = class {
3285
+ server;
3286
+ skillEngine;
3287
+ agentManager;
3288
+ commandRunner;
3289
+ toolRegistry;
3290
+ toolConfigManager;
3291
+ constructor() {
3292
+ const capabilities = {
3293
+ tools: {}
3294
+ };
3295
+ this.server = new Server(
3296
+ {
3297
+ name: "aikit",
3298
+ version: "0.1.0"
3299
+ },
3300
+ {
3301
+ capabilities
3302
+ }
3303
+ );
3304
+ this.setupHandlers();
3305
+ }
3306
+ setupHandlers() {
3307
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
3308
+ return {
3309
+ tools: await this.getAvailableTools()
3310
+ };
3311
+ });
3312
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
3313
+ return await this.handleToolCall(request);
3314
+ });
3315
+ }
3316
+ async getAvailableTools() {
3317
+ const tools = [];
3318
+ try {
3319
+ const skills = await this.skillEngine.listSkills();
3320
+ for (const skill of skills) {
3321
+ tools.push({
3322
+ name: `skill_${skill.name.replace(/\s+/g, "_")}`,
3323
+ description: `Execute the "${skill.name}" skill: ${skill.description}`,
3324
+ inputSchema: {
3325
+ type: "object",
3326
+ properties: {
3327
+ context: {
3328
+ type: "string",
3329
+ description: "Additional context for the skill"
3330
+ }
3331
+ },
3332
+ required: []
3333
+ }
3334
+ });
3335
+ }
3336
+ const agents = this.agentManager.listAgents();
3337
+ for (const agent of agents) {
3338
+ tools.push({
3339
+ name: `agent_${agent.name}`,
3340
+ description: `Delegate to the ${agent.name} agent: ${agent.description}`,
3341
+ inputSchema: {
3342
+ type: "object",
3343
+ properties: {
3344
+ task: {
3345
+ type: "string",
3346
+ description: "Task for the agent to handle"
3347
+ },
3348
+ context: {
3349
+ type: "string",
3350
+ description: "Optional context"
3351
+ }
3352
+ },
3353
+ required: ["task"]
3354
+ }
3355
+ });
3356
+ }
3357
+ const commands = await this.commandRunner.listCommands();
3358
+ for (const cmd of commands) {
3359
+ tools.push({
3360
+ name: `cmd_${cmd.name.replace(/\//g, "_").slice(1)}`,
3361
+ description: `Run command: ${cmd.description}`,
3362
+ inputSchema: {
3363
+ type: "object",
3364
+ properties: {
3365
+ args: {
3366
+ type: "string",
3367
+ description: "Arguments for the command"
3368
+ }
3369
+ },
3370
+ required: []
3371
+ }
3372
+ });
3373
+ }
3374
+ const aikitTools = await this.toolRegistry.listTools();
3375
+ for (const tool of aikitTools) {
3376
+ const properties = {};
3377
+ const required = [];
3378
+ for (const [argName, argDef] of Object.entries(tool.args)) {
3379
+ properties[argName] = {
3380
+ type: argDef.type,
3381
+ description: argDef.description
3382
+ };
3383
+ if (argDef.required) {
3384
+ required.push(argName);
3385
+ }
3386
+ }
3387
+ tools.push({
3388
+ name: `tool_${tool.name.replace(/\s+/g, "_")}`,
3389
+ description: tool.description,
3390
+ inputSchema: {
3391
+ type: "object",
3392
+ properties,
3393
+ required
3394
+ }
3395
+ });
3396
+ }
3397
+ } catch (error) {
3398
+ logger.error(`Failed to list tools: ${error}`);
3399
+ }
3400
+ return tools;
3401
+ }
3402
+ async handleToolCall(request) {
3403
+ const { name, arguments: args } = request.params;
3404
+ try {
3405
+ let result = "";
3406
+ if (name.startsWith("skill_")) {
3407
+ const skillName = name.replace("skill_", "").replace(/_/g, "-");
3408
+ const skill = await this.skillEngine.getSkill(skillName);
3409
+ if (skill) {
3410
+ const formatted = this.skillEngine.formatForAgent(skill);
3411
+ result = formatted;
3412
+ } else {
3413
+ result = `Skill not found: ${skillName}`;
3414
+ }
3415
+ } else if (name.startsWith("agent_")) {
3416
+ const task = args?.task || "Execute task";
3417
+ const context = args?.context || "";
3418
+ const decision = this.agentManager.decideAgent(task, context);
3419
+ result = `Delegated to agent: ${decision.agent}
3420
+
3421
+ Reasoning: ${decision.reason}`;
3422
+ } else if (name.startsWith("cmd_")) {
3423
+ const cmdName = "/" + name.replace("cmd_", "").replace(/_/g, "/");
3424
+ const cmdArgs = args?.args || "";
3425
+ result = `Execute: ${cmdName} ${cmdArgs}`;
3426
+ } else if (name.startsWith("tool_")) {
3427
+ const toolName = name.replace("tool_", "");
3428
+ try {
3429
+ if (!this.toolConfigManager) {
3430
+ result = `Error: Tool configuration manager not initialized. MCP server may not be properly started.`;
3431
+ } else {
3432
+ const context = { toolConfigManager: this.toolConfigManager };
3433
+ result = await this.toolRegistry.executeTool(toolName, args || {}, context);
3434
+ }
3435
+ } catch (error) {
3436
+ result = `Error executing tool ${toolName}: ${error instanceof Error ? error.message : String(error)}`;
3437
+ }
3438
+ } else {
3439
+ result = `Unknown tool: ${name}`;
3440
+ }
3441
+ return {
3442
+ content: [
3443
+ {
3444
+ type: "text",
3445
+ text: result
3446
+ }
3447
+ ]
3448
+ };
3449
+ } catch (error) {
3450
+ const errorMsg = error instanceof Error ? error.message : String(error);
3451
+ return {
3452
+ content: [
3453
+ {
3454
+ type: "text",
3455
+ text: `Error executing tool: ${errorMsg}`
3456
+ }
3457
+ ]
3458
+ };
3459
+ }
3460
+ }
3461
+ async initialize() {
3462
+ try {
3463
+ const config = await loadConfig();
3464
+ this.skillEngine = new SkillEngine(config);
3465
+ this.agentManager = new AgentManager(config);
3466
+ this.commandRunner = new CommandRunner(config);
3467
+ this.toolRegistry = new ToolRegistry(config);
3468
+ this.toolConfigManager = new ToolConfigManager(config);
3469
+ this.toolRegistry.setToolConfigManager(this.toolConfigManager);
3470
+ const figmaReady = await this.toolConfigManager.isToolReady("figma-analysis");
3471
+ if (figmaReady) {
3472
+ logger.info("Figma tool configured and ready");
3473
+ } else {
3474
+ logger.warn("Figma tool not configured - tools requiring config may not work");
3475
+ }
3476
+ } catch (error) {
3477
+ logger.error(`Failed to initialize: ${error}`);
3478
+ process.exit(1);
3479
+ }
3480
+ }
3481
+ async start() {
3482
+ const transport = new StdioServerTransport();
3483
+ await this.server.connect(transport);
3484
+ logger.info("AIKit MCP Server started");
3485
+ }
3486
+ };
3487
+ async function main() {
3488
+ const server = new AiKitMcpServer();
3489
+ await server.initialize();
3490
+ await server.start();
3491
+ }
3492
+ main().catch((error) => {
3493
+ logger.error(`Fatal error: ${error}`);
3494
+ process.exit(1);
3495
+ });
3496
+ //# sourceMappingURL=mcp-server.js.map