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