@tdsoft-tech/aikit 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -121,6 +121,184 @@ var init_paths = __esm({
121
121
  }
122
122
  });
123
123
 
124
+ // src/utils/version.ts
125
+ import { readFileSync } from "fs";
126
+ import { fileURLToPath as fileURLToPath2 } from "url";
127
+ import { dirname, join as join2 } from "path";
128
+ function getVersion() {
129
+ try {
130
+ const __filename2 = fileURLToPath2(import.meta.url);
131
+ const __dirname2 = dirname(__filename2);
132
+ const packageJsonPath = join2(__dirname2, "..", "package.json");
133
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
134
+ return packageJson.version;
135
+ } catch (error) {
136
+ console.warn("Warning: Could not read version from package.json, using fallback");
137
+ return "0.0.0";
138
+ }
139
+ }
140
+ var init_version = __esm({
141
+ "src/utils/version.ts"() {
142
+ "use strict";
143
+ init_esm_shims();
144
+ }
145
+ });
146
+
147
+ // src/core/config.ts
148
+ var config_exports = {};
149
+ __export(config_exports, {
150
+ Config: () => Config,
151
+ loadConfig: () => loadConfig
152
+ });
153
+ import { readFile, access, constants } from "fs/promises";
154
+ import { join as join3 } from "path";
155
+ import { z } from "zod";
156
+ async function loadConfig(projectPath) {
157
+ const project = projectPath || process.cwd();
158
+ const projectConfigPath = paths.projectConfig(project);
159
+ const globalConfigPath = paths.globalConfig();
160
+ let configPath;
161
+ let configData = {};
162
+ try {
163
+ await access(join3(globalConfigPath, "aikit.json"), constants.R_OK);
164
+ const globalContent = await readFile(join3(globalConfigPath, "aikit.json"), "utf-8");
165
+ configData = JSON.parse(globalContent);
166
+ configPath = globalConfigPath;
167
+ } catch {
168
+ configPath = projectConfigPath;
169
+ }
170
+ try {
171
+ await access(join3(projectConfigPath, "aikit.json"), constants.R_OK);
172
+ const projectContent = await readFile(join3(projectConfigPath, "aikit.json"), "utf-8");
173
+ const projectData = JSON.parse(projectContent);
174
+ configData = deepMerge(configData, projectData);
175
+ configPath = projectConfigPath;
176
+ } catch {
177
+ }
178
+ if (!configData.version) {
179
+ configData.version = getVersion();
180
+ }
181
+ const parsed = ConfigSchema.parse(configData);
182
+ return new Config({
183
+ ...parsed,
184
+ configPath,
185
+ projectPath: project
186
+ });
187
+ }
188
+ function deepMerge(base, override) {
189
+ const result = { ...base };
190
+ for (const key in override) {
191
+ const baseValue = base[key];
192
+ const overrideValue = override[key];
193
+ if (typeof baseValue === "object" && baseValue !== null && typeof overrideValue === "object" && overrideValue !== null && !Array.isArray(baseValue) && !Array.isArray(overrideValue)) {
194
+ result[key] = deepMerge(
195
+ baseValue,
196
+ overrideValue
197
+ );
198
+ } else if (overrideValue !== void 0) {
199
+ result[key] = overrideValue;
200
+ }
201
+ }
202
+ return result;
203
+ }
204
+ var ConfigSchema, Config;
205
+ var init_config = __esm({
206
+ "src/core/config.ts"() {
207
+ "use strict";
208
+ init_esm_shims();
209
+ init_paths();
210
+ init_version();
211
+ ConfigSchema = z.object({
212
+ version: z.string(),
213
+ skills: z.object({
214
+ enabled: z.boolean().default(true),
215
+ directory: z.string().optional()
216
+ }).default({}),
217
+ agents: z.object({
218
+ enabled: z.boolean().default(true),
219
+ default: z.string().default("build")
220
+ }).default({}),
221
+ commands: z.object({
222
+ enabled: z.boolean().default(true)
223
+ }).default({}),
224
+ tools: z.object({
225
+ enabled: z.boolean().default(true)
226
+ }).default({}),
227
+ plugins: z.object({
228
+ enabled: z.boolean().default(true),
229
+ autoload: z.array(z.string()).optional()
230
+ }).default({}),
231
+ memory: z.object({
232
+ enabled: z.boolean().default(true),
233
+ maxSize: z.number().optional()
234
+ }).default({}),
235
+ beads: z.object({
236
+ enabled: z.boolean().default(true),
237
+ autoInit: z.boolean().default(false)
238
+ }).default({}),
239
+ antiHallucination: z.object({
240
+ enabled: z.boolean().default(true),
241
+ specFile: z.string().default("spec.md"),
242
+ reviewFile: z.string().default("review.md")
243
+ }).default({}),
244
+ mcp: z.object({
245
+ context7: z.boolean().default(false),
246
+ githubGrep: z.boolean().default(false),
247
+ gkg: z.boolean().default(false)
248
+ }).optional(),
249
+ mode: z.string().default("build").optional()
250
+ });
251
+ Config = class {
252
+ config;
253
+ constructor(config) {
254
+ this.config = config;
255
+ }
256
+ get() {
257
+ return this.config;
258
+ }
259
+ get skills() {
260
+ return this.config.skills;
261
+ }
262
+ get agents() {
263
+ return this.config.agents;
264
+ }
265
+ get commands() {
266
+ return this.config.commands;
267
+ }
268
+ get tools() {
269
+ return this.config.tools;
270
+ }
271
+ get plugins() {
272
+ return this.config.plugins;
273
+ }
274
+ get memory() {
275
+ return this.config.memory;
276
+ }
277
+ get beads() {
278
+ return this.config.beads;
279
+ }
280
+ get antiHallucination() {
281
+ return this.config.antiHallucination;
282
+ }
283
+ get mode() {
284
+ return this.config.mode;
285
+ }
286
+ get configPath() {
287
+ return this.config.configPath;
288
+ }
289
+ get projectPath() {
290
+ return this.config.projectPath;
291
+ }
292
+ /**
293
+ * Get path to a specific resource
294
+ */
295
+ getPath(resource) {
296
+ return paths[resource](this.configPath);
297
+ }
298
+ };
299
+ }
300
+ });
301
+
124
302
  // src/utils/logger.ts
125
303
  import chalk from "chalk";
126
304
  var logger;
@@ -163,112 +341,328 @@ ${message}
163
341
  }
164
342
  });
165
343
 
