@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 +1166 -835
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.js +1176 -852
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +1047 -376
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
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/
|
|
173
|
-
var
|
|
174
|
-
__export(
|
|
175
|
-
|
|
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
|
-
|
|
180
|
-
var
|
|
181
|
-
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
constructor(
|
|
189
|
-
this.
|
|
362
|
+
init_paths();
|
|
363
|
+
MemoryManager = class {
|
|
364
|
+
config;
|
|
365
|
+
constructor(config) {
|
|
366
|
+
this.config = config;
|
|
190
367
|
}
|
|
191
368
|
/**
|
|
192
|
-
*
|
|
369
|
+
* List all memory entries
|
|
193
370
|
*/
|
|
194
|
-
async
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
400
|
+
* Read a memory entry
|
|
231
401
|
*/
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
*
|
|
428
|
+
* Update a memory entry
|
|
243
429
|
*/
|
|
244
|
-
async
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
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
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
"
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
455
|
+
await writeFile3(filePath, content);
|
|
270
456
|
}
|
|
271
457
|
/**
|
|
272
|
-
*
|
|
458
|
+
* Create a handoff bundle
|
|
273
459
|
*/
|
|
274
|
-
async
|
|
275
|
-
const
|
|
276
|
-
const
|
|
277
|
-
|
|
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 :
|
|
931
|
+
const fullAssetsDir = assetsDir.startsWith("/") ? assetsDir : join7(process.cwd(), assetsDir);
|
|
538
932
|
if (!existsSync2(fullAssetsDir)) {
|
|
539
|
-
await
|
|
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 =
|
|
559
|
-
await
|
|
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
|
|
599
|
-
import { join as
|
|
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(
|
|
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
|
|
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 =
|
|
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) =>
|
|
1032
|
+
status.cssFiles = cssFiles.map((f) => join8(stylesDir, f));
|
|
639
1033
|
}
|
|
640
1034
|
} catch (e) {
|
|
641
1035
|
}
|
|
642
1036
|
}
|
|
643
|
-
const assetsDir =
|
|
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/
|
|
717
|
-
var
|
|
718
|
-
__export(
|
|
719
|
-
|
|
1110
|
+
// src/core/beads.ts
|
|
1111
|
+
var beads_exports = {};
|
|
1112
|
+
__export(beads_exports, {
|
|
1113
|
+
BeadsIntegration: () => BeadsIntegration
|
|
720
1114
|
});
|
|
721
|
-
import { readFile as
|
|
722
|
-
import { join as
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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
|
-
*
|
|
1133
|
+
* Check if Beads CLI is installed
|
|
736
1134
|
*/
|
|
737
|
-
async
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
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
|
-
*
|
|
1144
|
+
* Get Beads version
|
|
767
1145
|
*/
|
|
768
|
-
async
|
|
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
|
-
|
|
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
|
-
*
|
|
1156
|
+
* Check if Beads is initialized in project
|
|
795
1157
|
*/
|
|
796
|
-
async
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
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
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
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
|
-
*
|
|
1180
|
+
* Initialize Beads in project using bd CLI
|
|
825
1181
|
*/
|
|
826
|
-
async
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
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
|
-
|
|
835
|
-
${summary.inProgress.map((item) => `- [ ] ${item}`).join("\n") || "- None"}
|
|
1201
|
+
This directory contains task beads for tracking work items.
|
|
836
1202
|
|
|
837
|
-
##
|
|
838
|
-
|
|
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
|
-
##
|
|
841
|
-
|
|
1209
|
+
## Beads CLI
|
|
1210
|
+
For full functionality, install beads globally:
|
|
1211
|
+
\`\`\`bash
|
|
1212
|
+
npm install -g beads
|
|
1213
|
+
bd init
|
|
1214
|
+
\`\`\`
|
|
842
1215
|
|
|
843
|
-
##
|
|
844
|
-
|
|
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
|
-
|
|
847
|
-
|
|
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
|
|
1231
|
+
* Get current status
|
|
851
1232
|
*/
|
|
852
|
-
async
|
|
853
|
-
const
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
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
|
-
*
|
|
1257
|
+
* List all beads in project
|
|
861
1258
|
*/
|
|
862
|
-
async
|
|
863
|
-
const
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
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
|
-
*
|
|
1275
|
+
* Get a specific bead
|
|
887
1276
|
*/
|
|
888
|
-
async
|
|
889
|
-
const
|
|
890
|
-
const
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
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
|
-
|
|
897
|
-
${findings.sources.map((s) => `- ${s}`).join("\n")}
|
|
1308
|
+
# ${title}
|
|
898
1309
|
|
|
899
|
-
##
|
|
900
|
-
${
|
|
1310
|
+
## Description
|
|
1311
|
+
${description}
|
|
901
1312
|
|
|
902
|
-
|
|
903
|
-
\`\`\`
|
|
904
|
-
${findings.codeExamples}
|
|
905
|
-
\`\`\`` : ""}
|
|
1313
|
+
## Notes
|
|
906
1314
|
|
|
907
|
-
---
|
|
908
|
-
_Researched: ${(/* @__PURE__ */ new Date()).toISOString()}_
|
|
909
1315
|
`;
|
|
910
|
-
await
|
|
911
|
-
return
|
|
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
|
-
*
|
|
1328
|
+
* Update bead status
|
|
915
1329
|
*/
|
|
916
|
-
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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
|
-
|
|
924
|
-
|
|
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
|
|
2817
|
-
import { join as
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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: "
|
|
3379
|
-
description: "List
|
|
4028
|
+
name: "bead-list",
|
|
4029
|
+
description: "List all beads in the project",
|
|
3380
4030
|
args: {
|
|
3381
|
-
|
|
3382
|
-
type: "
|
|
3383
|
-
description: "
|
|
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({
|
|
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
|
-
|
|
4042
|
+
if (filter !== void 0 && typeof filter !== "string") {
|
|
4043
|
+
return "Error: filter must be a string";
|
|
4044
|
+
}
|
|
3394
4045
|
try {
|
|
3395
|
-
const {
|
|
3396
|
-
const
|
|
3397
|
-
const
|
|
3398
|
-
|
|
3399
|
-
if (
|
|
3400
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3406
|
-
const
|
|
3407
|
-
|
|
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 += `
|
|
4068
|
+
result += ` Status: ${bead.status}
|
|
3410
4069
|
`;
|
|
3411
|
-
result += `
|
|
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
|
|
4081
|
+
return `Error listing beads: ${error instanceof Error ? error.message : String(error)}`;
|
|
3420
4082
|
}
|
|
3421
4083
|
}
|
|
3422
4084
|
},
|
|
3423
4085
|
{
|
|
3424
|
-
name: "
|
|
3425
|
-
description: "
|
|
4086
|
+
name: "bead-update-type",
|
|
4087
|
+
description: "Update the type of an existing bead (feature, pattern, decision, knowledge)",
|
|
3426
4088
|
args: {
|
|
3427
|
-
|
|
4089
|
+
id: {
|
|
3428
4090
|
type: "string",
|
|
3429
|
-
description: '
|
|
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({
|
|
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 {
|
|
3440
|
-
const
|
|
3441
|
-
const
|
|
3442
|
-
if (
|
|
3443
|
-
return
|
|
3444
|
-
|
|
3445
|
-
|
|
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
|
|
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
|
|
4194
|
+
await mkdir6(toolsDir, { recursive: true });
|
|
3524
4195
|
const fileName = `${name}.ts`;
|
|
3525
|
-
const filePath =
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
3599
|
-
import { join as
|
|
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
|
|
4368
|
+
await mkdir7(pluginsDir, { recursive: true });
|
|
3698
4369
|
const fileName = `${name}.ts`;
|
|
3699
|
-
const filePath =
|
|
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
|
|
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
|
|
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 =
|
|
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");
|