skill-tree 0.1.5 → 0.1.6

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.
Files changed (40) hide show
  1. package/dist/cli/index.js +1827 -1410
  2. package/dist/cli/index.js.map +1 -0
  3. package/dist/cli/index.mjs +9539 -1658
  4. package/dist/cli/index.mjs.map +1 -0
  5. package/dist/index.d.mts +316 -7
  6. package/dist/index.d.ts +316 -7
  7. package/dist/index.js +1539 -92
  8. package/dist/index.js.map +1 -0
  9. package/dist/index.mjs +9614 -70
  10. package/dist/index.mjs.map +1 -0
  11. package/package.json +2 -1
  12. package/dist/chunk-3SRB47JW.mjs +0 -8344
  13. package/dist/chunk-43YOKLZP.mjs +0 -6081
  14. package/dist/chunk-4AGZU52D.mjs +0 -7918
  15. package/dist/chunk-4OC5QFIF.mjs +0 -11267
  16. package/dist/chunk-55SMGVTP.mjs +0 -7126
  17. package/dist/chunk-6FX4IK4Z.mjs +0 -5368
  18. package/dist/chunk-7EGDKOHV.mjs +0 -9439
  19. package/dist/chunk-7LMOQW5H.mjs +0 -4893
  20. package/dist/chunk-7QIQJVNP.mjs +0 -14206
  21. package/dist/chunk-7VB4ZRZO.mjs +0 -7127
  22. package/dist/chunk-BPVRW25O.mjs +0 -6089
  23. package/dist/chunk-CI4476KM.mjs +0 -6607
  24. package/dist/chunk-DDXYQ74I.mjs +0 -13969
  25. package/dist/chunk-DQOFJXBX.mjs +0 -6595
  26. package/dist/chunk-E2CVK23F.mjs +0 -8751
  27. package/dist/chunk-F3YEUQAP.mjs +0 -654
  28. package/dist/chunk-FKJJ4RJG.mjs +0 -13874
  29. package/dist/chunk-II7DECZQ.mjs +0 -9111
  30. package/dist/chunk-INKVOZXK.mjs +0 -15898
  31. package/dist/chunk-K6NRCSAZ.mjs +0 -4355
  32. package/dist/chunk-OYHYXKXO.mjs +0 -7297
  33. package/dist/chunk-PDPN7FW7.mjs +0 -1045
  34. package/dist/chunk-TEUB6DZR.mjs +0 -6453
  35. package/dist/chunk-TWPEHDW4.mjs +0 -1067
  36. package/dist/chunk-Y54UK2J3.mjs +0 -13071
  37. package/dist/chunk-ZQVS7MQK.mjs +0 -6081
  38. package/dist/sqlite-OLU72GHB.mjs +0 -6
  39. package/dist/sqlite-XJRPMNAJ.mjs +0 -6
  40. package/dist/sync-BSWMMDA6.mjs +0 -14