166
- // src/core/tools/figma-mcp.ts
167
- var figma_mcp_exports = {};
168
- __export(figma_mcp_exports, {
169
- FigmaMcpClient: () => FigmaMcpClient
344
+ // src/core/memory.ts
345
+ var memory_exports = {};
346
+ __export(memory_exports, {
347
+ MemoryManager: () => MemoryManager
170
348
  });
171
- import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
349
+ import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3, access as access2, constants as constants2 } from "fs/promises";
172
350
  import { join as join6 } 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"() {
351
+ var MemoryManager;
352
+ var init_memory = __esm({
353
+ "src/core/memory.ts"() {
177
354
  "use strict";
178
355
  init_esm_shims();
179
- init_logger();
180
- FigmaMcpClient = class {
181
- apiKey;
182
- constructor(apiKey, _configManager) {
183
- this.apiKey = apiKey;
356
+ init_paths();
357
+ MemoryManager = class {
358
+ config;
359
+ constructor(config) {
360
+ this.config = config;
184
361
  }
185
362
  /**
186
- * Fetch helper with simple retry/backoff for 429/5xx
363
+ * List all memory entries
187
364
  */
188
- async fetchWithRetry(url, options, label, retries = 3, backoffMs = 1500) {
189
- let attempt = 0;
190
- let lastError;
191
- while (attempt <= retries) {
365
+ async list() {
366
+ const memories = [];
367
+ const memoryPath = paths.memory(this.config.configPath);
368
+ const subDirs = ["observations", "handoffs", "research"];
369
+ for (const subDir of subDirs) {
370
+ const dirPath = join6(memoryPath, subDir);
192
371
  try {
193
- const res = await fetch(url, options);
194
- if (res.ok) return res;
195
- if (res.status === 429 || res.status >= 500) {
196
- const retryAfter = Number(res.headers.get("retry-after")) || 0;
197
- const delay = retryAfter > 0 ? retryAfter * 1e3 : backoffMs * (attempt + 1);
198
- logger.warn(`${label} failed (status ${res.status}), retrying in ${Math.round(delay / 1e3)}s...`);
199
- await new Promise((r) => setTimeout(r, delay));
200
- attempt += 1;
201
- continue;
372
+ const { readdir: readdir8 } = await import("fs/promises");
373
+ const files = await readdir8(dirPath);
374
+ for (const file of files) {
375
+ if (!file.endsWith(".md")) continue;
376
+ const content = await readFile4(join6(dirPath, file), "utf-8");
377
+ const summary = this.extractSummary(content);
378
+ memories.push({
379
+ key: `${subDir}/${file.replace(".md", "")}`,
380
+ content,
381
+ summary,
382
+ createdAt: /* @__PURE__ */ new Date(),
383
+ // Would get from file stats
384
+ updatedAt: /* @__PURE__ */ new Date(),
385
+ type: subDir
386
+ });
202
387
  }
203
- const text = await res.text();
204
- throw new Error(`${label} error: ${res.status} ${res.statusText}
205
- ${text}`);
206
- } catch (err) {
207
- lastError = err;
208
- logger.warn(`${label} network error, attempt ${attempt + 1}/${retries + 1}: ${err instanceof Error ? err.message : String(err)}`);
209
- if (attempt >= retries) break;
210
- await new Promise((r) => setTimeout(r, backoffMs * (attempt + 1)));
211
- attempt += 1;
388
+ } catch {
212
389
  }
213
390
  }
214
- throw lastError instanceof Error ? lastError : new Error(`${label} failed after retries`);
215
- }
216
- /**
217
- * Extract file key from Figma URL
218
- */
219
- extractFileKey(url) {
220
- const match = url.match(/figma\.com\/design\/([a-zA-Z0-9]+)/);
221
- return match ? match[1] : null;
391
+ return memories;
222
392
  }
223
393
  /**
224
- * Extract node ID from Figma URL
394
+ * Read a memory entry
225
395
  */
226
- extractNodeId(url) {
227
- const match = url.match(/[?&]node-id=([^&]+)/);
228
- if (!match) return null;
229
- let nodeId = decodeURIComponent(match[1]);
230
- if (nodeId.includes("-") && !nodeId.includes(":")) {
231
- nodeId = nodeId.replace(/-/g, ":");
396
+ async read(key) {
397
+ const memoryPath = paths.memory(this.config.configPath);
398
+ let filePath;
399
+ if (key.includes("/")) {
400
+ filePath = join6(memoryPath, `${key}.md`);
401
+ } else {
402
+ const subDirs = ["observations", "handoffs", "research", "_templates"];
403
+ for (const subDir of subDirs) {
404
+ const testPath = join6(memoryPath, subDir, `${key}.md`);
405
+ try {
406
+ await access2(testPath, constants2.R_OK);
407
+ filePath = testPath;
408
+ break;
409
+ } catch {
410
+ continue;
411
+ }
412
+ }
413
+ filePath = filePath || join6(memoryPath, `${key}.md`);
414
+ }
415
+ try {
416
+ return await readFile4(filePath, "utf-8");
417
+ } catch {
418
+ return null;
232
419
  }
233
- return nodeId;
234
420
  }
235
421
  /**
236
- * Get Figma file data using API
422
+ * Update a memory entry
237
423
  */
238
- async getFileData(url) {
239
- const fileKey = this.extractFileKey(url);
240
- if (!fileKey) {
241
- throw new Error(`Invalid Figma URL: ${url}`);
424
+ async update(key, content, options) {
425
+ const memoryPath = paths.memory(this.config.configPath);
426
+ const type = options?.type || "custom";
427
+ let filePath;
428
+ if (key.includes("/")) {
429
+ filePath = join6(memoryPath, `${key}.md`);
430
+ } else {
431
+ const subDir = type === "observation" ? "observations" : type === "handoff" ? "handoffs" : type === "research" ? "research" : "";
432
+ filePath = join6(memoryPath, subDir, `${key}.md`);
242
433
  }
243
- const nodeId = this.extractNodeId(url);
244
- const apiUrl = nodeId ? `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${encodeURIComponent(nodeId)}` : `https://api.figma.com/v1/files/${fileKey}`;
245
- const response = await this.fetchWithRetry(apiUrl, {
246
- headers: {
247
- "X-Figma-Token": this.apiKey
248
- }
249
- }, "Figma file fetch");
250
- const data = await response.json();
251
- if (nodeId) {
252
- const nodes = data.nodes;
253
- const nodeData = Object.values(nodes)[0];
254
- if (!nodeData) {
255
- throw new Error(`Node not found: ${nodeId}`);
434
+ const { dirname: dirname2 } = await import("path");
435
+ await mkdir3(dirname2(filePath), { recursive: true });
436
+ if (options?.append) {
437
+ try {
438
+ const existing = await readFile4(filePath, "utf-8");
439
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
440
+ content = `${existing}
441
+
442
+ ---
443
+ _Updated: ${timestamp}_
444
+
445
+ ${content}`;
446
+ } catch {
256
447
  }
257
- return {
258
- document: nodeData.document,
259
- components: {},
260
- styles: {}
261
- };
262
448
  }
263
- return data;
449
+ await writeFile3(filePath, content);
264
450
  }
265
451
  /**
266
- * Extract design tokens from Figma file
452
+ * Create a handoff bundle
267
453
  */
268
- async extractDesignTokens(url, downloadAssets = true, assetsDir) {
269
- const fileData = await this.getFileData(url);
270
- const fileKey = this.extractFileKey(url);
271
- if (!fileKey) {
454
+ async createHandoff(summary) {
455
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
456
+ const key = `handoffs/${timestamp}`;
457
+ const content = `# Handoff: ${(/* @__PURE__ */ new Date()).toLocaleString()}
458
+
459
+ ## Completed
460
+ ${summary.completed.map((item) => `- [x] ${item}`).join("\n") || "- None"}
461
+
462
+ ## In Progress
463
+ ${summary.inProgress.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
464
+
465
+ ## Remaining
466
+ ${summary.remaining.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
467
+
468
+ ## Context
469
+ ${summary.context || "No additional context."}
470
+
471
+ ## Next Steps
472
+ ${summary.nextSteps.map((step, i) => `${i + 1}. ${step}`).join("\n") || "- Continue from where left off"}
473
+ `;
474
+ await this.update(key, content, { type: "handoff" });
475
+ return key;
476
+ }
477
+ /**
478
+ * Get the latest handoff
479
+ */
480
+ async getLatestHandoff() {
481
+ const memories = await this.list();
482
+ const handoffs = memories.filter((m) => m.type === "handoff");
483
+ if (handoffs.length === 0) return null;
484
+ handoffs.sort((a, b) => b.key.localeCompare(a.key));
485
+ return handoffs[0];
486
+ }
487
+ /**
488
+ * Create an observation
489
+ */
490
+ async createObservation(title, observation) {
491
+ const slug = title.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
492
+ const key = `observations/${slug}`;
493
+ const content = `# ${title}
494
+
495
+ ## What
496
+ ${observation.what}
497
+
498
+ ## Why
499
+ ${observation.why}
500
+
501
+ ## Impact
502
+ ${observation.impact}
503
+
504
+ ${observation.tags?.length ? `## Tags
505
+ ${observation.tags.map((t) => `- ${t}`).join("\n")}` : ""}
506
+
507
+ ---
508
+ _Created: ${(/* @__PURE__ */ new Date()).toISOString()}_
509
+ `;
510
+ await this.update(key, content, { type: "observation" });
511
+ return key;
512
+ }
513
+ /**
514
+ * Save research findings
515
+ */
516
+ async saveResearch(topic, findings) {
517
+ const slug = topic.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
518
+ const key = `research/${slug}`;
519
+ const content = `# Research: ${topic}
520
+
521
+ ## Summary
522
+ ${findings.summary}
523
+
524
+ ## Sources
525
+ ${findings.sources.map((s) => `- ${s}`).join("\n")}
526
+
527
+ ## Recommendations
528
+ ${findings.recommendations.map((r, i) => `${i + 1}. ${r}`).join("\n")}
529
+
530
+ ${findings.codeExamples ? `## Code Examples
531
+ \`\`\`
532
+ ${findings.codeExamples}
533
+ \`\`\`` : ""}
534
+
535
+ ---
536
+ _Researched: ${(/* @__PURE__ */ new Date()).toISOString()}_
537
+ `;
538
+ await this.update(key, content, { type: "research" });
539
+ return key;
540
+ }
541
+ /**
542
+ * Extract a summary from content (first paragraph or heading)
543
+ */
544
+ extractSummary(content) {
545
+ const lines = content.split("\n").filter((l) => l.trim());
546
+ for (const line of lines) {
547
+ if (!line.startsWith("#") && line.trim().length > 0) {
548
+ return line.slice(0, 100) + (line.length > 100 ? "..." : "");
549
+ }
550
+ }
551
+ if (lines[0]?.startsWith("#")) {
552
+ return lines[0].replace(/^#+\s*/, "");
553
+ }
554
+ return "No summary available";
555
+ }
556
+ };
557
+ }
558
+ });
559
+
560
+ // src/core/tools/figma-mcp.ts
561
+ var figma_mcp_exports = {};
562
+ __export(figma_mcp_exports, {
563
+ FigmaMcpClient: () => FigmaMcpClient
564
+ });
565
+ import { writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
566
+ import { join as join7 } from "path";
567
+ import { existsSync as existsSync2 } from "fs";
568
+ var FigmaMcpClient;
569
+ var init_figma_mcp = __esm({
570
+ "src/core/tools/figma-mcp.ts"() {
571
+ "use strict";
572
+ init_esm_shims();
573
+ init_logger();
574
+ FigmaMcpClient = class {
575
+ apiKey;
576
+ constructor(apiKey, _configManager) {
577
+ this.apiKey = apiKey;
578
+ }
579
+ /**
580
+ * Fetch helper with simple retry/backoff for 429/5xx
581
+ */
582
+ async fetchWithRetry(url, options, label, retries = 3, backoffMs = 1500) {
583
+ let attempt = 0;
584
+ let lastError;
585
+ while (attempt <= retries) {
586
+ try {
587
+ const res = await fetch(url, options);
588
+ if (res.ok) return res;
589
+ if (res.status === 429 || res.status >= 500) {
590
+ const retryAfter = Number(res.headers.get("retry-after")) || 0;
591
+ const delay = retryAfter > 0 ? retryAfter * 1e3 : backoffMs * (attempt + 1);
592
+ logger.warn(`${label} failed (status ${res.status}), retrying in ${Math.round(delay / 1e3)}s...`);
593
+ await new Promise((r) => setTimeout(r, delay));
594
+ attempt += 1;
595
+ continue;
596
+ }
597
+ const text = await res.text();
598
+ throw new Error(`${label} error: ${res.status} ${res.statusText}
599
+ ${text}`);
600
+ } catch (err) {
601
+ lastError = err;
602
+ logger.warn(`${label} network error, attempt ${attempt + 1}/${retries + 1}: ${err instanceof Error ? err.message : String(err)}`);
603
+ if (attempt >= retries) break;
604
+ await new Promise((r) => setTimeout(r, backoffMs * (attempt + 1)));
605
+ attempt += 1;
606
+ }
607
+ }
608
+ throw lastError instanceof Error ? lastError : new Error(`${label} failed after retries`);
609
+ }
610
+ /**
611
+ * Extract file key from Figma URL
612
+ */
613
+ extractFileKey(url) {
614
+ const match = url.match(/figma\.com\/design\/([a-zA-Z0-9]+)/);
615
+ return match ? match[1] : null;
616
+ }
617
+ /**
618
+ * Extract node ID from Figma URL
619
+ */
620
+ extractNodeId(url) {
621
+ const match = url.match(/[?&]node-id=([^&]+)/);
622
+ if (!match) return null;
623
+ let nodeId = decodeURIComponent(match[1]);
624
+ if (nodeId.includes("-") && !nodeId.includes(":")) {
625
+ nodeId = nodeId.replace(/-/g, ":");
626
+ }
627
+ return nodeId;
628
+ }
629
+ /**
630
+ * Get Figma file data using API
631
+ */
632
+ async getFileData(url) {
633
+ const fileKey = this.extractFileKey(url);
634
+ if (!fileKey) {
635
+ throw new Error(`Invalid Figma URL: ${url}`);
636
+ }
637
+ const nodeId = this.extractNodeId(url);
638
+ const apiUrl = nodeId ? `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${encodeURIComponent(nodeId)}` : `https://api.figma.com/v1/files/${fileKey}`;
639
+ const response = await this.fetchWithRetry(apiUrl, {
640
+ headers: {
641
+ "X-Figma-Token": this.apiKey
642
+ }
643
+ }, "Figma file fetch");
644
+ const data = await response.json();
645
+ if (nodeId) {
646
+ const nodes = data.nodes;
647
+ const nodeData = Object.values(nodes)[0];
648
+ if (!nodeData) {
649
+ throw new Error(`Node not found: ${nodeId}`);
650
+ }
651
+ return {
652
+ document: nodeData.document,
653
+ components: {},
654
+ styles: {}
655
+ };
656
+ }
657
+ return data;
658
+ }
659
+ /**
660
+ * Extract design tokens from Figma file
661
+ */
662
+ async extractDesignTokens(url, downloadAssets = true, assetsDir) {
663
+ const fileData = await this.getFileData(url);
664
+ const fileKey = this.extractFileKey(url);
665
+ if (!fileKey) {
272
666
  throw new Error(`Invalid Figma URL: ${url}`);
273
667
  }
274
668
  const tokens = {
@@ -528,9 +922,9 @@ ${text}`);
528
922
  }, "Figma images listing");
529
923
  const imageData = await response.json();
530
924
  const images = imageData.images;
531
- const fullAssetsDir = assetsDir.startsWith("/") ? assetsDir : join6(process.cwd(), assetsDir);
925
+ const fullAssetsDir = assetsDir.startsWith("/") ? assetsDir : join7(process.cwd(), assetsDir);
532
926
  if (!existsSync2(fullAssetsDir)) {
533
- await mkdir3(fullAssetsDir, { recursive: true });
927
+ await mkdir4(fullAssetsDir, { recursive: true });
534
928
  }
535
929
  const downloadedAssets = [];
536
930
  for (const node of nodesToDownload) {
@@ -549,8 +943,8 @@ ${text}`);
549
943
  const safeName = node.name.replace(/[^a-z0-9]/gi, "_").toLowerCase().substring(0, 50);
550
944
  const extension = "png";
551
945
  const filename = `${safeName}_${node.id.substring(0, 8)}.${extension}`;
552
- const filePath = join6(fullAssetsDir, filename);
553
- await writeFile3(filePath, Buffer.from(imageBuffer));
946
+ const filePath = join7(fullAssetsDir, filename);
947
+ await writeFile4(filePath, Buffer.from(imageBuffer));
554
948
  downloadedAssets.push({
555
949
  nodeId: node.id,
556
950
  nodeName: node.name,
@@ -589,8 +983,8 @@ __export(figma_screen_developer_exports, {
589
983
  checkCurrentCodeStatus: () => checkCurrentCodeStatus,
590
984
  compareCodeWithFigma: () => compareCodeWithFigma
591
985
  });
592
- import { readFile as readFile4, readdir as readdir3 } from "fs/promises";
593
- import { join as join7 } from "path";
986
+ import { readFile as readFile5, readdir as readdir3 } from "fs/promises";
987
+ import { join as join8 } from "path";
594
988
  import { existsSync as existsSync3 } from "fs";
595
989
  async function checkCurrentCodeStatus(projectPath = process.cwd()) {
596
990
  const status = {
@@ -604,13 +998,13 @@ async function checkCurrentCodeStatus(projectPath = process.cwd()) {
604
998
  };
605
999
  try {
606
1000
  const htmlFiles = ["index.html", "index.htm", "main.html"].filter(
607
- (file) => existsSync3(join7(projectPath, file))
1001
+ (file) => existsSync3(join8(projectPath, file))
608
1002
  );
609
1003
  if (htmlFiles.length > 0) {
610
1004
  status.hasHTML = true;
611
1005
  status.htmlFile = htmlFiles[0];
612
1006
  try {
613
- const htmlContent = await readFile4(join7(projectPath, htmlFiles[0]), "utf-8");
1007
+ const htmlContent = await readFile5(join8(projectPath, htmlFiles[0]), "utf-8");
614
1008
  const sectionMatches = htmlContent.match(/<(section|div|header|footer|main|article|aside)[^>]*(?:id|class)=["']([^"']+)["']/gi);
615
1009
  if (sectionMatches) {
616
1010
  status.sections = sectionMatches.map((match) => {
@@ -622,19 +1016,19 @@ async function checkCurrentCodeStatus(projectPath = process.cwd()) {
622
1016
  } catch (e) {
623
1017
  }
624
1018
  }
625
- const stylesDir = join7(projectPath, "styles");
1019
+ const stylesDir = join8(projectPath, "styles");
626
1020
  if (existsSync3(stylesDir)) {
627
1021
  try {
628
1022
  const files = await readdir3(stylesDir);
629
1023
  const cssFiles = files.filter((f) => f.endsWith(".css"));
630
1024
  if (cssFiles.length > 0) {
631
1025
  status.hasCSS = true;
632
- status.cssFiles = cssFiles.map((f) => join7(stylesDir, f));
1026
+ status.cssFiles = cssFiles.map((f) => join8(stylesDir, f));
633
1027
  }
634
1028
  } catch (e) {
635
1029
  }
636
1030
  }
637
- const assetsDir = join7(projectPath, "assets", "images");
1031
+ const assetsDir = join8(projectPath, "assets", "images");
638
1032
  if (existsSync3(assetsDir)) {
639
1033
  try {
640
1034
  const files = await readdir3(assetsDir);
@@ -707,387 +1101,402 @@ var init_figma_screen_developer = __esm({
707
1101
  }
708
1102
  });
709
1103
 
710
- // src/core/memory.ts
711
- var memory_exports = {};
712
- __export(memory_exports, {
713
- MemoryManager: () => MemoryManager
1104
+ // src/core/beads.ts
1105
+ var beads_exports = {};
1106
+ __export(beads_exports, {
1107
+ BeadsIntegration: () => BeadsIntegration
714
1108
  });
715
- import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, access as access2, constants as constants2 } from "fs/promises";
716
- import { join as join8 } from "path";
717
- var MemoryManager;
718
- var init_memory = __esm({
719
- "src/core/memory.ts"() {
1109
+ import { readFile as readFile6, writeFile as writeFile5, readdir as readdir4, access as access3, constants as constants3, mkdir as mkdir5 } from "fs/promises";
1110
+ import { join as join9 } from "path";
1111
+ import { exec } from "child_process";
1112
+ import { promisify } from "util";
1113
+ var execAsync, BeadsIntegration;
1114
+ var init_beads = __esm({
1115
+ "src/core/beads.ts"() {
720
1116
  "use strict";
721
1117
  init_esm_shims();
722
1118
  init_paths();
723
- MemoryManager = class {
724
- config;
725
- constructor(config) {
726
- this.config = config;
1119
+ init_logger();
1120
+ execAsync = promisify(exec);
1121
+ BeadsIntegration = class {
1122
+ projectPath;
1123
+ constructor(projectPath) {
1124
+ this.projectPath = projectPath || process.cwd();
727
1125
  }
728
1126
  /**
729
- * List all memory entries
1127
+ * Check if Beads CLI is installed
730
1128
  */
731
- async list() {
732
- const memories = [];
733
- const memoryPath = paths.memory(this.config.configPath);
734
- const subDirs = ["observations", "handoffs", "research"];
735
- for (const subDir of subDirs) {
736
- const dirPath = join8(memoryPath, subDir);
737
- try {
738
- const { readdir: readdir8 } = await import("fs/promises");
739
- const files = await readdir8(dirPath);
740
- for (const file of files) {
741
- if (!file.endsWith(".md")) continue;
742
- const content = await readFile5(join8(dirPath, file), "utf-8");
743
- const summary = this.extractSummary(content);
744
- memories.push({
745
- key: `${subDir}/${file.replace(".md", "")}`,
746
- content,
747
- summary,
748
- createdAt: /* @__PURE__ */ new Date(),
749
- // Would get from file stats
750
- updatedAt: /* @__PURE__ */ new Date(),
751
- type: subDir
752
- });
753
- }
754
- } catch {
755
- }
1129
+ async isInstalled() {
1130
+ try {
1131
+ await execAsync("bd --version");
1132
+ return true;
1133
+ } catch {
1134
+ return false;
756
1135
  }
757
- return memories;
758
1136
  }
759
1137
  /**
760
- * Read a memory entry
1138
+ * Get Beads version
761
1139
  */
762
- async read(key) {
763
- const memoryPath = paths.memory(this.config.configPath);
764
- let filePath;
765
- if (key.includes("/")) {
766
- filePath = join8(memoryPath, `${key}.md`);
767
- } else {
768
- const subDirs = ["observations", "handoffs", "research", "_templates"];
769
- for (const subDir of subDirs) {
770
- const testPath = join8(memoryPath, subDir, `${key}.md`);
771
- try {
772
- await access2(testPath, constants2.R_OK);
773
- filePath = testPath;
774
- break;
775
- } catch {
776
- continue;
777
- }
778
- }
779
- filePath = filePath || join8(memoryPath, `${key}.md`);
780
- }
1140
+ async getVersion() {
781
1141
  try {
782
- return await readFile5(filePath, "utf-8");
1142
+ const { stdout } = await execAsync("bd --version");
1143
+ const match = stdout.match(/bd version (\S+)/);
1144
+ return match?.[1] || null;
783
1145
  } catch {
784
1146
  return null;
785
1147
  }
786
1148
  }
787
1149
  /**
788
- * Update a memory entry
1150
+ * Check if Beads is initialized in project
789
1151
  */
790
- async update(key, content, options) {
791
- const memoryPath = paths.memory(this.config.configPath);
792
- const type = options?.type || "custom";
793
- let filePath;
794
- if (key.includes("/")) {
795
- filePath = join8(memoryPath, `${key}.md`);
796
- } else {
797
- const subDir = type === "observation" ? "observations" : type === "handoff" ? "handoffs" : type === "research" ? "research" : "";
798
- filePath = join8(memoryPath, subDir, `${key}.md`);
1152
+ async isInitialized() {
1153
+ const beadsDir = paths.beadsDir(this.projectPath);
1154
+ try {
1155
+ await access3(beadsDir, constants3.R_OK);
1156
+ return true;
1157
+ } catch {
1158
+ return false;
799
1159
  }
800
- const { dirname: dirname2 } = await import("path");
801
- await mkdir4(dirname2(filePath), { recursive: true });
802
- if (options?.append) {
803
- try {
804
- const existing = await readFile5(filePath, "utf-8");
805
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
806
- content = `${existing}
807
-
808
- ---
809
- _Updated: ${timestamp}_
810
-
811
- ${content}`;
812
- } catch {
813
- }
1160
+ }
1161
+ /**
1162
+ * Install Beads CLI globally
1163
+ */
1164
+ async install() {
1165
+ try {
1166
+ await execAsync("npm install -g beads");
1167
+ return true;
1168
+ } catch (error) {
1169
+ logger.error("Failed to install Beads CLI:", error);
1170
+ return false;
814
1171
  }
815
- await writeFile4(filePath, content);
816
1172
  }
817
1173
  /**
818
- * Create a handoff bundle
1174
+ * Initialize Beads in project using bd CLI
819
1175
  */
820
- async createHandoff(summary) {
821
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
822
- const key = `handoffs/${timestamp}`;
823
- const content = `# Handoff: ${(/* @__PURE__ */ new Date()).toLocaleString()}
824
-
825
- ## Completed
826
- ${summary.completed.map((item) => `- [x] ${item}`).join("\n") || "- None"}
1176
+ async init() {
1177
+ try {
1178
+ await execAsync("bd init", { cwd: this.projectPath });
1179
+ logger.success("Beads initialized");
1180
+ return true;
1181
+ } catch (error) {
1182
+ logger.error("Failed to initialize Beads:", error);
1183
+ return false;
1184
+ }
1185
+ }
1186
+ /**
1187
+ * Initialize local .beads directory (works without global beads CLI)
1188
+ */
1189
+ async initLocal() {
1190
+ try {
1191
+ const beadsDir = paths.beadsDir(this.projectPath);
1192
+ await mkdir5(beadsDir, { recursive: true });
1193
+ const readmeContent = `# Beads - Task Tracking
827
1194
 
828
- ## In Progress
829
- ${summary.inProgress.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
1195
+ This directory contains task beads for tracking work items.
830
1196
 
831
- ## Remaining
832
- ${summary.remaining.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
1197
+ ## How it works
1198
+ - Each file is a task bead (bead-001.md, bead-002.md, etc.)
1199
+ - Status: todo, in-progress, completed, blocked
1200
+ - Use \`/create\` command to create new tasks
1201
+ - Use \`/finish\` command to complete tasks with quality gates
833
1202
 
834
- ## Context
835
- ${summary.context || "No additional context."}
1203
+ ## Beads CLI
1204
+ For full functionality, install beads globally:
1205
+ \`\`\`bash
1206
+ npm install -g beads
1207
+ bd init
1208
+ \`\`\`
836
1209
 
837
- ## Next Steps
838
- ${summary.nextSteps.map((step, i) => `${i + 1}. ${step}`).join("\n") || "- Continue from where left off"}
1210
+ ## Available Commands
1211
+ - \`bd ready\` - Show available work
1212
+ - \`bd show <id>\` - View task details
1213
+ - \`bd update <id> --status in_progress\` - Update task status
1214
+ - \`bd close <id>\` - Complete task
1215
+ - \`bd sync\` - Sync with git
839
1216
  `;
840
- await this.update(key, content, { type: "handoff" });
841
- return key;
1217
+ await writeFile5(join9(beadsDir, "README.md"), readmeContent);
1218
+ return true;
1219
+ } catch (error) {
1220
+ logger.error("Failed to initialize .beads directory:", error);
1221
+ return false;
1222
+ }
842
1223
  }
843
1224
  /**
844
- * Get the latest handoff
1225
+ * Get current status
845
1226
  */
846
- async getLatestHandoff() {
847
- const memories = await this.list();
848
- const handoffs = memories.filter((m) => m.type === "handoff");
849
- if (handoffs.length === 0) return null;
850
- handoffs.sort((a, b) => b.key.localeCompare(a.key));
851
- return handoffs[0];
1227
+ async getStatus() {
1228
+ const installed = await this.isInstalled();
1229
+ const version = await this.getVersion();
1230
+ const initialized = await this.isInitialized();
1231
+ let activeTasks = 0;
1232
+ let completedTasks = 0;
1233
+ let currentTask;
1234
+ if (initialized) {
1235
+ const beads = await this.listBeads();
1236
+ activeTasks = beads.filter((b) => b.status === "in-progress" || b.status === "todo").length;
1237
+ completedTasks = beads.filter((b) => b.status === "completed").length;
1238
+ const active = beads.find((b) => b.status === "in-progress");
1239
+ currentTask = active?.title;
1240
+ }
1241
+ return {
1242
+ installed,
1243
+ version: version || void 0,
1244
+ initialized,
1245
+ activeTasks,
1246
+ completedTasks,
1247
+ currentTask
1248
+ };
852
1249
  }
853
1250
  /**
854
- * Create an observation
1251
+ * List all beads in project
855
1252
  */
856
- async createObservation(title, observation) {
857
- const slug = title.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
858
- const key = `observations/${slug}`;
859
- const content = `# ${title}
860
-
861
- ## What
862
- ${observation.what}
863
-
864
- ## Why
865
- ${observation.why}
866
-
867
- ## Impact
868
- ${observation.impact}
869
-
870
- ${observation.tags?.length ? `## Tags
871
- ${observation.tags.map((t) => `- ${t}`).join("\n")}` : ""}
872
-
873
- ---
874
- _Created: ${(/* @__PURE__ */ new Date()).toISOString()}_
875
- `;
876
- await this.update(key, content, { type: "observation" });
877
- return key;
1253
+ async listBeads() {
1254
+ const beadsDir = paths.beadsDir(this.projectPath);
1255
+ const beads = [];
1256
+ try {
1257
+ const files = await readdir4(beadsDir);
1258
+ for (const file of files) {
1259
+ if (!file.match(/^bead-\d+\.md$/)) continue;
1260
+ const content = await readFile6(join9(beadsDir, file), "utf-8");
1261
+ const bead = this.parseBeadFile(file, content);
1262
+ if (bead) beads.push(bead);
1263
+ }
1264
+ } catch {
1265
+ }
1266
+ return beads.sort((a, b) => a.id.localeCompare(b.id));
878
1267
  }
879
1268
  /**
880
- * Save research findings
1269
+ * Get a specific bead
881
1270
  */
882
- async saveResearch(topic, findings) {
883
- const slug = topic.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
884
- const key = `research/${slug}`;
885
- const content = `# Research: ${topic}
886
-
887
- ## Summary
888
- ${findings.summary}
1271
+ async getBead(id) {
1272
+ const beadsDir = paths.beadsDir(this.projectPath);
1273
+ const fileName = id.endsWith(".md") ? id : `${id}.md`;
1274
+ try {
1275
+ const content = await readFile6(join9(beadsDir, fileName), "utf-8");
1276
+ return this.parseBeadFile(fileName, content);
1277
+ } catch {
1278
+ return null;
1279
+ }
1280
+ }
1281
+ /**
1282
+ * Create a new bead
1283
+ */
1284
+ async createBead(title, description) {
1285
+ const beadsDir = paths.beadsDir(this.projectPath);
1286
+ await mkdir5(beadsDir, { recursive: true });
1287
+ const beads = await this.listBeads();
1288
+ const maxId = beads.reduce((max, b) => {
1289
+ const num = parseInt(b.id.replace("bead-", ""), 10);
1290
+ return num > max ? num : max;
1291
+ }, 0);
1292
+ const id = `bead-${String(maxId + 1).padStart(3, "0")}`;
1293
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1294
+ const content = `---
1295
+ id: ${id}
1296
+ title: ${title}
1297
+ status: in-progress
1298
+ created: ${now}
1299
+ updated: ${now}
1300
+ ---
889
1301
 
890
- ## Sources
891
- ${findings.sources.map((s) => `- ${s}`).join("\n")}
1302
+ # ${title}
892
1303
 
893
- ## Recommendations
894
- ${findings.recommendations.map((r, i) => `${i + 1}. ${r}`).join("\n")}
1304
+ ## Description
1305
+ ${description}
895
1306
 
896
- ${findings.codeExamples ? `## Code Examples
897
- \`\`\`
898
- ${findings.codeExamples}
899
- \`\`\`` : ""}
1307
+ ## Notes
900
1308
 
901
- ---
902
- _Researched: ${(/* @__PURE__ */ new Date()).toISOString()}_
903
1309
  `;
904
- await this.update(key, content, { type: "research" });
905
- return key;
1310
+ await writeFile5(join9(beadsDir, `${id}.md`), content);
1311
+ return {
1312
+ id,
1313
+ title,
1314
+ description,
1315
+ status: "in-progress",
1316
+ createdAt: new Date(now),
1317
+ updatedAt: new Date(now),
1318
+ notes: []
1319
+ };
906
1320
  }
907
1321
  /**
908
- * Extract a summary from content (first paragraph or heading)
1322
+ * Update bead status
909
1323
  */
910
- extractSummary(content) {
911
- const lines = content.split("\n").filter((l) => l.trim());
912
- for (const line of lines) {
913
- if (!line.startsWith("#") && line.trim().length > 0) {
914
- return line.slice(0, 100) + (line.length > 100 ? "..." : "");
1324
+ async updateBeadStatus(id, status) {
1325
+ const beadsDir = paths.beadsDir(this.projectPath);
1326
+ const fileName = id.endsWith(".md") ? id : `${id}.md`;
1327
+ const filePath = join9(beadsDir, fileName);
1328
+ try {
1329
+ let content = await readFile6(filePath, "utf-8");
1330
+ content = content.replace(
1331
+ /status:\s*\w+/,
1332
+ `status: ${status}`
1333
+ );
1334
+ content = content.replace(
1335
+ /updated:\s*.+/,
1336
+ `updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
1337
+ );
1338
+ await writeFile5(filePath, content);
1339
+ return true;
1340
+ } catch {
1341
+ return false;
1342
+ }
1343
+ }
1344
+ /**
1345
+ * Add note to bead
1346
+ */
1347
+ async addNote(id, note) {
1348
+ const beadsDir = paths.beadsDir(this.projectPath);
1349
+ const fileName = id.endsWith(".md") ? id : `${id}.md`;
1350
+ const filePath = join9(beadsDir, fileName);
1351
+ try {
1352
+ let content = await readFile6(filePath, "utf-8");
1353
+ const notesMatch = content.match(/## Notes\n([\s\S]*?)(?=\n##|$)/);
1354
+ if (notesMatch) {
1355
+ const timestamp = (/* @__PURE__ */ new Date()).toLocaleString();
1356
+ const newNote = `- [${timestamp}] ${note}`;
1357
+ content = content.replace(
1358
+ notesMatch[0],
1359
+ `## Notes
1360
+ ${notesMatch[1]}${newNote}
1361
+ `
1362
+ );
915
1363
  }
1364
+ content = content.replace(
1365
+ /updated:\s*.+/,
1366
+ `updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
1367
+ );
1368
+ await writeFile5(filePath, content);
1369
+ return true;
1370
+ } catch {
1371
+ return false;
916
1372
  }
917
- if (lines[0]?.startsWith("#")) {
918
- return lines[0].replace(/^#+\s*/, "");
1373
+ }
1374
+ /**
1375
+ * Update bead type
1376
+ */
1377
+ async updateBeadType(id, type) {
1378
+ const beadsDir = paths.beadsDir(this.projectPath);
1379
+ const fileName = id.endsWith(".md") ? id : `${id}.md`;
1380
+ const filePath = join9(beadsDir, fileName);
1381
+ try {
1382
+ let content = await readFile6(filePath, "utf-8");
1383
+ const typeMatch = content.match(/^type:\s*.+/m);
1384
+ if (typeMatch) {
1385
+ content = content.replace(typeMatch[0], `type: ${type}`);
1386
+ } else {
1387
+ content = content.replace(/^---\n([\s\S]*?)\n---/, (match) => {
1388
+ return match.replace(/^---\n/, `---
1389
+ type: ${type}
1390
+ `);
1391
+ });
1392
+ }
1393
+ content = content.replace(
1394
+ /updated:\s*.+/,
1395
+ `updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
1396
+ );
1397
+ await writeFile5(filePath, content);
1398
+ return true;
1399
+ } catch {
1400
+ return false;
919
1401
  }
920
- return "No summary available";
921
1402
  }
922
- };
923
- }
924
- });
925
-
926
- // src/index.ts
927
- init_esm_shims();
928
-
929
- // src/core/config.ts
930
- init_esm_shims();
931
- init_paths();
932
- import { readFile, access, constants } from "fs/promises";
933
- import { join as join3 } from "path";
934
- import { z } from "zod";
935
-
936
- // src/utils/version.ts
937
- init_esm_shims();
938
- import { readFileSync } from "fs";
939
- import { fileURLToPath as fileURLToPath2 } from "url";
940
- import { dirname, join as join2 } from "path";
941
- function getVersion() {
942
- try {
943
- const __filename2 = fileURLToPath2(import.meta.url);
944
- const __dirname2 = dirname(__filename2);
945
- const packageJsonPath = join2(__dirname2, "..", "package.json");
946
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
947
- return packageJson.version;
948
- } catch (error) {
949
- console.warn("Warning: Could not read version from package.json, using fallback");
950
- return "0.0.0";
1403
+ /**
1404
+ * Complete a bead with quality gates
1405
+ */
1406
+ async completeBead(id) {
1407
+ const gates = [
1408
+ { name: "Type Check", command: "npm run typecheck" },
1409
+ { name: "Tests", command: "npm run test" },
1410
+ { name: "Lint", command: "npm run lint" },
1411
+ { name: "Build", command: "npm run build" }
1412
+ ];
1413
+ const results = [];
1414
+ for (const gate of gates) {
1415
+ try {
1416
+ await execAsync(gate.command, { cwd: this.projectPath });
1417
+ results.push({ name: gate.name, passed: true });
1418
+ logger.success(`${gate.name}: passed`);
1419
+ } catch (error) {
1420
+ results.push({
1421
+ name: gate.name,
1422
+ passed: false,
1423
+ error: error instanceof Error ? error.message.slice(0, 200) : "Failed"
1424
+ });
1425
+ logger.error(`${gate.name}: failed`);
1426
+ }
1427
+ }
1428
+ const allPassed = results.every((r) => r.passed);
1429
+ if (allPassed) {
1430
+ await this.updateBeadStatus(id, "completed");
1431
+ await this.addNote(id, "Task completed - all quality gates passed");
1432
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
1433
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1434
+ const config = await loadConfig2(this.projectPath);
1435
+ const memory = new MemoryManager2(config);
1436
+ await memory.createHandoff({
1437
+ completed: [id],
1438
+ inProgress: [],
1439
+ remaining: [],
1440
+ context: `Auto-generated handoff for completed task: ${id}`,
1441
+ nextSteps: ["Review completed work", "Start new task if needed"]
1442
+ });
1443
+ } else {
1444
+ await this.addNote(id, "Completion attempted but quality gates failed");
1445
+ }
1446
+ return {
1447
+ success: allPassed,
1448
+ gates: results,
1449
+ handoffCreated: allPassed
1450
+ };
1451
+ }
1452
+ /**
1453
+ * Get current active bead
1454
+ */
1455
+ async getCurrentBead() {
1456
+ const beads = await this.listBeads();
1457
+ return beads.find((b) => b.status === "in-progress") || null;
1458
+ }
1459
+ /**
1460
+ * Parse a bead file
1461
+ */
1462
+ parseBeadFile(fileName, content) {
1463
+ try {
1464
+ const id = fileName.replace(".md", "");
1465
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
1466
+ const frontmatter = frontmatterMatch?.[1] || "";
1467
+ const getValue = (key) => {
1468
+ const match = frontmatter.match(new RegExp(`${key}:\\s*(.+)`));
1469
+ return match?.[1]?.trim() || "";
1470
+ };
1471
+ const titleMatch = content.match(/^# (.+)/m);
1472
+ const title = getValue("title") || titleMatch?.[1] || id;
1473
+ const descMatch = content.match(/## Description\n([\s\S]*?)(?=\n##|$)/);
1474
+ const description = descMatch?.[1]?.trim() || "";
1475
+ const notesMatch = content.match(/## Notes\n([\s\S]*?)(?=\n##|$)/);
1476
+ const notesContent = notesMatch?.[1] || "";
1477
+ const notes = notesContent.split("\n").filter((line) => line.trim().startsWith("-")).map((line) => line.replace(/^-\s*/, "").trim());
1478
+ const type = getValue("type");
1479
+ return {
1480
+ id,
1481
+ title,
1482
+ description,
1483
+ status: getValue("status") || "todo",
1484
+ type,
1485
+ createdAt: new Date(getValue("created") || Date.now()),
1486
+ updatedAt: new Date(getValue("updated") || Date.now()),
1487
+ notes
1488
+ };
1489
+ } catch {
1490
+ return null;
1491
+ }
1492
+ }
1493
+ };
951
1494
  }
952
- }
953
-
954
- // src/core/config.ts
955
- var ConfigSchema = z.object({
956
- version: z.string(),
957
- skills: z.object({
958
- enabled: z.boolean().default(true),
959
- directory: z.string().optional()
960
- }).default({}),
961
- agents: z.object({
962
- enabled: z.boolean().default(true),
963
- default: z.string().default("build")
964
- }).default({}),
965
- commands: z.object({
966
- enabled: z.boolean().default(true)
967
- }).default({}),
968
- tools: z.object({
969
- enabled: z.boolean().default(true)
970
- }).default({}),
971
- plugins: z.object({
972
- enabled: z.boolean().default(true),
973
- autoload: z.array(z.string()).optional()
974
- }).default({}),
975
- memory: z.object({
976
- enabled: z.boolean().default(true),
977
- maxSize: z.number().optional()
978
- }).default({}),
979
- beads: z.object({
980
- enabled: z.boolean().default(true),
981
- autoInit: z.boolean().default(false)
982
- }).default({}),
983
- antiHallucination: z.object({
984
- enabled: z.boolean().default(true),
985
- specFile: z.string().default("spec.md"),
986
- reviewFile: z.string().default("review.md")
987
- }).default({}),
988
- mcp: z.object({
989
- context7: z.boolean().default(false),
990
- githubGrep: z.boolean().default(false),
991
- gkg: z.boolean().default(false)
992
- }).optional(),
993
- mode: z.string().default("build").optional()
994
1495
  });
995
- var Config = class {
996
- config;
997
- constructor(config) {
998
- this.config = config;
999
- }
1000
- get() {
1001
- return this.config;
1002
- }
1003
- get skills() {
1004
- return this.config.skills;
1005
- }
1006
- get agents() {
1007
- return this.config.agents;
1008
- }
1009
- get commands() {
1010
- return this.config.commands;
1011
- }
1012
- get tools() {
1013
- return this.config.tools;
1014
- }
1015
- get plugins() {
1016
- return this.config.plugins;
1017
- }
1018
- get memory() {
1019
- return this.config.memory;
1020
- }
1021
- get beads() {
1022
- return this.config.beads;
1023
- }
1024
- get antiHallucination() {
1025
- return this.config.antiHallucination;
1026
- }
1027
- get mode() {
1028
- return this.config.mode;
1029
- }
1030
- get configPath() {
1031
- return this.config.configPath;
1032
- }
1033
- get projectPath() {
1034
- return this.config.projectPath;
1035
- }
1036
- /**
1037
- * Get path to a specific resource
1038
- */
1039
- getPath(resource) {
1040
- return paths[resource](this.configPath);
1041
- }
1042
- };
1043
- async function loadConfig(projectPath) {
1044
- const project = projectPath || process.cwd();
1045
- const projectConfigPath = paths.projectConfig(project);
1046
- const globalConfigPath = paths.globalConfig();
1047
- let configPath;
1048
- let configData = {};
1049
- try {
1050
- await access(join3(globalConfigPath, "aikit.json"), constants.R_OK);
1051
- const globalContent = await readFile(join3(globalConfigPath, "aikit.json"), "utf-8");
1052
- configData = JSON.parse(globalContent);
1053
- configPath = globalConfigPath;
1054
- } catch {
1055
- configPath = projectConfigPath;
1056
- }
1057
- try {
1058
- await access(join3(projectConfigPath, "aikit.json"), constants.R_OK);
1059
- const projectContent = await readFile(join3(projectConfigPath, "aikit.json"), "utf-8");
1060
- const projectData = JSON.parse(projectContent);
1061
- configData = deepMerge(configData, projectData);
1062
- configPath = projectConfigPath;
1063
- } catch {
1064
- }
1065
- if (!configData.version) {
1066
- configData.version = getVersion();
1067
- }
1068
- const parsed = ConfigSchema.parse(configData);
1069
- return new Config({
1070
- ...parsed,
1071
- configPath,
1072
- projectPath: project
1073
- });
1074
- }
1075
- function deepMerge(base, override) {
1076
- const result = { ...base };
1077
- for (const key in override) {
1078
- const baseValue = base[key];
1079
- const overrideValue = override[key];
1080
- if (typeof baseValue === "object" && baseValue !== null && typeof overrideValue === "object" && overrideValue !== null && !Array.isArray(baseValue) && !Array.isArray(overrideValue)) {
1081
- result[key] = deepMerge(
1082
- baseValue,
1083
- overrideValue
1084
- );
1085
- } else if (overrideValue !== void 0) {
1086
- result[key] = overrideValue;
1087
- }
1088
- }
1089
- return result;
1090
- }
1496
+
1497
+ // src/index.ts
1498
+ init_esm_shims();
1499
+ init_config();
1091
1500
 
1092
1501
  // src/core/skills.ts
1093
1502
  init_esm_shims();
@@ -2535,8 +2944,8 @@ ${command.content}
2535
2944
  init_esm_shims();
2536
2945
  init_paths();
2537
2946
  init_logger();
2538
- import { readdir as readdir4, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
2539
- import { join as join9, extname as extname3 } from "path";
2947
+ import { readdir as readdir5, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
2948
+ import { join as join10, extname as extname3 } from "path";
2540
2949
  import { z as z2 } from "zod";
2541
2950
  var ToolArgSchema = z2.object({
2542
2951
  type: z2.enum(["string", "number", "boolean", "array", "object"]),
@@ -2602,8 +3011,28 @@ Note: Configure GitHub integration in AIKit settings for actual results.`;
2602
3011
  required: true
2603
3012
  }
2604
3013
  },
2605
- async execute({ key }) {
2606
- return `Memory read: ${key}`;
3014
+ async execute({ key }, context) {
3015
+ const config = context?.config;
3016
+ if (!config) {
3017
+ return "Error: Configuration not available";
3018
+ }
3019
+ try {
3020
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3021
+ const memory = new MemoryManager2(config);
3022
+ const content = await memory.read(key);
3023
+ if (!content) {
3024
+ return `Memory entry not found: ${key}
3025
+
3026
+ Available keys (use \`list_session\` or \`aikit memory list\`):
3027
+ - handoffs/
3028
+ - observations/
3029
+ - research/
3030
+ - _templates/`;
3031
+ }
3032
+ return content;
3033
+ } catch (error) {
3034
+ return `Error reading memory: ${error instanceof Error ? error.message : String(error)}`;
3035
+ }
2607
3036
  }
2608
3037
  },
2609
3038
  {
@@ -2627,8 +3056,22 @@ Note: Configure GitHub integration in AIKit settings for actual results.`;
2627
3056
  default: true
2628
3057
  }
2629
3058
  },
2630
- async execute({ key, append = true }) {
2631
- return `Memory updated: ${key} (append: ${append})`;
3059
+ async execute({ key, content, append = true }, context) {
3060
+ const config = context?.config;
3061
+ if (!config) {
3062
+ return "Error: Configuration not available";
3063
+ }
3064
+ if (typeof content !== "string") {
3065
+ return "Error: content must be a string";
3066
+ }
3067
+ try {
3068
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3069
+ const memory = new MemoryManager2(config);
3070
+ await memory.update(key, content, { append });
3071
+ return `\u2713 Saved to memory: ${key}${append ? " (appended)" : " (overwrote)"}`;
3072
+ } catch (error) {
3073
+ return `Error updating memory: ${error instanceof Error ? error.message : String(error)}`;
3074
+ }
2632
3075
  }
2633
3076
  },
2634
3077
  {
@@ -3001,7 +3444,7 @@ ${tokens.screens?.map((s, i) => `${i + 1}. ${s.name} (ID: ${s.id})`).join("\n")
3001
3444
  try {
3002
3445
  const fileData = await client.getFileData(figmaUrl);
3003
3446
  const projectPath = process.cwd();
3004
- const assetsDir = join9(projectPath, "assets", "images");
3447
+ const assetsDir = join10(projectPath, "assets", "images");
3005
3448
  const assets = await client.downloadAssets(fileKey, fileData.document, assetsDir, selectedScreen.id);
3006
3449
  downloadedAssets = assets || [];
3007
3450
  logger.info(`Downloaded ${downloadedAssets.length} assets for screen ${selectedScreen.name}`);
@@ -3079,105 +3522,332 @@ ${tokens.screens?.map((s, i) => `${i + 1}. ${s.name} (ID: ${s.id})`).join("\n")
3079
3522
 
3080
3523
  `;
3081
3524
  }
3082
- result += `
3083
- ## Next Steps
3525
+ result += `
3526
+ ## Next Steps
3527
+
3528
+ `;
3529
+ result += `1. Review the plan above
3530
+ `;
3531
+ result += `2. Use @build agent to implement missing sections
3532
+ `;
3533
+ result += `3. Use extracted design tokens for CSS variables
3534
+ `;
3535
+ result += `4. Use downloaded assets in HTML
3536
+ `;
3537
+ result += `5. Verify pixel-perfect match with Figma design
3538
+ `;
3539
+ return result;
3540
+ } catch (error) {
3541
+ return `Error: ${error instanceof Error ? error.message : String(error)}`;
3542
+ }
3543
+ }
3544
+ },
3545
+ {
3546
+ name: "list_session",
3547
+ description: "List previous sessions to discover what happened and when. Use this before read_session to find the right context.",
3548
+ args: {
3549
+ limit: {
3550
+ type: "number",
3551
+ description: "Maximum number of sessions to return (default: 10)",
3552
+ required: false,
3553
+ default: 10
3554
+ }
3555
+ },
3556
+ async execute({ limit = 10 }, context) {
3557
+ const config = context?.config;
3558
+ if (!config) {
3559
+ return "Error: Configuration not available";
3560
+ }
3561
+ const limitNum = typeof limit === "number" ? limit : 10;
3562
+ try {
3563
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3564
+ const memory = new MemoryManager2(config);
3565
+ const memories = await memory.list();
3566
+ const handoffs = memories.filter((m) => m.type === "handoff").sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()).slice(0, limitNum);
3567
+ if (handoffs.length === 0) {
3568
+ return "No previous sessions found. Use /handoff to create a session handoff.";
3569
+ }
3570
+ let result = `# Previous Sessions (${handoffs.length})
3571
+
3572
+ `;
3573
+ handoffs.forEach((handoff, index) => {
3574
+ const sessionId = handoff.key.replace("handoffs/", "");
3575
+ result += `${index + 1}. **${sessionId}**
3576
+ `;
3577
+ result += ` - Updated: ${handoff.updatedAt.toLocaleString()}
3578
+ `;
3579
+ result += ` - Summary: ${handoff.summary}
3580
+
3581
+ `;
3582
+ });
3583
+ result += `
3584
+ Use \`read_session\` with a session ID to load full context.`;
3585
+ return result;
3586
+ } catch (error) {
3587
+ return `Error listing sessions: ${error instanceof Error ? error.message : String(error)}`;
3588
+ }
3589
+ }
3590
+ },
3591
+ {
3592
+ name: "read_session",
3593
+ description: "Load context from a previous session. Returns session summary, user tasks, and file changes.",
3594
+ args: {
3595
+ sessionId: {
3596
+ type: "string",
3597
+ description: 'Session ID from list_session (e.g., "2024-01-15T10-30-00")',
3598
+ required: true
3599
+ }
3600
+ },
3601
+ async execute({ sessionId }, context) {
3602
+ const config = context?.config;
3603
+ if (!config) {
3604
+ return "Error: Configuration not available";
3605
+ }
3606
+ try {
3607
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3608
+ const memory = new MemoryManager2(config);
3609
+ const content = await memory.read(`handoffs/${sessionId}`);
3610
+ if (!content) {
3611
+ return `Session not found: ${sessionId}
3612
+
3613
+ Use \`list_session\` to see available sessions.`;
3614
+ }
3615
+ return `# Session Context: ${sessionId}
3616
+
3617
+ ${content}
3618
+
3619
+ ---
3620
+
3621
+ This context has been loaded. Use /resume to continue from this point.`;
3622
+ } catch (error) {
3623
+ return `Error reading session: ${error instanceof Error ? error.message : String(error)}`;
3624
+ }
3625
+ }
3626
+ },
3627
+ {
3628
+ name: "bead-create",
3629
+ description: "Create a new bead/task for tracking work",
3630
+ args: {
3631
+ title: {
3632
+ type: "string",
3633
+ description: "Title of bead/task",
3634
+ required: true
3635
+ },
3636
+ description: {
3637
+ type: "string",
3638
+ description: "Description of what needs to be done",
3639
+ required: true
3640
+ }
3641
+ },
3642
+ async execute({ title, description }, context) {
3643
+ const config = context?.config;
3644
+ if (!config) {
3645
+ return "Error: Configuration not available";
3646
+ }
3647
+ if (typeof title !== "string" || typeof description !== "string") {
3648
+ return "Error: title and description must be strings";
3649
+ }
3650
+ try {
3651
+ const { BeadsIntegration: BeadsIntegration2 } = await Promise.resolve().then(() => (init_beads(), beads_exports));
3652
+ const beads = new BeadsIntegration2(config.projectPath);
3653
+ const bead = await beads.createBead(title, description);
3654
+ return `\u2713 Created bead: ${bead.id}
3655
+
3656
+ Title: ${bead.title}
3657
+ Description: ${description}
3658
+
3659
+ Use \`bead-update-status\` to change status or \`bead-complete\` to finish with quality gates.`;
3660
+ } catch (error) {
3661
+ return `Error creating bead: ${error instanceof Error ? error.message : String(error)}`;
3662
+ }
3663
+ }
3664
+ },
3665
+ {
3666
+ name: "bead-update-status",
3667
+ description: "Update bead status (todo, in-progress, completed, blocked)",
3668
+ args: {
3669
+ id: {
3670
+ type: "string",
3671
+ description: 'Bead ID (e.g., "bead-001")',
3672
+ required: true
3673
+ },
3674
+ status: {
3675
+ type: "string",
3676
+ description: "New status: todo, in-progress, completed, blocked",
3677
+ required: true
3678
+ }
3679
+ },
3680
+ async execute({ id, status }, context) {
3681
+ const config = context?.config;
3682
+ if (!config) {
3683
+ return "Error: Configuration not available";
3684
+ }
3685
+ if (typeof id !== "string" || typeof status !== "string") {
3686
+ return "Error: id and status must be strings";
3687
+ }
3688
+ const validStatuses = ["todo", "in-progress", "completed", "blocked"];
3689
+ if (!validStatuses.includes(status)) {
3690
+ return `Error: Invalid status "${status}". Valid statuses: ${validStatuses.join(", ")}`;
3691
+ }
3692
+ try {
3693
+ const { BeadsIntegration: BeadsIntegration2 } = await Promise.resolve().then(() => (init_beads(), beads_exports));
3694
+ const beads = new BeadsIntegration2(config.projectPath);
3695
+ const success = await beads.updateBeadStatus(id, status);
3696
+ if (success) {
3697
+ return `\u2713 Updated bead ${id} to status: ${status}`;
3698
+ } else {
3699
+ return `Error: Bead ${id} not found`;
3700
+ }
3701
+ } catch (error) {
3702
+ return `Error updating bead status: ${error instanceof Error ? error.message : String(error)}`;
3703
+ }
3704
+ }
3705
+ },
3706
+ {
3707
+ name: "bead-complete",
3708
+ description: "Complete a bead with quality gates (typecheck, test, lint, build)",
3709
+ args: {
3710
+ id: {
3711
+ type: "string",
3712
+ description: 'Bead ID (e.g., "bead-001")',
3713
+ required: true
3714
+ }
3715
+ },
3716
+ async execute({ id }, context) {
3717
+ const config = context?.config;
3718
+ if (!config) {
3719
+ return "Error: Configuration not available";
3720
+ }
3721
+ if (typeof id !== "string") {
3722
+ return "Error: id must be a string";
3723
+ }
3724
+ try {
3725
+ const { BeadsIntegration: BeadsIntegration2 } = await Promise.resolve().then(() => (init_beads(), beads_exports));
3726
+ const beads = new BeadsIntegration2(config.projectPath);
3727
+ const result = await beads.completeBead(id);
3728
+ if (!result.success) {
3729
+ let errorReport = `\u274C Quality gates failed for bead ${id}:
3730
+
3731
+ `;
3732
+ result.gates.forEach((gate) => {
3733
+ const status = gate.passed ? "\u2713" : "\u2717";
3734
+ errorReport += `${status} ${gate.name}`;
3735
+ if (gate.error) {
3736
+ errorReport += `
3737
+ Error: ${gate.error}`;
3738
+ }
3739
+ errorReport += "\n";
3740
+ });
3741
+ return errorReport;
3742
+ }
3743
+ return `\u2713 Bead ${id} completed successfully!
3084
3744
 
3085
- `;
3086
- result += `1. Review the plan above
3087
- `;
3088
- result += `2. Use @build agent to implement missing sections
3089
- `;
3090
- result += `3. Use extracted design tokens for CSS variables
3091
- `;
3092
- result += `4. Use downloaded assets in HTML
3093
- `;
3094
- result += `5. Verify pixel-perfect match with Figma design
3095
- `;
3096
- return result;
3745
+ All quality gates passed:
3746
+ ${result.gates.map((g) => ` \u2713 ${g.name}`).join("\n")}`;
3097
3747
  } catch (error) {
3098
- return `Error: ${error instanceof Error ? error.message : String(error)}`;
3748
+ return `Error completing bead: ${error instanceof Error ? error.message : String(error)}`;
3099
3749
  }
3100
3750
  }
3101
3751
  },
3102
3752
  {
3103
- name: "list_session",
3104
- description: "List previous sessions to discover what happened and when. Use this before read_session to find the right context.",
3753
+ name: "bead-list",
3754
+ description: "List all beads in the project",
3105
3755
  args: {
3106
- limit: {
3107
- type: "number",
3108
- description: "Maximum number of sessions to return (default: 10)",
3109
- required: false,
3110
- default: 10
3756
+ filter: {
3757
+ type: "string",
3758
+ description: "Filter by status: todo, in-progress, completed, blocked (optional)",
3759
+ required: false
3111
3760
  }
3112
3761
  },
3113
- async execute({ limit = 10 }, context) {
3762
+ async execute({ filter }, context) {
3114
3763
  const config = context?.config;
3115
3764
  if (!config) {
3116
3765
  return "Error: Configuration not available";
3117
3766
  }
3118
- const limitNum = typeof limit === "number" ? limit : 10;
3767
+ if (filter !== void 0 && typeof filter !== "string") {
3768
+ return "Error: filter must be a string";
3769
+ }
3119
3770
  try {
3120
- const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3121
- const memory = new MemoryManager2(config);
3122
- const memories = await memory.list();
3123
- const handoffs = memories.filter((m) => m.type === "handoff").sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()).slice(0, limitNum);
3124
- if (handoffs.length === 0) {
3125
- return "No previous sessions found. Use /handoff to create a session handoff.";
3771
+ const { BeadsIntegration: BeadsIntegration2 } = await Promise.resolve().then(() => (init_beads(), beads_exports));
3772
+ const beads = new BeadsIntegration2(config.projectPath);
3773
+ const allBeads = await beads.listBeads();
3774
+ let filtered = allBeads;
3775
+ if (filter && ["todo", "in-progress", "completed", "blocked"].includes(filter)) {
3776
+ filtered = allBeads.filter((b) => b.status === filter);
3126
3777
  }
3127
- let result = `# Previous Sessions (${handoffs.length})
3778
+ if (filtered.length === 0) {
3779
+ return `No beads found${filter ? ` with status "${filter}"` : ""}`;
3780
+ }
3781
+ let result = `# Beads (${filtered.length})
3128
3782
 
3129
3783
  `;
3130
- handoffs.forEach((handoff, index) => {
3131
- const sessionId = handoff.key.replace("handoffs/", "");
3132
- result += `${index + 1}. **${sessionId}**
3784
+ filtered.forEach((bead) => {
3785
+ const statusEmoji = {
3786
+ "todo": "\u23F8\uFE0F",
3787
+ "in-progress": "\u{1F504}",
3788
+ "completed": "\u2705",
3789
+ "blocked": "\u{1F6AB}"
3790
+ }[bead.status] || "\u2753";
3791
+ result += `${statusEmoji} **${bead.id}**: ${bead.title}
3133
3792
  `;
3134
- result += ` - Updated: ${handoff.updatedAt.toLocaleString()}
3793
+ result += ` Status: ${bead.status}
3135
3794
  `;
3136
- result += ` - Summary: ${handoff.summary}
3137
-
3795
+ result += ` Updated: ${bead.updatedAt.toLocaleString()}
3138
3796
  `;
3797
+ if (bead.description) {
3798
+ const desc = bead.description.length > 100 ? bead.description.slice(0, 100) + "..." : bead.description;
3799
+ result += ` Description: ${desc}
3800
+ `;
3801
+ }
3802
+ result += "\n";
3139
3803
  });
3140
- result += `
3141
- Use \`read_session\` with a session ID to load full context.`;
3142
- return result;
3804
+ return result.trim();
3143
3805
  } catch (error) {
3144
- return `Error listing sessions: ${error instanceof Error ? error.message : String(error)}`;
3806
+ return `Error listing beads: ${error instanceof Error ? error.message : String(error)}`;
3145
3807
  }
3146
3808
  }
3147
3809
  },
3148
3810
  {
3149
- name: "read_session",
3150
- description: "Load context from a previous session. Returns session summary, user tasks, and file changes.",
3811
+ name: "bead-update-type",
3812
+ description: "Update the type of an existing bead (feature, pattern, decision, knowledge)",
3151
3813
  args: {
3152
- sessionId: {
3814
+ id: {
3153
3815
  type: "string",
3154
- description: 'Session ID from list_session (e.g., "2024-01-15T10-30-00")',
3816
+ description: 'Bead ID (e.g., "bead-001")',
3817
+ required: true
3818
+ },
3819
+ type: {
3820
+ type: "string",
3821
+ description: "New type: feature, pattern, decision, or knowledge",
3155
3822
  required: true
3156
3823
  }
3157
3824
  },
3158
- async execute({ sessionId }, context) {
3825
+ async execute({ id, type }, context) {
3159
3826
  const config = context?.config;
3160
3827
  if (!config) {
3161
3828
  return "Error: Configuration not available";
3162
3829
  }
3830
+ if (typeof id !== "string") {
3831
+ return "Error: id must be a string";
3832
+ }
3833
+ if (typeof type !== "string") {
3834
+ return "Error: type must be a string";
3835
+ }
3836
+ const validTypes = ["feature", "pattern", "decision", "knowledge"];
3837
+ if (!validTypes.includes(type)) {
3838
+ return `Error: Invalid type "${type}". Valid types: ${validTypes.join(", ")}`;
3839
+ }
3163
3840
  try {
3164
- const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3165
- const memory = new MemoryManager2(config);
3166
- const content = await memory.read(`handoffs/${sessionId}`);
3167
- if (!content) {
3168
- return `Session not found: ${sessionId}
3169
-
3170
- Use \`list_session\` to see available sessions.`;
3841
+ const { BeadsIntegration: BeadsIntegration2 } = await Promise.resolve().then(() => (init_beads(), beads_exports));
3842
+ const beads = new BeadsIntegration2(config.projectPath);
3843
+ const success = await beads.updateBeadType(id, type);
3844
+ if (success) {
3845
+ return `\u2713 Updated bead ${id} type to: ${type}`;
3846
+ } else {
3847
+ return `Error: Bead ${id} not found`;
3171
3848
  }
3172
- return `# Session Context: ${sessionId}
3173
-
3174
- ${content}
3175
-
3176
- ---
3177
-
3178
- This context has been loaded. Use /resume to continue from this point.`;
3179
3849
  } catch (error) {
3180
- return `Error reading session: ${error instanceof Error ? error.message : String(error)}`;
3850
+ return `Error updating bead type: ${error instanceof Error ? error.message : String(error)}`;
3181
3851
  }
3182
3852
  }
3183
3853
  }
@@ -3232,6 +3902,7 @@ var ToolRegistry = class {
3232
3902
  }
3233
3903
  const mergedContext = {
3234
3904
  ...context,
3905
+ config: this.config,
3235
3906
  toolConfigManager: context?.toolConfigManager || this.toolConfigManager
3236
3907
  };
3237
3908
  if (tool.execute.length === 2) {
@@ -3245,9 +3916,9 @@ var ToolRegistry = class {
3245
3916
  async createTool(name, options) {
3246
3917
  const configPath = options.global ? paths.globalConfig() : this.config.configPath;
3247
3918
  const toolsDir = paths.tools(configPath);
3248
- await mkdir5(toolsDir, { recursive: true });
3919
+ await mkdir6(toolsDir, { recursive: true });
3249
3920
  const fileName = `${name}.ts`;
3250
- const filePath = join9(toolsDir, fileName);
3921
+ const filePath = join10(toolsDir, fileName);
3251
3922
  const argsSchema = Object.entries(options.args).map(([argName, arg]) => ` ${argName}: {
3252
3923
  type: '${arg.type}',
3253
3924
  description: '${arg.description}',
@@ -3266,7 +3937,7 @@ ${options.code}
3266
3937
  }
3267
3938
  });
3268
3939
  `;
3269
- await writeFile5(filePath, content);
3940
+ await writeFile6(filePath, content);
3270
3941
  }
3271
3942
  /**
3272
3943
  * Format tool for agent consumption
@@ -3295,13 +3966,13 @@ ${argsDesc}
3295
3966
  async loadToolsFromDir(dir) {
3296
3967
  let files;
3297
3968
  try {
3298
- files = await readdir4(dir);
3969
+ files = await readdir5(dir);
3299
3970
  } catch {
3300
3971
  return;
3301
3972
  }
3302
3973
  for (const file of files) {
3303
3974
  if (extname3(file) !== ".ts" && extname3(file) !== ".js") continue;
3304
- const filePath = join9(dir, file);
3975
+ const filePath = join10(dir, file);
3305
3976
  try {
3306
3977
  const toolModule = await import(`file://${filePath}`);
3307
3978
  const tool = toolModule.default;
@@ -3320,8 +3991,8 @@ ${argsDesc}
3320
3991
  init_esm_shims();
3321
3992
  init_paths();
3322
3993
  init_logger();
3323
- import { readdir as readdir5, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
3324
- import { join as join10, basename as basename3, extname as extname4 } from "path";
3994
+ import { readdir as readdir6, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
3995
+ import { join as join11, basename as basename3, extname as extname4 } from "path";
3325
3996
  var PluginSystem = class {
3326
3997
  config;
3327
3998
  plugins = /* @__PURE__ */ new Map();
@@ -3419,9 +4090,9 @@ var PluginSystem = class {
3419
4090
  async createPlugin(name, options) {
3420
4091
  const configPath = options.global ? paths.globalConfig() : this.config.configPath;
3421
4092
  const pluginsDir = paths.plugins(configPath);
3422
- await mkdir6(pluginsDir, { recursive: true });
4093
+ await mkdir7(pluginsDir, { recursive: true });
3423
4094
  const fileName = `${name}.ts`;
3424
- const filePath = join10(pluginsDir, fileName);
4095
+ const filePath = join11(pluginsDir, fileName);
3425
4096
  const content = `import { Plugin } from 'aikit';
3426
4097
 
3427
4098
  /**
@@ -3437,7 +4108,7 @@ ${options.code}
3437
4108
 
3438
4109
  export default ${toPascalCase(name)}Plugin;
3439
4110
  `;
3440
- await writeFile6(filePath, content);
4111
+ await writeFile7(filePath, content);
3441
4112
  }
3442
4113
  /**
3443
4114
  * Process event queue
@@ -3505,13 +4176,13 @@ export default ${toPascalCase(name)}Plugin;
3505
4176
  async loadPluginsFromDir(dir) {
3506
4177
  let files;
3507
4178
  try {
3508
- files = await readdir5(dir);
4179
+ files = await readdir6(dir);
3509
4180
  } catch {
3510
4181
  return;
3511
4182
  }
3512
4183
  for (const file of files) {
3513
4184
  if (extname4(file) !== ".ts" && extname4(file) !== ".js") continue;
3514
- const filePath = join10(dir, file);
4185
+ const filePath = join11(dir, file);
3515
4186
  const name = basename3(file, extname4(file));
3516
4187
  this.plugins.set(name, {
3517
4188
  name,
@@ -3612,355 +4283,7 @@ function toPascalCase(str) {
3612
4283
 
3613
4284
  // src/index.ts
3614
4285
  init_memory();
3615
-
3616
- // src/core/beads.ts
3617
- init_esm_shims();
3618
- init_paths();
3619
- init_logger();
3620
- import { readFile as readFile6, writeFile as writeFile7, readdir as readdir6, access as access3, constants as constants3, mkdir as mkdir7 } from "fs/promises";
3621
- import { join as join11 } from "path";
3622
- import { exec } from "child_process";
3623
- import { promisify } from "util";
3624
- var execAsync = promisify(exec);
3625
- var BeadsIntegration = class {
3626
- projectPath;
3627
- constructor(projectPath) {
3628
- this.projectPath = projectPath || process.cwd();
3629
- }
3630
- /**
3631
- * Check if Beads CLI is installed
3632
- */
3633
- async isInstalled() {
3634
- try {
3635
- await execAsync("bd --version");
3636
- return true;
3637
- } catch {
3638
- return false;
3639
- }
3640
- }
3641
- /**
3642
- * Get Beads version
3643
- */
3644
- async getVersion() {
3645
- try {
3646
- const { stdout } = await execAsync("bd --version");
3647
- const match = stdout.match(/bd version (\S+)/);
3648
- return match?.[1] || null;
3649
- } catch {
3650
- return null;
3651
- }
3652
- }
3653
- /**
3654
- * Check if Beads is initialized in project
3655
- */
3656
- async isInitialized() {
3657
- const beadsDir = paths.beadsDir(this.projectPath);
3658
- try {
3659
- await access3(beadsDir, constants3.R_OK);
3660
- return true;
3661
- } catch {
3662
- return false;
3663
- }
3664
- }
3665
- /**
3666
- * Install Beads CLI globally
3667
- */
3668
- async install() {
3669
- try {
3670
- await execAsync("npm install -g beads");
3671
- return true;
3672
- } catch (error) {
3673
- logger.error("Failed to install Beads CLI:", error);
3674
- return false;
3675
- }
3676
- }
3677
- /**
3678
- * Initialize Beads in project using bd CLI
3679
- */
3680
- async init() {
3681
- try {
3682
- await execAsync("bd init", { cwd: this.projectPath });
3683
- logger.success("Beads initialized");
3684
- return true;
3685
- } catch (error) {
3686
- logger.error("Failed to initialize Beads:", error);
3687
- return false;
3688
- }
3689
- }
3690
- /**
3691
- * Initialize local .beads directory (works without global beads CLI)
3692
- */
3693
- async initLocal() {
3694
- try {
3695
- const beadsDir = paths.beadsDir(this.projectPath);
3696
- await mkdir7(beadsDir, { recursive: true });
3697
- const readmeContent = `# Beads - Task Tracking
3698
-
3699
- This directory contains task beads for tracking work items.
3700
-
3701
- ## How it works
3702
- - Each file is a task bead (bead-001.md, bead-002.md, etc.)
3703
- - Status: todo, in-progress, completed, blocked
3704
- - Use \`/create\` command to create new tasks
3705
- - Use \`/finish\` command to complete tasks with quality gates
3706
-
3707
- ## Beads CLI
3708
- For full functionality, install beads globally:
3709
- \`\`\`bash
3710
- npm install -g beads
3711
- bd init
3712
- \`\`\`
3713
-
3714
- ## Available Commands
3715
- - \`bd ready\` - Show available work
3716
- - \`bd show <id>\` - View task details
3717
- - \`bd update <id> --status in_progress\` - Update task status
3718
- - \`bd close <id>\` - Complete task
3719
- - \`bd sync\` - Sync with git
3720
- `;
3721
- await writeFile7(join11(beadsDir, "README.md"), readmeContent);
3722
- return true;
3723
- } catch (error) {
3724
- logger.error("Failed to initialize .beads directory:", error);
3725
- return false;
3726
- }
3727
- }
3728
- /**
3729
- * Get current status
3730
- */
3731
- async getStatus() {
3732
- const installed = await this.isInstalled();
3733
- const version = await this.getVersion();
3734
- const initialized = await this.isInitialized();
3735
- let activeTasks = 0;
3736
- let completedTasks = 0;
3737
- let currentTask;
3738
- if (initialized) {
3739
- const beads = await this.listBeads();
3740
- activeTasks = beads.filter((b) => b.status === "in-progress" || b.status === "todo").length;
3741
- completedTasks = beads.filter((b) => b.status === "completed").length;
3742
- const active = beads.find((b) => b.status === "in-progress");
3743
- currentTask = active?.title;
3744
- }
3745
- return {
3746
- installed,
3747
- version: version || void 0,
3748
- initialized,
3749
- activeTasks,
3750
- completedTasks,
3751
- currentTask
3752
- };
3753
- }
3754
- /**
3755
- * List all beads in the project
3756
- */
3757
- async listBeads() {
3758
- const beadsDir = paths.beadsDir(this.projectPath);
3759
- const beads = [];
3760
- try {
3761
- const files = await readdir6(beadsDir);
3762
- for (const file of files) {
3763
- if (!file.match(/^bead-\d+\.md$/)) continue;
3764
- const content = await readFile6(join11(beadsDir, file), "utf-8");
3765
- const bead = this.parseBeadFile(file, content);
3766
- if (bead) beads.push(bead);
3767
- }
3768
- } catch {
3769
- }
3770
- return beads.sort((a, b) => a.id.localeCompare(b.id));
3771
- }
3772
- /**
3773
- * Get a specific bead
3774
- */
3775
- async getBead(id) {
3776
- const beadsDir = paths.beadsDir(this.projectPath);
3777
- const fileName = id.endsWith(".md") ? id : `${id}.md`;
3778
- try {
3779
- const content = await readFile6(join11(beadsDir, fileName), "utf-8");
3780
- return this.parseBeadFile(fileName, content);
3781
- } catch {
3782
- return null;
3783
- }
3784
- }
3785
- /**
3786
- * Create a new bead
3787
- */
3788
- async createBead(title, description) {
3789
- const beadsDir = paths.beadsDir(this.projectPath);
3790
- await mkdir7(beadsDir, { recursive: true });
3791
- const beads = await this.listBeads();
3792
- const maxId = beads.reduce((max, b) => {
3793
- const num = parseInt(b.id.replace("bead-", ""), 10);
3794
- return num > max ? num : max;
3795
- }, 0);
3796
- const id = `bead-${String(maxId + 1).padStart(3, "0")}`;
3797
- const now = (/* @__PURE__ */ new Date()).toISOString();
3798
- const content = `---
3799
- id: ${id}
3800
- title: ${title}
3801
- status: in-progress
3802
- created: ${now}
3803
- updated: ${now}
3804
- ---
3805
-
3806
- # ${title}
3807
-
3808
- ## Description
3809
- ${description}
3810
-
3811
- ## Notes
3812
-
3813
-
3814
- ## Checklist
3815
- - [ ] Requirements understood
3816
- - [ ] Implementation complete
3817
- - [ ] Tests passing
3818
- - [ ] Code reviewed
3819
-
3820
- ## Progress
3821
-
3822
- `;
3823
- await writeFile7(join11(beadsDir, `${id}.md`), content);
3824
- return {
3825
- id,
3826
- title,
3827
- description,
3828
- status: "in-progress",
3829
- createdAt: new Date(now),
3830
- updatedAt: new Date(now),
3831
- notes: []
3832
- };
3833
- }
3834
- /**
3835
- * Update bead status
3836
- */
3837
- async updateBeadStatus(id, status) {
3838
- const beadsDir = paths.beadsDir(this.projectPath);
3839
- const fileName = id.endsWith(".md") ? id : `${id}.md`;
3840
- const filePath = join11(beadsDir, fileName);
3841
- try {
3842
- let content = await readFile6(filePath, "utf-8");
3843
- content = content.replace(
3844
- /status:\s*\w+/,
3845
- `status: ${status}`
3846
- );
3847
- content = content.replace(
3848
- /updated:\s*.+/,
3849
- `updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
3850
- );
3851
- await writeFile7(filePath, content);
3852
- return true;
3853
- } catch {
3854
- return false;
3855
- }
3856
- }
3857
- /**
3858
- * Add note to bead
3859
- */
3860
- async addNote(id, note) {
3861
- const beadsDir = paths.beadsDir(this.projectPath);
3862
- const fileName = id.endsWith(".md") ? id : `${id}.md`;
3863
- const filePath = join11(beadsDir, fileName);
3864
- try {
3865
- let content = await readFile6(filePath, "utf-8");
3866
- const notesMatch = content.match(/## Notes\n([\s\S]*?)(?=\n##|$)/);
3867
- if (notesMatch) {
3868
- const timestamp = (/* @__PURE__ */ new Date()).toLocaleString();
3869
- const newNote = `- [${timestamp}] ${note}`;
3870
- content = content.replace(
3871
- notesMatch[0],
3872
- `## Notes
3873
- ${notesMatch[1]}${newNote}
3874
- `
3875
- );
3876
- }
3877
- content = content.replace(
3878
- /updated:\s*.+/,
3879
- `updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
3880
- );
3881
- await writeFile7(filePath, content);
3882
- return true;
3883
- } catch {
3884
- return false;
3885
- }
3886
- }
3887
- /**
3888
- * Complete a bead with quality gates
3889
- */
3890
- async completeBead(id) {
3891
- const gates = [
3892
- { name: "Type Check", command: "npm run typecheck" },
3893
- { name: "Tests", command: "npm run test" },
3894
- { name: "Lint", command: "npm run lint" },
3895
- { name: "Build", command: "npm run build" }
3896
- ];
3897
- const results = [];
3898
- for (const gate of gates) {
3899
- try {
3900
- await execAsync(gate.command, { cwd: this.projectPath });
3901
- results.push({ name: gate.name, passed: true });
3902
- logger.success(`${gate.name}: passed`);
3903
- } catch (error) {
3904
- results.push({
3905
- name: gate.name,
3906
- passed: false,
3907
- error: error instanceof Error ? error.message.slice(0, 200) : "Failed"
3908
- });
3909
- logger.error(`${gate.name}: failed`);
3910
- }
3911
- }
3912
- const allPassed = results.every((r) => r.passed);
3913
- if (allPassed) {
3914
- await this.updateBeadStatus(id, "completed");
3915
- await this.addNote(id, "Task completed - all quality gates passed");
3916
- } else {
3917
- await this.addNote(id, "Completion attempted but quality gates failed");
3918
- }
3919
- return {
3920
- success: allPassed,
3921
- gates: results
3922
- };
3923
- }
3924
- /**
3925
- * Get current active bead
3926
- */
3927
- async getCurrentBead() {
3928
- const beads = await this.listBeads();
3929
- return beads.find((b) => b.status === "in-progress") || null;
3930
- }
3931
- /**
3932
- * Parse a bead file
3933
- */
3934
- parseBeadFile(fileName, content) {
3935
- try {
3936
- const id = fileName.replace(".md", "");
3937
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
3938
- const frontmatter = frontmatterMatch?.[1] || "";
3939
- const getValue = (key) => {
3940
- const match = frontmatter.match(new RegExp(`${key}:\\s*(.+)`));
3941
- return match?.[1]?.trim() || "";
3942
- };
3943
- const titleMatch = content.match(/^# (.+)/m);
3944
- const title = getValue("title") || titleMatch?.[1] || id;
3945
- const descMatch = content.match(/## Description\n([\s\S]*?)(?=\n##|$)/);
3946
- const description = descMatch?.[1]?.trim() || "";
3947
- const notesMatch = content.match(/## Notes\n([\s\S]*?)(?=\n##|$)/);
3948
- const notesContent = notesMatch?.[1] || "";
3949
- const notes = notesContent.split("\n").filter((line) => line.trim().startsWith("-")).map((line) => line.replace(/^-\s*/, "").trim());
3950
- return {
3951
- id,
3952
- title,
3953
- description,
3954
- status: getValue("status") || "todo",
3955
- createdAt: new Date(getValue("created") || Date.now()),
3956
- updatedAt: new Date(getValue("updated") || Date.now()),
3957
- notes
3958
- };
3959
- } catch {
3960
- return null;
3961
- }
3962
- }
3963
- };
4286
+ init_beads();
3964
4287
 
3965
4288
  // src/core/anti-hallucination.ts
3966
4289
  init_esm_shims();
@@ -4234,6 +4557,7 @@ List approved dependencies here.
4234
4557
  // src/index.ts
4235
4558
  init_logger();
4236
4559
  init_paths();
4560
+ init_version();
4237
4561
  export {
4238
4562
  AgentManager,
4239
4563
  AntiHallucination,