@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/cli.js CHANGED
@@ -127,6 +127,184 @@ var init_paths = __esm({
127
127
  }
128
128
  });
129
129
 
130
+ // src/utils/version.ts
131
+ import { readFileSync } from "fs";
132
+ import { fileURLToPath as fileURLToPath2 } from "url";
133
+ import { dirname, join as join2 } from "path";
134
+ function getVersion() {
135
+ try {
136
+ const __filename2 = fileURLToPath2(import.meta.url);
137
+ const __dirname2 = dirname(__filename2);
138
+ const packageJsonPath = join2(__dirname2, "..", "package.json");
139
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
140
+ return packageJson.version;
141
+ } catch (error) {
142
+ console.warn("Warning: Could not read version from package.json, using fallback");
143
+ return "0.0.0";
144
+ }
145
+ }
146
+ var init_version = __esm({
147
+ "src/utils/version.ts"() {
148
+ "use strict";
149
+ init_esm_shims();
150
+ }
151
+ });
152
+
153
+ // src/core/config.ts
154
+ var config_exports = {};
155
+ __export(config_exports, {
156
+ Config: () => Config,
157
+ loadConfig: () => loadConfig
158
+ });
159
+ import { readFile, access, constants } from "fs/promises";
160
+ import { join as join3 } from "path";
161
+ import { z } from "zod";
162
+ async function loadConfig(projectPath) {
163
+ const project = projectPath || process.cwd();
164
+ const projectConfigPath = paths.projectConfig(project);
165
+ const globalConfigPath = paths.globalConfig();
166
+ let configPath;
167
+ let configData = {};
168
+ try {
169
+ await access(join3(globalConfigPath, "aikit.json"), constants.R_OK);
170
+ const globalContent = await readFile(join3(globalConfigPath, "aikit.json"), "utf-8");
171
+ configData = JSON.parse(globalContent);
172
+ configPath = globalConfigPath;
173
+ } catch {
174
+ configPath = projectConfigPath;
175
+ }
176
+ try {
177
+ await access(join3(projectConfigPath, "aikit.json"), constants.R_OK);
178
+ const projectContent = await readFile(join3(projectConfigPath, "aikit.json"), "utf-8");
179
+ const projectData = JSON.parse(projectContent);
180
+ configData = deepMerge(configData, projectData);
181
+ configPath = projectConfigPath;
182
+ } catch {
183
+ }
184
+ if (!configData.version) {
185
+ configData.version = getVersion();
186
+ }
187
+ const parsed = ConfigSchema.parse(configData);
188
+ return new Config({
189
+ ...parsed,
190
+ configPath,
191
+ projectPath: project
192
+ });
193
+ }
194
+ function deepMerge(base, override) {
195
+ const result = { ...base };
196
+ for (const key in override) {
197
+ const baseValue = base[key];
198
+ const overrideValue = override[key];
199
+ if (typeof baseValue === "object" && baseValue !== null && typeof overrideValue === "object" && overrideValue !== null && !Array.isArray(baseValue) && !Array.isArray(overrideValue)) {
200
+ result[key] = deepMerge(
201
+ baseValue,
202
+ overrideValue
203
+ );
204
+ } else if (overrideValue !== void 0) {
205
+ result[key] = overrideValue;
206
+ }
207
+ }
208
+ return result;
209
+ }
210
+ var ConfigSchema, Config;
211
+ var init_config = __esm({
212
+ "src/core/config.ts"() {
213
+ "use strict";
214
+ init_esm_shims();
215
+ init_paths();
216
+ init_version();
217
+ ConfigSchema = z.object({
218
+ version: z.string(),
219
+ skills: z.object({
220
+ enabled: z.boolean().default(true),
221
+ directory: z.string().optional()
222
+ }).default({}),
223
+ agents: z.object({
224
+ enabled: z.boolean().default(true),
225
+ default: z.string().default("build")
226
+ }).default({}),
227
+ commands: z.object({
228
+ enabled: z.boolean().default(true)
229
+ }).default({}),
230
+ tools: z.object({
231
+ enabled: z.boolean().default(true)
232
+ }).default({}),
233
+ plugins: z.object({
234
+ enabled: z.boolean().default(true),
235
+ autoload: z.array(z.string()).optional()
236
+ }).default({}),
237
+ memory: z.object({
238
+ enabled: z.boolean().default(true),
239
+ maxSize: z.number().optional()
240
+ }).default({}),
241
+ beads: z.object({
242
+ enabled: z.boolean().default(true),
243
+ autoInit: z.boolean().default(false)
244
+ }).default({}),
245
+ antiHallucination: z.object({
246
+ enabled: z.boolean().default(true),
247
+ specFile: z.string().default("spec.md"),
248
+ reviewFile: z.string().default("review.md")
249
+ }).default({}),
250
+ mcp: z.object({
251
+ context7: z.boolean().default(false),
252
+ githubGrep: z.boolean().default(false),
253
+ gkg: z.boolean().default(false)
254
+ }).optional(),
255
+ mode: z.string().default("build").optional()
256
+ });
257
+ Config = class {
258
+ config;
259
+ constructor(config) {
260
+ this.config = config;
261
+ }
262
+ get() {
263
+ return this.config;
264
+ }
265
+ get skills() {
266
+ return this.config.skills;
267
+ }
268
+ get agents() {
269
+ return this.config.agents;
270
+ }
271
+ get commands() {
272
+ return this.config.commands;
273
+ }
274
+ get tools() {
275
+ return this.config.tools;
276
+ }
277
+ get plugins() {
278
+ return this.config.plugins;
279
+ }
280
+ get memory() {
281
+ return this.config.memory;
282
+ }
283
+ get beads() {
284
+ return this.config.beads;
285
+ }
286
+ get antiHallucination() {
287
+ return this.config.antiHallucination;
288
+ }
289
+ get mode() {
290
+ return this.config.mode;
291
+ }
292
+ get configPath() {
293
+ return this.config.configPath;
294
+ }
295
+ get projectPath() {
296
+ return this.config.projectPath;
297
+ }
298
+ /**
299
+ * Get path to a specific resource
300
+ */
301
+ getPath(resource) {
302
+ return paths[resource](this.configPath);
303
+ }
304
+ };
305
+ }
306
+ });
307
+
130
308
  // src/utils/logger.ts
131
309
  import chalk from "chalk";
132
310
  var logger;
@@ -169,112 +347,328 @@ ${message}
169
347
  }
170
348
  });
171
349
 