@@ -1,1067 +0,0 @@
1
- // src/storage/sqlite.ts
2
- import Database from "better-sqlite3";
3
- import * as path from "path";
4
- import * as fs from "fs";
5
-
6
- // src/storage/base.ts
7
- var BaseStorageAdapter = class {
8
- constructor() {
9
- this.initialized = false;
10
- }
11
- /**
12
- * Ensure storage is initialized before operations
13
- */
14
- ensureInitialized() {
15
- if (!this.initialized) {
16
- throw new Error("Storage not initialized. Call initialize() first.");
17
- }
18
- }
19
- /**
20
- * Apply filters to a list of skills
21
- */
22
- applyFilter(skills, filter) {
23
- if (!filter) return skills;
24
- return skills.filter((skill) => {
25
- if (filter.status && filter.status.length > 0) {
26
- if (!filter.status.includes(skill.status)) return false;
27
- }
28
- if (filter.tags && filter.tags.length > 0) {
29
- const hasTag = filter.tags.some((tag) => skill.tags.includes(tag));
30
- if (!hasTag) return false;
31
- }
32
- if (filter.author && skill.author !== filter.author) {
33
- return false;
34
- }
35
- if (filter.minSuccessRate !== void 0 && skill.metrics.successRate < filter.minSuccessRate) {
36
- return false;
37
- }
38
- if (filter.createdAfter && skill.createdAt < filter.createdAfter) {
39
- return false;
40
- }
41
- if (filter.createdBefore && skill.createdAt > filter.createdBefore) {
42
- return false;
43
- }
44
- if (!this.applyNamespaceFilter(skill, filter)) {
45
- return false;
46
- }
47
- return true;
48
- });
49
- }
50
- /**
51
- * Apply namespace-related filters to a skill
52
- */
53
- applyNamespaceFilter(skill, filter) {
54
- const namespace = skill.namespace;
55
- if (filter.scope) {
56
- const scopes = Array.isArray(filter.scope) ? filter.scope : [filter.scope];
57
- const skillScope = namespace?.scope || "personal";
58
- if (!scopes.includes(skillScope)) return false;
59
- }
60
- if (filter.owner) {
61
- const skillOwner = namespace?.owner;
62
- if (skillOwner !== filter.owner) return false;
63
- }
64
- if (filter.team) {
65
- const skillTeam = namespace?.team;
66
- if (skillTeam !== filter.team) return false;
67
- }
68
- if (filter.visibility) {
69
- const visibilities = Array.isArray(filter.visibility) ? filter.visibility : [filter.visibility];
70
- const skillVisibility = namespace?.visibility || "private";
71
- if (!visibilities.includes(skillVisibility)) return false;
72
- }
73
- if (filter.accessibleBy) {
74
- if (!this.canAgentAccessSkill(skill, filter.accessibleBy, filter.accessibleByTeam)) {
75
- return false;
76
- }
77
- }
78
- return true;
79
- }
80
- /**
81
- * Check if an agent can access a skill based on namespace rules
82
- */
83
- canAgentAccessSkill(skill, agentId, agentTeam) {
84
- const namespace = skill.namespace;
85
- if (!namespace) return true;
86
- if (namespace.owner === agentId) return true;
87
- switch (namespace.visibility) {
88
- case "public":
89
- return true;
90
- case "team-only":
91
- return agentTeam !== void 0 && agentTeam === namespace.team;
92
- case "private":
93
- return namespace.owner === agentId;
94
- default:
95
- return false;
96
- }
97
- }
98
- /**
99
- * Simple text search across skill fields
100
- */
101
- textSearch(skills, query) {
102
- const lowerQuery = query.toLowerCase();
103
- const terms = lowerQuery.split(/\s+/).filter((t) => t.length > 0);
104
- return skills.filter((skill) => {
105
- const searchText = [
106
- skill.name,
107
- skill.description,
108
- skill.instructions,
109
- ...skill.tags
110
- ].join(" ").toLowerCase();
111
- return terms.every((term) => searchText.includes(term));
112
- });
113
- }
114
- };
115
- var MemoryStorageAdapter = class extends BaseStorageAdapter {
116
- constructor() {
117
- super(...arguments);
118
- this.skills = /* @__PURE__ */ new Map();
119
- // skillId -> version -> skill
120
- this.lineages = /* @__PURE__ */ new Map();
121
- }
122
- async initialize() {
123
- this.initialized = true;
124
- }
125
- async saveSkill(skill) {
126
- this.ensureInitialized();
127
- if (!this.skills.has(skill.id)) {
128
- this.skills.set(skill.id, /* @__PURE__ */ new Map());
129
- }
130
- const versionMap = this.skills.get(skill.id);
131
- versionMap.set(skill.version, { ...skill });
132
- this.updateLineage(skill);
133
- }
134
- async getSkill(id, version) {
135
- this.ensureInitialized();
136
- const versionMap = this.skills.get(id);
137
- if (!versionMap) return null;
138
- if (version) {
139
- return versionMap.get(version) || null;
140
- }
141
- const versions = Array.from(versionMap.keys()).sort(this.compareVersions);
142
- const latestVersion = versions[versions.length - 1];
143
- return latestVersion ? versionMap.get(latestVersion) || null : null;
144
- }
145
- async listSkills(filter) {
146
- this.ensureInitialized();
147
- const skills = [];
148
- for (const versionMap of this.skills.values()) {
149
- const versions = Array.from(versionMap.keys()).sort(this.compareVersions);
150
- const latestVersion = versions[versions.length - 1];
151
- if (latestVersion) {
152
- const skill = versionMap.get(latestVersion);
153
- if (skill) skills.push(skill);
154
- }
155
- }
156
- return this.applyFilter(skills, filter);
157
- }
158
- async deleteSkill(id, version) {
159
- this.ensureInitialized();
160
- if (version) {
161
- const versionMap = this.skills.get(id);
162
- if (!versionMap) return false;
163
- return versionMap.delete(version);
164
- }
165
- const existed = this.skills.has(id);
166
- this.skills.delete(id);
167
- this.lineages.delete(id);
168
- return existed;
169
- }
170
- async getVersionHistory(skillId) {
171
- this.ensureInitialized();
172
- const versionMap = this.skills.get(skillId);
173
- if (!versionMap) return [];
174
- const versions = [];
175
- for (const [version, skill] of versionMap) {
176
- versions.push({
177
- skillId,
178
- version,
179
- skill,
180
- changelog: "",
181
- // Not tracked in memory
182
- createdAt: skill.createdAt,
183
- contentHash: this.hashSkill(skill)
184
- });
185
- }
186
- return versions.sort((a, b) => this.compareVersions(a.version, b.version));
187
- }
188
- async getLineage(skillId) {
189
- this.ensureInitialized();
190
- return this.lineages.get(skillId) || null;
191
- }
192
- async searchSkills(query) {
193
- this.ensureInitialized();
194
- const allSkills = await this.listSkills();
195
- return this.textSearch(allSkills, query);
196
- }
197
- updateLineage(skill) {
198
- if (!this.lineages.has(skill.id)) {
199
- this.lineages.set(skill.id, {
200
- rootId: skill.id,
201
- versions: [],
202
- forks: []
203
- });
204
- }
205
- const lineage = this.lineages.get(skill.id);
206
- const existingIndex = lineage.versions.findIndex((v) => v.version === skill.version);
207
- const versionEntry = {
208
- skillId: skill.id,
209
- version: skill.version,
210
- skill,
211
- changelog: "",
212
- createdAt: skill.createdAt,
213
- contentHash: this.hashSkill(skill)
214
- };
215
- if (existingIndex >= 0) {
216
- lineage.versions[existingIndex] = versionEntry;
217
- } else {
218
- lineage.versions.push(versionEntry);
219
- lineage.versions.sort((a, b) => this.compareVersions(a.version, b.version));
220
- }
221
- }
222
- compareVersions(a, b) {
223
- const partsA = a.split(".").map(Number);
224
- const partsB = b.split(".").map(Number);
225
- for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
226
- const numA = partsA[i] || 0;
227
- const numB = partsB[i] || 0;
228
- if (numA !== numB) return numA - numB;
229
- }
230
- return 0;
231
- }
232
- hashSkill(skill) {
233
- const content = JSON.stringify({
234
- instructions: skill.instructions
235
- });
236
- let hash = 0;
237
- for (let i = 0; i < content.length; i++) {
238
- const char = content.charCodeAt(i);
239
- hash = (hash << 5) - hash + char;
240
- hash = hash & hash;
241
- }
242
- return Math.abs(hash).toString(16);
243
- }
244
- /**
245
- * Clear all stored skills (for testing)
246
- */
247
- clear() {
248
- this.skills.clear();
249
- this.lineages.clear();
250
- }
251
- // ==========================================================================
252
- // Fork Tracking
253
- // ==========================================================================
254
- async recordFork(sourceSkillId, fork) {
255
- this.ensureInitialized();
256
- const lineage = this.lineages.get(sourceSkillId);
257
- if (lineage) {
258
- const exists = lineage.forks.some(
259
- (f) => f.forkedSkillId === fork.forkedSkillId
260
- );
261
- if (!exists) {
262
- lineage.forks.push(fork);
263
- }
264
- }
265
- }
266
- };
267
-
268
- // src/storage/sqlite.ts
269
- var SCHEMA_VERSION = 3;
270
- var SQLiteStorageAdapter = class extends BaseStorageAdapter {
271
- constructor(config) {
272
- super();
273
- this.db = null;
274
- this.config = {
275
- walMode: true,
276
- enableFTS: true,
277
- ...config
278
- };
279
- }
280
- async initialize() {
281
- const dir = path.dirname(this.config.dbPath);
282
- if (dir && !fs.existsSync(dir)) {
283
- fs.mkdirSync(dir, { recursive: true });
284
- }
285
- this.db = new Database(this.config.dbPath);
286
- if (this.config.walMode) {
287
- this.db.pragma("journal_mode = WAL");
288
- }
289
- this.db.pragma("foreign_keys = ON");
290
- this.createSchema();
291
- this.runMigrations();
292
- this.initialized = true;
293
- }
294
- createSchema() {
295
- const db = this.getDb();
296
- db.exec(`
297
- CREATE TABLE IF NOT EXISTS schema_version (
298
- version INTEGER PRIMARY KEY
299
- )
300
- `);
301
- db.exec(`
302
- CREATE TABLE IF NOT EXISTS skills (
303
- id TEXT PRIMARY KEY,
304
- version TEXT NOT NULL,
305
- name TEXT NOT NULL,
306
- description TEXT NOT NULL,
307
- instructions TEXT NOT NULL DEFAULT '',
308
- related TEXT,
309
- author TEXT NOT NULL,
310
- tags TEXT NOT NULL,
311
- created_at TEXT NOT NULL,
312
- updated_at TEXT NOT NULL,
313
- status TEXT NOT NULL,
314
- parent_version TEXT,
315
- derived_from TEXT,
316
- metrics TEXT NOT NULL,
317
- source TEXT,
318
- taxonomy TEXT,
319
- external_source TEXT
320
- )
321
- `);
322
- db.exec(`
323
- CREATE TABLE IF NOT EXISTS skill_versions (
324
- id INTEGER PRIMARY KEY AUTOINCREMENT,
325
- skill_id TEXT NOT NULL,
326
- version TEXT NOT NULL,
327
- skill_data TEXT NOT NULL,
328
- changelog TEXT NOT NULL,
329
- created_at TEXT NOT NULL,
330
- content_hash TEXT NOT NULL,
331
- UNIQUE(skill_id, version)
332
- )
333
- `);
334
- db.exec(`
335
- CREATE TABLE IF NOT EXISTS skill_lineage (
336
- skill_id TEXT PRIMARY KEY,
337
- root_id TEXT NOT NULL
338
- )
339
- `);
340
- db.exec(`
341
- CREATE TABLE IF NOT EXISTS skill_forks (
342
- id INTEGER PRIMARY KEY AUTOINCREMENT,
343
- root_skill_id TEXT NOT NULL,
344
- forked_skill_id TEXT NOT NULL,
345
- from_version TEXT NOT NULL,
346
- reason TEXT NOT NULL,
347
- forked_at TEXT NOT NULL,
348
- FOREIGN KEY (root_skill_id) REFERENCES skill_lineage(skill_id)
349
- )
350
- `);
351
- db.exec(`
352
- CREATE INDEX IF NOT EXISTS idx_skills_status ON skills(status);
353
- CREATE INDEX IF NOT EXISTS idx_skills_author ON skills(author);
354
- CREATE INDEX IF NOT EXISTS idx_skills_updated ON skills(updated_at);
355
- CREATE INDEX IF NOT EXISTS idx_versions_skill ON skill_versions(skill_id);
356
- `);
357
- if (this.config.enableFTS) {
358
- db.exec(`
359
- CREATE VIRTUAL TABLE IF NOT EXISTS skills_fts USING fts5(
360
- skill_id,
361
- name,
362
- description,
363
- instructions,
364
- tags
365
- )
366
- `);
367
- }
368
- db.exec(`
369
- CREATE TABLE IF NOT EXISTS taxonomy_nodes (
370
- id TEXT PRIMARY KEY,
371
- name TEXT NOT NULL,
372
- description TEXT,
373
- parent_id TEXT REFERENCES taxonomy_nodes(id),
374
- path TEXT NOT NULL,
375
- skill_count INTEGER DEFAULT 0,
376
- created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
377
- )
378
- `);
379
- db.exec(`
380
- CREATE TABLE IF NOT EXISTS skill_taxonomy_placements (
381
- skill_id TEXT NOT NULL,
382
- node_id TEXT NOT NULL,
383
- is_primary INTEGER DEFAULT 0,
384
- confidence REAL,
385
- reasoning TEXT,
386
- created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
387
- PRIMARY KEY (skill_id, node_id)
388
- )
389
- `);
390
- db.exec(`
391
- CREATE TABLE IF NOT EXISTS skill_relationships (
392
- source_skill_id TEXT NOT NULL,
393
- target_skill_id TEXT NOT NULL,
394
- type TEXT NOT NULL,
395
- confidence REAL NOT NULL,
396
- reasoning TEXT,
397
- created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
398
- PRIMARY KEY (source_skill_id, target_skill_id, type)
399
- )
400
- `);
401
- db.exec(`
402
- CREATE TABLE IF NOT EXISTS skill_sources (
403
- id TEXT PRIMARY KEY,
404
- type TEXT NOT NULL,
405
- url TEXT,
406
- last_scraped TEXT,
407
- etag TEXT,
408
- skill_count INTEGER DEFAULT 0
409
- )
410
- `);
411
- db.exec(`
412
- CREATE INDEX IF NOT EXISTS idx_taxonomy_parent ON taxonomy_nodes(parent_id);
413
- CREATE INDEX IF NOT EXISTS idx_taxonomy_path ON taxonomy_nodes(path);
414
- CREATE INDEX IF NOT EXISTS idx_placements_skill ON skill_taxonomy_placements(skill_id);
415
- CREATE INDEX IF NOT EXISTS idx_placements_node ON skill_taxonomy_placements(node_id);
416
- CREATE INDEX IF NOT EXISTS idx_relationships_source ON skill_relationships(source_skill_id);
417
- CREATE INDEX IF NOT EXISTS idx_relationships_target ON skill_relationships(target_skill_id);
418
- `);
419
- const version = db.prepare("SELECT version FROM schema_version").get();
420
- if (!version) {
421
- db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(SCHEMA_VERSION);
422
- }
423
- }
424
- runMigrations() {
425
- const db = this.getDb();
426
- const row = db.prepare("SELECT version FROM schema_version").get();
427
- const currentVersion = row?.version || 0;
428
- if (currentVersion < SCHEMA_VERSION) {
429
- if (currentVersion < 2) {
430
- try {
431
- db.exec("ALTER TABLE skills ADD COLUMN taxonomy TEXT");
432
- } catch {
433
- }
434
- try {
435
- db.exec("ALTER TABLE skills ADD COLUMN external_source TEXT");
436
- } catch {
437
- }
438
- }
439
- if (currentVersion < 3) {
440
- try {
441
- db.exec("ALTER TABLE skills ADD COLUMN instructions TEXT NOT NULL DEFAULT ''");
442
- } catch {
443
- }
444
- try {
445
- db.exec("ALTER TABLE skills ADD COLUMN related TEXT");
446
- } catch {
447
- }
448
- try {
449
- db.exec(`
450
- UPDATE skills SET instructions =
451
- COALESCE(problem, '') || CHAR(10) || CHAR(10) ||
452
- COALESCE(solution, '') || CHAR(10) || CHAR(10) ||
453
- COALESCE(verification, '')
454
- WHERE instructions = ''
455
- `);
456
- } catch {
457
- }
458
- if (this.config.enableFTS) {
459
- try {
460
- db.exec("DROP TABLE IF EXISTS skills_fts");
461
- db.exec(`
462
- CREATE VIRTUAL TABLE skills_fts USING fts5(
463
- skill_id, name, description, instructions, tags
464
- )
465
- `);
466
- const rows = db.prepare("SELECT id, name, description, instructions, tags FROM skills").all();
467
- const insertFts = db.prepare("INSERT INTO skills_fts (skill_id, name, description, instructions, tags) VALUES (?, ?, ?, ?, ?)");
468
- for (const row2 of rows) {
469
- const tags = (() => {
470
- try {
471
- return JSON.parse(row2.tags).join(" ");
472
- } catch {
473
- return row2.tags;
474
- }
475
- })();
476
- insertFts.run(row2.id, row2.name, row2.description, row2.instructions, tags);
477
- }
478
- } catch {
479
- }
480
- }
481
- }
482
- db.prepare("UPDATE schema_version SET version = ?").run(SCHEMA_VERSION);
483
- }
484
- }
485
- getDb() {
486
- if (!this.db) {
487
- throw new Error("Database not initialized. Call initialize() first.");
488
- }
489
- return this.db;
490
- }
491
- async saveSkill(skill) {
492
- this.ensureInitialized();
493
- const db = this.getDb();
494
- const stmt = db.prepare(`
495
- INSERT OR REPLACE INTO skills (
496
- id, version, name, description, instructions, related, author, tags,
497
- created_at, updated_at, status, parent_version, derived_from, metrics,
498
- source, taxonomy, external_source
499
- ) VALUES (
500
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
501
- )
502
- `);
503
- stmt.run(
504
- skill.id,
505
- skill.version,
506
- skill.name,
507
- skill.description,
508
- skill.instructions,
509
- skill.related ? JSON.stringify(skill.related) : null,
510
- skill.author,
511
- JSON.stringify(skill.tags),
512
- skill.createdAt.toISOString(),
513
- skill.updatedAt.toISOString(),
514
- skill.status,
515
- skill.parentVersion || null,
516
- skill.derivedFrom ? JSON.stringify(skill.derivedFrom) : null,
517
- JSON.stringify(skill.metrics),
518
- skill.source ? JSON.stringify(skill.source) : null,
519
- skill.taxonomy ? JSON.stringify(skill.taxonomy) : null,
520
- skill.externalSource ? JSON.stringify({
521
- ...skill.externalSource,
522
- scrapedAt: skill.externalSource.scrapedAt.toISOString()
523
- }) : null
524
- );
525
- if (skill.relationships && skill.relationships.length > 0) {
526
- await this.saveSkillRelationships(skill.id, skill.relationships);
527
- }
528
- if (this.config.enableFTS) {
529
- this.updateFTSIndex(skill);
530
- }
531
- await this.saveVersionSnapshot(skill);
532
- this.updateLineage(skill);
533
- }
534
- updateFTSIndex(skill) {
535
- const db = this.getDb();
536
- db.prepare("DELETE FROM skills_fts WHERE skill_id = ?").run(skill.id);
537
- const tags = skill.tags.join(" ");
538
- db.prepare(`
539
- INSERT INTO skills_fts (skill_id, name, description, instructions, tags)
540
- VALUES (?, ?, ?, ?, ?)
541
- `).run(skill.id, skill.name, skill.description, skill.instructions, tags);
542
- }
543
- async saveVersionSnapshot(skill) {
544
- const db = this.getDb();
545
- const existing = db.prepare(
546
- "SELECT id FROM skill_versions WHERE skill_id = ? AND version = ?"
547
- ).get(skill.id, skill.version);
548
- if (existing) {
549
- db.prepare(`
550
- UPDATE skill_versions
551
- SET skill_data = ?, content_hash = ?
552
- WHERE skill_id = ? AND version = ?
553
- `).run(
554
- JSON.stringify(this.serializeSkill(skill)),
555
- this.hashSkill(skill),
556
- skill.id,
557
- skill.version
558
- );
559
- } else {
560
- db.prepare(`
561
- INSERT INTO skill_versions (skill_id, version, skill_data, changelog, created_at, content_hash)
562
- VALUES (?, ?, ?, ?, ?, ?)
563
- `).run(
564
- skill.id,
565
- skill.version,
566
- JSON.stringify(this.serializeSkill(skill)),
567
- "",
568
- // Changelog can be added via separate method
569
- skill.createdAt.toISOString(),
570
- this.hashSkill(skill)
571
- );
572
- }
573
- }
574
- updateLineage(skill) {
575
- const db = this.getDb();
576
- const existing = db.prepare(
577
- "SELECT skill_id FROM skill_lineage WHERE skill_id = ?"
578
- ).get(skill.id);
579
- if (!existing) {
580
- db.prepare(
581
- "INSERT INTO skill_lineage (skill_id, root_id) VALUES (?, ?)"
582
- ).run(skill.id, skill.id);
583
- }
584
- }
585
- async getSkill(id, version) {
586
- this.ensureInitialized();
587
- const db = this.getDb();
588
- if (version) {
589
- const row2 = db.prepare(
590
- "SELECT skill_data FROM skill_versions WHERE skill_id = ? AND version = ?"
591
- ).get(id, version);
592
- if (!row2) return null;
593
- return this.deserializeSkill(JSON.parse(row2.skill_data));
594
- }
595
- const row = db.prepare("SELECT * FROM skills WHERE id = ?").get(id);
596
- if (!row) return null;
597
- return this.rowToSkill(row);
598
- }
599
- async listSkills(filter) {
600
- this.ensureInitialized();
601
- const db = this.getDb();
602
- let sql = "SELECT * FROM skills WHERE 1=1";
603
- const params = [];
604
- if (filter?.status && filter.status.length > 0) {
605
- sql += ` AND status IN (${filter.status.map(() => "?").join(",")})`;
606
- params.push(...filter.status);
607
- }
608
- if (filter?.author) {
609
- sql += " AND author = ?";
610
- params.push(filter.author);
611
- }
612
- if (filter?.minSuccessRate !== void 0) {
613
- sql += " AND json_extract(metrics, '$.successRate') >= ?";
614
- params.push(filter.minSuccessRate);
615
- }
616
- if (filter?.createdAfter) {
617
- sql += " AND created_at >= ?";
618
- params.push(filter.createdAfter.toISOString());
619
- }
620
- if (filter?.createdBefore) {
621
- sql += " AND created_at <= ?";
622
- params.push(filter.createdBefore.toISOString());
623
- }
624
- sql += " ORDER BY updated_at DESC";
625
- const rows = db.prepare(sql).all(...params);
626
- let skills = rows.map((row) => this.rowToSkill(row));
627
- if (filter?.tags && filter.tags.length > 0) {
628
- skills = skills.filter(
629
- (skill) => filter.tags.some((tag) => skill.tags.includes(tag))
630
- );
631
- }
632
- return skills;
633
- }
634
- async deleteSkill(id, version) {
635
- this.ensureInitialized();
636
- const db = this.getDb();
637
- if (version) {
638
- const result = db.prepare(
639
- "DELETE FROM skill_versions WHERE skill_id = ? AND version = ?"
640
- ).run(id, version);
641
- return result.changes > 0;
642
- }
643
- const transaction = db.transaction(() => {
644
- db.prepare("DELETE FROM skill_versions WHERE skill_id = ?").run(id);
645
- db.prepare("DELETE FROM skill_forks WHERE root_skill_id = ? OR forked_skill_id = ?").run(id, id);
646
- db.prepare("DELETE FROM skill_lineage WHERE skill_id = ?").run(id);
647
- if (this.config.enableFTS) {
648
- db.prepare("DELETE FROM skills_fts WHERE skill_id = ?").run(id);
649
- }
650
- db.prepare("DELETE FROM skill_taxonomy_placements WHERE skill_id = ?").run(id);
651
- db.prepare("DELETE FROM skill_relationships WHERE source_skill_id = ? OR target_skill_id = ?").run(id, id);
652
- const result = db.prepare("DELETE FROM skills WHERE id = ?").run(id);
653
- return result.changes > 0;
654
- });
655
- return transaction();
656
- }
657
- async getVersionHistory(skillId) {
658
- this.ensureInitialized();
659
- const db = this.getDb();
660
- const rows = db.prepare(`
661
- SELECT skill_id, version, skill_data, changelog, created_at, content_hash
662
- FROM skill_versions
663
- WHERE skill_id = ?
664
- ORDER BY created_at ASC
665
- `).all(skillId);
666
- return rows.map((row) => ({
667
- skillId: row.skill_id,
668
- version: row.version,
669
- skill: this.deserializeSkill(JSON.parse(row.skill_data)),
670
- changelog: row.changelog,
671
- createdAt: new Date(row.created_at),
672
- contentHash: row.content_hash
673
- }));
674
- }
675
- async getLineage(skillId) {
676
- this.ensureInitialized();
677
- const db = this.getDb();
678
- const lineageRow = db.prepare(
679
- "SELECT * FROM skill_lineage WHERE skill_id = ?"
680
- ).get(skillId);
681
- if (!lineageRow) return null;
682
- const versions = await this.getVersionHistory(skillId);
683
- const forkRows = db.prepare(`
684
- SELECT forked_skill_id, from_version, reason, forked_at
685
- FROM skill_forks
686
- WHERE root_skill_id = ?
687
- `).all(skillId);
688
- const forks = forkRows.map((row) => ({
689
- forkedSkillId: row.forked_skill_id,
690
- fromVersion: row.from_version,
691
- reason: row.reason,
692
- forkedAt: new Date(row.forked_at)
693
- }));
694
- return {
695
- rootId: lineageRow.root_id,
696
- versions,
697
- forks
698
- };
699
- }
700
- async searchSkills(query) {
701
- this.ensureInitialized();
702
- const db = this.getDb();
703
- if (this.config.enableFTS) {
704
- const rows = db.prepare(`
705
- SELECT s.* FROM skills s
706
- JOIN skills_fts fts ON s.id = fts.skill_id
707
- WHERE skills_fts MATCH ?
708
- ORDER BY rank
709
- `).all(query);
710
- return rows.map((row) => this.rowToSkill(row));
711
- }
712
- const allSkills = await this.listSkills();
713
- return this.textSearch(allSkills, query);
714
- }
715
- /**
716
- * Add a fork record
717
- */
718
- async addFork(rootSkillId, forkedSkillId, fromVersion, reason) {
719
- this.ensureInitialized();
720
- const db = this.getDb();
721
- db.prepare(`
722
- INSERT INTO skill_forks (root_skill_id, forked_skill_id, from_version, reason, forked_at)
723
- VALUES (?, ?, ?, ?, ?)
724
- `).run(rootSkillId, forkedSkillId, fromVersion, reason, (/* @__PURE__ */ new Date()).toISOString());
725
- }
726
- /**
727
- * Update changelog for a version
728
- */
729
- async updateChangelog(skillId, version, changelog) {
730
- this.ensureInitialized();
731
- const db = this.getDb();
732
- db.prepare(`
733
- UPDATE skill_versions SET changelog = ? WHERE skill_id = ? AND version = ?
734
- `).run(changelog, skillId, version);
735
- }
736
- /**
737
- * Get skills by tag
738
- */
739
- async getSkillsByTag(tag) {
740
- return this.listSkills({ tags: [tag] });
741
- }
742
- /**
743
- * Get all tags with counts
744
- */
745
- async getTagCounts() {
746
- this.ensureInitialized();
747
- const db = this.getDb();
748
- const rows = db.prepare("SELECT tags FROM skills").all();
749
- const counts = /* @__PURE__ */ new Map();
750
- for (const row of rows) {
751
- const tags = JSON.parse(row.tags);
752
- for (const tag of tags) {
753
- counts.set(tag, (counts.get(tag) || 0) + 1);
754
- }
755
- }
756
- return counts;
757
- }
758
- /**
759
- * Get skill count by status
760
- */
761
- async getStatusCounts() {
762
- this.ensureInitialized();
763
- const db = this.getDb();
764
- const rows = db.prepare(`
765
- SELECT status, COUNT(*) as count FROM skills GROUP BY status
766
- `).all();
767
- const counts = /* @__PURE__ */ new Map();
768
- for (const row of rows) {
769
- counts.set(row.status, row.count);
770
- }
771
- return counts;
772
- }
773
- /**
774
- * Close the database connection
775
- */
776
- close() {
777
- if (this.db) {
778
- this.db.close();
779
- this.db = null;
780
- this.initialized = false;
781
- }
782
- }
783
- /**
784
- * Export all skills for backup
785
- */
786
- async exportAll() {
787
- return this.listSkills();
788
- }
789
- /**
790
- * Import skills from backup
791
- */
792
- async importSkills(skills) {
793
- let imported = 0;
794
- let failed = 0;
795
- for (const skill of skills) {
796
- try {
797
- await this.saveSkill(skill);
798
- imported++;
799
- } catch {
800
- failed++;
801
- }
802
- }
803
- return { imported, failed };
804
- }
805
- // =========================================================================
806
- // TAXONOMY METHODS
807
- // =========================================================================
808
- /**
809
- * Get or create a taxonomy node
810
- */
811
- async ensureTaxonomyNode(path2) {
812
- this.ensureInitialized();
813
- const db = this.getDb();
814
- const pathStr = path2.join("/");
815
- const existing = db.prepare("SELECT id FROM taxonomy_nodes WHERE path = ?").get(pathStr);
816
- if (existing) return existing.id;
817
- const id = `node-${pathStr.replace(/\//g, "-").toLowerCase()}`;
818
- const name = path2[path2.length - 1] || "Root";
819
- const parentPath = path2.slice(0, -1);
820
- let parentId = null;
821
- if (parentPath.length > 0) {
822
- parentId = await this.ensureTaxonomyNode(parentPath);
823
- }
824
- db.prepare(`
825
- INSERT INTO taxonomy_nodes (id, name, parent_id, path, created_at)
826
- VALUES (?, ?, ?, ?, ?)
827
- `).run(id, name, parentId, pathStr, (/* @__PURE__ */ new Date()).toISOString());
828
- return id;
829
- }
830
- /**
831
- * Place a skill in the taxonomy
832
- */
833
- async placeInTaxonomy(skillId, taxonomy) {
834
- this.ensureInitialized();
835
- const db = this.getDb();
836
- const primaryNodeId = await this.ensureTaxonomyNode(taxonomy.primaryPath);
837
- db.prepare(`
838
- INSERT OR REPLACE INTO skill_taxonomy_placements
839
- (skill_id, node_id, is_primary, confidence, created_at)
840
- VALUES (?, ?, 1, ?, ?)
841
- `).run(skillId, primaryNodeId, taxonomy.confidence || null, (/* @__PURE__ */ new Date()).toISOString());
842
- db.prepare("UPDATE taxonomy_nodes SET skill_count = skill_count + 1 WHERE id = ?").run(primaryNodeId);
843
- if (taxonomy.secondaryPaths) {
844
- for (const secondaryPath of taxonomy.secondaryPaths) {
845
- const secondaryNodeId = await this.ensureTaxonomyNode(secondaryPath);
846
- db.prepare(`
847
- INSERT OR IGNORE INTO skill_taxonomy_placements
848
- (skill_id, node_id, is_primary, created_at)
849
- VALUES (?, ?, 0, ?)
850
- `).run(skillId, secondaryNodeId, (/* @__PURE__ */ new Date()).toISOString());
851
- }
852
- }
853
- }
854
- /**
855
- * Get taxonomy tree
856
- */
857
- async getTaxonomyTree(rootPath) {
858
- this.ensureInitialized();
859
- const db = this.getDb();
860
- let sql = "SELECT * FROM taxonomy_nodes";
861
- const params = [];
862
- if (rootPath && rootPath.length > 0) {
863
- const pathPrefix = rootPath.join("/");
864
- sql += " WHERE path LIKE ? OR path = ?";
865
- params.push(`${pathPrefix}/%`, pathPrefix);
866
- }
867
- sql += " ORDER BY path";
868
- const rows = db.prepare(sql).all(...params);
869
- const nodeMap = /* @__PURE__ */ new Map();
870
- const roots = [];
871
- for (const row of rows) {
872
- const node = {
873
- id: row.id,
874
- name: row.name,
875
- path: row.path.split("/"),
876
- skillCount: row.skill_count,
877
- children: []
878
- };
879
- nodeMap.set(row.id, node);
880
- if (row.parent_id && nodeMap.has(row.parent_id)) {
881
- nodeMap.get(row.parent_id).children.push(node);
882
- } else {
883
- roots.push(node);
884
- }
885
- }
886
- return roots;
887
- }
888
- /**
889
- * Get skills in a taxonomy node
890
- */
891
- async getSkillsInTaxonomyNode(nodeId) {
892
- this.ensureInitialized();
893
- const db = this.getDb();
894
- const rows = db.prepare(`
895
- SELECT s.* FROM skills s
896
- JOIN skill_taxonomy_placements p ON s.id = p.skill_id
897
- WHERE p.node_id = ?
898
- `).all(nodeId);
899
- return rows.map((row) => this.rowToSkill(row));
900
- }
901
- // =========================================================================
902
- // RELATIONSHIP METHODS
903
- // =========================================================================
904
- /**
905
- * Save skill relationships
906
- */
907
- async saveSkillRelationships(skillId, relationships) {
908
- const db = this.getDb();
909
- db.prepare("DELETE FROM skill_relationships WHERE source_skill_id = ?").run(skillId);
910
- const stmt = db.prepare(`
911
- INSERT OR REPLACE INTO skill_relationships
912
- (source_skill_id, target_skill_id, type, confidence, reasoning, created_at)
913
- VALUES (?, ?, ?, ?, ?, ?)
914
- `);
915
- for (const rel of relationships) {
916
- stmt.run(
917
- skillId,
918
- rel.targetSkillId,
919
- rel.type,
920
- rel.confidence,
921
- rel.reasoning || null,
922
- (/* @__PURE__ */ new Date()).toISOString()
923
- );
924
- }
925
- }
926
- /**
927
- * Add a relationship between skills
928
- */
929
- async addRelationship(sourceSkillId, targetSkillId, type, confidence, reasoning) {
930
- this.ensureInitialized();
931
- const db = this.getDb();
932
- db.prepare(`
933
- INSERT OR REPLACE INTO skill_relationships
934
- (source_skill_id, target_skill_id, type, confidence, reasoning, created_at)
935
- VALUES (?, ?, ?, ?, ?, ?)
936
- `).run(sourceSkillId, targetSkillId, type, confidence, reasoning || null, (/* @__PURE__ */ new Date()).toISOString());
937
- }
938
- /**
939
- * Get relationships for a skill
940
- */
941
- async getRelationships(skillId) {
942
- this.ensureInitialized();
943
- const db = this.getDb();
944
- const rows = db.prepare(`
945
- SELECT target_skill_id, type, confidence, reasoning
946
- FROM skill_relationships
947
- WHERE source_skill_id = ?
948
- `).all(skillId);
949
- return rows.map((row) => ({
950
- targetSkillId: row.target_skill_id,
951
- type: row.type,
952
- confidence: row.confidence,
953
- reasoning: row.reasoning || void 0
954
- }));
955
- }
956
- /**
957
- * Get skills that depend on a given skill
958
- */
959
- async getDependentSkills(skillId) {
960
- this.ensureInitialized();
961
- const db = this.getDb();
962
- const rows = db.prepare(`
963
- SELECT s.* FROM skills s
964
- JOIN skill_relationships r ON s.id = r.source_skill_id
965
- WHERE r.target_skill_id = ? AND r.type = 'depends_on'
966
- `).all(skillId);
967
- return rows.map((row) => this.rowToSkill(row));
968
- }
969
- /**
970
- * Get related skills (any relationship type)
971
- */
972
- async getRelatedSkills(skillId) {
973
- this.ensureInitialized();
974
- const db = this.getDb();
975
- const rows = db.prepare(`
976
- SELECT s.*, r.type, r.confidence, r.reasoning
977
- FROM skills s
978
- JOIN skill_relationships r ON s.id = r.target_skill_id
979
- WHERE r.source_skill_id = ?
980
- `).all(skillId);
981
- return rows.map((row) => ({
982
- skill: this.rowToSkill(row),
983
- relationship: {
984
- targetSkillId: row.id,
985
- type: row.type,
986
- confidence: row.confidence,
987
- reasoning: row.reasoning || void 0
988
- }
989
- }));
990
- }
991
- // =========================================================================
992
- // HELPER METHODS
993
- // =========================================================================
994
- rowToSkill(row) {
995
- const externalSource = row.external_source ? JSON.parse(row.external_source) : void 0;
996
- return {
997
- id: row.id,
998
- version: row.version,
999
- name: row.name,
1000
- description: row.description,
1001
- instructions: row.instructions,
1002
- related: row.related ? JSON.parse(row.related) : void 0,
1003
- author: row.author,
1004
- tags: JSON.parse(row.tags),
1005
- createdAt: new Date(row.created_at),
1006
- updatedAt: new Date(row.updated_at),
1007
- status: row.status,
1008
- parentVersion: row.parent_version || void 0,
1009
- derivedFrom: row.derived_from ? JSON.parse(row.derived_from) : void 0,
1010
- metrics: JSON.parse(row.metrics),
1011
- source: row.source ? JSON.parse(row.source) : void 0,
1012
- taxonomy: row.taxonomy ? JSON.parse(row.taxonomy) : void 0,
1013
- externalSource: externalSource ? {
1014
- ...externalSource,
1015
- scrapedAt: new Date(externalSource.scrapedAt)
1016
- } : void 0
1017
- };
1018
- }
1019
- serializeSkill(skill) {
1020
- return {
1021
- ...skill,
1022
- createdAt: skill.createdAt.toISOString(),
1023
- updatedAt: skill.updatedAt.toISOString(),
1024
- source: skill.source ? {
1025
- ...skill.source,
1026
- importedAt: skill.source.importedAt.toISOString()
1027
- } : void 0,
1028
- metrics: {
1029
- ...skill.metrics,
1030
- lastUsed: skill.metrics.lastUsed?.toISOString()
1031
- }
1032
- };
1033
- }
1034
- deserializeSkill(data) {
1035
- return {
1036
- ...data,
1037
- createdAt: new Date(data.createdAt),
1038
- updatedAt: new Date(data.updatedAt),
1039
- source: data.source ? {
1040
- ...data.source,
1041
- importedAt: new Date(data.source.importedAt)
1042
- } : void 0,
1043
- metrics: {
1044
- ...data.metrics,
1045
- lastUsed: data.metrics.lastUsed ? new Date(data.metrics.lastUsed) : void 0
1046
- }
1047
- };
1048
- }
1049
- hashSkill(skill) {
1050
- const content = JSON.stringify({
1051
- instructions: skill.instructions
1052
- });
1053
- let hash = 0;
1054
- for (let i = 0; i < content.length; i++) {
1055
- const char = content.charCodeAt(i);
1056
- hash = (hash << 5) - hash + char;
1057
- hash = hash & hash;
1058
- }
1059
- return Math.abs(hash).toString(16);
1060
- }
1061
- };
1062
-
1063
- export {
1064
- BaseStorageAdapter,
1065
- MemoryStorageAdapter,
1066
- SQLiteStorageAdapter
1067
- };