172
- // src/core/tools/figma-mcp.ts
173
- var figma_mcp_exports = {};
174
- __export(figma_mcp_exports, {
175
- FigmaMcpClient: () => FigmaMcpClient
350
+ // src/core/memory.ts
351
+ var memory_exports = {};
352
+ __export(memory_exports, {
353
+ MemoryManager: () => MemoryManager
176
354
  });
177
- import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
355
+ import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3, access as access2, constants as constants2 } from "fs/promises";
178
356
  import { join as join6 } from "path";
179
- import { existsSync as existsSync2 } from "fs";
180
- var FigmaMcpClient;
181
- var init_figma_mcp = __esm({
182
- "src/core/tools/figma-mcp.ts"() {
357
+ var MemoryManager;
358
+ var init_memory = __esm({
359
+ "src/core/memory.ts"() {
183
360
  "use strict";
184
361
  init_esm_shims();
185
- init_logger();
186
- FigmaMcpClient = class {
187
- apiKey;
188
- constructor(apiKey, _configManager) {
189
- this.apiKey = apiKey;
362
+ init_paths();
363
+ MemoryManager = class {
364
+ config;
365
+ constructor(config) {
366
+ this.config = config;
190
367
  }
191
368
  /**
192
- * Fetch helper with simple retry/backoff for 429/5xx
369
+ * List all memory entries
193
370
  */
194
- async fetchWithRetry(url, options, label, retries = 3, backoffMs = 1500) {
195
- let attempt = 0;
196
- let lastError;
197
- while (attempt <= retries) {
371
+ async list() {
372
+ const memories = [];
373
+ const memoryPath = paths.memory(this.config.configPath);
374
+ const subDirs = ["observations", "handoffs", "research"];
375
+ for (const subDir of subDirs) {
376
+ const dirPath = join6(memoryPath, subDir);
198
377
  try {
199
- const res = await fetch(url, options);
200
- if (res.ok) return res;
201
- if (res.status === 429 || res.status >= 500) {
202
- const retryAfter = Number(res.headers.get("retry-after")) || 0;
203
- const delay = retryAfter > 0 ? retryAfter * 1e3 : backoffMs * (attempt + 1);
204
- logger.warn(`${label} failed (status ${res.status}), retrying in ${Math.round(delay / 1e3)}s...`);
205
- await new Promise((r) => setTimeout(r, delay));
206
- attempt += 1;
207
- continue;
378
+ const { readdir: readdir10 } = await import("fs/promises");
379
+ const files = await readdir10(dirPath);
380
+ for (const file of files) {
381
+ if (!file.endsWith(".md")) continue;
382
+ const content = await readFile4(join6(dirPath, file), "utf-8");
383
+ const summary = this.extractSummary(content);
384
+ memories.push({
385
+ key: `${subDir}/${file.replace(".md", "")}`,
386
+ content,
387
+ summary,
388
+ createdAt: /* @__PURE__ */ new Date(),
389
+ // Would get from file stats
390
+ updatedAt: /* @__PURE__ */ new Date(),
391
+ type: subDir
392
+ });
208
393
  }
209
- const text = await res.text();
210
- throw new Error(`${label} error: ${res.status} ${res.statusText}
211
- ${text}`);
212
- } catch (err) {
213
- lastError = err;
214
- logger.warn(`${label} network error, attempt ${attempt + 1}/${retries + 1}: ${err instanceof Error ? err.message : String(err)}`);
215
- if (attempt >= retries) break;
216
- await new Promise((r) => setTimeout(r, backoffMs * (attempt + 1)));
217
- attempt += 1;
394
+ } catch {
218
395
  }
219
396
  }
220
- throw lastError instanceof Error ? lastError : new Error(`${label} failed after retries`);
221
- }
222
- /**
223
- * Extract file key from Figma URL
224
- */
225
- extractFileKey(url) {
226
- const match = url.match(/figma\.com\/design\/([a-zA-Z0-9]+)/);
227
- return match ? match[1] : null;
397
+ return memories;
228
398
  }
229
399
  /**
230
- * Extract node ID from Figma URL
400
+ * Read a memory entry
231
401
  */
232
- extractNodeId(url) {
233
- const match = url.match(/[?&]node-id=([^&]+)/);
234
- if (!match) return null;
235
- let nodeId = decodeURIComponent(match[1]);
236
- if (nodeId.includes("-") && !nodeId.includes(":")) {
237
- nodeId = nodeId.replace(/-/g, ":");
402
+ async read(key) {
403
+ const memoryPath = paths.memory(this.config.configPath);
404
+ let filePath;
405
+ if (key.includes("/")) {
406
+ filePath = join6(memoryPath, `${key}.md`);
407
+ } else {
408
+ const subDirs = ["observations", "handoffs", "research", "_templates"];
409
+ for (const subDir of subDirs) {
410
+ const testPath = join6(memoryPath, subDir, `${key}.md`);
411
+ try {
412
+ await access2(testPath, constants2.R_OK);
413
+ filePath = testPath;
414
+ break;
415
+ } catch {
416
+ continue;
417
+ }
418
+ }
419
+ filePath = filePath || join6(memoryPath, `${key}.md`);
420
+ }
421
+ try {
422
+ return await readFile4(filePath, "utf-8");
423
+ } catch {
424
+ return null;
238
425
  }
239
- return nodeId;
240
426
  }
241
427
  /**
242
- * Get Figma file data using API
428
+ * Update a memory entry
243
429
  */
244
- async getFileData(url) {
245
- const fileKey = this.extractFileKey(url);
246
- if (!fileKey) {
247
- throw new Error(`Invalid Figma URL: ${url}`);
430
+ async update(key, content, options) {
431
+ const memoryPath = paths.memory(this.config.configPath);
432
+ const type = options?.type || "custom";
433
+ let filePath;
434
+ if (key.includes("/")) {
435
+ filePath = join6(memoryPath, `${key}.md`);
436
+ } else {
437
+ const subDir = type === "observation" ? "observations" : type === "handoff" ? "handoffs" : type === "research" ? "research" : "";
438
+ filePath = join6(memoryPath, subDir, `${key}.md`);
248
439
  }
249
- const nodeId = this.extractNodeId(url);
250
- const apiUrl = nodeId ? `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${encodeURIComponent(nodeId)}` : `https://api.figma.com/v1/files/${fileKey}`;
251
- const response = await this.fetchWithRetry(apiUrl, {
252
- headers: {
253
- "X-Figma-Token": this.apiKey
254
- }
255
- }, "Figma file fetch");
256
- const data = await response.json();
257
- if (nodeId) {
258
- const nodes = data.nodes;
259
- const nodeData = Object.values(nodes)[0];
260
- if (!nodeData) {
261
- throw new Error(`Node not found: ${nodeId}`);
440
+ const { dirname: dirname5 } = await import("path");
441
+ await mkdir3(dirname5(filePath), { recursive: true });
442
+ if (options?.append) {
443
+ try {
444
+ const existing = await readFile4(filePath, "utf-8");
445
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
446
+ content = `${existing}
447
+
448
+ ---
449
+ _Updated: ${timestamp}_
450
+
451
+ ${content}`;
452
+ } catch {
262
453
  }
263
- return {
264
- document: nodeData.document,
265
- components: {},
266
- styles: {}
267
- };
268
454
  }
269
- return data;
455
+ await writeFile3(filePath, content);
270
456
  }
271
457
  /**
272
- * Extract design tokens from Figma file
458
+ * Create a handoff bundle
273
459
  */
274
- async extractDesignTokens(url, downloadAssets = true, assetsDir) {
275
- const fileData = await this.getFileData(url);
276
- const fileKey = this.extractFileKey(url);
277
- if (!fileKey) {
460
+ async createHandoff(summary) {
461
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
462
+ const key = `handoffs/${timestamp}`;
463
+ const content = `# Handoff: ${(/* @__PURE__ */ new Date()).toLocaleString()}
464
+
465
+ ## Completed
466
+ ${summary.completed.map((item) => `- [x] ${item}`).join("\n") || "- None"}
467
+
468
+ ## In Progress
469
+ ${summary.inProgress.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
470
+
471
+ ## Remaining
472
+ ${summary.remaining.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
473
+
474
+ ## Context
475
+ ${summary.context || "No additional context."}
476
+
477
+ ## Next Steps
478
+ ${summary.nextSteps.map((step, i) => `${i + 1}. ${step}`).join("\n") || "- Continue from where left off"}
479
+ `;
480
+ await this.update(key, content, { type: "handoff" });
481
+ return key;
482
+ }
483
+ /**
484
+ * Get the latest handoff
485
+ */
486
+ async getLatestHandoff() {
487
+ const memories = await this.list();
488
+ const handoffs = memories.filter((m) => m.type === "handoff");
489
+ if (handoffs.length === 0) return null;
490
+ handoffs.sort((a, b) => b.key.localeCompare(a.key));
491
+ return handoffs[0];
492
+ }
493
+ /**
494
+ * Create an observation
495
+ */
496
+ async createObservation(title, observation) {
497
+ const slug = title.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
498
+ const key = `observations/${slug}`;
499
+ const content = `# ${title}
500
+
501
+ ## What
502
+ ${observation.what}
503
+
504
+ ## Why
505
+ ${observation.why}
506
+
507
+ ## Impact
508
+ ${observation.impact}
509
+
510
+ ${observation.tags?.length ? `## Tags
511
+ ${observation.tags.map((t) => `- ${t}`).join("\n")}` : ""}
512
+
513
+ ---
514
+ _Created: ${(/* @__PURE__ */ new Date()).toISOString()}_
515
+ `;
516
+ await this.update(key, content, { type: "observation" });
517
+ return key;
518
+ }
519
+ /**
520
+ * Save research findings
521
+ */
522
+ async saveResearch(topic, findings) {
523
+ const slug = topic.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
524
+ const key = `research/${slug}`;
525
+ const content = `# Research: ${topic}
526
+
527
+ ## Summary
528
+ ${findings.summary}
529
+
530
+ ## Sources
531
+ ${findings.sources.map((s) => `- ${s}`).join("\n")}
532
+
533
+ ## Recommendations
534
+ ${findings.recommendations.map((r, i) => `${i + 1}. ${r}`).join("\n")}
535
+
536
+ ${findings.codeExamples ? `## Code Examples
537
+ \`\`\`
538
+ ${findings.codeExamples}
539
+ \`\`\`` : ""}
540
+
541
+ ---
542
+ _Researched: ${(/* @__PURE__ */ new Date()).toISOString()}_
543
+ `;
544
+ await this.update(key, content, { type: "research" });
545
+ return key;
546
+ }
547
+ /**
548
+ * Extract a summary from content (first paragraph or heading)
549
+ */
550
+ extractSummary(content) {
551
+ const lines = content.split("\n").filter((l) => l.trim());
552
+ for (const line of lines) {
553
+ if (!line.startsWith("#") && line.trim().length > 0) {
554
+ return line.slice(0, 100) + (line.length > 100 ? "..." : "");
555
+ }
556
+ }
557
+ if (lines[0]?.startsWith("#")) {
558
+ return lines[0].replace(/^#+\s*/, "");
559
+ }
560
+ return "No summary available";
561
+ }
562
+ };
563
+ }
564
+ });
565
+
566
+ // src/core/tools/figma-mcp.ts
567
+ var figma_mcp_exports = {};
568
+ __export(figma_mcp_exports, {
569
+ FigmaMcpClient: () => FigmaMcpClient
570
+ });
571
+ import { writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
572
+ import { join as join7 } from "path";
573
+ import { existsSync as existsSync2 } from "fs";
574
+ var FigmaMcpClient;
575
+ var init_figma_mcp = __esm({
576
+ "src/core/tools/figma-mcp.ts"() {
577
+ "use strict";
578
+ init_esm_shims();
579
+ init_logger();
580
+ FigmaMcpClient = class {
581
+ apiKey;
582
+ constructor(apiKey, _configManager) {
583
+ this.apiKey = apiKey;
584
+ }
585
+ /**
586
+ * Fetch helper with simple retry/backoff for 429/5xx
587
+ */
588
+ async fetchWithRetry(url, options, label, retries = 3, backoffMs = 1500) {
589
+ let attempt = 0;
590
+ let lastError;
591
+ while (attempt <= retries) {
592
+ try {
593
+ const res = await fetch(url, options);
594
+ if (res.ok) return res;
595
+ if (res.status === 429 || res.status >= 500) {
596
+ const retryAfter = Number(res.headers.get("retry-after")) || 0;
597
+ const delay = retryAfter > 0 ? retryAfter * 1e3 : backoffMs * (attempt + 1);
598
+ logger.warn(`${label} failed (status ${res.status}), retrying in ${Math.round(delay / 1e3)}s...`);
599
+ await new Promise((r) => setTimeout(r, delay));
600
+ attempt += 1;
601
+ continue;
602
+ }
603
+ const text = await res.text();
604
+ throw new Error(`${label} error: ${res.status} ${res.statusText}
605
+ ${text}`);
606
+ } catch (err) {
607
+ lastError = err;
608
+ logger.warn(`${label} network error, attempt ${attempt + 1}/${retries + 1}: ${err instanceof Error ? err.message : String(err)}`);
609
+ if (attempt >= retries) break;
610
+ await new Promise((r) => setTimeout(r, backoffMs * (attempt + 1)));
611
+ attempt += 1;
612
+ }
613
+ }
614
+ throw lastError instanceof Error ? lastError : new Error(`${label} failed after retries`);
615
+ }
616
+ /**
617
+ * Extract file key from Figma URL
618
+ */
619
+ extractFileKey(url) {
620
+ const match = url.match(/figma\.com\/design\/([a-zA-Z0-9]+)/);
621
+ return match ? match[1] : null;
622
+ }
623
+ /**
624
+ * Extract node ID from Figma URL
625
+ */
626
+ extractNodeId(url) {
627
+ const match = url.match(/[?&]node-id=([^&]+)/);
628
+ if (!match) return null;
629
+ let nodeId = decodeURIComponent(match[1]);
630
+ if (nodeId.includes("-") && !nodeId.includes(":")) {
631
+ nodeId = nodeId.replace(/-/g, ":");
632
+ }
633
+ return nodeId;
634
+ }
635
+ /**
636
+ * Get Figma file data using API
637
+ */
638
+ async getFileData(url) {
639
+ const fileKey = this.extractFileKey(url);
640
+ if (!fileKey) {
641
+ throw new Error(`Invalid Figma URL: ${url}`);
642
+ }
643
+ const nodeId = this.extractNodeId(url);
644
+ const apiUrl = nodeId ? `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${encodeURIComponent(nodeId)}` : `https://api.figma.com/v1/files/${fileKey}`;
645
+ const response = await this.fetchWithRetry(apiUrl, {
646
+ headers: {
647
+ "X-Figma-Token": this.apiKey
648
+ }
649
+ }, "Figma file fetch");
650
+ const data = await response.json();
651
+ if (nodeId) {
652
+ const nodes = data.nodes;
653
+ const nodeData = Object.values(nodes)[0];
654
+ if (!nodeData) {
655
+ throw new Error(`Node not found: ${nodeId}`);
656
+ }
657
+ return {
658
+ document: nodeData.document,
659
+ components: {},
660
+ styles: {}
661
+ };
662
+ }
663
+ return data;
664
+ }
665
+ /**
666
+ * Extract design tokens from Figma file
667
+ */
668
+ async extractDesignTokens(url, downloadAssets = true, assetsDir) {
669
+ const fileData = await this.getFileData(url);
670
+ const fileKey = this.extractFileKey(url);
671
+ if (!fileKey) {
278
672
  throw new Error(`Invalid Figma URL: ${url}`);
279
673
  }
280
674
  const tokens = {
@@ -534,9 +928,9 @@ ${text}`);
534
928
  }, "Figma images listing");
535
929
  const imageData = await response.json();
536
930
  const images = imageData.images;
537
- const fullAssetsDir = assetsDir.startsWith("/") ? assetsDir : join6(process.cwd(), assetsDir);
931
+ const fullAssetsDir = assetsDir.startsWith("/") ? assetsDir : join7(process.cwd(), assetsDir);
538
932
  if (!existsSync2(fullAssetsDir)) {
539
- await mkdir3(fullAssetsDir, { recursive: true });
933
+ await mkdir4(fullAssetsDir, { recursive: true });
540
934
  }
541
935
  const downloadedAssets = [];
542
936
  for (const node of nodesToDownload) {
@@ -555,8 +949,8 @@ ${text}`);
555
949
  const safeName = node.name.replace(/[^a-z0-9]/gi, "_").toLowerCase().substring(0, 50);
556
950
  const extension = "png";
557
951
  const filename = `${safeName}_${node.id.substring(0, 8)}.${extension}`;
558
- const filePath = join6(fullAssetsDir, filename);
559
- await writeFile3(filePath, Buffer.from(imageBuffer));
952
+ const filePath = join7(fullAssetsDir, filename);
953
+ await writeFile4(filePath, Buffer.from(imageBuffer));
560
954
  downloadedAssets.push({
561
955
  nodeId: node.id,
562
956
  nodeName: node.name,
@@ -595,8 +989,8 @@ __export(figma_screen_developer_exports, {
595
989
  checkCurrentCodeStatus: () => checkCurrentCodeStatus,
596
990
  compareCodeWithFigma: () => compareCodeWithFigma
597
991
  });
598
- import { readFile as readFile4, readdir as readdir3 } from "fs/promises";
599
- import { join as join7 } from "path";
992
+ import { readFile as readFile5, readdir as readdir3 } from "fs/promises";
993
+ import { join as join8 } from "path";
600
994
  import { existsSync as existsSync3 } from "fs";
601
995
  async function checkCurrentCodeStatus(projectPath = process.cwd()) {
602
996
  const status = {
@@ -610,13 +1004,13 @@ async function checkCurrentCodeStatus(projectPath = process.cwd()) {
610
1004
  };
611
1005
  try {
612
1006
  const htmlFiles = ["index.html", "index.htm", "main.html"].filter(
613
- (file) => existsSync3(join7(projectPath, file))
1007
+ (file) => existsSync3(join8(projectPath, file))
614
1008
  );
615
1009
  if (htmlFiles.length > 0) {
616
1010
  status.hasHTML = true;
617
1011
  status.htmlFile = htmlFiles[0];
618
1012
  try {
619
- const htmlContent = await readFile4(join7(projectPath, htmlFiles[0]), "utf-8");
1013
+ const htmlContent = await readFile5(join8(projectPath, htmlFiles[0]), "utf-8");
620
1014
  const sectionMatches = htmlContent.match(/<(section|div|header|footer|main|article|aside)[^>]*(?:id|class)=["']([^"']+)["']/gi);
621
1015
  if (sectionMatches) {
622
1016
  status.sections = sectionMatches.map((match) => {
@@ -628,19 +1022,19 @@ async function checkCurrentCodeStatus(projectPath = process.cwd()) {
628
1022
  } catch (e) {
629
1023
  }
630
1024
  }
631
- const stylesDir = join7(projectPath, "styles");
1025
+ const stylesDir = join8(projectPath, "styles");
632
1026
  if (existsSync3(stylesDir)) {
633
1027
  try {
634
1028
  const files = await readdir3(stylesDir);
635
1029
  const cssFiles = files.filter((f) => f.endsWith(".css"));
636
1030
  if (cssFiles.length > 0) {
637
1031
  status.hasCSS = true;
638
- status.cssFiles = cssFiles.map((f) => join7(stylesDir, f));
1032
+ status.cssFiles = cssFiles.map((f) => join8(stylesDir, f));
639
1033
  }
640
1034
  } catch (e) {
641
1035
  }
642
1036
  }
643
- const assetsDir = join7(projectPath, "assets", "images");
1037
+ const assetsDir = join8(projectPath, "assets", "images");
644
1038
  if (existsSync3(assetsDir)) {
645
1039
  try {
646
1040
  const files = await readdir3(assetsDir);
@@ -713,217 +1107,394 @@ var init_figma_screen_developer = __esm({
713
1107
  }
714
1108
  });
715
1109
 
716
- // src/core/memory.ts
717
- var memory_exports = {};
718
- __export(memory_exports, {
719
- MemoryManager: () => MemoryManager
1110
+ // src/core/beads.ts
1111
+ var beads_exports = {};
1112
+ __export(beads_exports, {
1113
+ BeadsIntegration: () => BeadsIntegration
720
1114
  });
721
- import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, access as access2, constants as constants2 } from "fs/promises";
722
- import { join as join8 } from "path";
723
- var MemoryManager;
724
- var init_memory = __esm({
725
- "src/core/memory.ts"() {
1115
+ import { readFile as readFile6, writeFile as writeFile5, readdir as readdir4, access as access3, constants as constants3, mkdir as mkdir5 } from "fs/promises";
1116
+ import { join as join9 } from "path";
1117
+ import { exec } from "child_process";
1118
+ import { promisify } from "util";
1119
+ var execAsync, BeadsIntegration;
1120
+ var init_beads = __esm({
1121
+ "src/core/beads.ts"() {
726
1122
  "use strict";
727
1123
  init_esm_shims();
728
1124
  init_paths();
729
- MemoryManager = class {
730
- config;
731
- constructor(config) {
732
- this.config = config;
1125
+ init_logger();
1126
+ execAsync = promisify(exec);
1127
+ BeadsIntegration = class {
1128
+ projectPath;
1129
+ constructor(projectPath) {
1130
+ this.projectPath = projectPath || process.cwd();
733
1131
  }
734
1132
  /**
735
- * List all memory entries
1133
+ * Check if Beads CLI is installed
736
1134
  */
737
- async list() {
738
- const memories = [];
739
- const memoryPath = paths.memory(this.config.configPath);
740
- const subDirs = ["observations", "handoffs", "research"];
741
- for (const subDir of subDirs) {
742
- const dirPath = join8(memoryPath, subDir);
743
- try {
744
- const { readdir: readdir10 } = await import("fs/promises");
745
- const files = await readdir10(dirPath);
746
- for (const file of files) {
747
- if (!file.endsWith(".md")) continue;
748
- const content = await readFile5(join8(dirPath, file), "utf-8");
749
- const summary = this.extractSummary(content);
750
- memories.push({
751
- key: `${subDir}/${file.replace(".md", "")}`,
752
- content,
753
- summary,
754
- createdAt: /* @__PURE__ */ new Date(),
755
- // Would get from file stats
756
- updatedAt: /* @__PURE__ */ new Date(),
757
- type: subDir
758
- });
759
- }
760
- } catch {
761
- }
1135
+ async isInstalled() {
1136
+ try {
1137
+ await execAsync("bd --version");
1138
+ return true;
1139
+ } catch {
1140
+ return false;
762
1141
  }
763
- return memories;
764
1142
  }
765
1143
  /**
766
- * Read a memory entry
1144
+ * Get Beads version
767
1145
  */
768
- async read(key) {
769
- const memoryPath = paths.memory(this.config.configPath);
770
- let filePath;
771
- if (key.includes("/")) {
772
- filePath = join8(memoryPath, `${key}.md`);
773
- } else {
774
- const subDirs = ["observations", "handoffs", "research", "_templates"];
775
- for (const subDir of subDirs) {
776
- const testPath = join8(memoryPath, subDir, `${key}.md`);
777
- try {
778
- await access2(testPath, constants2.R_OK);
779
- filePath = testPath;
780
- break;
781
- } catch {
782
- continue;
783
- }
784
- }
785
- filePath = filePath || join8(memoryPath, `${key}.md`);
786
- }
1146
+ async getVersion() {
787
1147
  try {
788
- return await readFile5(filePath, "utf-8");
1148
+ const { stdout } = await execAsync("bd --version");
1149
+ const match = stdout.match(/bd version (\S+)/);
1150
+ return match?.[1] || null;
789
1151
  } catch {
790
1152
  return null;
791
1153
  }
792
1154
  }
793
1155
  /**
794
- * Update a memory entry
1156
+ * Check if Beads is initialized in project
795
1157
  */
796
- async update(key, content, options) {
797
- const memoryPath = paths.memory(this.config.configPath);
798
- const type = options?.type || "custom";
799
- let filePath;
800
- if (key.includes("/")) {
801
- filePath = join8(memoryPath, `${key}.md`);
802
- } else {
803
- const subDir = type === "observation" ? "observations" : type === "handoff" ? "handoffs" : type === "research" ? "research" : "";
804
- filePath = join8(memoryPath, subDir, `${key}.md`);
1158
+ async isInitialized() {
1159
+ const beadsDir = paths.beadsDir(this.projectPath);
1160
+ try {
1161
+ await access3(beadsDir, constants3.R_OK);
1162
+ return true;
1163
+ } catch {
1164
+ return false;
805
1165
  }
806
- const { dirname: dirname5 } = await import("path");
807
- await mkdir4(dirname5(filePath), { recursive: true });
808
- if (options?.append) {
809
- try {
810
- const existing = await readFile5(filePath, "utf-8");
811
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
812
- content = `${existing}
813
-
814
- ---
815
- _Updated: ${timestamp}_
816
-
817
- ${content}`;
818
- } catch {
819
- }
1166
+ }
1167
+ /**
1168
+ * Install Beads CLI globally
1169
+ */
1170
+ async install() {
1171
+ try {
1172
+ await execAsync("npm install -g beads");
1173
+ return true;
1174
+ } catch (error) {
1175
+ logger.error("Failed to install Beads CLI:", error);
1176
+ return false;
820
1177
  }
821
- await writeFile4(filePath, content);
822
1178
  }
823
1179
  /**
824
- * Create a handoff bundle
1180
+ * Initialize Beads in project using bd CLI
825
1181
  */
826
- async createHandoff(summary) {
827
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
828
- const key = `handoffs/${timestamp}`;
829
- const content = `# Handoff: ${(/* @__PURE__ */ new Date()).toLocaleString()}
830
-
831
- ## Completed
832
- ${summary.completed.map((item) => `- [x] ${item}`).join("\n") || "- None"}
1182
+ async init() {
1183
+ try {
1184
+ await execAsync("bd init", { cwd: this.projectPath });
1185
+ logger.success("Beads initialized");
1186
+ return true;
1187
+ } catch (error) {
1188
+ logger.error("Failed to initialize Beads:", error);
1189
+ return false;
1190
+ }
1191
+ }
1192
+ /**
1193
+ * Initialize local .beads directory (works without global beads CLI)
1194
+ */
1195
+ async initLocal() {
1196
+ try {
1197
+ const beadsDir = paths.beadsDir(this.projectPath);
1198
+ await mkdir5(beadsDir, { recursive: true });
1199
+ const readmeContent = `# Beads - Task Tracking
833
1200
 
834
- ## In Progress
835
- ${summary.inProgress.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
1201
+ This directory contains task beads for tracking work items.
836
1202
 
837
- ## Remaining
838
- ${summary.remaining.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
1203
+ ## How it works
1204
+ - Each file is a task bead (bead-001.md, bead-002.md, etc.)
1205
+ - Status: todo, in-progress, completed, blocked
1206
+ - Use \`/create\` command to create new tasks
1207
+ - Use \`/finish\` command to complete tasks with quality gates
839
1208
 
840
- ## Context
841
- ${summary.context || "No additional context."}
1209
+ ## Beads CLI
1210
+ For full functionality, install beads globally:
1211
+ \`\`\`bash
1212
+ npm install -g beads
1213
+ bd init
1214
+ \`\`\`
842
1215
 
843
- ## Next Steps
844
- ${summary.nextSteps.map((step, i) => `${i + 1}. ${step}`).join("\n") || "- Continue from where left off"}
1216
+ ## Available Commands
1217
+ - \`bd ready\` - Show available work
1218
+ - \`bd show <id>\` - View task details
1219
+ - \`bd update <id> --status in_progress\` - Update task status
1220
+ - \`bd close <id>\` - Complete task
1221
+ - \`bd sync\` - Sync with git
845
1222
  `;
846
- await this.update(key, content, { type: "handoff" });
847
- return key;
1223
+ await writeFile5(join9(beadsDir, "README.md"), readmeContent);
1224
+ return true;
1225
+ } catch (error) {
1226
+ logger.error("Failed to initialize .beads directory:", error);
1227
+ return false;
1228
+ }
848
1229
  }
849
1230
  /**
850
- * Get the latest handoff
1231
+ * Get current status
851
1232
  */
852
- async getLatestHandoff() {
853
- const memories = await this.list();
854
- const handoffs = memories.filter((m) => m.type === "handoff");
855
- if (handoffs.length === 0) return null;
856
- handoffs.sort((a, b) => b.key.localeCompare(a.key));
857
- return handoffs[0];
1233
+ async getStatus() {
1234
+ const installed = await this.isInstalled();
1235
+ const version = await this.getVersion();
1236
+ const initialized = await this.isInitialized();
1237
+ let activeTasks = 0;
1238
+ let completedTasks = 0;
1239
+ let currentTask;
1240
+ if (initialized) {
1241
+ const beads = await this.listBeads();
1242
+ activeTasks = beads.filter((b) => b.status === "in-progress" || b.status === "todo").length;
1243
+ completedTasks = beads.filter((b) => b.status === "completed").length;
1244
+ const active = beads.find((b) => b.status === "in-progress");
1245
+ currentTask = active?.title;
1246
+ }
1247
+ return {
1248
+ installed,
1249
+ version: version || void 0,
1250
+ initialized,
1251
+ activeTasks,
1252
+ completedTasks,
1253
+ currentTask
1254
+ };
858
1255
  }
859
1256
  /**
860
- * Create an observation
1257
+ * List all beads in project
861
1258
  */
862
- async createObservation(title, observation) {
863
- const slug = title.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
864
- const key = `observations/${slug}`;
865
- const content = `# ${title}
866
-
867
- ## What
868
- ${observation.what}
869
-
870
- ## Why
871
- ${observation.why}
872
-
873
- ## Impact
874
- ${observation.impact}
875
-
876
- ${observation.tags?.length ? `## Tags
877
- ${observation.tags.map((t) => `- ${t}`).join("\n")}` : ""}
878
-
879
- ---
880
- _Created: ${(/* @__PURE__ */ new Date()).toISOString()}_
881
- `;
882
- await this.update(key, content, { type: "observation" });
883
- return key;
1259
+ async listBeads() {
1260
+ const beadsDir = paths.beadsDir(this.projectPath);
1261
+ const beads = [];
1262
+ try {
1263
+ const files = await readdir4(beadsDir);
1264
+ for (const file of files) {
1265
+ if (!file.match(/^bead-\d+\.md$/)) continue;
1266
+ const content = await readFile6(join9(beadsDir, file), "utf-8");
1267
+ const bead = this.parseBeadFile(file, content);
1268
+ if (bead) beads.push(bead);
1269
+ }
1270
+ } catch {
1271
+ }
1272
+ return beads.sort((a, b) => a.id.localeCompare(b.id));
884
1273
  }
885
1274
  /**
886
- * Save research findings
1275
+ * Get a specific bead
887
1276
  */
888
- async saveResearch(topic, findings) {
889
- const slug = topic.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
890
- const key = `research/${slug}`;
891
- const content = `# Research: ${topic}
892
-
893
- ## Summary
894
- ${findings.summary}
1277
+ async getBead(id) {
1278
+ const beadsDir = paths.beadsDir(this.projectPath);
1279
+ const fileName = id.endsWith(".md") ? id : `${id}.md`;
1280
+ try {
1281
+ const content = await readFile6(join9(beadsDir, fileName), "utf-8");
1282
+ return this.parseBeadFile(fileName, content);
1283
+ } catch {
1284
+ return null;
1285
+ }
1286
+ }
1287
+ /**
1288
+ * Create a new bead
1289
+ */
1290
+ async createBead(title, description) {
1291
+ const beadsDir = paths.beadsDir(this.projectPath);
1292
+ await mkdir5(beadsDir, { recursive: true });
1293
+ const beads = await this.listBeads();
1294
+ const maxId = beads.reduce((max, b) => {
1295
+ const num = parseInt(b.id.replace("bead-", ""), 10);
1296
+ return num > max ? num : max;
1297
+ }, 0);
1298
+ const id = `bead-${String(maxId + 1).padStart(3, "0")}`;
1299
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1300
+ const content = `---
1301
+ id: ${id}
1302
+ title: ${title}
1303
+ status: in-progress
1304
+ created: ${now}
1305
+ updated: ${now}
1306
+ ---
895
1307
 
896
- ## Sources
897
- ${findings.sources.map((s) => `- ${s}`).join("\n")}
1308
+ # ${title}
898
1309
 
899
- ## Recommendations
900
- ${findings.recommendations.map((r, i) => `${i + 1}. ${r}`).join("\n")}
1310
+ ## Description
1311
+ ${description}
901
1312
 
902
- ${findings.codeExamples ? `## Code Examples
903
- \`\`\`
904
- ${findings.codeExamples}
905
- \`\`\`` : ""}
1313
+ ## Notes
906
1314
 
907
- ---
908
- _Researched: ${(/* @__PURE__ */ new Date()).toISOString()}_
909
1315
  `;
910
- await this.update(key, content, { type: "research" });
911
- return key;
1316
+ await writeFile5(join9(beadsDir, `${id}.md`), content);
1317
+ return {
1318
+ id,
1319
+ title,
1320
+ description,
1321
+ status: "in-progress",
1322
+ createdAt: new Date(now),
1323
+ updatedAt: new Date(now),
1324
+ notes: []
1325
+ };
912
1326
  }
913
1327
  /**
914
- * Extract a summary from content (first paragraph or heading)
1328
+ * Update bead status
915
1329
  */
916
- extractSummary(content) {
917
- const lines = content.split("\n").filter((l) => l.trim());
918
- for (const line of lines) {
919
- if (!line.startsWith("#") && line.trim().length > 0) {
920
- return line.slice(0, 100) + (line.length > 100 ? "..." : "");
1330
+ async updateBeadStatus(id, status) {
1331
+ const beadsDir = paths.beadsDir(this.projectPath);
1332
+ const fileName = id.endsWith(".md") ? id : `${id}.md`;
1333
+ const filePath = join9(beadsDir, fileName);
1334
+ try {
1335
+ let content = await readFile6(filePath, "utf-8");
1336
+ content = content.replace(
1337
+ /status:\s*\w+/,
1338
+ `status: ${status}`
1339
+ );
1340
+ content = content.replace(
1341
+ /updated:\s*.+/,
1342
+ `updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
1343
+ );
1344
+ await writeFile5(filePath, content);
1345
+ return true;
1346
+ } catch {
1347
+ return false;
1348
+ }
1349
+ }
1350
+ /**
1351
+ * Add note to bead
1352
+ */
1353
+ async addNote(id, note) {
1354
+ const beadsDir = paths.beadsDir(this.projectPath);
1355
+ const fileName = id.endsWith(".md") ? id : `${id}.md`;
1356
+ const filePath = join9(beadsDir, fileName);
1357
+ try {
1358
+ let content = await readFile6(filePath, "utf-8");
1359
+ const notesMatch = content.match(/## Notes\n([\s\S]*?)(?=\n##|$)/);
1360
+ if (notesMatch) {
1361
+ const timestamp = (/* @__PURE__ */ new Date()).toLocaleString();
1362
+ const newNote = `- [${timestamp}] ${note}`;
1363
+ content = content.replace(
1364
+ notesMatch[0],
1365
+ `## Notes
1366
+ ${notesMatch[1]}${newNote}
1367
+ `
1368
+ );
921
1369
  }
1370
+ content = content.replace(
1371
+ /updated:\s*.+/,
1372
+ `updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
1373
+ );
1374
+ await writeFile5(filePath, content);
1375
+ return true;
1376
+ } catch {
1377
+ return false;
922
1378
  }
923
- if (lines[0]?.startsWith("#")) {
924
- return lines[0].replace(/^#+\s*/, "");
1379
+ }
1380
+ /**
1381
+ * Update bead type
1382
+ */
1383
+ async updateBeadType(id, type) {
1384
+ const beadsDir = paths.beadsDir(this.projectPath);
1385
+ const fileName = id.endsWith(".md") ? id : `${id}.md`;
1386
+ const filePath = join9(beadsDir, fileName);
1387
+ try {
1388
+ let content = await readFile6(filePath, "utf-8");
1389
+ const typeMatch = content.match(/^type:\s*.+/m);
1390
+ if (typeMatch) {
1391
+ content = content.replace(typeMatch[0], `type: ${type}`);
1392
+ } else {
1393
+ content = content.replace(/^---\n([\s\S]*?)\n---/, (match) => {
1394
+ return match.replace(/^---\n/, `---
1395
+ type: ${type}
1396
+ `);
1397
+ });
1398
+ }
1399
+ content = content.replace(
1400
+ /updated:\s*.+/,
1401
+ `updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
1402
+ );
1403
+ await writeFile5(filePath, content);
1404
+ return true;
1405
+ } catch {
1406
+ return false;
1407
+ }
1408
+ }
1409
+ /**
1410
+ * Complete a bead with quality gates
1411
+ */
1412
+ async completeBead(id) {
1413
+ const gates = [
1414
+ { name: "Type Check", command: "npm run typecheck" },
1415
+ { name: "Tests", command: "npm run test" },
1416
+ { name: "Lint", command: "npm run lint" },
1417
+ { name: "Build", command: "npm run build" }
1418
+ ];
1419
+ const results = [];
1420
+ for (const gate of gates) {
1421
+ try {
1422
+ await execAsync(gate.command, { cwd: this.projectPath });
1423
+ results.push({ name: gate.name, passed: true });
1424
+ logger.success(`${gate.name}: passed`);
1425
+ } catch (error) {
1426
+ results.push({
1427
+ name: gate.name,
1428
+ passed: false,
1429
+ error: error instanceof Error ? error.message.slice(0, 200) : "Failed"
1430
+ });
1431
+ logger.error(`${gate.name}: failed`);
1432
+ }
1433
+ }
1434
+ const allPassed = results.every((r) => r.passed);
1435
+ if (allPassed) {
1436
+ await this.updateBeadStatus(id, "completed");
1437
+ await this.addNote(id, "Task completed - all quality gates passed");
1438
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
1439
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1440
+ const config = await loadConfig2(this.projectPath);
1441
+ const memory = new MemoryManager2(config);
1442
+ await memory.createHandoff({
1443
+ completed: [id],
1444
+ inProgress: [],
1445
+ remaining: [],
1446
+ context: `Auto-generated handoff for completed task: ${id}`,
1447
+ nextSteps: ["Review completed work", "Start new task if needed"]
1448
+ });
1449
+ } else {
1450
+ await this.addNote(id, "Completion attempted but quality gates failed");
1451
+ }
1452
+ return {
1453
+ success: allPassed,
1454
+ gates: results,
1455
+ handoffCreated: allPassed
1456
+ };
1457
+ }
1458
+ /**
1459
+ * Get current active bead
1460
+ */
1461
+ async getCurrentBead() {
1462
+ const beads = await this.listBeads();
1463
+ return beads.find((b) => b.status === "in-progress") || null;
1464
+ }
1465
+ /**
1466
+ * Parse a bead file
1467
+ */
1468
+ parseBeadFile(fileName, content) {
1469
+ try {
1470
+ const id = fileName.replace(".md", "");
1471
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
1472
+ const frontmatter = frontmatterMatch?.[1] || "";
1473
+ const getValue = (key) => {
1474
+ const match = frontmatter.match(new RegExp(`${key}:\\s*(.+)`));
1475
+ return match?.[1]?.trim() || "";
1476
+ };
1477
+ const titleMatch = content.match(/^# (.+)/m);
1478
+ const title = getValue("title") || titleMatch?.[1] || id;
1479
+ const descMatch = content.match(/## Description\n([\s\S]*?)(?=\n##|$)/);
1480
+ const description = descMatch?.[1]?.trim() || "";
1481
+ const notesMatch = content.match(/## Notes\n([\s\S]*?)(?=\n##|$)/);
1482
+ const notesContent = notesMatch?.[1] || "";
1483
+ const notes = notesContent.split("\n").filter((line) => line.trim().startsWith("-")).map((line) => line.replace(/^-\s*/, "").trim());
1484
+ const type = getValue("type");
1485
+ return {
1486
+ id,
1487
+ title,
1488
+ description,
1489
+ status: getValue("status") || "todo",
1490
+ type,
1491
+ createdAt: new Date(getValue("created") || Date.now()),
1492
+ updatedAt: new Date(getValue("updated") || Date.now()),
1493
+ notes
1494
+ };
1495
+ } catch {
1496
+ return null;
925
1497
  }
926
- return "No summary available";
927
1498
  }
928
1499
  };
929
1500
  }
@@ -1203,169 +1774,7 @@ import { Command } from "commander";
1203
1774
 
1204
1775
  // src/index.ts
1205
1776
  init_esm_shims();
1206
-
1207
- // src/core/config.ts
1208
- init_esm_shims();
1209
- init_paths();
1210
- import { readFile, access, constants } from "fs/promises";
1211
- import { join as join3 } from "path";
1212
- import { z } from "zod";
1213
-
1214
- // src/utils/version.ts
1215
- init_esm_shims();
1216
- import { readFileSync } from "fs";
1217
- import { fileURLToPath as fileURLToPath2 } from "url";
1218
- import { dirname, join as join2 } from "path";
1219
- function getVersion() {
1220
- try {
1221
- const __filename2 = fileURLToPath2(import.meta.url);
1222
- const __dirname2 = dirname(__filename2);
1223
- const packageJsonPath = join2(__dirname2, "..", "package.json");
1224
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
1225
- return packageJson.version;
1226
- } catch (error) {
1227
- console.warn("Warning: Could not read version from package.json, using fallback");
1228
- return "0.0.0";
1229
- }
1230
- }
1231
-
1232
- // src/core/config.ts
1233
- var ConfigSchema = z.object({
1234
- version: z.string(),
1235
- skills: z.object({
1236
- enabled: z.boolean().default(true),
1237
- directory: z.string().optional()
1238
- }).default({}),
1239
- agents: z.object({
1240
- enabled: z.boolean().default(true),
1241
- default: z.string().default("build")
1242
- }).default({}),
1243
- commands: z.object({
1244
- enabled: z.boolean().default(true)
1245
- }).default({}),
1246
- tools: z.object({
1247
- enabled: z.boolean().default(true)
1248
- }).default({}),
1249
- plugins: z.object({
1250
- enabled: z.boolean().default(true),
1251
- autoload: z.array(z.string()).optional()
1252
- }).default({}),
1253
- memory: z.object({
1254
- enabled: z.boolean().default(true),
1255
- maxSize: z.number().optional()
1256
- }).default({}),
1257
- beads: z.object({
1258
- enabled: z.boolean().default(true),
1259
- autoInit: z.boolean().default(false)
1260
- }).default({}),
1261
- antiHallucination: z.object({
1262
- enabled: z.boolean().default(true),
1263
- specFile: z.string().default("spec.md"),
1264
- reviewFile: z.string().default("review.md")
1265
- }).default({}),
1266
- mcp: z.object({
1267
- context7: z.boolean().default(false),
1268
- githubGrep: z.boolean().default(false),
1269
- gkg: z.boolean().default(false)
1270
- }).optional(),
1271
- mode: z.string().default("build").optional()
1272
- });
1273
- var Config = class {
1274
- config;
1275
- constructor(config) {
1276
- this.config = config;
1277
- }
1278
- get() {
1279
- return this.config;
1280
- }
1281
- get skills() {
1282
- return this.config.skills;
1283
- }
1284
- get agents() {
1285
- return this.config.agents;
1286
- }
1287
- get commands() {
1288
- return this.config.commands;
1289
- }
1290
- get tools() {
1291
- return this.config.tools;
1292
- }
1293
- get plugins() {
1294
- return this.config.plugins;
1295
- }
1296
- get memory() {
1297
- return this.config.memory;
1298
- }
1299
- get beads() {
1300
- return this.config.beads;
1301
- }
1302
- get antiHallucination() {
1303
- return this.config.antiHallucination;
1304
- }
1305
- get mode() {
1306
- return this.config.mode;
1307
- }
1308
- get configPath() {
1309
- return this.config.configPath;
1310
- }
1311
- get projectPath() {
1312
- return this.config.projectPath;
1313
- }
1314
- /**
1315
- * Get path to a specific resource
1316
- */
1317
- getPath(resource) {
1318
- return paths[resource](this.configPath);
1319
- }
1320
- };
1321
- async function loadConfig(projectPath) {
1322
- const project = projectPath || process.cwd();
1323
- const projectConfigPath = paths.projectConfig(project);
1324
- const globalConfigPath = paths.globalConfig();
1325
- let configPath;
1326
- let configData = {};
1327
- try {
1328
- await access(join3(globalConfigPath, "aikit.json"), constants.R_OK);
1329
- const globalContent = await readFile(join3(globalConfigPath, "aikit.json"), "utf-8");
1330
- configData = JSON.parse(globalContent);
1331
- configPath = globalConfigPath;
1332
- } catch {
1333
- configPath = projectConfigPath;
1334
- }
1335
- try {
1336
- await access(join3(projectConfigPath, "aikit.json"), constants.R_OK);
1337
- const projectContent = await readFile(join3(projectConfigPath, "aikit.json"), "utf-8");
1338
- const projectData = JSON.parse(projectContent);
1339
- configData = deepMerge(configData, projectData);
1340
- configPath = projectConfigPath;
1341
- } catch {
1342
- }
1343
- if (!configData.version) {
1344
- configData.version = getVersion();
1345
- }
1346
- const parsed = ConfigSchema.parse(configData);
1347
- return new Config({
1348
- ...parsed,
1349
- configPath,
1350
- projectPath: project
1351
- });
1352
- }
1353
- function deepMerge(base, override) {
1354
- const result = { ...base };
1355
- for (const key in override) {
1356
- const baseValue = base[key];
1357
- const overrideValue = override[key];
1358
- if (typeof baseValue === "object" && baseValue !== null && typeof overrideValue === "object" && overrideValue !== null && !Array.isArray(baseValue) && !Array.isArray(overrideValue)) {
1359
- result[key] = deepMerge(
1360
- baseValue,
1361
- overrideValue
1362
- );
1363
- } else if (overrideValue !== void 0) {
1364
- result[key] = overrideValue;
1365
- }
1366
- }
1367
- return result;
1368
- }
1777
+ init_config();
1369
1778
 
1370
1779
  // src/core/skills.ts
1371
1780
  init_esm_shims();
@@ -2813,8 +3222,8 @@ ${command.content}
2813
3222
  init_esm_shims();
2814
3223
  init_paths();
2815
3224
  init_logger();
2816
- import { readdir as readdir4, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
2817
- import { join as join9, extname as extname3 } from "path";
3225
+ import { readdir as readdir5, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
3226
+ import { join as join10, extname as extname3 } from "path";
2818
3227
  import { z as z2 } from "zod";
2819
3228
  var ToolArgSchema = z2.object({
2820
3229
  type: z2.enum(["string", "number", "boolean", "array", "object"]),
@@ -2877,8 +3286,28 @@ Note: Configure GitHub integration in AIKit settings for actual results.`;
2877
3286
  required: true
2878
3287
  }
2879
3288
  },
2880
- async execute({ key }) {
2881
- return `Memory read: ${key}`;
3289
+ async execute({ key }, context) {
3290
+ const config = context?.config;
3291
+ if (!config) {
3292
+ return "Error: Configuration not available";
3293
+ }
3294
+ try {
3295
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3296
+ const memory = new MemoryManager2(config);
3297
+ const content = await memory.read(key);
3298
+ if (!content) {
3299
+ return `Memory entry not found: ${key}
3300
+
3301
+ Available keys (use \`list_session\` or \`aikit memory list\`):
3302
+ - handoffs/
3303
+ - observations/
3304
+ - research/
3305
+ - _templates/`;
3306
+ }
3307
+ return content;
3308
+ } catch (error) {
3309
+ return `Error reading memory: ${error instanceof Error ? error.message : String(error)}`;
3310
+ }
2882
3311
  }
2883
3312
  },
2884
3313
  {
@@ -2902,8 +3331,22 @@ Note: Configure GitHub integration in AIKit settings for actual results.`;
2902
3331
  default: true
2903
3332
  }
2904
3333
  },
2905
- async execute({ key, append = true }) {
2906
- return `Memory updated: ${key} (append: ${append})`;
3334
+ async execute({ key, content, append = true }, context) {
3335
+ const config = context?.config;
3336
+ if (!config) {
3337
+ return "Error: Configuration not available";
3338
+ }
3339
+ if (typeof content !== "string") {
3340
+ return "Error: content must be a string";
3341
+ }
3342
+ try {
3343
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3344
+ const memory = new MemoryManager2(config);
3345
+ await memory.update(key, content, { append });
3346
+ return `\u2713 Saved to memory: ${key}${append ? " (appended)" : " (overwrote)"}`;
3347
+ } catch (error) {
3348
+ return `Error updating memory: ${error instanceof Error ? error.message : String(error)}`;
3349
+ }
2907
3350
  }
2908
3351
  },
2909
3352
  {
@@ -3276,7 +3719,7 @@ ${tokens.screens?.map((s, i) => `${i + 1}. ${s.name} (ID: ${s.id})`).join("\n")
3276
3719
  try {
3277
3720
  const fileData = await client.getFileData(figmaUrl);
3278
3721
  const projectPath = process.cwd();
3279
- const assetsDir = join9(projectPath, "assets", "images");
3722
+ const assetsDir = join10(projectPath, "assets", "images");
3280
3723
  const assets = await client.downloadAssets(fileKey, fileData.document, assetsDir, selectedScreen.id);
3281
3724
  downloadedAssets = assets || [];
3282
3725
  logger.info(`Downloaded ${downloadedAssets.length} assets for screen ${selectedScreen.name}`);
@@ -3368,91 +3811,318 @@ ${tokens.screens?.map((s, i) => `${i + 1}. ${s.name} (ID: ${s.id})`).join("\n")
3368
3811
  `;
3369
3812
  result += `5. Verify pixel-perfect match with Figma design
3370
3813
  `;
3371
- return result;
3814
+ return result;
3815
+ } catch (error) {
3816
+ return `Error: ${error instanceof Error ? error.message : String(error)}`;
3817
+ }
3818
+ }
3819
+ },
3820
+ {
3821
+ name: "list_session",
3822
+ description: "List previous sessions to discover what happened and when. Use this before read_session to find the right context.",
3823
+ args: {
3824
+ limit: {
3825
+ type: "number",
3826
+ description: "Maximum number of sessions to return (default: 10)",
3827
+ required: false,
3828
+ default: 10
3829
+ }
3830
+ },
3831
+ async execute({ limit = 10 }, context) {
3832
+ const config = context?.config;
3833
+ if (!config) {
3834
+ return "Error: Configuration not available";
3835
+ }
3836
+ const limitNum = typeof limit === "number" ? limit : 10;
3837
+ try {
3838
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3839
+ const memory = new MemoryManager2(config);
3840
+ const memories = await memory.list();
3841
+ const handoffs = memories.filter((m) => m.type === "handoff").sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()).slice(0, limitNum);
3842
+ if (handoffs.length === 0) {
3843
+ return "No previous sessions found. Use /handoff to create a session handoff.";
3844
+ }
3845
+ let result = `# Previous Sessions (${handoffs.length})
3846
+
3847
+ `;
3848
+ handoffs.forEach((handoff, index) => {
3849
+ const sessionId = handoff.key.replace("handoffs/", "");
3850
+ result += `${index + 1}. **${sessionId}**
3851
+ `;
3852
+ result += ` - Updated: ${handoff.updatedAt.toLocaleString()}
3853
+ `;
3854
+ result += ` - Summary: ${handoff.summary}
3855
+
3856
+ `;
3857
+ });
3858
+ result += `
3859
+ Use \`read_session\` with a session ID to load full context.`;
3860
+ return result;
3861
+ } catch (error) {
3862
+ return `Error listing sessions: ${error instanceof Error ? error.message : String(error)}`;
3863
+ }
3864
+ }
3865
+ },
3866
+ {
3867
+ name: "read_session",
3868
+ description: "Load context from a previous session. Returns session summary, user tasks, and file changes.",
3869
+ args: {
3870
+ sessionId: {
3871
+ type: "string",
3872
+ description: 'Session ID from list_session (e.g., "2024-01-15T10-30-00")',
3873
+ required: true
3874
+ }
3875
+ },
3876
+ async execute({ sessionId }, context) {
3877
+ const config = context?.config;
3878
+ if (!config) {
3879
+ return "Error: Configuration not available";
3880
+ }
3881
+ try {
3882
+ const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3883
+ const memory = new MemoryManager2(config);
3884
+ const content = await memory.read(`handoffs/${sessionId}`);
3885
+ if (!content) {
3886
+ return `Session not found: ${sessionId}
3887
+
3888
+ Use \`list_session\` to see available sessions.`;
3889
+ }
3890
+ return `# Session Context: ${sessionId}
3891
+
3892
+ ${content}
3893
+
3894
+ ---
3895
+
3896
+ This context has been loaded. Use /resume to continue from this point.`;
3897
+ } catch (error) {
3898
+ return `Error reading session: ${error instanceof Error ? error.message : String(error)}`;
3899
+ }
3900
+ }
3901
+ },
3902
+ {
3903
+ name: "bead-create",
3904
+ description: "Create a new bead/task for tracking work",
3905
+ args: {
3906
+ title: {
3907
+ type: "string",
3908
+ description: "Title of bead/task",
3909
+ required: true
3910
+ },
3911
+ description: {
3912
+ type: "string",
3913
+ description: "Description of what needs to be done",
3914
+ required: true
3915
+ }
3916
+ },
3917
+ async execute({ title, description }, context) {
3918
+ const config = context?.config;
3919
+ if (!config) {
3920
+ return "Error: Configuration not available";
3921
+ }
3922
+ if (typeof title !== "string" || typeof description !== "string") {
3923
+ return "Error: title and description must be strings";
3924
+ }
3925
+ try {
3926
+ const { BeadsIntegration: BeadsIntegration2 } = await Promise.resolve().then(() => (init_beads(), beads_exports));
3927
+ const beads = new BeadsIntegration2(config.projectPath);
3928
+ const bead = await beads.createBead(title, description);
3929
+ return `\u2713 Created bead: ${bead.id}
3930
+
3931
+ Title: ${bead.title}
3932
+ Description: ${description}
3933
+
3934
+ Use \`bead-update-status\` to change status or \`bead-complete\` to finish with quality gates.`;
3935
+ } catch (error) {
3936
+ return `Error creating bead: ${error instanceof Error ? error.message : String(error)}`;
3937
+ }
3938
+ }
3939
+ },
3940
+ {
3941
+ name: "bead-update-status",
3942
+ description: "Update bead status (todo, in-progress, completed, blocked)",
3943
+ args: {
3944
+ id: {
3945
+ type: "string",
3946
+ description: 'Bead ID (e.g., "bead-001")',
3947
+ required: true
3948
+ },
3949
+ status: {
3950
+ type: "string",
3951
+ description: "New status: todo, in-progress, completed, blocked",
3952
+ required: true
3953
+ }
3954
+ },
3955
+ async execute({ id, status }, context) {
3956
+ const config = context?.config;
3957
+ if (!config) {
3958
+ return "Error: Configuration not available";
3959
+ }
3960
+ if (typeof id !== "string" || typeof status !== "string") {
3961
+ return "Error: id and status must be strings";
3962
+ }
3963
+ const validStatuses = ["todo", "in-progress", "completed", "blocked"];
3964
+ if (!validStatuses.includes(status)) {
3965
+ return `Error: Invalid status "${status}". Valid statuses: ${validStatuses.join(", ")}`;
3966
+ }
3967
+ try {
3968
+ const { BeadsIntegration: BeadsIntegration2 } = await Promise.resolve().then(() => (init_beads(), beads_exports));
3969
+ const beads = new BeadsIntegration2(config.projectPath);
3970
+ const success = await beads.updateBeadStatus(id, status);
3971
+ if (success) {
3972
+ return `\u2713 Updated bead ${id} to status: ${status}`;
3973
+ } else {
3974
+ return `Error: Bead ${id} not found`;
3975
+ }
3976
+ } catch (error) {
3977
+ return `Error updating bead status: ${error instanceof Error ? error.message : String(error)}`;
3978
+ }
3979
+ }
3980
+ },
3981
+ {
3982
+ name: "bead-complete",
3983
+ description: "Complete a bead with quality gates (typecheck, test, lint, build)",
3984
+ args: {
3985
+ id: {
3986
+ type: "string",
3987
+ description: 'Bead ID (e.g., "bead-001")',
3988
+ required: true
3989
+ }
3990
+ },
3991
+ async execute({ id }, context) {
3992
+ const config = context?.config;
3993
+ if (!config) {
3994
+ return "Error: Configuration not available";
3995
+ }
3996
+ if (typeof id !== "string") {
3997
+ return "Error: id must be a string";
3998
+ }
3999
+ try {
4000
+ const { BeadsIntegration: BeadsIntegration2 } = await Promise.resolve().then(() => (init_beads(), beads_exports));
4001
+ const beads = new BeadsIntegration2(config.projectPath);
4002
+ const result = await beads.completeBead(id);
4003
+ if (!result.success) {
4004
+ let errorReport = `\u274C Quality gates failed for bead ${id}:
4005
+
4006
+ `;
4007
+ result.gates.forEach((gate) => {
4008
+ const status = gate.passed ? "\u2713" : "\u2717";
4009
+ errorReport += `${status} ${gate.name}`;
4010
+ if (gate.error) {
4011
+ errorReport += `
4012
+ Error: ${gate.error}`;
4013
+ }
4014
+ errorReport += "\n";
4015
+ });
4016
+ return errorReport;
4017
+ }
4018
+ return `\u2713 Bead ${id} completed successfully!
4019
+
4020
+ All quality gates passed:
4021
+ ${result.gates.map((g) => ` \u2713 ${g.name}`).join("\n")}`;
3372
4022
  } catch (error) {
3373
- return `Error: ${error instanceof Error ? error.message : String(error)}`;
4023
+ return `Error completing bead: ${error instanceof Error ? error.message : String(error)}`;
3374
4024
  }
3375
4025
  }
3376
4026
  },
3377
4027
  {
3378
- name: "list_session",
3379
- description: "List previous sessions to discover what happened and when. Use this before read_session to find the right context.",
4028
+ name: "bead-list",
4029
+ description: "List all beads in the project",
3380
4030
  args: {
3381
- limit: {
3382
- type: "number",
3383
- description: "Maximum number of sessions to return (default: 10)",
3384
- required: false,
3385
- default: 10
4031
+ filter: {
4032
+ type: "string",
4033
+ description: "Filter by status: todo, in-progress, completed, blocked (optional)",
4034
+ required: false
3386
4035
  }
3387
4036
  },
3388
- async execute({ limit = 10 }, context) {
4037
+ async execute({ filter }, context) {
3389
4038
  const config = context?.config;
3390
4039
  if (!config) {
3391
4040
  return "Error: Configuration not available";
3392
4041
  }
3393
- const limitNum = typeof limit === "number" ? limit : 10;
4042
+ if (filter !== void 0 && typeof filter !== "string") {
4043
+ return "Error: filter must be a string";
4044
+ }
3394
4045
  try {
3395
- const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3396
- const memory = new MemoryManager2(config);
3397
- const memories = await memory.list();
3398
- const handoffs = memories.filter((m) => m.type === "handoff").sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()).slice(0, limitNum);
3399
- if (handoffs.length === 0) {
3400
- return "No previous sessions found. Use /handoff to create a session handoff.";
4046
+ const { BeadsIntegration: BeadsIntegration2 } = await Promise.resolve().then(() => (init_beads(), beads_exports));
4047
+ const beads = new BeadsIntegration2(config.projectPath);
4048
+ const allBeads = await beads.listBeads();
4049
+ let filtered = allBeads;
4050
+ if (filter && ["todo", "in-progress", "completed", "blocked"].includes(filter)) {
4051
+ filtered = allBeads.filter((b) => b.status === filter);
3401
4052
  }
3402
- let result = `# Previous Sessions (${handoffs.length})
4053
+ if (filtered.length === 0) {
4054
+ return `No beads found${filter ? ` with status "${filter}"` : ""}`;
4055
+ }
4056
+ let result = `# Beads (${filtered.length})
3403
4057
 
3404
4058
  `;
3405
- handoffs.forEach((handoff, index) => {
3406
- const sessionId = handoff.key.replace("handoffs/", "");
3407
- result += `${index + 1}. **${sessionId}**
4059
+ filtered.forEach((bead) => {
4060
+ const statusEmoji = {
4061
+ "todo": "\u23F8\uFE0F",
4062
+ "in-progress": "\u{1F504}",
4063
+ "completed": "\u2705",
4064
+ "blocked": "\u{1F6AB}"
4065
+ }[bead.status] || "\u2753";
4066
+ result += `${statusEmoji} **${bead.id}**: ${bead.title}
3408
4067
  `;
3409
- result += ` - Updated: ${handoff.updatedAt.toLocaleString()}
4068
+ result += ` Status: ${bead.status}
3410
4069
  `;
3411
- result += ` - Summary: ${handoff.summary}
3412
-
4070
+ result += ` Updated: ${bead.updatedAt.toLocaleString()}
3413
4071
  `;
4072
+ if (bead.description) {
4073
+ const desc = bead.description.length > 100 ? bead.description.slice(0, 100) + "..." : bead.description;
4074
+ result += ` Description: ${desc}
4075
+ `;
4076
+ }
4077
+ result += "\n";
3414
4078
  });
3415
- result += `
3416
- Use \`read_session\` with a session ID to load full context.`;
3417
- return result;
4079
+ return result.trim();
3418
4080
  } catch (error) {
3419
- return `Error listing sessions: ${error instanceof Error ? error.message : String(error)}`;
4081
+ return `Error listing beads: ${error instanceof Error ? error.message : String(error)}`;
3420
4082
  }
3421
4083
  }
3422
4084
  },
3423
4085
  {
3424
- name: "read_session",
3425
- description: "Load context from a previous session. Returns session summary, user tasks, and file changes.",
4086
+ name: "bead-update-type",
4087
+ description: "Update the type of an existing bead (feature, pattern, decision, knowledge)",
3426
4088
  args: {
3427
- sessionId: {
4089
+ id: {
3428
4090
  type: "string",
3429
- description: 'Session ID from list_session (e.g., "2024-01-15T10-30-00")',
4091
+ description: 'Bead ID (e.g., "bead-001")',
4092
+ required: true
4093
+ },
4094
+ type: {
4095
+ type: "string",
4096
+ description: "New type: feature, pattern, decision, or knowledge",
3430
4097
  required: true
3431
4098
  }
3432
4099
  },
3433
- async execute({ sessionId }, context) {
4100
+ async execute({ id, type }, context) {
3434
4101
  const config = context?.config;
3435
4102
  if (!config) {
3436
4103
  return "Error: Configuration not available";
3437
4104
  }
4105
+ if (typeof id !== "string") {
4106
+ return "Error: id must be a string";
4107
+ }
4108
+ if (typeof type !== "string") {
4109
+ return "Error: type must be a string";
4110
+ }
4111
+ const validTypes = ["feature", "pattern", "decision", "knowledge"];
4112
+ if (!validTypes.includes(type)) {
4113
+ return `Error: Invalid type "${type}". Valid types: ${validTypes.join(", ")}`;
4114
+ }
3438
4115
  try {
3439
- const { MemoryManager: MemoryManager2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
3440
- const memory = new MemoryManager2(config);
3441
- const content = await memory.read(`handoffs/${sessionId}`);
3442
- if (!content) {
3443
- return `Session not found: ${sessionId}
3444
-
3445
- Use \`list_session\` to see available sessions.`;
4116
+ const { BeadsIntegration: BeadsIntegration2 } = await Promise.resolve().then(() => (init_beads(), beads_exports));
4117
+ const beads = new BeadsIntegration2(config.projectPath);
4118
+ const success = await beads.updateBeadType(id, type);
4119
+ if (success) {
4120
+ return `\u2713 Updated bead ${id} type to: ${type}`;
4121
+ } else {
4122
+ return `Error: Bead ${id} not found`;
3446
4123
  }
3447
- return `# Session Context: ${sessionId}
3448
-
3449
- ${content}
3450
-
3451
- ---
3452
-
3453
- This context has been loaded. Use /resume to continue from this point.`;
3454
4124
  } catch (error) {
3455
- return `Error reading session: ${error instanceof Error ? error.message : String(error)}`;
4125
+ return `Error updating bead type: ${error instanceof Error ? error.message : String(error)}`;
3456
4126
  }
3457
4127
  }
3458
4128
  }
@@ -3507,6 +4177,7 @@ var ToolRegistry = class {
3507
4177
  }
3508
4178
  const mergedContext = {
3509
4179
  ...context,
4180
+ config: this.config,
3510
4181
  toolConfigManager: context?.toolConfigManager || this.toolConfigManager
3511
4182
  };
3512
4183
  if (tool.execute.length === 2) {
@@ -3520,9 +4191,9 @@ var ToolRegistry = class {
3520
4191
  async createTool(name, options) {
3521
4192
  const configPath = options.global ? paths.globalConfig() : this.config.configPath;
3522
4193
  const toolsDir = paths.tools(configPath);
3523
- await mkdir5(toolsDir, { recursive: true });
4194
+ await mkdir6(toolsDir, { recursive: true });
3524
4195
  const fileName = `${name}.ts`;
3525
- const filePath = join9(toolsDir, fileName);
4196
+ const filePath = join10(toolsDir, fileName);
3526
4197
  const argsSchema = Object.entries(options.args).map(([argName, arg]) => ` ${argName}: {
3527
4198
  type: '${arg.type}',
3528
4199
  description: '${arg.description}',
@@ -3541,7 +4212,7 @@ ${options.code}
3541
4212
  }
3542
4213
  });
3543
4214
  `;
3544
- await writeFile5(filePath, content);
4215
+ await writeFile6(filePath, content);
3545
4216
  }
3546
4217
  /**
3547
4218
  * Format tool for agent consumption
@@ -3570,13 +4241,13 @@ ${argsDesc}
3570
4241
  async loadToolsFromDir(dir) {
3571
4242
  let files;
3572
4243
  try {
3573
- files = await readdir4(dir);
4244
+ files = await readdir5(dir);
3574
4245
  } catch {
3575
4246
  return;
3576
4247
  }
3577
4248
  for (const file of files) {
3578
4249
  if (extname3(file) !== ".ts" && extname3(file) !== ".js") continue;
3579
- const filePath = join9(dir, file);
4250
+ const filePath = join10(dir, file);
3580
4251
  try {
3581
4252
  const toolModule = await import(`file://${filePath}`);
3582
4253
  const tool = toolModule.default;
@@ -3595,8 +4266,8 @@ ${argsDesc}
3595
4266
  init_esm_shims();
3596
4267
  init_paths();
3597
4268
  init_logger();
3598
- import { readdir as readdir5, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
3599
- import { join as join10, basename as basename3, extname as extname4 } from "path";
4269
+ import { readdir as readdir6, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
4270
+ import { join as join11, basename as basename3, extname as extname4 } from "path";
3600
4271
  var PluginSystem = class {
3601
4272
  config;
3602
4273
  plugins = /* @__PURE__ */ new Map();
@@ -3694,9 +4365,9 @@ var PluginSystem = class {
3694
4365
  async createPlugin(name, options) {
3695
4366
  const configPath = options.global ? paths.globalConfig() : this.config.configPath;
3696
4367
  const pluginsDir = paths.plugins(configPath);
3697
- await mkdir6(pluginsDir, { recursive: true });
4368
+ await mkdir7(pluginsDir, { recursive: true });
3698
4369
  const fileName = `${name}.ts`;
3699
- const filePath = join10(pluginsDir, fileName);
4370
+ const filePath = join11(pluginsDir, fileName);
3700
4371
  const content = `import { Plugin } from 'aikit';
3701
4372
 
3702
4373
  /**
@@ -3712,7 +4383,7 @@ ${options.code}
3712
4383
 
3713
4384
  export default ${toPascalCase(name)}Plugin;
3714
4385
  `;
3715
- await writeFile6(filePath, content);
4386
+ await writeFile7(filePath, content);
3716
4387
  }
3717
4388
  /**
3718
4389
  * Process event queue
@@ -3780,13 +4451,13 @@ export default ${toPascalCase(name)}Plugin;
3780
4451
  async loadPluginsFromDir(dir) {
3781
4452
  let files;
3782
4453
  try {
3783
- files = await readdir5(dir);
4454
+ files = await readdir6(dir);
3784
4455
  } catch {
3785
4456
  return;
3786
4457
  }
3787
4458
  for (const file of files) {
3788
4459
  if (extname4(file) !== ".ts" && extname4(file) !== ".js") continue;
3789
- const filePath = join10(dir, file);
4460
+ const filePath = join11(dir, file);
3790
4461
  const name = basename3(file, extname4(file));
3791
4462
  this.plugins.set(name, {
3792
4463
  name,
@@ -3887,355 +4558,7 @@ function toPascalCase(str) {
3887
4558
 
3888
4559
  // src/index.ts
3889
4560
  init_memory();
3890
-
3891
- // src/core/beads.ts
3892
- init_esm_shims();
3893
- init_paths();
3894
- init_logger();
3895
- import { readFile as readFile6, writeFile as writeFile7, readdir as readdir6, access as access3, constants as constants3, mkdir as mkdir7 } from "fs/promises";
3896
- import { join as join11 } from "path";
3897
- import { exec } from "child_process";
3898
- import { promisify } from "util";
3899
- var execAsync = promisify(exec);
3900
- var BeadsIntegration = class {
3901
- projectPath;
3902
- constructor(projectPath) {
3903
- this.projectPath = projectPath || process.cwd();
3904
- }
3905
- /**
3906
- * Check if Beads CLI is installed
3907
- */
3908
- async isInstalled() {
3909
- try {
3910
- await execAsync("bd --version");
3911
- return true;
3912
- } catch {
3913
- return false;
3914
- }
3915
- }
3916
- /**
3917
- * Get Beads version
3918
- */
3919
- async getVersion() {
3920
- try {
3921
- const { stdout } = await execAsync("bd --version");
3922
- const match = stdout.match(/bd version (\S+)/);
3923
- return match?.[1] || null;
3924
- } catch {
3925
- return null;
3926
- }
3927
- }
3928
- /**
3929
- * Check if Beads is initialized in project
3930
- */
3931
- async isInitialized() {
3932
- const beadsDir = paths.beadsDir(this.projectPath);
3933
- try {
3934
- await access3(beadsDir, constants3.R_OK);
3935
- return true;
3936
- } catch {
3937
- return false;
3938
- }
3939
- }
3940
- /**
3941
- * Install Beads CLI globally
3942
- */
3943
- async install() {
3944
- try {
3945
- await execAsync("npm install -g beads");
3946
- return true;
3947
- } catch (error) {
3948
- logger.error("Failed to install Beads CLI:", error);
3949
- return false;
3950
- }
3951
- }
3952
- /**
3953
- * Initialize Beads in project using bd CLI
3954
- */
3955
- async init() {
3956
- try {
3957
- await execAsync("bd init", { cwd: this.projectPath });
3958
- logger.success("Beads initialized");
3959
- return true;
3960
- } catch (error) {
3961
- logger.error("Failed to initialize Beads:", error);
3962
- return false;
3963
- }
3964
- }
3965
- /**
3966
- * Initialize local .beads directory (works without global beads CLI)
3967
- */
3968
- async initLocal() {
3969
- try {
3970
- const beadsDir = paths.beadsDir(this.projectPath);
3971
- await mkdir7(beadsDir, { recursive: true });
3972
- const readmeContent = `# Beads - Task Tracking
3973
-
3974
- This directory contains task beads for tracking work items.
3975
-
3976
- ## How it works
3977
- - Each file is a task bead (bead-001.md, bead-002.md, etc.)
3978
- - Status: todo, in-progress, completed, blocked
3979
- - Use \`/create\` command to create new tasks
3980
- - Use \`/finish\` command to complete tasks with quality gates
3981
-
3982
- ## Beads CLI
3983
- For full functionality, install beads globally:
3984
- \`\`\`bash
3985
- npm install -g beads
3986
- bd init
3987
- \`\`\`
3988
-
3989
- ## Available Commands
3990
- - \`bd ready\` - Show available work
3991
- - \`bd show <id>\` - View task details
3992
- - \`bd update <id> --status in_progress\` - Update task status
3993
- - \`bd close <id>\` - Complete task
3994
- - \`bd sync\` - Sync with git
3995
- `;
3996
- await writeFile7(join11(beadsDir, "README.md"), readmeContent);
3997
- return true;
3998
- } catch (error) {
3999
- logger.error("Failed to initialize .beads directory:", error);
4000
- return false;
4001
- }
4002
- }
4003
- /**
4004
- * Get current status
4005
- */
4006
- async getStatus() {
4007
- const installed = await this.isInstalled();
4008
- const version = await this.getVersion();
4009
- const initialized = await this.isInitialized();
4010
- let activeTasks = 0;
4011
- let completedTasks = 0;
4012
- let currentTask;
4013
- if (initialized) {
4014
- const beads = await this.listBeads();
4015
- activeTasks = beads.filter((b) => b.status === "in-progress" || b.status === "todo").length;
4016
- completedTasks = beads.filter((b) => b.status === "completed").length;
4017
- const active = beads.find((b) => b.status === "in-progress");
4018
- currentTask = active?.title;
4019
- }
4020
- return {
4021
- installed,
4022
- version: version || void 0,
4023
- initialized,
4024
- activeTasks,
4025
- completedTasks,
4026
- currentTask
4027
- };
4028
- }
4029
- /**
4030
- * List all beads in the project
4031
- */
4032
- async listBeads() {
4033
- const beadsDir = paths.beadsDir(this.projectPath);
4034
- const beads = [];
4035
- try {
4036
- const files = await readdir6(beadsDir);
4037
- for (const file of files) {
4038
- if (!file.match(/^bead-\d+\.md$/)) continue;
4039
- const content = await readFile6(join11(beadsDir, file), "utf-8");
4040
- const bead = this.parseBeadFile(file, content);
4041
- if (bead) beads.push(bead);
4042
- }
4043
- } catch {
4044
- }
4045
- return beads.sort((a, b) => a.id.localeCompare(b.id));
4046
- }
4047
- /**
4048
- * Get a specific bead
4049
- */
4050
- async getBead(id) {
4051
- const beadsDir = paths.beadsDir(this.projectPath);
4052
- const fileName = id.endsWith(".md") ? id : `${id}.md`;
4053
- try {
4054
- const content = await readFile6(join11(beadsDir, fileName), "utf-8");
4055
- return this.parseBeadFile(fileName, content);
4056
- } catch {
4057
- return null;
4058
- }
4059
- }
4060
- /**
4061
- * Create a new bead
4062
- */
4063
- async createBead(title, description) {
4064
- const beadsDir = paths.beadsDir(this.projectPath);
4065
- await mkdir7(beadsDir, { recursive: true });
4066
- const beads = await this.listBeads();
4067
- const maxId = beads.reduce((max, b) => {
4068
- const num = parseInt(b.id.replace("bead-", ""), 10);
4069
- return num > max ? num : max;
4070
- }, 0);
4071
- const id = `bead-${String(maxId + 1).padStart(3, "0")}`;
4072
- const now = (/* @__PURE__ */ new Date()).toISOString();
4073
- const content = `---
4074
- id: ${id}
4075
- title: ${title}
4076
- status: in-progress
4077
- created: ${now}
4078
- updated: ${now}
4079
- ---
4080
-
4081
- # ${title}
4082
-
4083
- ## Description
4084
- ${description}
4085
-
4086
- ## Notes
4087
-
4088
-
4089
- ## Checklist
4090
- - [ ] Requirements understood
4091
- - [ ] Implementation complete
4092
- - [ ] Tests passing
4093
- - [ ] Code reviewed
4094
-
4095
- ## Progress
4096
-
4097
- `;
4098
- await writeFile7(join11(beadsDir, `${id}.md`), content);
4099
- return {
4100
- id,
4101
- title,
4102
- description,
4103
- status: "in-progress",
4104
- createdAt: new Date(now),
4105
- updatedAt: new Date(now),
4106
- notes: []
4107
- };
4108
- }
4109
- /**
4110
- * Update bead status
4111
- */
4112
- async updateBeadStatus(id, status) {
4113
- const beadsDir = paths.beadsDir(this.projectPath);
4114
- const fileName = id.endsWith(".md") ? id : `${id}.md`;
4115
- const filePath = join11(beadsDir, fileName);
4116
- try {
4117
- let content = await readFile6(filePath, "utf-8");
4118
- content = content.replace(
4119
- /status:\s*\w+/,
4120
- `status: ${status}`
4121
- );
4122
- content = content.replace(
4123
- /updated:\s*.+/,
4124
- `updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
4125
- );
4126
- await writeFile7(filePath, content);
4127
- return true;
4128
- } catch {
4129
- return false;
4130
- }
4131
- }
4132
- /**
4133
- * Add note to bead
4134
- */
4135
- async addNote(id, note) {
4136
- const beadsDir = paths.beadsDir(this.projectPath);
4137
- const fileName = id.endsWith(".md") ? id : `${id}.md`;
4138
- const filePath = join11(beadsDir, fileName);
4139
- try {
4140
- let content = await readFile6(filePath, "utf-8");
4141
- const notesMatch = content.match(/## Notes\n([\s\S]*?)(?=\n##|$)/);
4142
- if (notesMatch) {
4143
- const timestamp = (/* @__PURE__ */ new Date()).toLocaleString();
4144
- const newNote = `- [${timestamp}] ${note}`;
4145
- content = content.replace(
4146
- notesMatch[0],
4147
- `## Notes
4148
- ${notesMatch[1]}${newNote}
4149
- `
4150
- );
4151
- }
4152
- content = content.replace(
4153
- /updated:\s*.+/,
4154
- `updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
4155
- );
4156
- await writeFile7(filePath, content);
4157
- return true;
4158
- } catch {
4159
- return false;
4160
- }
4161
- }
4162
- /**
4163
- * Complete a bead with quality gates
4164
- */
4165
- async completeBead(id) {
4166
- const gates = [
4167
- { name: "Type Check", command: "npm run typecheck" },
4168
- { name: "Tests", command: "npm run test" },
4169
- { name: "Lint", command: "npm run lint" },
4170
- { name: "Build", command: "npm run build" }
4171
- ];
4172
- const results = [];
4173
- for (const gate of gates) {
4174
- try {
4175
- await execAsync(gate.command, { cwd: this.projectPath });
4176
- results.push({ name: gate.name, passed: true });
4177
- logger.success(`${gate.name}: passed`);
4178
- } catch (error) {
4179
- results.push({
4180
- name: gate.name,
4181
- passed: false,
4182
- error: error instanceof Error ? error.message.slice(0, 200) : "Failed"
4183
- });
4184
- logger.error(`${gate.name}: failed`);
4185
- }
4186
- }
4187
- const allPassed = results.every((r) => r.passed);
4188
- if (allPassed) {
4189
- await this.updateBeadStatus(id, "completed");
4190
- await this.addNote(id, "Task completed - all quality gates passed");
4191
- } else {
4192
- await this.addNote(id, "Completion attempted but quality gates failed");
4193
- }
4194
- return {
4195
- success: allPassed,
4196
- gates: results
4197
- };
4198
- }
4199
- /**
4200
- * Get current active bead
4201
- */
4202
- async getCurrentBead() {
4203
- const beads = await this.listBeads();
4204
- return beads.find((b) => b.status === "in-progress") || null;
4205
- }
4206
- /**
4207
- * Parse a bead file
4208
- */
4209
- parseBeadFile(fileName, content) {
4210
- try {
4211
- const id = fileName.replace(".md", "");
4212
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
4213
- const frontmatter = frontmatterMatch?.[1] || "";
4214
- const getValue = (key) => {
4215
- const match = frontmatter.match(new RegExp(`${key}:\\s*(.+)`));
4216
- return match?.[1]?.trim() || "";
4217
- };
4218
- const titleMatch = content.match(/^# (.+)/m);
4219
- const title = getValue("title") || titleMatch?.[1] || id;
4220
- const descMatch = content.match(/## Description\n([\s\S]*?)(?=\n##|$)/);
4221
- const description = descMatch?.[1]?.trim() || "";
4222
- const notesMatch = content.match(/## Notes\n([\s\S]*?)(?=\n##|$)/);
4223
- const notesContent = notesMatch?.[1] || "";
4224
- const notes = notesContent.split("\n").filter((line) => line.trim().startsWith("-")).map((line) => line.replace(/^-\s*/, "").trim());
4225
- return {
4226
- id,
4227
- title,
4228
- description,
4229
- status: getValue("status") || "todo",
4230
- createdAt: new Date(getValue("created") || Date.now()),
4231
- updatedAt: new Date(getValue("updated") || Date.now()),
4232
- notes
4233
- };
4234
- } catch {
4235
- return null;
4236
- }
4237
- }
4238
- };
4561
+ init_beads();
4239
4562
 
4240
4563
  // src/core/anti-hallucination.ts
4241
4564
  init_esm_shims();
@@ -4244,14 +4567,17 @@ init_logger();
4244
4567
  // src/index.ts
4245
4568
  init_logger();
4246
4569
  init_paths();
4570
+ init_version();
4247
4571
 
4248
4572
  // src/cli/commands/index.ts
4249
4573
  init_esm_shims();
4250
4574
 
4251
4575
  // src/cli/commands/init.ts
4252
4576
  init_esm_shims();
4577
+ init_config();
4253
4578
  import chalk2 from "chalk";
4254
4579
  import inquirer from "inquirer";
4580
+ init_beads();
4255
4581
 
4256
4582
  // src/utils/cli-detector.ts
4257
4583
  init_esm_shims();
@@ -4380,6 +4706,7 @@ import { join as join13, dirname as dirname2 } from "path";
4380
4706
  import { homedir as homedir3 } from "os";
4381
4707
  import { fileURLToPath as fileURLToPath3 } from "url";
4382
4708
  import { execSync as execSync2 } from "child_process";
4709
+ init_config();
4383
4710
  init_logger();
4384
4711
  init_paths();
4385
4712
  async function initializeConfig(configDir, _isGlobal) {
@@ -4986,6 +5313,7 @@ function registerInstallCommand(program2) {
4986
5313
 
4987
5314
  // src/cli/commands/sync.ts
4988
5315
  init_esm_shims();
5316
+ init_config();
4989
5317
  import chalk4 from "chalk";
4990
5318
 
4991
5319
  // src/core/sync-engine.ts
@@ -6066,6 +6394,7 @@ function registerSyncCommand(program2) {
6066
6394
 
6067
6395
  // src/cli/commands/skills.ts
6068
6396
  init_esm_shims();
6397
+ init_config();
6069
6398
  import chalk5 from "chalk";
6070
6399
  init_logger();
6071
6400
  function registerSkillsCommand(program2) {
@@ -6229,7 +6558,9 @@ init_esm_shims();
6229
6558
  import chalk6 from "chalk";
6230
6559
  import { readFile as readFile13, writeFile as writeFile14 } from "fs/promises";
6231
6560
  import { join as join19 } from "path";
6561
+ init_config();
6232
6562
  init_memory();
6563
+ init_beads();
6233
6564
  init_logger();
6234
6565
  function registerAgentsCommand(program2) {
6235
6566
  const agentsCmd = program2.command("agents").description("Manage agents");