@vpxa/aikit 0.1.115 → 0.1.117
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/package.json +1 -1
- package/packages/cli/dist/index.js +13 -10
- package/packages/cli/dist/init-CEdDN3Lc.js +7 -0
- package/packages/cli/dist/rolldown-runtime-BbypZo7q.js +1 -0
- package/packages/cli/dist/scaffold--Z8D_CTu.js +2 -0
- package/packages/cli/dist/{templates-DRfiihP4.js → templates-Bdyt_d_F.js} +7 -7
- package/packages/cli/dist/user-DVWGiGOO.js +6 -0
- package/packages/embeddings/dist/embedder-worker.js +2 -0
- package/packages/embeddings/dist/index.js +1 -1
- package/packages/server/dist/index.js +1 -1
- package/packages/server/dist/{server-Cbe4LQqt.js → server--HYPpWw3.js} +178 -178
- package/packages/store/dist/index.d.ts +2 -0
- package/packages/store/dist/index.js +2 -2
- package/packages/store/dist/sqlite-vec-store-Dfo9BAg8.js +88 -0
- package/scaffold/dist/definitions/skills.mjs +1 -1
- package/packages/cli/dist/constants-Nz_Z7XS-.js +0 -1
- package/packages/cli/dist/init-C4Rr0k00.js +0 -7
- package/packages/cli/dist/scaffold-BLeqLPMe.js +0 -2
- package/packages/cli/dist/user-HCdUUwXe.js +0 -6
- package/packages/store/dist/sqlite-vec-store-DA6fzQiD.js +0 -88
|
@@ -362,6 +362,7 @@ declare class SqliteVecStore implements IKnowledgeStore {
|
|
|
362
362
|
/** Coarse embedding dimension for multi-resolution search (matryoshka). */
|
|
363
363
|
private readonly coarseDim;
|
|
364
364
|
private vectorEnabled;
|
|
365
|
+
private ftsEnabled;
|
|
365
366
|
private warnedVectorDisabled;
|
|
366
367
|
private _draining;
|
|
367
368
|
private _priorityQueue;
|
|
@@ -376,6 +377,7 @@ declare class SqliteVecStore implements IKnowledgeStore {
|
|
|
376
377
|
getDiagnostics(): {
|
|
377
378
|
adapterType: string;
|
|
378
379
|
vectorSearchEnabled: boolean;
|
|
380
|
+
ftsEnabled: boolean;
|
|
379
381
|
degradedMode: boolean;
|
|
380
382
|
dbPath: string;
|
|
381
383
|
dbSizeBytes: number | null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{i as e,r as t,t as n}from"./sqlite-vec-store-
|
|
1
|
+
import{i as e,r as t,t as n}from"./sqlite-vec-store-Dfo9BAg8.js";import{existsSync as r,mkdirSync as i}from"node:fs";import{AIKIT_PATHS as a}from"../../core/dist/index.js";import{dirname as o,join as s}from"node:path";var c=class{adapter=null;reopenPromise=null;dbPath;externalAdapter;constructor(e={}){if(e.adapter)this.adapter=e.adapter,this.externalAdapter=!0,this.dbPath=``;else{let t=e.path??a.data;this.dbPath=s(t,`graph.db`),this.externalAdapter=!1}}async initialize(){if(this.externalAdapter){let e=this.getAdapter();this.createTables(e),this.migrateSchema(e),e.flush();return}let t=o(this.dbPath);r(t)||i(t,{recursive:!0}),this.adapter=await e(this.dbPath),this.configureAdapter(this.adapter),this.createTables(this.adapter),this.migrateSchema(this.adapter),this.adapter.flush()}configureAdapter(e){e.pragma(`journal_mode = WAL`),e.pragma(`foreign_keys = ON`)}createTables(e){e.exec(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
3
3
|
id TEXT PRIMARY KEY,
|
|
4
4
|
type TEXT NOT NULL,
|
|
@@ -87,4 +87,4 @@ import{i as e,r as t,t as n}from"./sqlite-vec-store-DA6fzQiD.js";import{existsSy
|
|
|
87
87
|
VALUES (?, ?, ?, '{}', ?)`,[a,e,t,o]);for(let e=0;e<n.length;e++)this.run(`INSERT INTO process_steps (process_id, node_id, step_order) VALUES (?, ?, ?)`,[a,n[e],e]);s.exec(`COMMIT`),s.flush()}catch(e){throw s.exec(`ROLLBACK`),e}return{id:a,entryNodeId:e,label:t,properties:{},steps:n,createdAt:o}}async getProcesses(e){await this.ensureOpen();let t;t=e?this.query(`SELECT DISTINCT p.id, p.entry_node_id, p.label, p.properties, p.created_at
|
|
88
88
|
FROM processes p
|
|
89
89
|
JOIN process_steps ps ON p.id = ps.process_id
|
|
90
|
-
WHERE ps.node_id = ?`,[e]):this.query(`SELECT * FROM processes`);let n=[];for(let e of t){let t=this.query(`SELECT node_id FROM process_steps WHERE process_id = ? ORDER BY step_order`,[e.id]);n.push({id:e.id,entryNodeId:e.entry_node_id,label:e.label,properties:l(e.properties),steps:t.map(e=>e.node_id),createdAt:e.created_at})}return n}async deleteProcess(e){await this.ensureOpen();let t=this.getAdapter();t.exec(`BEGIN TRANSACTION`);try{this.run(`DELETE FROM process_steps WHERE process_id = ?`,[e]),this.run(`DELETE FROM processes WHERE id = ?`,[e]),t.exec(`COMMIT`),t.flush()}catch(e){throw t.exec(`ROLLBACK`),e}}async depthGroupedTraversal(e,t=3,n){await this.ensureOpen();let r=n?.direction??`both`,i=n?.edgeType,a=n?.limit??100,o={},s=new Set;s.add(e);let c=[e];for(let e=1;e<=t;e++){let t=[],n=[];for(let e of c){let o=await this.getNeighbors(e,{direction:r,edgeType:i,limit:a});for(let e of o.nodes)s.has(e.id)||(s.add(e.id),t.push(e.id),n.push(e))}if(n.length>0&&(o[e]=n),c=t,c.length===0||s.size>=a)break}return o}async getCohesionScore(e){await this.ensureOpen();let t=this.query(`SELECT id FROM nodes WHERE community = ?`,[e]);if(t.length===0)return 0;let n=new Set(t.map(e=>e.id)),r=t.map(()=>`?`).join(`,`),i=t.map(e=>e.id),a=this.query(`SELECT from_id, to_id FROM edges WHERE from_id IN (${r}) OR to_id IN (${r})`,[...i,...i]);if(a.length===0)return 0;let o=0;for(let e of a)n.has(e.from_id)&&n.has(e.to_id)&&o++;return o/a.length}async getSymbol360(e){let t=await this.getNode(e);if(!t)throw Error(`Node '${e}' not found`);let n=await this.findEdges({toId:e}),r=await this.findEdges({fromId:e}),i=await this.getProcesses(e);return{node:t,incoming:n,outgoing:r,community:t.community??null,processes:i}}releaseMemory(){if(this.adapter)try{this.adapter.exec(`PRAGMA shrink_memory`),this.adapter.exec(`PRAGMA wal_checkpoint(TRUNCATE)`)}catch{}}async close(){this.adapter&&=(this.externalAdapter||this.adapter.close(),null)}};function l(e){if(!e)return{};try{return JSON.parse(e)}catch{return{}}}function u(e){return{id:e.id,type:e.type,name:e.name,properties:l(e.properties),sourceRecordId:e.source_record_id??void 0,sourcePath:e.source_path??void 0,createdAt:e.created_at,community:e.community??void 0}}function d(e){return{id:e.id,fromId:e.from_id,toId:e.to_id,type:e.type,weight:e.weight??1,confidence:e.confidence??1,properties:l(e.properties)}}function f(e){return{id:e.edge_id,fromId:e.from_id,toId:e.to_id,type:e.edge_type,weight:e.weight??1,confidence:e.edge_confidence??1,properties:l(e.edge_props??`{}`)}}function p(e){return{id:e.node_id,type:e.node_type,name:e.node_name,properties:l(e.node_props??`{}`),sourceRecordId:e.node_src_rec??void 0,sourcePath:e.node_src_path??void 0,createdAt:e.node_created,community:e.node_community??void 0}}async function m(e){switch(e.backend){case`lancedb`:{let{LanceStore:t}=await import(`./lance-store-D1JolXlO.js`);return new t({path:e.path})}case`sqlite-vec`:{let{SqliteVecStore:t}=await import(`./sqlite-vec-store-
|
|
90
|
+
WHERE ps.node_id = ?`,[e]):this.query(`SELECT * FROM processes`);let n=[];for(let e of t){let t=this.query(`SELECT node_id FROM process_steps WHERE process_id = ? ORDER BY step_order`,[e.id]);n.push({id:e.id,entryNodeId:e.entry_node_id,label:e.label,properties:l(e.properties),steps:t.map(e=>e.node_id),createdAt:e.created_at})}return n}async deleteProcess(e){await this.ensureOpen();let t=this.getAdapter();t.exec(`BEGIN TRANSACTION`);try{this.run(`DELETE FROM process_steps WHERE process_id = ?`,[e]),this.run(`DELETE FROM processes WHERE id = ?`,[e]),t.exec(`COMMIT`),t.flush()}catch(e){throw t.exec(`ROLLBACK`),e}}async depthGroupedTraversal(e,t=3,n){await this.ensureOpen();let r=n?.direction??`both`,i=n?.edgeType,a=n?.limit??100,o={},s=new Set;s.add(e);let c=[e];for(let e=1;e<=t;e++){let t=[],n=[];for(let e of c){let o=await this.getNeighbors(e,{direction:r,edgeType:i,limit:a});for(let e of o.nodes)s.has(e.id)||(s.add(e.id),t.push(e.id),n.push(e))}if(n.length>0&&(o[e]=n),c=t,c.length===0||s.size>=a)break}return o}async getCohesionScore(e){await this.ensureOpen();let t=this.query(`SELECT id FROM nodes WHERE community = ?`,[e]);if(t.length===0)return 0;let n=new Set(t.map(e=>e.id)),r=t.map(()=>`?`).join(`,`),i=t.map(e=>e.id),a=this.query(`SELECT from_id, to_id FROM edges WHERE from_id IN (${r}) OR to_id IN (${r})`,[...i,...i]);if(a.length===0)return 0;let o=0;for(let e of a)n.has(e.from_id)&&n.has(e.to_id)&&o++;return o/a.length}async getSymbol360(e){let t=await this.getNode(e);if(!t)throw Error(`Node '${e}' not found`);let n=await this.findEdges({toId:e}),r=await this.findEdges({fromId:e}),i=await this.getProcesses(e);return{node:t,incoming:n,outgoing:r,community:t.community??null,processes:i}}releaseMemory(){if(this.adapter)try{this.adapter.exec(`PRAGMA shrink_memory`),this.adapter.exec(`PRAGMA wal_checkpoint(TRUNCATE)`)}catch{}}async close(){this.adapter&&=(this.externalAdapter||this.adapter.close(),null)}};function l(e){if(!e)return{};try{return JSON.parse(e)}catch{return{}}}function u(e){return{id:e.id,type:e.type,name:e.name,properties:l(e.properties),sourceRecordId:e.source_record_id??void 0,sourcePath:e.source_path??void 0,createdAt:e.created_at,community:e.community??void 0}}function d(e){return{id:e.id,fromId:e.from_id,toId:e.to_id,type:e.type,weight:e.weight??1,confidence:e.confidence??1,properties:l(e.properties)}}function f(e){return{id:e.edge_id,fromId:e.from_id,toId:e.to_id,type:e.edge_type,weight:e.weight??1,confidence:e.edge_confidence??1,properties:l(e.edge_props??`{}`)}}function p(e){return{id:e.node_id,type:e.node_type,name:e.node_name,properties:l(e.node_props??`{}`),sourceRecordId:e.node_src_rec??void 0,sourcePath:e.node_src_path??void 0,createdAt:e.node_created,community:e.node_community??void 0}}async function m(e){switch(e.backend){case`lancedb`:{let{LanceStore:t}=await import(`./lance-store-D1JolXlO.js`);return new t({path:e.path})}case`sqlite-vec`:{let{SqliteVecStore:t}=await import(`./sqlite-vec-store-Dfo9BAg8.js`).then(e=>e.n);return new t({path:e.path,adapter:e.adapter,embeddingDim:e.embeddingDim})}default:{let t=e.backend;throw Error(`Unknown store backend: "${t}". Supported: lancedb, sqlite-vec`)}}}export{c as SqliteGraphStore,n as SqliteVecStore,t as createSqlJsAdapter,e as createSqliteAdapter,m as createStore};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as n,readFileSync as r,renameSync as i,unlinkSync as a,writeFileSync as o}from"node:fs";import{EMBEDDING_DEFAULTS as s,SEARCH_DEFAULTS as c,STORE_DEFAULTS as l,createLogger as u,serializeError as d,sourceTypeContentTypes as f}from"../../core/dist/index.js";import{dirname as p}from"node:path";var m=Object.defineProperty,h=(e,t)=>{let n={};for(var r in e)m(n,r,{get:e[r],enumerable:!0});return t||m(n,Symbol.toStringTag,{value:`Module`}),n},g=e(import.meta.url);const _=u(`sqlite-adapter`),v=e(import.meta.url);var y=class{type=`better-sqlite3`;vectorCapable=!1;db=null;stmtCache=new Map;async open(e){let t;try{t=v(`better-sqlite3`)}catch(e){throw Error(`better-sqlite3 native binding unavailable: ${e instanceof Error?e.message:String(e)}`)}this.db=new t(e),this.db.pragma(`journal_mode = WAL`),this.db.pragma(`foreign_keys = ON`),this.db.pragma(`synchronous = NORMAL`);try{v(`sqlite-vec`).load(this.db),this.vectorCapable=!0,_.info(`sqlite-vec extension loaded`)}catch(e){this.vectorCapable=!1,_.warn(`sqlite-vec extension failed to load; vector search disabled`,d(e))}}exec(e){this.getDb().exec(e)}pragma(e){this.getDb().pragma(e)}queryAll(e,t=[]){let n=this.prepareCached(e);return t.length>0?n.all(...t):n.all()}run(e,t=[]){let n=this.prepareCached(e);t.length>0?n.run(...t):n.run()}flush(){}close(){this.db&&=(this.stmtCache.clear(),this.db.close(),null)}getDb(){if(!this.db)throw Error(`BetterSqlite3Adapter: database not opened`);return this.db}prepareCached(e){let t=this.stmtCache.get(e);if(t)return t;let n=this.getDb().prepare(e);return this.stmtCache.set(e,n),n}};function b(e){return v.resolve(`sql.js/dist/${e}`)}var x=class{type=`sql.js`;vectorCapable=!1;db=null;dbPath=``;dirty=!1;inTransaction=!1;async open(e){this.dbPath=e;let n=(await import(`sql.js`)).default,i=await n({locateFile:e=>b(e)});if(t(e)){let t=r(e);this.db=new i.Database(t)}else this.db=new i.Database}exec(e){let t=e.trimStart().toUpperCase();this.getDb().run(e),t.startsWith(`BEGIN`)?this.inTransaction=!0:(t.startsWith(`COMMIT`)||t.startsWith(`ROLLBACK`))&&(this.inTransaction=!1),this.dirty=!0}pragma(e){this.getDb().exec(`PRAGMA ${e}`)}queryAll(e,t=[]){let n=this.getDb().prepare(e);try{t.length>0&&n.bind(t);let e=[];for(;n.step();)e.push(n.getAsObject());return e}finally{n.free()}}run(e,t=[]){let n=this.getDb(),r=e.trimStart().toUpperCase(),i=!this.inTransaction&&(r.startsWith(`INSERT`)||r.startsWith(`UPDATE`));i&&n.run(`SAVEPOINT fk_check`);try{if(t.length>0){let r=n.prepare(e);try{r.bind(t),r.step()}finally{r.free()}}else n.run(e);if(i){if(n.exec(`PRAGMA foreign_key_check`).length>0)throw n.run(`ROLLBACK TO fk_check`),n.run(`RELEASE fk_check`),Error(`FOREIGN KEY constraint failed`);n.run(`RELEASE fk_check`)}}catch(e){if(i)try{n.run(`ROLLBACK TO fk_check`),n.run(`RELEASE fk_check`)}catch{}throw e}this.dirty=!0}flush(){if(!this.dirty||!this.db)return;let e=this.db.export(),t=`${this.dbPath}.tmp`;o(t,Buffer.from(e)),i(t,this.dbPath),this.dirty=!1}close(){if(this.db){let e=this.db,t;try{this.flush()}catch(e){t=e;try{a(`${this.dbPath}.tmp`)}catch{}}try{e.close()}finally{this.db=null}if(t)throw t}}getDb(){if(!this.db)throw Error(`SqlJsAdapter: database not opened`);return this.db}};async function S(){try{let{execSync:e}=await import(`node:child_process`),{createRequire:t}=await import(`node:module`),n=t(import.meta.url).resolve(`better-sqlite3/package.json`).replace(/[\\/]package\.json$/,``).replace(/[\\/]node_modules[\\/]better-sqlite3$/,``);return _.info(`Attempting native module rebuild for better-sqlite3`,{cwd:n}),e(`npm rebuild better-sqlite3`,{cwd:n,stdio:`pipe`,timeout:6e4}),_.info(`Native module rebuild completed successfully`),!0}catch(e){return _.warn(`Native module rebuild failed — continuing with sql.js fallback`,d(e)),!1}}let C=!1;async function w(e){let t=new y;try{return await t.open(e),t}catch(t){let n=t instanceof Error?t.message:String(t);if(/NODE_MODULE_VERSION/.test(n)&&await S()){let t=new y;try{return await t.open(e),_.info(`better-sqlite3 recovered after native module rebuild`),t}catch{}}C||(C=!0,_.warn(`[aikit] better-sqlite3 unavailable — falling back to sql.js (vector search disabled). Reinstall with prebuild support or rebuild from source to enable vector search.`,d(t)))}let n=new x;try{return await n.open(e),n}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`[aikit] SQLite adapter "sql.js" failed to load: ${t}`)}}async function T(e){let t=new x;return await t.open(e),t}var E=h({SqliteVecStore:()=>M});function D(e){let t=0;for(let n=0;n<e.length;n++){let r=e[n]<0?-e[n]:e[n];r>t&&(t=r)}let n=new Int8Array(e.length);if(t===0)return Buffer.from(n.buffer,n.byteOffset,n.byteLength);let r=127/t;for(let t=0;t<e.length;t++){let i=Math.round(e[t]*r);n[t]=i<-127?-127:i>127?127:i}return Buffer.from(n.buffer,n.byteOffset,n.byteLength)}const O=/^[\w.\-/ ]+$/,k=u(`sqlite-vec-store`);function A(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function j(e,t){if(!O.test(e))throw Error(`Invalid ${t} filter value: contains disallowed characters`);return e}var M=class{adapter=null;externalAdapter;dbPath;embeddingDim;coarseDim;vectorEnabled=!1;ftsEnabled=!1;warnedVectorDisabled=!1;_draining=!1;_priorityQueue=[];_normalQueue=[];_onCloseHooks=[];constructor(e={}){this.embeddingDim=e.embeddingDim??s.dimensions,this.coarseDim=Math.min(128,this.embeddingDim),e.adapter?(this.adapter=e.adapter,this.externalAdapter=!0,this.dbPath=``):(this.dbPath=e.path??`${l.path}/aikit.db`,this.externalAdapter=!1)}async initialize(){if(!this.adapter){let e=p(this.dbPath);t(e)||n(e,{recursive:!0}),this.adapter=await w(this.dbPath)}this.vectorEnabled=this.adapter.vectorCapable,this.createKnowledgeTable(),this.createFtsTable(),this.vectorEnabled?this.ensureVecTable():this.warnedVectorDisabled||(this.warnedVectorDisabled=!0,k.warn(`SqliteVecStore: vector search disabled (sqlite-vec extension not loaded). Hybrid search will return FTS-only results.`))}getDiagnostics(){let e=this.adapter,t=e?e.constructor.name:`unknown`,n=null,r=this.externalAdapter?``:this.dbPath;if(r)try{let{statSync:e}=g(`node:fs`);n=e(r).size}catch{n=null}return{adapterType:t,vectorSearchEnabled:this.vectorEnabled,ftsEnabled:this.ftsEnabled,degradedMode:!this.vectorEnabled,dbPath:r,dbSizeBytes:n,embeddingDim:this.embeddingDim,vectorDtype:`int8`}}createKnowledgeTable(){let e=this.getAdapter();e.exec(`
|
|
2
|
+
CREATE TABLE IF NOT EXISTS knowledge (
|
|
3
|
+
id TEXT PRIMARY KEY,
|
|
4
|
+
content TEXT NOT NULL,
|
|
5
|
+
sourcePath TEXT NOT NULL,
|
|
6
|
+
contentType TEXT NOT NULL,
|
|
7
|
+
headingPath TEXT NOT NULL DEFAULT '',
|
|
8
|
+
chunkIndex INTEGER NOT NULL DEFAULT 0,
|
|
9
|
+
totalChunks INTEGER NOT NULL DEFAULT 1,
|
|
10
|
+
startLine INTEGER NOT NULL DEFAULT 0,
|
|
11
|
+
endLine INTEGER NOT NULL DEFAULT 0,
|
|
12
|
+
fileHash TEXT NOT NULL DEFAULT '',
|
|
13
|
+
content_hash TEXT NOT NULL DEFAULT '',
|
|
14
|
+
indexedAt TEXT NOT NULL,
|
|
15
|
+
origin TEXT NOT NULL DEFAULT 'indexed',
|
|
16
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
17
|
+
category TEXT NOT NULL DEFAULT '',
|
|
18
|
+
version INTEGER NOT NULL DEFAULT 1
|
|
19
|
+
)
|
|
20
|
+
`),e.queryAll(`PRAGMA table_info(knowledge)`).some(e=>e.name===`content_hash`)||e.exec(`ALTER TABLE knowledge ADD COLUMN content_hash TEXT NOT NULL DEFAULT ''`),e.exec(`CREATE INDEX IF NOT EXISTS idx_knowledge_sourcePath ON knowledge(sourcePath)`),e.exec(`CREATE INDEX IF NOT EXISTS idx_knowledge_contentType ON knowledge(contentType)`),e.exec(`CREATE INDEX IF NOT EXISTS idx_knowledge_content_hash ON knowledge(content_hash)`),e.exec(`CREATE INDEX IF NOT EXISTS idx_knowledge_origin ON knowledge(origin)`)}createFtsTable(){let e=this.getAdapter();try{e.exec(`
|
|
21
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_fts USING fts5(
|
|
22
|
+
id UNINDEXED,
|
|
23
|
+
content,
|
|
24
|
+
tokenize = 'unicode61 remove_diacritics 2'
|
|
25
|
+
)
|
|
26
|
+
`),this.ftsEnabled=!0}catch(e){this.ftsEnabled=!1,k.warn(`FTS5 unavailable — keyword search disabled`,d(e))}}ensureVecTable(){let e=this.getAdapter(),t=e.queryAll(`SELECT name, sql FROM sqlite_master WHERE type='table' AND name='vec_knowledge'`);if(t.length>0){let n=t[0].sql??``,r=n.match(/(?:float|int8)\[(\d+)\]/i),i=r?Number(r[1]):null,a=/int8\[/i.test(n);(i!==null&&i!==this.embeddingDim||!a)&&(k.warn(`Vec table schema mismatch — dropping for recreation`,{existingDim:i,newDim:this.embeddingDim,wasInt8:a}),e.exec(`DROP TABLE vec_knowledge`))}e.exec(`
|
|
27
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS vec_knowledge USING vec0(
|
|
28
|
+
embedding int8[${this.embeddingDim}] distance_metric=cosine,
|
|
29
|
+
+knowledge_id TEXT
|
|
30
|
+
)
|
|
31
|
+
`),this.coarseDim<this.embeddingDim&&e.exec(`
|
|
32
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS vec_knowledge_coarse USING vec0(
|
|
33
|
+
embedding int8[${this.coarseDim}] distance_metric=cosine,
|
|
34
|
+
+knowledge_id TEXT
|
|
35
|
+
)
|
|
36
|
+
`)}enqueueWrite(e,t=!1){return new Promise((n,r)=>{let i=async()=>{try{n(await e())}catch(e){r(e)}};t?this._priorityQueue.push(i):this._normalQueue.push(i),this._drain()})}async _drain(){if(!this._draining){this._draining=!0;try{for(;this._priorityQueue.length>0||this._normalQueue.length>0;){let e=this._priorityQueue.shift()??this._normalQueue.shift();e&&await e()}}finally{this._draining=!1}}}async upsert(e,t){if(e.length!==0){if(e.length!==t.length)throw Error(`Record count (${e.length}) does not match vector count (${t.length})`);return this.enqueueWrite(()=>this._upsertImpl(e,t))}}async upsertInteractive(e,t){if(e.length!==0){if(e.length!==t.length)throw Error(`Record count (${e.length}) does not match vector count (${t.length})`);return this.enqueueWrite(()=>this._upsertImpl(e,t),!0)}}async upsertWithoutVector(e,t){return this.enqueueWrite(async()=>{let n=this.getAdapter(),r=e;n.exec(`BEGIN`);try{if(this.upsertKnowledgeRow(r),this.ftsEnabled&&(n.run(`DELETE FROM knowledge_fts WHERE id = ?`,[r.id]),n.run(`INSERT INTO knowledge_fts (id, content) VALUES (?, ?)`,[r.id,r.content])),this.vectorEnabled){let e=n.queryAll(`SELECT embedding FROM vec_knowledge WHERE knowledge_id = ?`,[t]);if(e.length>0){if(n.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[r.id]),n.run(`INSERT INTO vec_knowledge (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[e[0].embedding,r.id]),this.coarseDim<this.embeddingDim){let e=n.queryAll(`SELECT embedding FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[t]);e.length>0&&(n.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[r.id]),n.run(`INSERT INTO vec_knowledge_coarse (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[e[0].embedding,r.id]))}}else k.warn(`upsertWithoutVector: source vector not found, record will lack vector`,{recordId:r.id,sourceRecordId:t})}n.exec(`COMMIT`)}catch(e){try{n.exec(`ROLLBACK`)}catch{}throw e}n.flush()})}upsertKnowledgeRow(e){this.getAdapter().run(`INSERT INTO knowledge (id, content, sourcePath, contentType, headingPath, chunkIndex,
|
|
37
|
+
totalChunks, startLine, endLine, fileHash, content_hash, indexedAt, origin, tags, category, version)
|
|
38
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
39
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
40
|
+
content = excluded.content,
|
|
41
|
+
sourcePath = excluded.sourcePath,
|
|
42
|
+
contentType = excluded.contentType,
|
|
43
|
+
headingPath = excluded.headingPath,
|
|
44
|
+
chunkIndex = excluded.chunkIndex,
|
|
45
|
+
totalChunks = excluded.totalChunks,
|
|
46
|
+
startLine = excluded.startLine,
|
|
47
|
+
endLine = excluded.endLine,
|
|
48
|
+
fileHash = excluded.fileHash,
|
|
49
|
+
content_hash = excluded.content_hash,
|
|
50
|
+
indexedAt = excluded.indexedAt,
|
|
51
|
+
origin = excluded.origin,
|
|
52
|
+
tags = excluded.tags,
|
|
53
|
+
category = excluded.category,
|
|
54
|
+
version = excluded.version`,[e.id,e.content,e.sourcePath,e.contentType,e.headingPath??``,e.chunkIndex,e.totalChunks,e.startLine,e.endLine,e.fileHash,e.contentHash??``,e.indexedAt,e.origin,JSON.stringify(e.tags??[]),e.category??``,e.version])}async _upsertImpl(e,t){let n=this.getAdapter();n.exec(`BEGIN`);try{for(let r=0;r<e.length;r++){let i=e[r];if(this.upsertKnowledgeRow(i),this.ftsEnabled&&(n.run(`DELETE FROM knowledge_fts WHERE id = ?`,[i.id]),n.run(`INSERT INTO knowledge_fts (id, content) VALUES (?, ?)`,[i.id,i.content])),this.vectorEnabled&&(n.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[i.id]),n.run(`INSERT INTO vec_knowledge (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[D(t[r]),i.id]),this.coarseDim<this.embeddingDim)){let e=t[r].subarray(0,this.coarseDim);n.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[i.id]),n.run(`INSERT INTO vec_knowledge_coarse (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[D(e),i.id])}}n.exec(`COMMIT`)}catch(e){try{n.exec(`ROLLBACK`)}catch{}throw e}n.flush()}async search(e,t){if(!this.vectorEnabled)return this.warnedVectorDisabled||(this.warnedVectorDisabled=!0,k.warn(`search() called but vector backend is disabled — returning []`)),[];let n=this.getAdapter(),r=t?.limit??c.maxResults,i=t?.minScore??c.minScore,a=r*4,o=`
|
|
55
|
+
SELECT k.*, v.distance AS _distance
|
|
56
|
+
FROM (
|
|
57
|
+
SELECT knowledge_id, distance
|
|
58
|
+
FROM vec_knowledge
|
|
59
|
+
WHERE embedding MATCH vec_int8(?)
|
|
60
|
+
ORDER BY distance
|
|
61
|
+
LIMIT ?
|
|
62
|
+
) v
|
|
63
|
+
JOIN knowledge k ON k.id = v.knowledge_id
|
|
64
|
+
${this.buildFilterSqlSuffix(t)}
|
|
65
|
+
ORDER BY v.distance ASC
|
|
66
|
+
LIMIT ?
|
|
67
|
+
`,s;try{s=n.queryAll(o,[D(e),a,r])}catch(e){return k.warn(`vector search failed`,d(e)),[]}return s.map(e=>({record:this.fromRow(e),score:1-(e._distance??1)})).filter(e=>e.score>=i).slice(0,r)}async coarseSearch(e,t){if(!this.vectorEnabled||this.coarseDim>=this.embeddingDim)return this.search(e,t);let n=this.getAdapter(),r=t?.limit??c.maxResults,i=t?.minScore??c.minScore,a=r*4,o=e.subarray(0,this.coarseDim),s=`
|
|
68
|
+
SELECT k.*, v.distance AS _distance
|
|
69
|
+
FROM (
|
|
70
|
+
SELECT knowledge_id, distance
|
|
71
|
+
FROM vec_knowledge_coarse
|
|
72
|
+
WHERE embedding MATCH vec_int8(?)
|
|
73
|
+
ORDER BY distance
|
|
74
|
+
LIMIT ?
|
|
75
|
+
) v
|
|
76
|
+
JOIN knowledge k ON k.id = v.knowledge_id
|
|
77
|
+
${this.buildFilterSqlSuffix(t)}
|
|
78
|
+
ORDER BY v.distance ASC
|
|
79
|
+
LIMIT ?
|
|
80
|
+
`,l;try{l=n.queryAll(s,[D(o),a,r])}catch(n){return k.warn(`coarse vector search failed, falling back to full search`,d(n)),this.search(e,t)}return l.length===0?this.search(e,t):l.map(e=>({record:this.fromRow(e),score:1-(e._distance??1)})).filter(e=>e.score>=i).slice(0,r)}async ftsSearch(e,t){if(!e||e.trim().length===0||!this.ftsEnabled)return[];let n=this.getAdapter(),r=t?.limit??c.maxResults,i=`
|
|
81
|
+
SELECT k.*, bm25(knowledge_fts) AS _bm25
|
|
82
|
+
FROM knowledge_fts
|
|
83
|
+
JOIN knowledge k ON k.id = knowledge_fts.id
|
|
84
|
+
WHERE knowledge_fts MATCH ?
|
|
85
|
+
${this.buildFilterSqlSuffix(t,!0)}
|
|
86
|
+
ORDER BY _bm25 ASC
|
|
87
|
+
LIMIT ?
|
|
88
|
+
`,a;try{let t=N(e);a=n.queryAll(i,[t,r])}catch(e){return k.warn(`fts search failed`,d(e)),[]}return a.map(e=>({record:this.fromRow(e),score:P(e._bm25)}))}async getById(e){let t=this.getAdapter().queryAll(`SELECT * FROM knowledge WHERE id = ? LIMIT 1`,[e]);return t.length===0?null:this.fromRow(t[0])}async deleteBySourcePath(e){return this.enqueueWrite(()=>this._deleteBySourcePathImpl(e))}async _deleteBySourcePathImpl(e){let t=this.getAdapter(),n=t.queryAll(`SELECT id FROM knowledge WHERE sourcePath = ?`,[e]);if(n.length===0)return 0;t.exec(`BEGIN`);try{for(let{id:e}of n)this.vectorEnabled&&(t.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[e]),this.coarseDim<this.embeddingDim&&t.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[e])),this.ftsEnabled&&t.run(`DELETE FROM knowledge_fts WHERE id = ?`,[e]);t.run(`DELETE FROM knowledge WHERE sourcePath = ?`,[e]),t.exec(`COMMIT`)}catch(e){try{t.exec(`ROLLBACK`)}catch{}throw e}return t.flush(),n.length}async deleteById(e){return this.enqueueWrite(()=>this._deleteByIdImpl(e))}async deleteByIdInteractive(e){return this.enqueueWrite(()=>this._deleteByIdImpl(e),!0)}async _deleteByIdImpl(e){let t=this.getAdapter();if(t.queryAll(`SELECT id FROM knowledge WHERE id = ? LIMIT 1`,[e]).length===0)return!1;t.exec(`BEGIN`);try{this.vectorEnabled&&(t.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[e]),this.coarseDim<this.embeddingDim&&t.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[e])),this.ftsEnabled&&t.run(`DELETE FROM knowledge_fts WHERE id = ?`,[e]),t.run(`DELETE FROM knowledge WHERE id = ?`,[e]),t.exec(`COMMIT`)}catch(e){try{t.exec(`ROLLBACK`)}catch{}throw e}return t.flush(),!0}async getBySourcePath(e){return this.getAdapter().queryAll(`SELECT * FROM knowledge WHERE sourcePath = ? ORDER BY chunkIndex ASC`,[e]).map(e=>this.fromRow(e))}async getStats(){let e=this.getAdapter(),t=e.queryAll(`SELECT COUNT(*) AS n FROM knowledge`)[0]?.n??0;if(t===0)return{totalRecords:0,totalFiles:0,contentTypeBreakdown:{},lastIndexedAt:null,storeBackend:`sqlite-vec`,embeddingModel:s.model};let n=e.queryAll(`SELECT COUNT(DISTINCT sourcePath) AS n FROM knowledge`),r=e.queryAll(`SELECT contentType, COUNT(*) AS n FROM knowledge GROUP BY contentType`),i=e.queryAll(`SELECT MAX(indexedAt) AS ts FROM knowledge`),a={};for(let e of r)a[e.contentType]=e.n;return{totalRecords:t,totalFiles:n[0]?.n??0,contentTypeBreakdown:a,lastIndexedAt:i[0]?.ts??null,storeBackend:`sqlite-vec`,embeddingModel:s.model}}async listSourcePaths(){return this.getAdapter().queryAll(`SELECT DISTINCT sourcePath FROM knowledge ORDER BY sourcePath`).map(e=>e.sourcePath)}async createFtsIndex(){this.createFtsTable()}async dropTable(){return this.enqueueWrite(()=>this._dropTableImpl())}async _dropTableImpl(){let e=this.getAdapter();this.ftsEnabled&&e.exec(`DROP TABLE IF EXISTS knowledge_fts`),this.vectorEnabled&&(this.coarseDim<this.embeddingDim&&e.exec(`DROP TABLE IF EXISTS vec_knowledge_coarse`),e.exec(`DROP TABLE IF EXISTS vec_knowledge`)),e.exec(`DROP TABLE IF EXISTS knowledge`),e.flush(),this.createKnowledgeTable(),this.createFtsTable(),this.vectorEnabled&&this.ensureVecTable()}releaseMemory(){if(this.adapter)try{this.adapter.exec(`PRAGMA shrink_memory`),this.adapter.exec(`PRAGMA wal_checkpoint(TRUNCATE)`)}catch{}}onBeforeClose(e){this._onCloseHooks.push(e)}async close(){for(let e of this._onCloseHooks)try{e()}catch{}for(this._onCloseHooks.length=0;this._priorityQueue.length>0||this._normalQueue.length>0||this._draining;)await new Promise(e=>setTimeout(e,5));this.adapter&&!this.externalAdapter&&this.adapter.close(),this.adapter=null}getAdapter(){if(!this.adapter)throw Error(`SqliteVecStore: not initialized — call initialize() first`);return this.adapter}buildFilterSqlSuffix(e,t=!1){if(!e)return``;let n=[];if(e.contentType&&n.push(`k.contentType = '${j(e.contentType,`contentType`)}'`),e.sourceType){let t=f(e.sourceType);if(t.length>0){let e=t.map(e=>`'${j(e,`sourceType`)}'`).join(`, `);n.push(`k.contentType IN (${e})`)}}if(e.origin&&n.push(`k.origin = '${j(e.origin,`origin`)}'`),e.category&&n.push(`k.category = '${j(e.category,`category`)}'`),e.tags&&e.tags.length>0){let t=e.tags.map(e=>`k.tags LIKE '%' || '${j(e,`tag`)}' || '%'`);n.push(`(${t.join(` OR `)})`)}if(n.length===0)return``;let r=n.join(` AND `);return t?`AND ${r}`:`WHERE ${r}`}fromRow(e){return{id:e.id,content:e.content,sourcePath:e.sourcePath,contentType:e.contentType,headingPath:e.headingPath||void 0,chunkIndex:e.chunkIndex,totalChunks:e.totalChunks,startLine:e.startLine,endLine:e.endLine,fileHash:e.fileHash,contentHash:e.content_hash||void 0,indexedAt:e.indexedAt,origin:e.origin,tags:A(e.tags),category:e.category||void 0,version:e.version}}};function N(e){let t=e.replace(/["'()*:^-]/g,` `).trim();return t?t.split(/\s+/).filter(e=>e.length>0).map(e=>`"${e}"`).join(` OR `):`""`}function P(e){return e==null||Number.isNaN(e)?0:1-Math.exp(-Math.abs(e)/5)}export{w as i,E as n,T as r,M as t};
|
|
@@ -1704,7 +1704,7 @@ Notes:
|
|
|
1704
1704
|
- Scripts auto-detect ADR directory and filename strategy.
|
|
1705
1705
|
- Use \`--dir\` and \`--strategy\` to override.
|
|
1706
1706
|
- Use \`--json\` to emit machine-readable output.
|
|
1707
|
-
`}],aikit:[{file:`SKILL.md`,content:'---\nname: aikit\ndescription: "Use the @vpxa/aikit AI Kit MCP server for codebase search, analysis, and persistent memory. Load this skill when using aikit_search, aikit_analyze, aikit_knowledge, or any aikit_* tool. Covers all 61 MCP tools: search (hybrid/semantic/keyword), code analysis (analyze aspects for structure, dependencies, symbols, patterns, entry points, diagrams, plus blast radius), knowledge graph (auto-populated module/symbol/import graph with traversal), context management (worksets, stash, checkpoints, restore, lanes), code manipulation (rename, codemod, eval), execution and validation (check, test_run, audit), knowledge management (knowledge actions: remember/read/update/forget/list), web access (fetch, search, http), FORGE protocol (ground, classify, evidence map, stratum cards, digest), flow management (list, start, navigate steps, read instructions, inspect runs, add/remove/update flows), presentation (rich dashboards via present tool), onboarding (full codebase analysis in one call), meta-tools (tool discovery and descriptions), and developer utilities (regex, encode, measure, changelog, schema-validate, env, time)."\nmetadata:\n category: cross-cutting\n domain: general\n applicability: always\n inputs: [codebase]\n outputs: [search-results, analysis, knowledge]\n relatedSkills: [present]\n---\n\n# @vpxa/aikit — AI Kit\n\nLocal-first AI developer toolkit — 61 MCP tools for search, analysis, context compression, FORGE quality gates, knowledge management, code manipulation, execution, web access, flow management, presentation, meta-tool discovery, and developer utilities.\n\n## When to Use\n\n- You need long-term memory across coding sessions\n- You want to search a codebase semantically (by meaning, not just keywords)\n- You need to compress large contexts to focus on what matters\n- You want structured output from build tools (tsc, vitest, biome, git)\n- You need to plan which files to read for a task\n- You want to safely explore refactors in isolated lanes\n- You need to rename symbols, apply codemods, or run code transformations\n- You want to fetch and read web pages or search the web\n- You need to make HTTP requests, test APIs, or debug endpoints\n- You want to test regex patterns, encode/decode data, or validate JSON schemas\n- You need code complexity metrics or a git changelog\n\n## Skills Reference\n\n| Context | Skill | Load when |\n|---------|-------|----------|\n| AI Kit search, analysis, memory | `aikit` | **Always load at session start.** Tool signatures, workflows, session protocol. |\n| Brainstorming & design | `brainstorming` | Before any creative/design work — new features, components, behavior changes. |\n| Session context preservation | `session-handoff` | Context window filling up, session ending, or major milestone completed. |\n| Requirements scoring | `requirements-clarity` | Before planning vague or complex features — score 0-100 until ≥ 90. |\n| Engineering lessons | `lesson-learned` | After completing work — extract principles from git diffs. |\n| Architecture diagrams | `c4-architecture` | When documenting or reviewing architecture — C4 Mermaid diagrams. |\n| Architecture decisions | `adr-skill` | When making non-trivial technical decisions — executable ADRs. |\n| Rich presentation | `present` | When presenting dashboards, charts, tables, or complex visual content to users. |\n| TypeScript patterns | `typescript` | Before TypeScript implementation — type system, compiler config, advanced types. |\n| React patterns | `react` | Before React work — component architecture, React 19 APIs, Server Components. |\n| Frontend design | `frontend-design` | Before UI/UX work — visual design, typography, color, layout, accessibility. |\n| Multi-agent orchestration | `multi-agents-development` | Before delegating to multiple agents — task decomposition, dispatch, review pipelines. |\n| Living documentation | `docs` | When creating or updating project documentation — Diátaxis framework, staleness detection. |\n| Repository access recovery | `repo-access` | When encountering git auth failures, accessing private/enterprise repos, or when `web_fetch`/`http` returns auth errors on repository URLs. |\n| Browser automation & auth | `browser-use` | When needing browser interaction — login flows, SAML SSO bypass, cookie extraction, form filling, web scraping, or authenticated browsing. Pairs with `repo-access` for auth recovery via browser. |\n\n## Architecture\n\n16-package monorepo published as a single npm package:\n\n```\ncore → store → embeddings → chunker → indexer → analyzers → tools → server → cli\n ↕\n dashboard, elicitation, enterprise-bridge, flows, present, settings-ui, aikit-client\n```\n\n- **MCP server**: 61 tools + 2 resources (via `@modelcontextprotocol/sdk`)\n- **CLI**: 49 commands (thin dispatcher + 11 command groups)\n- **Search**: Hybrid vector + keyword + RRF fusion\n- **Embeddings**: ONNX local (mxbai-embed-large-v1, 512 dimensions, int8 quantized)\n- **Vector store**: SQLite-vec (embedded, zero infrastructure)\n- **Chunking**: Tree-sitter AST (TS/JS/Python/Go/Rust/Java) + regex fallback\n- **Dashboard**: Web-based dashboard for knowledge graph visualization and settings management\n\n## Session Protocol (MANDATORY)\n\n### Start (do ALL)\n```\nflow({ action: \'status\' }) # Check/resume active flow FIRST\n# If flow active → flow({ action: \'read\', step }) → follow step instructions\nstatus({}) # Check AI Kit health + onboard state\n# If onboard not run → onboard({ path: "." }) # First-time codebase analysis\nflow({ action: \'list\' }) # See available flows\n# Select flow based on task → flow({ action: \'start\', name: "<name>", topic: "<task>" }) # Start — creates .flows/{topic}/\nknowledge({ action: "list" }) # See stored knowledge\nsearch({ query: "SESSION CHECKPOINT", origin: "curated" }) # Resume prior work\n```\n\n### During Session\n```\nsearch → scope_map → symbol → trace (orient)\ncheck → test_run (validate changes)\nknowledge({ action: "remember", ... }) (capture insights)\n```\n\n### End of Session\n```\nsession_digest({ persist: true }) # Auto-capture session activity\nknowledge({ action: "remember", title: "Session checkpoint: <topic>", content: "<what was done, decisions made, next steps>", category: "conventions" })\n```\n\n## Tool Catalog\n\n### Search & Discovery (8)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `search` | `aikit search` | Hybrid/semantic/keyword search with `search_mode` param |\n| `find` | `aikit find` | Federated search: vector + FTS + glob + regex in one call. Use `mode: \'examples\'` to find usage examples. |\n| `symbol` | `aikit symbol` | Resolve symbol definition, imports, and references |\n| `lookup` | `aikit lookup` | Full-file retrieval by path or record ID |\n| `scope_map` | `aikit scope-map` | Task-scoped reading plan with token estimates |\n| `trace` | `aikit trace` | Forward/backward flow tracing through call chains |\n| `dead_symbols` | `aikit dead-symbols` | Find exported symbols never imported — separates source (actionable) from docs (informational). Accepts `path` to scope the search. |\n| `file_summary` | `aikit summarize` | Structural overview of a file (exports, imports, functions) |\n\n### Code Analysis (2)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `analyze` | `aikit analyze <aspect>` | Unified analyzer for structure, dependencies, symbols, patterns, entry_points, and diagram aspects |\n| `blast_radius` | `aikit analyze blast-radius` | Change impact analysis |\n\n### Context Management (6)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `compact` | `aikit compact` | Compress text to relevant sections using embeddings (no LLM). Accepts `path` for server-side file read. |\n| `workset` | `aikit workset` | Named file set management (save/load/add/remove) |\n| `stash` | `aikit stash` | Named key-value store for session data |\n| `checkpoint` | `aikit checkpoint` | Save/restore session checkpoints |\n| `restore` | `aikit restore` | Restore a previously saved checkpoint |\n| `parse_output` | `aikit parse-output` | Parse tsc/vitest/biome/git output → structured JSON |\n\n### Code Manipulation (4)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `rename` | `aikit rename` | Smart whole-word symbol rename across files (dry-run supported) |\n| `codemod` | `aikit codemod` | Regex-based code transformations with rules (dry-run supported) |\n| `diff_parse` | `aikit diff` | Parse unified diff → structured changes |\n| `data_transform` | `aikit transform` | JQ-like JSON transformations |\n\n### Execution & Validation (4)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `eval` | `aikit eval` | Sandboxed JavaScript/TypeScript execution |\n| `check` | `aikit check` | Incremental typecheck + lint. `detail` param: efficient (default, minimal), normal (parsed errors), full (includes raw) |\n| `test_run` | `aikit test` | Run tests with structured pass/fail results |\n| `audit` | `aikit audit` | Unified project audit: structure, deps, patterns, health, dead symbols, check, entry points → synthesized report with score and recommendations. 6 round-trips → 1. |\n\n### Knowledge Management (2)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `knowledge` | `aikit knowledge <action>` | Unified knowledge tool for remember, read, update, forget, and list actions |\n| `produce_knowledge` | — | Auto-generate knowledge from analysis |\n\n### Auto-Knowledge (automatic)\n\nTool outputs are automatically analyzed after every call. Useful facts (conventions, test patterns, build commands, errors) are extracted and stored as curated entries. Quality gate (score >= 0.3), deduplication, TTL for transient facts, max 50/session.\n\nSearch auto-knowledge with: `search({ query: "...", origin: "curated" })` or `knowledge({ action: "list", category: "conventions" })`\n\n### Verified Lanes (1 tool, 6 actions)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `lane` | `aikit lane` | Manage isolated file copies for parallel exploration |\n\nLane actions: `create` (copy files to lane), `list`, `status` (modified/added/deleted), `diff` (line-level diff), `merge` (apply back to originals), `discard`.\n\n### Git & Environment (4)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `git_context` | `aikit git` | Branch, status, recent commits |\n| `process` | `aikit proc` | Process supervisor (start/stop/logs) |\n| `watch` | `aikit watch` | Filesystem watcher |\n| `delegate` | `aikit delegate` | Delegate subtask to local Ollama model |\n\n### Web & Network (3)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `web_fetch` | — | Fetch web page → markdown/raw/links/outline for LLM consumption |\n| `web_search` | — | Multi-provider web search (DuckDuckGo + Bing-HTML + Mojeek fan-out, no API key needed) |\n| `http` | — | Make HTTP requests for API testing/debugging |\n\n### Developer Utilities (7)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `regex_test` | — | Test regex patterns with match/replace/split modes |\n| `encode` | — | Base64, URL, SHA-256, MD5, hex encode/decode, JWT decode |\n| `measure` | — | Code complexity metrics (cyclomatic, cognitive complexity, lines, functions) |\n| `changelog` | — | Generate changelog from git history (conventional commits) |\n| `schema_validate` | — | Validate JSON data against JSON Schema |\n| `env` | — | System and runtime environment info (sensitive values redacted) |\n| `time` | — | Date parsing, timezone conversion, duration math |\n\n### FORGE Quality Gates (5)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `forge_ground` | — | Full Ground phase: classify tier, scope map, unknowns, constraints |\n| `forge_classify` | — | Quick tier classification (Floor/Standard/Critical) |\n| `evidence_map` | — | CRUD + Gate evaluation for verified/assumed/unknown claims. Safety gate tags (`provenance`/`commitment`/`coverage`) enable mandatory pre-YIELD checks |\n| `stratum_card` | — | Generate T1/T2 compressed context cards from files (10-100x token reduction) |\n| `digest` | — | Compress N text sources into token-budgeted summary |\n\n### System (9)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `config` | `aikit config` | View or update project configuration (aikit.config.json) |\n| `status` | `aikit status` | Index statistics |\n| `reindex` | `aikit reindex` | Rebuild index |\n| `health` | `aikit health` | Project health checks (package.json, tsconfig, lockfile, circular deps) |\n| `guide` | `aikit guide` | Tool discovery — given a goal, recommends tools and workflow order |\n| `onboard` | `aikit onboard` | Full codebase onboarding in one call (structure + deps + patterns + knowledge) |\n| `graph` | `aikit graph` | Query the auto-populated knowledge graph (modules, symbols, imports) |\n| `queue` | `aikit queue` | Task queue for sequential agent operations (create/push/next/done/fail) |\n| `replay` | `aikit replay` | View or clear the audit trail of tool invocations (action: list/clear) |\n\n### Flows (1)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `flow` | `aikit flow` | Manage flows — list, start, navigate steps, read instructions, inspect runs, add/remove/update. |\n\n### Presentation (1)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `present` | — | Rich dashboards, charts, tables, timelines. Use `format: "browser"` then `openBrowserPage` to display. **In CLI mode (no IDE chat panel), always use `format: "browser"`** — `html` UIResource is invisible in terminal. |\n\n### Meta-Tools — Tool Discovery (3)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `list_tools` | — | List all active AI Kit tools with names, titles, and categories. Accepts optional `category` filter. Use for initial tool discovery. |\n| `describe_tool` | — | Get detailed metadata for a specific tool (title, categories, annotations). Use after `list_tools` to understand a tool before calling it. |\n| `search_tools` | — | Search active tools by keyword across names, titles, and categories. Use when you know what you need but not the tool name. |\n\n### Session Management (1)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `session_digest` | — | Generate a compressed digest of session activity (replay log, stash, checkpoints). Options: `scope` (tools/stash/all), `since`, `last`, `focus`, `mode` (deterministic/sampling), `tokenBudget`, `persist`. Use at session end for handoff or mid-session to review what happened. |\n\n## Execution & Data Tools\n\n### `eval` — Execute Code in Sandbox\n\nRun JavaScript or TypeScript snippets in a constrained VM. Captures console output and return values.\n\n**Parameters:**\n| Param | Type | Default | Description |\n|-------|------|---------|-------------|\n| `code` | string | — | Code to execute |\n| `lang` | `"js"` \\| `"ts"` | `"js"` | Language mode (ts strips type syntax first) |\n| `timeout` | number | 5000 | Execution timeout in ms (max 60000) |\n\n**Examples:**\n```\n// Quick calculation\neval({ code: "return [1,2,3].reduce((a,b) => a+b, 0)" })\n\n// Process data\neval({ code: "const data = [1,2,3,4,5]; return { sum: data.reduce((a,b)=>a+b), avg: data.reduce((a,b)=>a+b)/data.length }" })\n\n// TypeScript\neval({ code: "interface Point { x: number; y: number }; const p: Point = {x: 1, y: 2}; return p.x + p.y", lang: "ts" })\n```\n\n---\n\n### `data_transform` — jq-like JSON Transforms\n\nApply jq-inspired expressions to JSON input for filtering, projection, grouping, and extraction.\n\n**Parameters:**\n| Param | Type | Description |\n|-------|------|-------------|\n| `input` | string | JSON string to transform |\n| `expression` | string | Transform expression (see syntax below) |\n\n**Supported Expressions:**\n\n| Expression | Description | Example |\n|------------|-------------|---------|\n| `.` | Identity (return input as-is) | `.` |\n| `.field` | Access object field | `.name` |\n| `.[N]` | Array index access | `.[0]` |\n| `.field1.field2` | Nested field access | `.user.name` |\n| `| filter(condition)` | Filter array items | `| filter(.age > 18)` |\n| `| map(expr)` | Transform each item | `| map(.name)` |\n| `| sort_by(.field)` | Sort by field | `| sort_by(.date)` |\n| `| group_by(.field)` | Group items by field | `| group_by(.category)` |\n| `| select(cond)` | Keep items matching condition | `| select(.active == true)` |\n| `| flatten` | Flatten nested arrays | `| flatten` |\n| `| unique` | Remove duplicates | `| unique` |\n| `| keys` | Get object keys | `| keys` |\n| `| values` | Get object values | `| values` |\n| `| length` | Array/string length | `| length` |\n| `| join(sep)` | Join array with separator | `| join(", ")` |\n| `| first` | First element | `| first` |\n| `| last` | Last element | `| last` |\n| `| sum` | Sum numeric array | `| sum` |\n| `| avg` | Average of numeric array | `| avg` |\n| `| min` / `| max` | Min/max value | `| min` |\n\n**Comparisons:** `==`, `!=`, `>`, `<`, `>=`, `<=`\n**Logical:** `and`, `or`, `not`\n\n**Examples:**\n```\n// Filter and project\ndata_transform({ input: \'[{"name":"Alice","age":30},{"name":"Bob","age":17}]\', expression: \'| filter(.age >= 18) | map(.name)\' })\n// → ["Alice"]\n\n// Group and count\ndata_transform({ input: \'[{"type":"bug"},{"type":"feat"},{"type":"bug"}]\', expression: \'| group_by(.type)\' })\n// → {"bug":[...], "feat":[...]}\n\n// Sort and take first\ndata_transform({ input: \'[{"score":3},{"score":1},{"score":5}]\', expression: \'| sort_by(.score) | first\' })\n// → {"score":1}\n```\n\n---\n\n### `time` — Date & Time Operations\n\nParse dates, convert timezones, calculate durations, add time. Supports ISO 8601, unix timestamps, and human-readable formats.\n\n**Parameters:**\n| Param | Type | Description |\n|-------|------|-------------|\n| `operation` | string | `now`, `parse`, `convert`, `diff`, `add` |\n| `input` | string | Date input (ISO, unix, or parseable string). For `diff`: two comma-separated dates |\n| `timezone` | string | Target timezone (e.g., "America/New_York") |\n| `duration` | string | Duration to add (e.g., "2h30m", "1d", "30s") — for `add` |\n\n**Operations:**\n| Op | Purpose | Example |\n|----|---------|---------|\n| `now` | Current time in all formats | `time({ operation: "now" })` |\n| `parse` | Parse any date string | `time({ operation: "parse", input: "2024-03-15T10:30:00Z" })` |\n| `convert` | Convert to timezone | `time({ operation: "convert", input: "2024-03-15T10:30:00Z", timezone: "Asia/Tokyo" })` |\n| `diff` | Duration between dates | `time({ operation: "diff", input: "2024-01-01,2024-12-31" })` |\n| `add` | Add duration to date | `time({ operation: "add", input: "2024-03-15", duration: "2h30m" })` |\n\n**Duration format:** Combine: `Nd` (days), `Nh` (hours), `Nm` (minutes), `Ns` (seconds)\nExample: `"1d2h30m"` = 1 day, 2 hours, 30 minutes\n\n## Flow System\n\nFlows are multi-step guided workflows that structure complex tasks. Each step has a skill file with detailed instructions, required artifacts, and agent assignments.\n\n### Built-in Flows\n\n| Flow | Steps | Use When |\n|------|-------|----------|\n| `aikit:basic` | assess → implement → verify | Bug fixes, config changes, small features |\n| `aikit:advanced` | spec → plan → task → execute → verify | New modules, cross-service changes, architectural work |\n\n### Flow Lifecycle\n\n```text\nflow({ action: \'list\' }) # See available flows\nflow({ action: \'info\', name: \'aikit:basic\' }) # View steps, skills, agents\nflow({ action: \'start\', name: \'aikit:basic\', topic: \'Fix login bug\' }) # Start — creates .flows/fix-login-bug/\nflow({ action: \'read\' }) # Read current step\'s instructions ({{artifacts_path}} resolved)\n# ... do the work described in the instruction ...\nflow({ action: \'step\', advance: \'next\' }) # Advance to next step\nflow({ action: \'step\', advance: \'skip\' }) # Skip current step\nflow({ action: \'step\', advance: \'redo\' }) # Redo current step\nflow({ action: \'status\' }) # Check progress (includes slug, runDir, artifactsPath, phase, isEpilogue)\nflow({ action: \'reset\' }) # Abandon active flow\nflow({ action: \'runs\' }) # List all runs (current + past)\n# Epilogue steps (mandatory, injected after every flow):\n# After last flow step → _docs-sync epilogue runs automatically\n# flow({ action: \'status\' }) shows phase: \'after\', isEpilogue: true during epilogue\n```\n\nCustom flow lifecycle management:\n\n```text\nflow({ action: \'add\', source: \'.github/flows/my-flow\' })\nflow({ action: \'update\', name: \'my-flow\' })\nflow({ action: \'remove\', name: \'my-flow\' })\n```\n\n## CRITICAL: Use AI Kit Tools Instead of Native IDE Tools\n\nAI Kit tools provide **10x richer output** than native IDE tools — with AST-analyzed call graphs, scope context, import classification, and cognitive complexity. **You MUST use AI Kit tools instead of native read/search tools.**\n\n### ⛔ PROHIBITED: Native File Reading\n\n**`read_file` / `read_file_raw` MUST NOT be used to understand code.** They waste tokens and miss structural information.\n\nThe **ONLY** acceptable use of `read_file`: getting exact lines immediately before an edit (to verify the `old_str` for replacement). Even then, use `file_summary` first to identify which lines to read.\n\n### Tool Replacement Table\n\n| ❌ NEVER do this | ✅ Use AI Kit Tool | Why |\n|---|---|---|\n| `read_file` (full file) | `file_summary` | Exports, imports, call edges — **10x fewer tokens** |\n| `read_file` (specific section) | `compact({ path, query })` | Server-side read + extract — **5-20x reduction** |\n| `grep_search` / `textSearch` | `search` | Semantic + keyword hybrid across all indexed content |\n| `grep_search` for a symbol | `symbol` | Definition + references **with scope context** |\n| Multiple `read_file` calls | `digest` | Compresses multiple sources into token-budgeted summary |\n| `listDirectory` + `read_file` | `scope_map` | Identifies relevant files for a task automatically |\n| Manual code tracing | `trace` | AST call-graph traversal with scope context |\n| Line counting / `wc` | `measure` | Lines, complexity, **cognitive complexity**, functions |\n| Grep for unused exports | `dead_symbols` | AST-powered export detection with regex fallback |\n| Repeated file reads | `stratum_card` | Reusable compressed context — **10-100x reduction** |\n| `fetch_webpage` | `web_fetch` | Readability extract + token budget — richer output |\n| Web research / browsing | `web_search` | Multi-provider web search without browser — **unique to AI Kit** |\n\n### Decision Tree — How to Read Code\n\n```\nNeed to understand a file?\n├─ Just structure? → file_summary (exports, imports, call edges — ~50 tokens)\n├─ Specific section? → compact({ path, query }) — 5-20x reduction\n├─ Multiple files? → digest (multi-source compression — token-budgeted)\n├─ Repeated reference? → stratum_card (T1/T2 card — 10-100x reduction)\n├─ Need exact lines to EDIT? → read_file (the ONLY acceptable use)\n└─ "I want to read the whole file" → ⛔ STOP. Use file_summary or compact instead.\n```\n\n### Decision Tree — Need Structural Relationships?\n\nWhen vector search and file reads don\'t answer the question (e.g. "who imports this?",\n"what does this depend on?", "how are these files connected?"), use `graph`:\n\n```\nNeed to understand relationships between code?\n├─ Who imports / calls this? → graph({action:\'find_nodes\', name_pattern}) → graph({action:\'neighbors\', node_id, direction:\'incoming\'})\n├─ What does this depend on? → graph({action:\'neighbors\', node_id, direction:\'outgoing\'})\n├─ Full context for a symbol? → graph({action:\'symbol360\', name})\n├─ Related files within N hops? → graph({action:\'traverse\', node_id, max_depth:2})\n├─ Layer/module isolation check? → graph({action:\'depth_traverse\', node_id, max_depth:3})\n└─ Graph size/health? → graph({action:\'stats\'})\n```\n\n**Use this BEFORE** reaching for `analyze({ aspect: "dependencies", ... })` (slower, less precise) or manually\ntracing via `symbol` + `trace` chains. The graph is auto-populated during indexing.\n\n### What AI Kit Tools Return (AST-Enhanced)\n\nThese tools use Tree-sitter WASM to analyze source code at the AST level, providing structured data that raw file reads cannot:\n\n| Tool | Rich Output |\n|------|-------------|\n| `file_summary` | Imports classified as **external vs internal** (`isExternal` flag). **Call edges** between functions (e.g., `handleRequest() → validateInput() @ line 42`). `exported` flag on interfaces and types. Import count breakdown. |\n| `symbol` | References include **scope** — which function/class/method contains each usage (e.g., `referenced in processOrder() at auth-service.ts:55`). |\n| `trace` | **Call-graph edges** discovered via AST syntax tree, not text matching. Supports forward (who does X call?) and backward (who calls X?) tracing. Scope context on each node. |\n| `measure` | **Cognitive complexity** — weights nesting depth (nested `if` inside `for` inside `try` scores higher). More useful than cyclomatic complexity for understanding code difficulty. |\n| `dead_symbols` | **AST export enumeration** — catches `export default`, barrel re-exports (`export { x } from`), `export =`, and `export type`. Regex fallback for non-AST-supported languages. |\n\n### Example: `file_summary` Output\n\n```\nsrc/auth-service.ts\nLanguage: typescript | Lines: 180 | Estimated tokens: ~1400\n\nImports (6): 3 external, 3 internal\n - import { hash } from \'bcrypt\' [external]\n - import { UserRepo } from \'./user-repo\' [internal]\n\nFunctions (4):\n - authenticate @ line 22 [exported]\n - validateToken @ line 55 [exported]\n - hashPassword @ line 90\n - generateJwt @ line 110\n\nCall edges (12 intra-file):\n - authenticate() → hashPassword() @ line 35\n - authenticate() → generateJwt() @ line 42\n - validateToken() → UserRepo.findById() @ line 68\n\nInterfaces (2):\n - AuthResult @ line 8 [exported]\n - TokenPayload @ line 14\n```\n\nCompare: `read_file` would cost ~1400 tokens for raw text. `file_summary` gives structured data in ~120 tokens — **12x reduction** with richer information.\n\n## Token Efficiency\n\n`config.tokenBudget` controls output verbosity globally:\n\n| Level | Output | Compression | Context Strategy |\n|-------|--------|-------------|------------------|\n| `efficient` (default) | Minimal output, score + top issues only | threshold 2000, budget 1000 | `stratum_card(T1)` |\n| `normal` | Standard output with parsed errors/pattern names | threshold 4000, budget 2000 | `compact()` |\n| `full` | Maximum detail with raw output/full tables | No compression | `digest()` |\n\nSet via: `config({ action: \'update\', updates: { tokenBudget: \'normal\' } })`\n\nTools with `detail` param inherit from `config.tokenBudget` when not explicitly set.\nCompression middleware applies to ALL tool responses automatically.\n\n## Search Strategy\n\n1. **Start broad**: `search({ query: "topic", search_mode: "hybrid" })`\n2. **Narrow**: Add `content_type`, `origin`, or `category` filters\n3. **Exact match**: Use `search_mode: "keyword"` for identifiers\n4. **Federated**: Use `find` to combine vector + glob + regex\n\n## Workflow Chains\n\n### Codebase Onboarding\n```\nanalyze({ aspect: "structure", path: "src/" })\n→ analyze({ aspect: "dependencies", path: "src/" })\n→ analyze({ aspect: "entry_points", path: "src/" })\n→ produce_knowledge({ path: "src/" })\n→ knowledge({ action: "remember", title: "Codebase onboarding complete", ... })\n```\n\n### Planning a Task\n```\nscope_map({ task: "implement user auth" })\n→ compact({ path: "src/auth.ts", query: "auth flow" })\n→ workset({ action: "save", name: "auth-task", files: [...] })\n```\n\n### Bug Investigation\n```\nparse_output({ output: <error> }) → symbol({ name: "failingFn" })\n→ trace({ symbol: "failingFn", direction: "backward" })\n→ blast_radius({ changed_files: ["suspect.ts"] })\n→ eval({ code: "hypothesis test" }) → check({ files: ["suspect.ts"] })\n```\n\n### Multi-Task Orchestration (DAG Queue)\n```\nqueue({ action: "create", name: "my-tasks" })\n→ queue({ action: "push", name: "my-tasks", title: "Task 1", data: { deps: [] } })\n→ queue({ action: "push", name: "my-tasks", title: "Task 2", data: { deps: ["task-1-id"] } })\n→ queue({ action: "next", name: "my-tasks" }) # Gets next ready task\n→ [do work]\n→ queue({ action: "done", name: "my-tasks", id: "<id>" })\n```\n\n### Safe Refactor with Lanes\n```\nscope_map({ task: "rename UserService" })\n→ lane({ action: "create", name: "refactor", files: [...] })\n→ [make changes in lane files]\n→ lane({ action: "diff", name: "refactor" })\n→ check({}) → test_run({})\n→ lane({ action: "merge", name: "refactor" })\n```\n\n### Lane — isolated read-only exploration\n\n`lane({ action:\'create\', name })` creates an isolated copy of the workspace. Use to try approach A vs B WITHOUT touching canonical source. Other actions: `list`, `diff`, `delete`. Compare with `lane({ action:\'diff\', names:[\'a\',\'b\'] })`. Do NOT use `lane` for actual refactors — use `checkpoint` instead (`checkpoint` = reversible on canonical source; `lane` = isolated copies for comparison).\n\n### After Making Changes\n```\nblast_radius({ changed_files: ["src/auth.ts"] })\n→ check({}) → test_run({ grep: "auth" })\n→ reindex()\n→ knowledge({ action: "remember", title: "Implemented auth", content: "..." })\n```\n\n### Pre-Commit Validation\n```\ngit_context({ diff: true })\n→ diff_parse({ diff: <staged diff> })\n→ blast_radius({ changed_files: [...] })\n→ check({}) → test_run({})\n```\n\n---\n\n## Persistent Memory\n\n| Action | Tool | Category |\n|--------|------|----------|\n| Store | `knowledge({ action: "remember", title, content, category })` | conventions, decisions, patterns, context, session |\n| Search | `search({ query, origin: "curated" })` | — |\n| Browse | `knowledge({ action: "list" })` or `knowledge({ action: "list", category })` | — |\n| Read | `knowledge({ action: "read", id })` | — |\n| Update | `knowledge({ action: "update", id, content })` | — |\n| Remove | `knowledge({ action: "forget", id })` | — |\n\n**Session checkpoint** (end of session): `knowledge({ action: "remember", title: "Session checkpoint: <topic>", content: "Done/Decisions/Next/Blockers", category: "session" })`\n\n## CLI Quick Reference\n\n```bash\naikit init # Scaffold AI Kit in current directory\naikit init --force # Overwrite all scaffold/skill files\naikit init --guide # JSON report of stale files for LLM-driven updates\naikit serve # Start MCP server (stdio or HTTP)\naikit search <q> # Hybrid search\naikit find <q> # Federated search\naikit symbol <name> # Resolve symbol\naikit scope-map <t> # Task reading plan\naikit compact <q> # Context compression (--path file or stdin)\naikit check # Typecheck + lint (--detail efficient|normal|full)\naikit test # Run tests\naikit rename <old> <new> <path> # Rename symbol\naikit lane create <name> --files f1,f2 # Create lane\naikit lane diff <name> # View lane changes\naikit lane merge <name> # Merge lane back\naikit status # Index stats\naikit reindex # Rebuild index\n```\n\n## Configuration\n\n`aikit.config.json` in project root:\n```json\n{\n "sources": [{ "path": ".", "excludePatterns": ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.git/**", "**/.aikit-data/**", "**/coverage/**"] }],\n "indexing": { "chunkSize": 1500, "chunkOverlap": 200, "minChunkSize": 100 },\n "embedding": { "model": "mixedbread-ai/mxbai-embed-large-v1", "dimensions": 1024 },\n "store": { "backend": "sqlite-vec", "path": ".aikit-data" },\n "curated": { "path": ".ai/curated" }\n}\n```\n\n## Tool Profiles\n\nTool profiles control which subset of the 61 tools are active. Profiles reduce token overhead by exposing only relevant tools for a given task.\n\n### Built-in Profiles\n\n| Profile | Description | Use When |\n|---------|-------------|----------|\n| `full` | All tools enabled (default) | General development, orchestration |\n| `safe` | Read-only tools — no file/state modifications | Code review, analysis, research |\n| `research` | Search, analysis, knowledge, web access | Investigation, documentation |\n| `minimal` | Essential tools only — search, check, test | Simple tasks, low-token budgets |\n| `discovery` | Full toolset + meta-tools for guided exploration | New users, onboarding, tool learning |\n\n### Activating a Profile\n\nSet `toolProfile` in `aikit.config.json`:\n\n```json\n{\n "toolProfile": "research"\n}\n```\n\nBase tools (`status`, `config`, `guide`, `health`) are **always available** regardless of profile.\n\n## Development (Self-Dogfooding)\n\nWhen developing @vpxa/aikit itself: always `pnpm build` before using CLI/server (runs from `dist/`), and always `reindex` after structural code changes.\n\n---\n\n## Flows\n\nFlows are structured multi-step workflows that guide agents through complex tasks. They are the **primary workflow system** — use them instead of ad-hoc planning when a matching flow exists.\n\n### Flow Tools\n\n| Tool | Purpose |\n|------|---------|\n| `flow` | Check if a flow is active + current step + phase (before/flow/after) + isEpilogue |\n\nDifferent `action` values handle listing, starting, reading steps, advancing, resets, run inspection, and add/update/remove flow management.\n\n### Flow Selection\n\n| Task Type | Flow | Why |\n|-----------|------|-----|\n| Bug fix, config change, small refactor | `aikit:basic` | Known scope, low risk |\n| New feature in existing module | `aikit:basic` | Clear boundaries |\n| New system/service/module | `aikit:advanced` | Needs spec + planning |\n| Cross-service changes | `aikit:advanced` | Multiple boundaries |\n| Architectural change | `aikit:advanced` | High impact |\n| Unclear scope or exploratory | No flow | Use agent\'s native workflow |\n\n### Flow Lifecycle\n\n1. **Start**: `flow({ action: \'list\' })` → choose flow → `flow({ action: \'start\', name: "<name>", topic: "<task>" })`\n2. **Each step**: `flow({ action: \'read\', step: "<name>" })` → follow step instructions → complete work\n3. **Advance**: `flow({ action: \'step\', advance: \'next\' })` → repeat from step 2\n4. **Epilogue**: After last flow step, mandatory epilogue steps run (e.g., `_docs-sync` updates `docs/`)\n5. **Resume**: `flow({ action: \'status\' })` → if active, `flow({ action: \'read\' })` for current step → continue\n6. **Reset**: `flow({ action: \'reset\' })` if you need to start over\n\n---\n\n## Reference Documentation\n\nFor detailed patterns on specific topics, load these reference files:\n\n| Topic | File | When to load |\n|-------|------|-------------|\n| Multi-task orchestration | `references/coordination.md` | Queue, DAG, lanes, worksets, stash, checkpoints |\n| Quality gates (FORGE) | `references/forge-protocol.md` | Complex tasks, evidence maps, tier classification |\n| Search & relationships | `references/search-patterns.md` | Finding code, tracing data flow, graph traversal |\n'},{file:`references/coordination.md`,content:`# Coordination & Multi-Task Orchestration
|
|
1707
|
+
`}],aikit:[{file:`SKILL.md`,content:'---\nname: aikit\ndescription: "Use the @vpxa/aikit AI Kit MCP server for codebase search, analysis, and persistent memory. Load when using any aikit_* tool. 61 tools: search (hybrid/semantic/keyword), code analysis (structure, deps, symbols, patterns, entry points, diagrams, blast radius), knowledge graph (module/symbol/import traversal), context (worksets, stash, checkpoints, lanes), code manipulation (rename, codemod, eval), validation (check, test_run, audit), knowledge (remember/read/update/forget/list), web (fetch, search, http), FORGE (ground, classify, evidence map, stratum cards, digest), flows (list, start, step, read, runs, add/remove/update), presentation (dashboards), onboarding, meta-tools, and utilities (regex, encode, measure, changelog, schema-validate, env, time)."\nmetadata:\n category: cross-cutting\n domain: general\n applicability: always\n inputs: [codebase]\n outputs: [search-results, analysis, knowledge]\n relatedSkills: [present]\n---\n\n# @vpxa/aikit — AI Kit\n\nLocal-first AI developer toolkit — 61 MCP tools for search, analysis, context compression, FORGE quality gates, knowledge management, code manipulation, execution, web access, flow management, presentation, meta-tool discovery, and developer utilities.\n\n## When to Use\n\n- You need long-term memory across coding sessions\n- You want to search a codebase semantically (by meaning, not just keywords)\n- You need to compress large contexts to focus on what matters\n- You want structured output from build tools (tsc, vitest, biome, git)\n- You need to plan which files to read for a task\n- You want to safely explore refactors in isolated lanes\n- You need to rename symbols, apply codemods, or run code transformations\n- You want to fetch and read web pages or search the web\n- You need to make HTTP requests, test APIs, or debug endpoints\n- You want to test regex patterns, encode/decode data, or validate JSON schemas\n- You need code complexity metrics or a git changelog\n\n## Skills Reference\n\n| Context | Skill | Load when |\n|---------|-------|----------|\n| AI Kit search, analysis, memory | `aikit` | **Always load at session start.** Tool signatures, workflows, session protocol. |\n| Brainstorming & design | `brainstorming` | Before any creative/design work — new features, components, behavior changes. |\n| Session context preservation | `session-handoff` | Context window filling up, session ending, or major milestone completed. |\n| Requirements scoring | `requirements-clarity` | Before planning vague or complex features — score 0-100 until ≥ 90. |\n| Engineering lessons | `lesson-learned` | After completing work — extract principles from git diffs. |\n| Architecture diagrams | `c4-architecture` | When documenting or reviewing architecture — C4 Mermaid diagrams. |\n| Architecture decisions | `adr-skill` | When making non-trivial technical decisions — executable ADRs. |\n| Rich presentation | `present` | When presenting dashboards, charts, tables, or complex visual content to users. |\n| TypeScript patterns | `typescript` | Before TypeScript implementation — type system, compiler config, advanced types. |\n| React patterns | `react` | Before React work — component architecture, React 19 APIs, Server Components. |\n| Frontend design | `frontend-design` | Before UI/UX work — visual design, typography, color, layout, accessibility. |\n| Multi-agent orchestration | `multi-agents-development` | Before delegating to multiple agents — task decomposition, dispatch, review pipelines. |\n| Living documentation | `docs` | When creating or updating project documentation — Diátaxis framework, staleness detection. |\n| Repository access recovery | `repo-access` | When encountering git auth failures, accessing private/enterprise repos, or when `web_fetch`/`http` returns auth errors on repository URLs. |\n| Browser automation & auth | `browser-use` | When needing browser interaction — login flows, SAML SSO bypass, cookie extraction, form filling, web scraping, or authenticated browsing. Pairs with `repo-access` for auth recovery via browser. |\n\n## Architecture\n\n16-package monorepo published as a single npm package:\n\n```\ncore → store → embeddings → chunker → indexer → analyzers → tools → server → cli\n ↕\n dashboard, elicitation, enterprise-bridge, flows, present, settings-ui, aikit-client\n```\n\n- **MCP server**: 61 tools + 2 resources (via `@modelcontextprotocol/sdk`)\n- **CLI**: 49 commands (thin dispatcher + 11 command groups)\n- **Search**: Hybrid vector + keyword + RRF fusion\n- **Embeddings**: ONNX local (mxbai-embed-large-v1, 512 dimensions, int8 quantized)\n- **Vector store**: SQLite-vec (embedded, zero infrastructure)\n- **Chunking**: Tree-sitter AST (TS/JS/Python/Go/Rust/Java) + regex fallback\n- **Dashboard**: Web-based dashboard for knowledge graph visualization and settings management\n\n## Session Protocol (MANDATORY)\n\n### Start (do ALL)\n```\nflow({ action: \'status\' }) # Check/resume active flow FIRST\n# If flow active → flow({ action: \'read\', step }) → follow step instructions\nstatus({}) # Check AI Kit health + onboard state\n# If onboard not run → onboard({ path: "." }) # First-time codebase analysis\nflow({ action: \'list\' }) # See available flows\n# Select flow based on task → flow({ action: \'start\', name: "<name>", topic: "<task>" }) # Start — creates .flows/{topic}/\nknowledge({ action: "list" }) # See stored knowledge\nsearch({ query: "SESSION CHECKPOINT", origin: "curated" }) # Resume prior work\n```\n\n### During Session\n```\nsearch → scope_map → symbol → trace (orient)\ncheck → test_run (validate changes)\nknowledge({ action: "remember", ... }) (capture insights)\n```\n\n### End of Session\n```\nsession_digest({ persist: true }) # Auto-capture session activity\nknowledge({ action: "remember", title: "Session checkpoint: <topic>", content: "<what was done, decisions made, next steps>", category: "conventions" })\n```\n\n## Tool Catalog\n\n### Search & Discovery (8)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `search` | `aikit search` | Hybrid/semantic/keyword search with `search_mode` param |\n| `find` | `aikit find` | Federated search: vector + FTS + glob + regex in one call. Use `mode: \'examples\'` to find usage examples. |\n| `symbol` | `aikit symbol` | Resolve symbol definition, imports, and references |\n| `lookup` | `aikit lookup` | Full-file retrieval by path or record ID |\n| `scope_map` | `aikit scope-map` | Task-scoped reading plan with token estimates |\n| `trace` | `aikit trace` | Forward/backward flow tracing through call chains |\n| `dead_symbols` | `aikit dead-symbols` | Find exported symbols never imported — separates source (actionable) from docs (informational). Accepts `path` to scope the search. |\n| `file_summary` | `aikit summarize` | Structural overview of a file (exports, imports, functions) |\n\n### Code Analysis (2)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `analyze` | `aikit analyze <aspect>` | Unified analyzer for structure, dependencies, symbols, patterns, entry_points, and diagram aspects |\n| `blast_radius` | `aikit analyze blast-radius` | Change impact analysis |\n\n### Context Management (6)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `compact` | `aikit compact` | Compress text to relevant sections using embeddings (no LLM). Accepts `path` for server-side file read. |\n| `workset` | `aikit workset` | Named file set management (save/load/add/remove) |\n| `stash` | `aikit stash` | Named key-value store for session data |\n| `checkpoint` | `aikit checkpoint` | Save/restore session checkpoints |\n| `restore` | `aikit restore` | Restore a previously saved checkpoint |\n| `parse_output` | `aikit parse-output` | Parse tsc/vitest/biome/git output → structured JSON |\n\n### Code Manipulation (4)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `rename` | `aikit rename` | Smart whole-word symbol rename across files (dry-run supported) |\n| `codemod` | `aikit codemod` | Regex-based code transformations with rules (dry-run supported) |\n| `diff_parse` | `aikit diff` | Parse unified diff → structured changes |\n| `data_transform` | `aikit transform` | JQ-like JSON transformations |\n\n### Execution & Validation (4)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `eval` | `aikit eval` | Sandboxed JavaScript/TypeScript execution |\n| `check` | `aikit check` | Incremental typecheck + lint. `detail` param: efficient (default, minimal), normal (parsed errors), full (includes raw) |\n| `test_run` | `aikit test` | Run tests with structured pass/fail results |\n| `audit` | `aikit audit` | Unified project audit: structure, deps, patterns, health, dead symbols, check, entry points → synthesized report with score and recommendations. 6 round-trips → 1. |\n\n### Knowledge Management (2)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `knowledge` | `aikit knowledge <action>` | Unified knowledge tool for remember, read, update, forget, and list actions |\n| `produce_knowledge` | — | Auto-generate knowledge from analysis |\n\n### Auto-Knowledge (automatic)\n\nTool outputs are automatically analyzed after every call. Useful facts (conventions, test patterns, build commands, errors) are extracted and stored as curated entries. Quality gate (score >= 0.3), deduplication, TTL for transient facts, max 50/session.\n\nSearch auto-knowledge with: `search({ query: "...", origin: "curated" })` or `knowledge({ action: "list", category: "conventions" })`\n\n### Verified Lanes (1 tool, 6 actions)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `lane` | `aikit lane` | Manage isolated file copies for parallel exploration |\n\nLane actions: `create` (copy files to lane), `list`, `status` (modified/added/deleted), `diff` (line-level diff), `merge` (apply back to originals), `discard`.\n\n### Git & Environment (4)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `git_context` | `aikit git` | Branch, status, recent commits |\n| `process` | `aikit proc` | Process supervisor (start/stop/logs) |\n| `watch` | `aikit watch` | Filesystem watcher |\n| `delegate` | `aikit delegate` | Delegate subtask to local Ollama model |\n\n### Web & Network (3)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `web_fetch` | — | Fetch web page → markdown/raw/links/outline for LLM consumption |\n| `web_search` | — | Multi-provider web search (DuckDuckGo + Bing-HTML + Mojeek fan-out, no API key needed) |\n| `http` | — | Make HTTP requests for API testing/debugging |\n\n### Developer Utilities (7)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `regex_test` | — | Test regex patterns with match/replace/split modes |\n| `encode` | — | Base64, URL, SHA-256, MD5, hex encode/decode, JWT decode |\n| `measure` | — | Code complexity metrics (cyclomatic, cognitive complexity, lines, functions) |\n| `changelog` | — | Generate changelog from git history (conventional commits) |\n| `schema_validate` | — | Validate JSON data against JSON Schema |\n| `env` | — | System and runtime environment info (sensitive values redacted) |\n| `time` | — | Date parsing, timezone conversion, duration math |\n\n### FORGE Quality Gates (5)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `forge_ground` | — | Full Ground phase: classify tier, scope map, unknowns, constraints |\n| `forge_classify` | — | Quick tier classification (Floor/Standard/Critical) |\n| `evidence_map` | — | CRUD + Gate evaluation for verified/assumed/unknown claims. Safety gate tags (`provenance`/`commitment`/`coverage`) enable mandatory pre-YIELD checks |\n| `stratum_card` | — | Generate T1/T2 compressed context cards from files (10-100x token reduction) |\n| `digest` | — | Compress N text sources into token-budgeted summary |\n\n### System (9)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `config` | `aikit config` | View or update project configuration (aikit.config.json) |\n| `status` | `aikit status` | Index statistics |\n| `reindex` | `aikit reindex` | Rebuild index |\n| `health` | `aikit health` | Project health checks (package.json, tsconfig, lockfile, circular deps) |\n| `guide` | `aikit guide` | Tool discovery — given a goal, recommends tools and workflow order |\n| `onboard` | `aikit onboard` | Full codebase onboarding in one call (structure + deps + patterns + knowledge) |\n| `graph` | `aikit graph` | Query the auto-populated knowledge graph (modules, symbols, imports) |\n| `queue` | `aikit queue` | Task queue for sequential agent operations (create/push/next/done/fail) |\n| `replay` | `aikit replay` | View or clear the audit trail of tool invocations (action: list/clear) |\n\n### Flows (1)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `flow` | `aikit flow` | Manage flows — list, start, navigate steps, read instructions, inspect runs, add/remove/update. |\n\n### Presentation (1)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `present` | — | Rich dashboards, charts, tables, timelines. Use `format: "browser"` then `openBrowserPage` to display. **In CLI mode (no IDE chat panel), always use `format: "browser"`** — `html` UIResource is invisible in terminal. |\n\n### Meta-Tools — Tool Discovery (3)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `list_tools` | — | List all active AI Kit tools with names, titles, and categories. Accepts optional `category` filter. Use for initial tool discovery. |\n| `describe_tool` | — | Get detailed metadata for a specific tool (title, categories, annotations). Use after `list_tools` to understand a tool before calling it. |\n| `search_tools` | — | Search active tools by keyword across names, titles, and categories. Use when you know what you need but not the tool name. |\n\n### Session Management (1)\n| Tool | CLI | Purpose |\n|------|-----|---------|\n| `session_digest` | — | Generate a compressed digest of session activity (replay log, stash, checkpoints). Options: `scope` (tools/stash/all), `since`, `last`, `focus`, `mode` (deterministic/sampling), `tokenBudget`, `persist`. Use at session end for handoff or mid-session to review what happened. |\n\n## Execution & Data Tools\n\n### `eval` — Execute Code in Sandbox\n\nRun JavaScript or TypeScript snippets in a constrained VM. Captures console output and return values.\n\n**Parameters:**\n| Param | Type | Default | Description |\n|-------|------|---------|-------------|\n| `code` | string | — | Code to execute |\n| `lang` | `"js"` \\| `"ts"` | `"js"` | Language mode (ts strips type syntax first) |\n| `timeout` | number | 5000 | Execution timeout in ms (max 60000) |\n\n**Examples:**\n```\n// Quick calculation\neval({ code: "return [1,2,3].reduce((a,b) => a+b, 0)" })\n\n// Process data\neval({ code: "const data = [1,2,3,4,5]; return { sum: data.reduce((a,b)=>a+b), avg: data.reduce((a,b)=>a+b)/data.length }" })\n\n// TypeScript\neval({ code: "interface Point { x: number; y: number }; const p: Point = {x: 1, y: 2}; return p.x + p.y", lang: "ts" })\n```\n\n---\n\n### `data_transform` — jq-like JSON Transforms\n\nApply jq-inspired expressions to JSON input for filtering, projection, grouping, and extraction.\n\n**Parameters:**\n| Param | Type | Description |\n|-------|------|-------------|\n| `input` | string | JSON string to transform |\n| `expression` | string | Transform expression (see syntax below) |\n\n**Supported Expressions:**\n\n| Expression | Description | Example |\n|------------|-------------|---------|\n| `.` | Identity (return input as-is) | `.` |\n| `.field` | Access object field | `.name` |\n| `.[N]` | Array index access | `.[0]` |\n| `.field1.field2` | Nested field access | `.user.name` |\n| `| filter(condition)` | Filter array items | `| filter(.age > 18)` |\n| `| map(expr)` | Transform each item | `| map(.name)` |\n| `| sort_by(.field)` | Sort by field | `| sort_by(.date)` |\n| `| group_by(.field)` | Group items by field | `| group_by(.category)` |\n| `| select(cond)` | Keep items matching condition | `| select(.active == true)` |\n| `| flatten` | Flatten nested arrays | `| flatten` |\n| `| unique` | Remove duplicates | `| unique` |\n| `| keys` | Get object keys | `| keys` |\n| `| values` | Get object values | `| values` |\n| `| length` | Array/string length | `| length` |\n| `| join(sep)` | Join array with separator | `| join(", ")` |\n| `| first` | First element | `| first` |\n| `| last` | Last element | `| last` |\n| `| sum` | Sum numeric array | `| sum` |\n| `| avg` | Average of numeric array | `| avg` |\n| `| min` / `| max` | Min/max value | `| min` |\n\n**Comparisons:** `==`, `!=`, `>`, `<`, `>=`, `<=`\n**Logical:** `and`, `or`, `not`\n\n**Examples:**\n```\n// Filter and project\ndata_transform({ input: \'[{"name":"Alice","age":30},{"name":"Bob","age":17}]\', expression: \'| filter(.age >= 18) | map(.name)\' })\n// → ["Alice"]\n\n// Group and count\ndata_transform({ input: \'[{"type":"bug"},{"type":"feat"},{"type":"bug"}]\', expression: \'| group_by(.type)\' })\n// → {"bug":[...], "feat":[...]}\n\n// Sort and take first\ndata_transform({ input: \'[{"score":3},{"score":1},{"score":5}]\', expression: \'| sort_by(.score) | first\' })\n// → {"score":1}\n```\n\n---\n\n### `time` — Date & Time Operations\n\nParse dates, convert timezones, calculate durations, add time. Supports ISO 8601, unix timestamps, and human-readable formats.\n\n**Parameters:**\n| Param | Type | Description |\n|-------|------|-------------|\n| `operation` | string | `now`, `parse`, `convert`, `diff`, `add` |\n| `input` | string | Date input (ISO, unix, or parseable string). For `diff`: two comma-separated dates |\n| `timezone` | string | Target timezone (e.g., "America/New_York") |\n| `duration` | string | Duration to add (e.g., "2h30m", "1d", "30s") — for `add` |\n\n**Operations:**\n| Op | Purpose | Example |\n|----|---------|---------|\n| `now` | Current time in all formats | `time({ operation: "now" })` |\n| `parse` | Parse any date string | `time({ operation: "parse", input: "2024-03-15T10:30:00Z" })` |\n| `convert` | Convert to timezone | `time({ operation: "convert", input: "2024-03-15T10:30:00Z", timezone: "Asia/Tokyo" })` |\n| `diff` | Duration between dates | `time({ operation: "diff", input: "2024-01-01,2024-12-31" })` |\n| `add` | Add duration to date | `time({ operation: "add", input: "2024-03-15", duration: "2h30m" })` |\n\n**Duration format:** Combine: `Nd` (days), `Nh` (hours), `Nm` (minutes), `Ns` (seconds)\nExample: `"1d2h30m"` = 1 day, 2 hours, 30 minutes\n\n## Flow System\n\nFlows are multi-step guided workflows that structure complex tasks. Each step has a skill file with detailed instructions, required artifacts, and agent assignments.\n\n### Built-in Flows\n\n| Flow | Steps | Use When |\n|------|-------|----------|\n| `aikit:basic` | assess → implement → verify | Bug fixes, config changes, small features |\n| `aikit:advanced` | spec → plan → task → execute → verify | New modules, cross-service changes, architectural work |\n\n### Flow Lifecycle\n\n```text\nflow({ action: \'list\' }) # See available flows\nflow({ action: \'info\', name: \'aikit:basic\' }) # View steps, skills, agents\nflow({ action: \'start\', name: \'aikit:basic\', topic: \'Fix login bug\' }) # Start — creates .flows/fix-login-bug/\nflow({ action: \'read\' }) # Read current step\'s instructions ({{artifacts_path}} resolved)\n# ... do the work described in the instruction ...\nflow({ action: \'step\', advance: \'next\' }) # Advance to next step\nflow({ action: \'step\', advance: \'skip\' }) # Skip current step\nflow({ action: \'step\', advance: \'redo\' }) # Redo current step\nflow({ action: \'status\' }) # Check progress (includes slug, runDir, artifactsPath, phase, isEpilogue)\nflow({ action: \'reset\' }) # Abandon active flow\nflow({ action: \'runs\' }) # List all runs (current + past)\n# Epilogue steps (mandatory, injected after every flow):\n# After last flow step → _docs-sync epilogue runs automatically\n# flow({ action: \'status\' }) shows phase: \'after\', isEpilogue: true during epilogue\n```\n\nCustom flow lifecycle management:\n\n```text\nflow({ action: \'add\', source: \'.github/flows/my-flow\' })\nflow({ action: \'update\', name: \'my-flow\' })\nflow({ action: \'remove\', name: \'my-flow\' })\n```\n\n## CRITICAL: Use AI Kit Tools Instead of Native IDE Tools\n\nAI Kit tools provide **10x richer output** than native IDE tools — with AST-analyzed call graphs, scope context, import classification, and cognitive complexity. **You MUST use AI Kit tools instead of native read/search tools.**\n\n### ⛔ PROHIBITED: Native File Reading\n\n**`read_file` / `read_file_raw` MUST NOT be used to understand code.** They waste tokens and miss structural information.\n\nThe **ONLY** acceptable use of `read_file`: getting exact lines immediately before an edit (to verify the `old_str` for replacement). Even then, use `file_summary` first to identify which lines to read.\n\n### Tool Replacement Table\n\n| ❌ NEVER do this | ✅ Use AI Kit Tool | Why |\n|---|---|---|\n| `read_file` (full file) | `file_summary` | Exports, imports, call edges — **10x fewer tokens** |\n| `read_file` (specific section) | `compact({ path, query })` | Server-side read + extract — **5-20x reduction** |\n| `grep_search` / `textSearch` | `search` | Semantic + keyword hybrid across all indexed content |\n| `grep_search` for a symbol | `symbol` | Definition + references **with scope context** |\n| Multiple `read_file` calls | `digest` | Compresses multiple sources into token-budgeted summary |\n| `listDirectory` + `read_file` | `scope_map` | Identifies relevant files for a task automatically |\n| Manual code tracing | `trace` | AST call-graph traversal with scope context |\n| Line counting / `wc` | `measure` | Lines, complexity, **cognitive complexity**, functions |\n| Grep for unused exports | `dead_symbols` | AST-powered export detection with regex fallback |\n| Repeated file reads | `stratum_card` | Reusable compressed context — **10-100x reduction** |\n| `fetch_webpage` | `web_fetch` | Readability extract + token budget — richer output |\n| Web research / browsing | `web_search` | Multi-provider web search without browser — **unique to AI Kit** |\n\n### Decision Tree — How to Read Code\n\n```\nNeed to understand a file?\n├─ Just structure? → file_summary (exports, imports, call edges — ~50 tokens)\n├─ Specific section? → compact({ path, query }) — 5-20x reduction\n├─ Multiple files? → digest (multi-source compression — token-budgeted)\n├─ Repeated reference? → stratum_card (T1/T2 card — 10-100x reduction)\n├─ Need exact lines to EDIT? → read_file (the ONLY acceptable use)\n└─ "I want to read the whole file" → ⛔ STOP. Use file_summary or compact instead.\n```\n\n### Decision Tree — Need Structural Relationships?\n\nWhen vector search and file reads don\'t answer the question (e.g. "who imports this?",\n"what does this depend on?", "how are these files connected?"), use `graph`:\n\n```\nNeed to understand relationships between code?\n├─ Who imports / calls this? → graph({action:\'find_nodes\', name_pattern}) → graph({action:\'neighbors\', node_id, direction:\'incoming\'})\n├─ What does this depend on? → graph({action:\'neighbors\', node_id, direction:\'outgoing\'})\n├─ Full context for a symbol? → graph({action:\'symbol360\', name})\n├─ Related files within N hops? → graph({action:\'traverse\', node_id, max_depth:2})\n├─ Layer/module isolation check? → graph({action:\'depth_traverse\', node_id, max_depth:3})\n└─ Graph size/health? → graph({action:\'stats\'})\n```\n\n**Use this BEFORE** reaching for `analyze({ aspect: "dependencies", ... })` (slower, less precise) or manually\ntracing via `symbol` + `trace` chains. The graph is auto-populated during indexing.\n\n### What AI Kit Tools Return (AST-Enhanced)\n\nThese tools use Tree-sitter WASM to analyze source code at the AST level, providing structured data that raw file reads cannot:\n\n| Tool | Rich Output |\n|------|-------------|\n| `file_summary` | Imports classified as **external vs internal** (`isExternal` flag). **Call edges** between functions (e.g., `handleRequest() → validateInput() @ line 42`). `exported` flag on interfaces and types. Import count breakdown. |\n| `symbol` | References include **scope** — which function/class/method contains each usage (e.g., `referenced in processOrder() at auth-service.ts:55`). |\n| `trace` | **Call-graph edges** discovered via AST syntax tree, not text matching. Supports forward (who does X call?) and backward (who calls X?) tracing. Scope context on each node. |\n| `measure` | **Cognitive complexity** — weights nesting depth (nested `if` inside `for` inside `try` scores higher). More useful than cyclomatic complexity for understanding code difficulty. |\n| `dead_symbols` | **AST export enumeration** — catches `export default`, barrel re-exports (`export { x } from`), `export =`, and `export type`. Regex fallback for non-AST-supported languages. |\n\n### Example: `file_summary` Output\n\n```\nsrc/auth-service.ts\nLanguage: typescript | Lines: 180 | Estimated tokens: ~1400\n\nImports (6): 3 external, 3 internal\n - import { hash } from \'bcrypt\' [external]\n - import { UserRepo } from \'./user-repo\' [internal]\n\nFunctions (4):\n - authenticate @ line 22 [exported]\n - validateToken @ line 55 [exported]\n - hashPassword @ line 90\n - generateJwt @ line 110\n\nCall edges (12 intra-file):\n - authenticate() → hashPassword() @ line 35\n - authenticate() → generateJwt() @ line 42\n - validateToken() → UserRepo.findById() @ line 68\n\nInterfaces (2):\n - AuthResult @ line 8 [exported]\n - TokenPayload @ line 14\n```\n\nCompare: `read_file` would cost ~1400 tokens for raw text. `file_summary` gives structured data in ~120 tokens — **12x reduction** with richer information.\n\n## Token Efficiency\n\n`config.tokenBudget` controls output verbosity globally:\n\n| Level | Output | Compression | Context Strategy |\n|-------|--------|-------------|------------------|\n| `efficient` (default) | Minimal output, score + top issues only | threshold 2000, budget 1000 | `stratum_card(T1)` |\n| `normal` | Standard output with parsed errors/pattern names | threshold 4000, budget 2000 | `compact()` |\n| `full` | Maximum detail with raw output/full tables | No compression | `digest()` |\n\nSet via: `config({ action: \'update\', updates: { tokenBudget: \'normal\' } })`\n\nTools with `detail` param inherit from `config.tokenBudget` when not explicitly set.\nCompression middleware applies to ALL tool responses automatically.\n\n## Search Strategy\n\n1. **Start broad**: `search({ query: "topic", search_mode: "hybrid" })`\n2. **Narrow**: Add `content_type`, `origin`, or `category` filters\n3. **Exact match**: Use `search_mode: "keyword"` for identifiers\n4. **Federated**: Use `find` to combine vector + glob + regex\n\n## Workflow Chains\n\n### Codebase Onboarding\n```\nanalyze({ aspect: "structure", path: "src/" })\n→ analyze({ aspect: "dependencies", path: "src/" })\n→ analyze({ aspect: "entry_points", path: "src/" })\n→ produce_knowledge({ path: "src/" })\n→ knowledge({ action: "remember", title: "Codebase onboarding complete", ... })\n```\n\n### Planning a Task\n```\nscope_map({ task: "implement user auth" })\n→ compact({ path: "src/auth.ts", query: "auth flow" })\n→ workset({ action: "save", name: "auth-task", files: [...] })\n```\n\n### Bug Investigation\n```\nparse_output({ output: <error> }) → symbol({ name: "failingFn" })\n→ trace({ symbol: "failingFn", direction: "backward" })\n→ blast_radius({ changed_files: ["suspect.ts"] })\n→ eval({ code: "hypothesis test" }) → check({ files: ["suspect.ts"] })\n```\n\n### Multi-Task Orchestration (DAG Queue)\n```\nqueue({ action: "create", name: "my-tasks" })\n→ queue({ action: "push", name: "my-tasks", title: "Task 1", data: { deps: [] } })\n→ queue({ action: "push", name: "my-tasks", title: "Task 2", data: { deps: ["task-1-id"] } })\n→ queue({ action: "next", name: "my-tasks" }) # Gets next ready task\n→ [do work]\n→ queue({ action: "done", name: "my-tasks", id: "<id>" })\n```\n\n### Safe Refactor with Lanes\n```\nscope_map({ task: "rename UserService" })\n→ lane({ action: "create", name: "refactor", files: [...] })\n→ [make changes in lane files]\n→ lane({ action: "diff", name: "refactor" })\n→ check({}) → test_run({})\n→ lane({ action: "merge", name: "refactor" })\n```\n\n### Lane — isolated read-only exploration\n\n`lane({ action:\'create\', name })` creates an isolated copy of the workspace. Use to try approach A vs B WITHOUT touching canonical source. Other actions: `list`, `diff`, `delete`. Compare with `lane({ action:\'diff\', names:[\'a\',\'b\'] })`. Do NOT use `lane` for actual refactors — use `checkpoint` instead (`checkpoint` = reversible on canonical source; `lane` = isolated copies for comparison).\n\n### After Making Changes\n```\nblast_radius({ changed_files: ["src/auth.ts"] })\n→ check({}) → test_run({ grep: "auth" })\n→ reindex()\n→ knowledge({ action: "remember", title: "Implemented auth", content: "..." })\n```\n\n### Pre-Commit Validation\n```\ngit_context({ diff: true })\n→ diff_parse({ diff: <staged diff> })\n→ blast_radius({ changed_files: [...] })\n→ check({}) → test_run({})\n```\n\n---\n\n## Persistent Memory\n\n| Action | Tool | Category |\n|--------|------|----------|\n| Store | `knowledge({ action: "remember", title, content, category })` | conventions, decisions, patterns, context, session |\n| Search | `search({ query, origin: "curated" })` | — |\n| Browse | `knowledge({ action: "list" })` or `knowledge({ action: "list", category })` | — |\n| Read | `knowledge({ action: "read", id })` | — |\n| Update | `knowledge({ action: "update", id, content })` | — |\n| Remove | `knowledge({ action: "forget", id })` | — |\n\n**Session checkpoint** (end of session): `knowledge({ action: "remember", title: "Session checkpoint: <topic>", content: "Done/Decisions/Next/Blockers", category: "session" })`\n\n## CLI Quick Reference\n\n```bash\naikit init # Scaffold AI Kit in current directory\naikit init --force # Overwrite all scaffold/skill files\naikit init --guide # JSON report of stale files for LLM-driven updates\naikit serve # Start MCP server (stdio or HTTP)\naikit search <q> # Hybrid search\naikit find <q> # Federated search\naikit symbol <name> # Resolve symbol\naikit scope-map <t> # Task reading plan\naikit compact <q> # Context compression (--path file or stdin)\naikit check # Typecheck + lint (--detail efficient|normal|full)\naikit test # Run tests\naikit rename <old> <new> <path> # Rename symbol\naikit lane create <name> --files f1,f2 # Create lane\naikit lane diff <name> # View lane changes\naikit lane merge <name> # Merge lane back\naikit status # Index stats\naikit reindex # Rebuild index\n```\n\n## Configuration\n\n`aikit.config.json` in project root:\n```json\n{\n "sources": [{ "path": ".", "excludePatterns": ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.git/**", "**/.aikit-data/**", "**/coverage/**"] }],\n "indexing": { "chunkSize": 1500, "chunkOverlap": 200, "minChunkSize": 100 },\n "embedding": { "model": "mixedbread-ai/mxbai-embed-large-v1", "dimensions": 1024 },\n "store": { "backend": "sqlite-vec", "path": ".aikit-data" },\n "curated": { "path": ".ai/curated" }\n}\n```\n\n## Tool Profiles\n\nTool profiles control which subset of the 61 tools are active. Profiles reduce token overhead by exposing only relevant tools for a given task.\n\n### Built-in Profiles\n\n| Profile | Description | Use When |\n|---------|-------------|----------|\n| `full` | All tools enabled (default) | General development, orchestration |\n| `safe` | Read-only tools — no file/state modifications | Code review, analysis, research |\n| `research` | Search, analysis, knowledge, web access | Investigation, documentation |\n| `minimal` | Essential tools only — search, check, test | Simple tasks, low-token budgets |\n| `discovery` | Full toolset + meta-tools for guided exploration | New users, onboarding, tool learning |\n\n### Activating a Profile\n\nSet `toolProfile` in `aikit.config.json`:\n\n```json\n{\n "toolProfile": "research"\n}\n```\n\nBase tools (`status`, `config`, `guide`, `health`) are **always available** regardless of profile.\n\n## Development (Self-Dogfooding)\n\nWhen developing @vpxa/aikit itself: always `pnpm build` before using CLI/server (runs from `dist/`), and always `reindex` after structural code changes.\n\n---\n\n## Flows\n\nFlows are structured multi-step workflows that guide agents through complex tasks. They are the **primary workflow system** — use them instead of ad-hoc planning when a matching flow exists.\n\n### Flow Tools\n\n| Tool | Purpose |\n|------|---------|\n| `flow` | Check if a flow is active + current step + phase (before/flow/after) + isEpilogue |\n\nDifferent `action` values handle listing, starting, reading steps, advancing, resets, run inspection, and add/update/remove flow management.\n\n### Flow Selection\n\n| Task Type | Flow | Why |\n|-----------|------|-----|\n| Bug fix, config change, small refactor | `aikit:basic` | Known scope, low risk |\n| New feature in existing module | `aikit:basic` | Clear boundaries |\n| New system/service/module | `aikit:advanced` | Needs spec + planning |\n| Cross-service changes | `aikit:advanced` | Multiple boundaries |\n| Architectural change | `aikit:advanced` | High impact |\n| Unclear scope or exploratory | No flow | Use agent\'s native workflow |\n\n### Flow Lifecycle\n\n1. **Start**: `flow({ action: \'list\' })` → choose flow → `flow({ action: \'start\', name: "<name>", topic: "<task>" })`\n2. **Each step**: `flow({ action: \'read\', step: "<name>" })` → follow step instructions → complete work\n3. **Advance**: `flow({ action: \'step\', advance: \'next\' })` → repeat from step 2\n4. **Epilogue**: After last flow step, mandatory epilogue steps run (e.g., `_docs-sync` updates `docs/`)\n5. **Resume**: `flow({ action: \'status\' })` → if active, `flow({ action: \'read\' })` for current step → continue\n6. **Reset**: `flow({ action: \'reset\' })` if you need to start over\n\n---\n\n## Reference Documentation\n\nFor detailed patterns on specific topics, load these reference files:\n\n| Topic | File | When to load |\n|-------|------|-------------|\n| Multi-task orchestration | `references/coordination.md` | Queue, DAG, lanes, worksets, stash, checkpoints |\n| Quality gates (FORGE) | `references/forge-protocol.md` | Complex tasks, evidence maps, tier classification |\n| Search & relationships | `references/search-patterns.md` | Finding code, tracing data flow, graph traversal |\n'},{file:`references/coordination.md`,content:`# Coordination & Multi-Task Orchestration
|
|
1708
1708
|
|
|
1709
1709
|
Patterns for managing multiple tasks, parallel exploration, and session state.
|
|
1710
1710
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=[`const{execFileSync:x}=require('child_process')`,`const{renameSync:m}=require('fs')`,`const{join:j}=require('path')`,`const d=process.platform==='win32'?j(process.env.LOCALAPPDATA||'','npm-cache','_npx'):j(require('os').homedir(),'.npm','_npx')`,`const s={stdio:'inherit',shell:true}`,`try{x('npx',['-y','@vpxa/aikit','serve'],s)}catch(e){try{m(d,d+'_'+Date.now())}catch{};x('npx',['-y','@vpxa/aikit','serve'],s)}`].join(`;`),t=`aikit`,n={type:`stdio`,command:`node`,args:[`-e`,e]},r=[`aikit`,`brainstorming`,`multi-agents-development`,`session-handoff`,`requirements-clarity`,`lesson-learned`,`c4-architecture`,`adr-skill`,`present`,`frontend-design`,`react`,`typescript`,`docs`,`repo-access`],i=[`aikit-basic`,`aikit-advanced`,`_epilogue`],a={"chat.agentFilesLocations":{"~/.claude/agents":!1},"github.copilot.chat.copilotMemory.enabled":!0,"chat.customAgentInSubagent.enabled":!0,"chat.useNestedAgentsMdFiles":!0,"chat.useAgentSkills":!0,"github.copilot.chat.switchAgent.enabled":!0,"workbench.browser.enableChatTools":!0,"chat.mcp.apps.enabled":!0,"chat.instructionsFilesLocations":{"~/.copilot/instructions":!0,".github/instructions":!0}};export{a,r as i,n,t as r,i as t};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import{i as e,n as t,r as n,t as r}from"./constants-Nz_Z7XS-.js";import{n as i,t as a}from"./templates-DRfiihP4.js";import{guideFlows as o,guideScaffold as s,guideSkills as c,smartCopyFlows as l,smartCopyScaffold as u,smartCopySkills as d}from"./scaffold-BLeqLPMe.js";import{appendFileSync as f,existsSync as p,mkdirSync as m,readFileSync as h,unlinkSync as g,writeFileSync as _}from"node:fs";import{basename as v,dirname as y,join as b,resolve as x}from"node:path";import{fileURLToPath as S}from"node:url";import{AIKIT_PATHS as C,EMBEDDING_DEFAULTS as w,isUserInstalled as T}from"../../core/dist/index.js";function E(e){return p(x(e,`.cursor`))?`cursor`:p(x(e,`.claude`))?`claude-code`:p(x(e,`.windsurf`))?`windsurf`:p(x(e,`.zed`))?`zed`:p(x(e,`.idea`))?`intellij`:`copilot`}function D(e){let t=[];return p(x(e,`.cursor`))&&t.push(`cursor`),(p(x(e,`.claude`))||p(x(e,`CLAUDE.md`)))&&t.push(`claude-code`),p(x(e,`.windsurf`))&&t.push(`windsurf`),p(x(e,`.zed`))&&t.push(`zed`),p(x(e,`.idea`))&&t.push(`intellij`),p(x(e,`.gemini`))&&t.push(`gemini-cli`),(p(x(e,`.codex`))||p(x(e,`codex.md`)))&&t.push(`codex-cli`),t.push(`copilot`),[...new Set(t)]}function O(e){return{servers:{[e]:{...t}}}}function k(e){let{type:n,...r}=t;return{mcpServers:{[e]:r}}}function A(e){let{type:n,...r}=t;return{context_servers:{[e]:{...r}}}}const j={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.vscode`),r=x(n,`mcp.json`);p(r)||(m(n,{recursive:!0}),_(r,`${JSON.stringify(O(t),null,2)}\n`,`utf-8`),console.log(` Created .vscode/mcp.json`))},writeInstructions(e,t){let n=x(e,`.github`),r=x(n,`copilot-instructions.md`);m(n,{recursive:!0}),_(r,i(v(e),t),`utf-8`),console.log(` Updated .github/copilot-instructions.md`)},writeAgentsMd(e,t){_(x(e,`AGENTS.md`),a(v(e),t),`utf-8`),console.log(` Updated AGENTS.md`)}},M={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.mcp.json`);p(n)||(_(n,`${JSON.stringify(k(t),null,2)}\n`,`utf-8`),console.log(` Created .mcp.json`))},writeInstructions(e,t){let n=x(e,`CLAUDE.md`),r=v(e);_(n,`${i(r,t)}\n---\n\n${a(r,t)}`,`utf-8`),console.log(` Updated CLAUDE.md`)},writeAgentsMd(e,t){}},N={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.cursor`),r=x(n,`mcp.json`);p(r)||(m(n,{recursive:!0}),_(r,`${JSON.stringify(k(t),null,2)}\n`,`utf-8`),console.log(` Created .cursor/mcp.json`))},writeInstructions(e,t){let n=x(e,`.cursor`,`rules`),r=x(n,`aikit.mdc`);m(n,{recursive:!0});let o=v(e);_(r,`${i(o,t)}\n---\n\n${a(o,t)}`,`utf-8`),console.log(` Updated .cursor/rules/aikit.mdc`);let s=x(n,`kb.mdc`);p(s)&&s!==r&&(g(s),console.log(` Removed legacy .cursor/rules/kb.mdc`))},writeAgentsMd(e,t){}},P={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.vscode`),r=x(n,`mcp.json`);p(r)||(m(n,{recursive:!0}),_(r,`${JSON.stringify(O(t),null,2)}\n`,`utf-8`),console.log(` Created .vscode/mcp.json (Windsurf-compatible)`))},writeInstructions(e,t){let n=x(e,`.windsurfrules`),r=v(e);_(n,`${i(r,t)}\n---\n\n${a(r,t)}`,`utf-8`),console.log(` Updated .windsurfrules`)},writeAgentsMd(e,t){}},F={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.zed`),r=x(n,`settings.json`);if(m(n,{recursive:!0}),!p(r))_(r,`${JSON.stringify(A(t),null,2)}\n`,`utf-8`),console.log(` Created .zed/settings.json`);else{let e;try{e=JSON.parse(h(r,`utf-8`))}catch{console.warn(` ⚠ .zed/settings.json contains invalid JSON — skipping MCP config merge`);return}e.context_servers?.[t]||(e.context_servers={...e.context_servers,...A(t).context_servers},_(r,`${JSON.stringify(e,null,2)}\n`,`utf-8`),console.log(` Updated .zed/settings.json with context_servers`))}},writeInstructions(e,t){let n=x(e,`.rules`),r=v(e);_(n,`${i(r,t)}\n---\n\n${a(r,t)}`,`utf-8`),console.log(` Updated .rules`)},writeAgentsMd(e,t){}},I={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`mcp.json`);p(n)||(_(n,`${JSON.stringify(k(t),null,2)}\n`,`utf-8`),console.log(` Created mcp.json`))},writeInstructions(e,t){let n=x(e,`.aiassistant`,`rules`),r=x(n,`aikit.md`);m(n,{recursive:!0});let o=v(e);_(r,`${i(o,t)}\n---\n\n${a(o,t)}`,`utf-8`),console.log(` Updated .aiassistant/rules/aikit.md`)},writeAgentsMd(e,t){}};function L(e){switch(e){case`copilot`:return j;case`claude-code`:return M;case`cursor`:return N;case`windsurf`:return P;case`zed`:return F;case`intellij`:return I;case`gemini-cli`:case`codex-cli`:return j}}const R={serverName:n,sources:[{path:`.`,excludePatterns:[`**/node_modules/**`,`**/dist/**`,`**/build/**`,`**/.git/**`,`**/${C.data}/**`,`**/coverage/**`,`**/*.min.js`,`**/package-lock.json`,`**/pnpm-lock.yaml`]}],indexing:{chunkSize:1500,chunkOverlap:200,minChunkSize:100},embedding:{model:w.model,dimensions:w.dimensions},store:{backend:`sqlite-vec`,path:C.data},curated:{path:C.aiCurated}};function z(e,t){let n=x(e,`aikit.config.json`);return p(n)&&!t?(console.log(`aikit.config.json already exists. Use --force to overwrite.`),!1):(_(n,`${JSON.stringify(R,null,2)}\n`,`utf-8`),console.log(` Created aikit.config.json`),!0)}function B(e){let t=x(e,`.gitignore`),n=[{dir:`${C.data}/`,label:`AI Kit vector store`},{dir:`${C.state}/`,label:`AI Kit session state`},{dir:`${C.restorePoints}/`,label:`Restore points (codemod/rename undo snapshots)`},{dir:`${C.brainstorm}/`,label:`Brainstorming sessions`},{dir:`${C.handoffs}/`,label:`Handoff documents`}];if(p(t)){let e=h(t,`utf-8`),r=n.filter(t=>!e.includes(t.dir));r.length>0&&(f(t,`\n${r.map(e=>`# ${e.label}\n${e.dir}`).join(`
|
|
2
|
-
`)}\n`,`utf-8`),console.log(` Added ${r.map(e=>e.dir).join(`, `)} to .gitignore`))}else _(t,`${n.map(e=>`# ${e.label}\n${e.dir}`).join(`
|
|
3
|
-
`)}\n`,`utf-8`),console.log(` Created .gitignore with AI Kit entries`)}function V(){return R.serverName}const H=[`decisions`,`patterns`,`conventions`,`troubleshooting`];function U(e){let t=x(e,`.ai`,`curated`);p(t)||(m(t,{recursive:!0}),console.log(` Created .ai/curated/`));for(let e of H){let n=x(t,e);p(n)||m(n,{recursive:!0})}console.log(` Created .ai/curated/{${H.join(`,`)}}/`)}function W(e){switch(e){case`zed`:return`zed`;case`intellij`:return`intellij`;case`claude-code`:return`claude-code`;case`gemini-cli`:return`gemini`;case`codex-cli`:return`codex`;default:return`copilot`}}function G(e){let t=e;for(let e=0;e<10;e++){try{let e=b(t,`package.json`);if(p(e)&&JSON.parse(h(e,`utf8`)).name===`@vpxa/aikit`)return t}catch{}let e=y(t);if(e===t)break;t=e}return x(e,`..`,`..`,`..`)}async function K(t){let n=process.cwd();if(!z(n,t.force))return;B(n);let i=V(),a=L(E(n));a.writeMcpConfig(n,i),a.writeInstructions(n,i),a.writeAgentsMd(n,i);let o=G(y(S(import.meta.url))),s=JSON.parse(h(x(o,`package.json`),`utf-8`)).version;await d(n,o,[...e],s,t.force),await l(n,o,[...r],s,t.force);let c=D(n),f=new Set;for(let e of c){let r=W(e);f.has(r)||(f.add(r),await u(n,o,r,s,t.force))}U(n),console.log(`
|
|
4
|
-
AI Kit initialized! Next steps:`),console.log(` aikit reindex Index your codebase`),console.log(` aikit search Search indexed content`),console.log(` aikit serve Start MCP server for IDE integration`),T()&&console.log(`
|
|
5
|
-
Note: User-level AI Kit is also installed. This workspace uses its own local data store.`)}async function q(e){T()?await J(e):await K(e)}async function J(e){let t=process.cwd(),n=V(),i=L(E(t));i.writeInstructions(t,n),i.writeAgentsMd(t,n);let a=G(y(S(import.meta.url))),o=JSON.parse(h(x(a,`package.json`),`utf-8`)).version,s=D(t),c=new Set;for(let n of s){let r=W(n);c.has(r)||(c.add(r),await u(t,a,r,o,e.force))}await l(t,a,[...r],o,e.force),U(t),B(t),console.log(`
|
|
6
|
-
Workspace scaffolded for user-level AI Kit! Files added:`),console.log(` Instruction files (AGENTS.md, copilot-instructions.md, etc.)`),console.log(` .ai/curated/ directories`),console.log(` .github/agents/ & .github/prompts/`),console.log(`
|
|
7
|
-
The user-level AI Kit server will auto-index this workspace when opened in your IDE.`)}async function Y(){let t=process.cwd(),n=D(t),i=G(y(S(import.meta.url))),a=[...await c(t,i,[...e])],l=new Set;for(let e of n){let n=W(e);l.has(n)||(l.add(n),a.push(...await s(t,i,n)))}a.push(...await o(t,i,[...r]));let u={summary:{total:a.length,new:a.filter(e=>e.status===`new`).length,outdated:a.filter(e=>e.status===`outdated`).length,current:a.filter(e=>e.status===`current`).length},files:a};console.log(JSON.stringify(u,null,2))}export{Y as guideProject,K as initProject,J as initScaffoldOnly,q as initSmart};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{existsSync as e,mkdirSync as t,readFileSync as n,rmSync as r,unlinkSync as i,writeFileSync as a}from"node:fs";import{dirname as o,resolve as s}from"node:path";import{pathToFileURL as c}from"node:url";import{createHash as l}from"node:crypto";const u=[`inputs`,`outputs`,`requires`,`relatedSkills`],d=[`model`],f=[`category`,`domain`,`applicability`],p=new Set([`__proto__`,`constructor`,`prototype`]);function m(e,t){return e.metadata[t]??e.fields[t]}function h(e,t){let n=m(e,t);return n?_(n):[]}function g(e){let t=Object.create(null),n=Object.create(null),r=[],i=e,a=e.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);if(!a)return{fields:t,metadata:n,tools:r,body:i};let o=a[1];i=a[2];let s=!1;for(let e of o.split(/\r?\n/)){if(s){if(e.trim()===``)continue;let t=e.match(/^\s{2,}(\S+?):\s*(.*)$/);if(t){let[,e,r]=t;if(p.has(e))continue;n[e]=r;continue}s=!1}let i=e.match(/^(\S+?):\s*(.*)$/);if(!i)continue;let[,a,o]=i;if(a===`metadata`&&o.trim()===``){s=!0;continue}p.has(a)||(t[a]=o,a===`tools`&&(r=_(o)))}return{fields:t,metadata:n,tools:r,body:i}}function _(e){let t=e.trim();if(!t.startsWith(`[`)||!t.endsWith(`]`))return[];let n=t.slice(1,-1).trim();return n?n.split(`,`).map(e=>e.trim()).filter(Boolean):[]}function v(e){return`[${e.join(`, `)}]`}function y(e,t){let n=g(e),r=g(t),i={...n.fields},a={...n.metadata};for(let[e,t]of Object.entries(r.metadata))e in a||(a[e]=t);for(let e of d)r.fields[e]&&(i[e]=r.fields[e]);for(let e of f){let t=m(r,e);t&&(a[e]=t)}for(let e of[...f,...u])delete i[e];delete i.metadata;let o=n.tools,s=r.tools,c=new Set(o),l=s.filter(e=>!c.has(e)),p=[...o,...l];p.length>0&&(i.tools=v(p));for(let e of u){let t=h(n,e),i=h(r,e),o=new Set(t),s=i.filter(e=>!o.has(e)),c=[...t,...s];c.length>0?a[e]=v(c):delete a[e]}let _=[`---`],y=[`name`,`description`,`argument-hint`,`tools`,`model`],b=[`category`,`domain`,`applicability`,`inputs`,`outputs`,`requires`,`relatedSkills`],x=new Set;for(let e of y)e in i&&(_.push(`${e}: ${i[e]}`),x.add(e));for(let[e,t]of Object.entries(i))x.has(e)||_.push(`${e}: ${t}`);let S=[],C=new Set;for(let e of b){let t=a[e];t&&(S.push(` ${e}: ${t}`),C.add(e))}for(let[e,t]of Object.entries(a))!C.has(e)&&t&&S.push(` ${e}: ${t}`);return S.length>0&&(_.push(`metadata:`),_.push(...S)),_.push(`---`),`${_.join(`
|
|
2
|
-
`)}\n${n.body}`}function b(e){return l(`sha256`).update(e).digest(`hex`).slice(0,16)}function x(t){if(!e(t))return null;try{let e=n(t,`utf-8`),r=JSON.parse(e);return!r.version||!r.files?null:r}catch{return null}}function S(e,t){a(e,`${JSON.stringify(t,null,2)}\n`,`utf-8`)}function C(e){return e.endsWith(`.agent.md`)&&!e.startsWith(`_shared/`)&&!e.startsWith(`agents/_shared/`)&&!e.startsWith(`templates/`)||e.endsWith(`SKILL.md`)&&!e.startsWith(`_shared/`)&&!e.startsWith(`templates/`)?`merge-frontmatter`:`overwrite`}function w(e,t,n){if(!e)return`new`;let r=e.files[t];return r?r.sourceHash===b(n)?`current`:`outdated`:`new`}function T(e,t,n,r){e.files[t]={sourceHash:b(n),strategy:r??C(t),updatedAt:new Date().toISOString()}}function E(e){return{version:e,generatedAt:new Date().toISOString(),files:{}}}async function D(e,t){let n=await import(c(s(e,`scaffold`,`dist`,`adapters`,`${t}.mjs`)).href),r=n[`generate${t.split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``)}`]??n.generate??n.default;if(typeof r!=`function`)throw Error(`Adapter ${t} does not export a generate function`);return r()}function O(r,i,c,l,u=!1){t(i,{recursive:!0});for(let d of r){let r=l?`${l}/${d.path}`:d.path,f=s(i,d.path);if(u){t(o(f),{recursive:!0}),a(f,d.content,`utf-8`),T(c,r,d.content);continue}let p=w(c,r,d.content);if(p!==`current`){if(p===`new`&&!e(f)){t(o(f),{recursive:!0}),a(f,d.content,`utf-8`),T(c,r,d.content);continue}if(p===`new`&&e(f)){T(c,r,d.content);continue}if(C(r)===`merge-frontmatter`&&e(f)){let e=n(f,`utf-8`);a(f,y(d.content,e),`utf-8`)}else t(o(f),{recursive:!0}),a(f,d.content,`utf-8`);T(c,r,d.content)}}u&&k(i,c,l,new Set(r.map(e=>l?`${l}/${e.path}`:e.path)))}function k(t,n,r,a){let o=[];for(let c of Object.keys(n.files)){if(!c.startsWith(`${r}/`)||a.has(c))continue;let l=s(t,c.slice(r.length+1));e(l)&&(i(l),o.push(c)),delete n.files[c]}return o}async function A(e,t,n=`copilot`,r,i=!1){let a=await D(t,n),o=a.some(e=>e.path.startsWith(`.`)),c=o?e:s(e,`.github`),l=s(c,`.aikit-scaffold.json`),u=x(l)??E(r);u.version=r;let d=new Map;for(let e of a){let t=e.path.indexOf(`/`);if(t===-1){let t=d.get(``)??[];t.push({path:e.path,content:e.content}),d.set(``,t);continue}let n=e.path.substring(0,t),r=e.path.substring(t+1),i=d.get(n)??[];i.push({path:r,content:e.content}),d.set(n,i)}for(let[e,t]of d)O(t,e?s(c,e):c,u,o?`${n}/${e||`.`}`:e||`.`,i);S(l,u)}async function j(e,t,n,r,i=!1){let a=s(e,`.github`),o=s(a,`.aikit-scaffold.json`),c=x(o)??E(r);c.version=r;let l=await D(t,`skills`),u=new Map;for(let e of l){let t=e.path.indexOf(`/`);if(t===-1)continue;let n=e.path.substring(0,t),r=e.path.substring(t+1),i=u.get(n)??[];i.push({path:r,content:e.content}),u.set(n,i)}for(let[e,t]of u)O(t,s(a,`skills`,e),c,`skills/${e}`,i);S(o,c)}async function M(t,r,i=`copilot`){let a=[],o=await D(r,i),c=o.some(e=>e.path.startsWith(`.`))?t:s(t,`.github`);for(let t of o){let r=s(c,t.path);if(!e(r))a.push({status:`new`,relativePath:t.path,sourcePath:``,content:t.content});else{let e=n(r,`utf-8`);t.content===e?a.push({status:`current`,relativePath:t.path,sourcePath:``}):a.push({status:`outdated`,relativePath:t.path,sourcePath:``,content:t.content})}}return a}async function N(t,r,i){let a=[],o=await D(r,`skills`),c=s(t,`.github`);for(let t of o){let r=`skills/${t.path}`,i=s(c,`skills`,t.path);if(!e(i))a.push({status:`new`,relativePath:r,sourcePath:``,content:t.content});else{let e=n(i,`utf-8`);t.content===e?a.push({status:`current`,relativePath:r,sourcePath:``}):a.push({status:`outdated`,relativePath:r,sourcePath:``,content:t.content})}}return a}async function P(t,n,i,a,o=!1){let c=s(t,`.github`),l=s(c,`.aikit-scaffold.json`),u=x(l)??E(a);u.version=a;let d=await D(n,`flows`),f=new Set,p=new Map;for(let e of d){let t=e.path.indexOf(`/`);if(t===-1)continue;let n=e.path.substring(0,t);f.add(n);let r=e.path.substring(t+1),i=p.get(n)??[];i.push({path:r,content:e.content}),p.set(n,i)}for(let t of f){let n=s(c,`flows`,t,`skills`);e(n)&&r(n,{recursive:!0,force:!0})}for(let[e,t]of p)O(t,s(c,`flows`,e),u,`flows/${e}`,o);S(l,u)}async function F(t,r,i){let a=[],o=await D(r,`flows`),c=s(t,`.github`);for(let t of o){let r=`flows/${t.path}`,i=s(c,`flows`,t.path);if(!e(i))a.push({status:`new`,relativePath:r,sourcePath:``,content:t.content});else{let e=n(i,`utf-8`);t.content===e?a.push({status:`current`,relativePath:r,sourcePath:``}):a.push({status:`outdated`,relativePath:r,sourcePath:``,content:t.content})}}return a}export{F as guideFlows,M as guideScaffold,N as guideSkills,D as loadAdapter,x as n,S as r,P as smartCopyFlows,O as smartCopyFromMemory,A as smartCopyScaffold,j as smartCopySkills,E as t};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{a as e,n as t,r as n}from"./constants-Nz_Z7XS-.js";import{n as r,t as i}from"./templates-DRfiihP4.js";import{loadAdapter as a,n as o,r as s,smartCopyFromMemory as c,t as l}from"./scaffold-BLeqLPMe.js";import{existsSync as u,mkdirSync as d,readFileSync as f,readdirSync as p,rmSync as ee,unlinkSync as m,writeFileSync as h}from"node:fs";import{dirname as g,join as _,posix as v,resolve as y,win32 as b}from"node:path";import{fileURLToPath as x}from"node:url";import{mkdir as S,readFile as C,rename as w,unlink as T,writeFile as E}from"node:fs/promises";import{getGlobalDataDir as D,saveRegistry as O}from"../../core/dist/index.js";import{execFileSync as k}from"node:child_process";import{randomUUID as A}from"node:crypto";import{homedir as j}from"node:os";function M(e){let t=``,n=0,r=e.length;for(;n<r;)if(e[n]===`"`){for(t+=`"`,n++;n<r&&e[n]!==`"`;)e[n]===`\\`&&(t+=e[n++]),n<r&&(t+=e[n++]);n<r&&(t+=e[n++])}else if(e[n]===`/`&&n+1<r&&e[n+1]===`/`)for(n+=2;n<r&&e[n]!==`
|
|
2
|
-
`;)n++;else if(e[n]===`/`&&n+1<r&&e[n+1]===`*`){for(n+=2;n+1<r&&!(e[n]===`*`&&e[n+1]===`/`);)n++;n+=2}else t+=e[n++];return t.replace(/,(\s*[}\]])/g,`$1`)}var N=class{isPlatformSupported(){return this.platforms.includes(process.platform)}async readConfig(e){let t=this.getConfigPath(e);if(!u(t))return{};let n=await C(t,`utf-8`);try{return JSON.parse(M(n))}catch{throw Error(`Invalid JSON in ${t}. Please fix or remove the file before retrying.`)}}async writeConfig(e,t){let n=this.getConfigPath(t),r=g(n),i=_(r,`.aikit-tmp-${A()}.json`);await S(r,{recursive:!0});try{await E(i,`${JSON.stringify(e,null,2)}\n`,`utf-8`),await w(i,n)}catch(e){try{await T(i)}catch{}throw e}}async registerMcp(e,t,n){let r=await this.readConfig(n);r[this.configKey]||(r[this.configKey]={}),r[this.configKey][e]=t,await this.writeConfig(r,n)}async unregisterMcp(e,t){let n=await this.readConfig(t);n[this.configKey]&&(delete n[this.configKey][e],await this.writeConfig(n,t))}getScaffoldRoot(e){return null}getInstructionsRoot(e){return null}getScaffoldPaths(e){return{agents:null,skills:null,prompts:null,flows:null,commands:null,instructions:null,manifest:null}}buildInstructionContent(e,t){return`${e}\n---\n\n${t}`}},P=class extends N{id=`claude-code`;name=`Claude Code`;family=`claude`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`mcpServers`;async detect(){return this.isPlatformSupported()?u(this.getPathModule().resolve(j(),`.claude`)):!1}getConfigPath(){return this.getPathModule().resolve(j(),`.claude`,`mcp.json`)}getScaffoldRoot(){return this.getPathModule().resolve(j(),`.claude`)}getScaffoldPaths(){let e=this.getPathModule(),t=e.resolve(j(),`.claude`);return{agents:e.resolve(t,`agents`),skills:e.resolve(t,`skills`),prompts:null,flows:e.resolve(t,`flows`),commands:e.resolve(t,`commands`),instructions:e.resolve(t,`CLAUDE.md`),manifest:e.resolve(t,`.aikit-scaffold.json`)}}buildInstructionContent(e,t){return`${e}\n---\n\n${t}`}getPathModule(){return process.platform===`win32`?b:v}},F=class extends N{id=`codex-cli`;name=`Codex CLI`;family=`codex`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`mcpServers`;async detect(){return this.isPlatformSupported()?u(this.getPathModule().resolve(j(),`.codex`)):!1}getConfigPath(){return this.getPathModule().resolve(j(),`.codex`,`config.json`)}getScaffoldRoot(){return this.getPathModule().resolve(j(),`.codex`)}getScaffoldPaths(){let e=this.getPathModule(),t=e.resolve(j(),`.codex`);return{agents:e.resolve(t,`agents`),skills:e.resolve(t,`skills`),prompts:null,flows:e.resolve(t,`flows`),commands:null,instructions:e.resolve(t,`AGENTS.md`),manifest:e.resolve(t,`.aikit-scaffold.json`)}}getPathModule(){return process.platform===`win32`?b:v}},I=class extends N{family=`copilot`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;hasInstructions=!1;async detect(){if(!this.isPlatformSupported())return!1;let e=this.getConfigDir();if(process.platform===`darwin`){let t=this.getMacAppPath();return u(e)||t!==null&&u(t)}return u(e)}getConfigPath(){return this.getPathModule().resolve(this.getConfigDir(),`mcp.json`)}getScaffoldRoot(){return this.getPathModule().resolve(j(),this.scaffoldBase)}getInstructionsRoot(){let e=this.getInstructionFilePath();return e?this.getPathModule().dirname(e):null}getScaffoldPaths(){let e=this.getPathModule(),t=e.resolve(j(),this.scaffoldBase);return{agents:e.resolve(t,`agents`),skills:e.resolve(t,`skills`),prompts:e.resolve(this.getConfigDir(),`prompts`),flows:e.resolve(t,`flows`),commands:null,instructions:this.getInstructionFilePath(),manifest:e.resolve(t,`.aikit-scaffold.json`)}}getConfigDir(){let e=this.getPathModule(),t=j();if(process.platform===`win32`){let n=process.env.APPDATA??e.resolve(t,`AppData`,`Roaming`);return e.resolve(n,this.configDirName,`User`)}if(process.platform===`darwin`)return e.resolve(t,`Library`,`Application Support`,this.configDirName,`User`);let n=process.env.XDG_CONFIG_HOME??e.resolve(t,`.config`);return e.resolve(n,this.configDirName,`User`)}getMacAppPath(){return null}getInstructionFilePath(){return null}getPathModule(){return process.platform===`win32`?b:v}},L=class extends N{id=`copilot-cli`;name=`Copilot CLI`;family=`copilot`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`mcpServers`;async detect(){return this.isPlatformSupported()?u(y(j(),`.copilot`)):!1}getConfigPath(){return y(j(),`.copilot`,`mcp-config.json`)}async registerMcp(e,t,n){let r={...t,tools:[`*`]},i=await this.readConfig(n);i[this.configKey]||(i[this.configKey]={}),i[this.configKey][e]=r,await this.writeConfig(i,n)}getScaffoldRoot(){return y(j(),`.copilot`)}getInstructionsRoot(){return null}getScaffoldPaths(){let e=y(j(),`.copilot`);return{agents:y(e,`agents`),skills:y(e,`skills`),prompts:null,flows:y(e,`flows`),commands:null,instructions:y(j(),`.github`,`copilot-instructions.md`),manifest:y(e,`.aikit-scaffold.json`)}}},R=class extends I{id=`cursor`;name=`Cursor`;configKey=`mcpServers`;configDirName=`Cursor`;scaffoldBase=`.cursor`;getMacAppPath(){return`/Applications/Cursor.app`}getInstructionFilePath(){return this.getPathModule().resolve(j(),this.scaffoldBase,`rules`,`aikit.mdc`)}},z=class extends I{id=`cursor-nightly`;name=`Cursor Nightly`;configKey=`mcpServers`;configDirName=`Cursor Nightly`;scaffoldBase=`.cursor`;getMacAppPath(){return`/Applications/Cursor Nightly.app`}getInstructionFilePath(){return this.getPathModule().resolve(j(),this.scaffoldBase,`rules`,`aikit.mdc`)}},B=class e extends N{id=`intellij`;name=`IntelliJ IDEA`;family=`copilot`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`servers`;static PRODUCTS=[`IntelliJIdea`,`IdeaIC`,`WebStorm`,`PyCharm`,`PyCharmCE`,`GoLand`,`PhpStorm`,`CLion`,`Rider`,`RubyMine`,`RustRover`,`DataGrip`];async detect(){if(!this.isPlatformSupported())return!1;if(u(this.getCopilotConfigDir()))return!0;let t=this.getJetBrainsBaseDir();if(!u(t))return!1;try{return p(t,{withFileTypes:!0}).some(t=>t.isDirectory()&&e.PRODUCTS.some(e=>t.name.startsWith(e)))}catch{return!1}}getConfigPath(){return y(this.getCopilotConfigDir(),`mcp.json`)}async registerMcp(e,t,n){let r=this.getCopilotConfigDir();await S(r,{recursive:!0});let i=t.args??[],a=i.indexOf(`-e`),o;if(a!==-1&&a+1<i.length){let t=i[a+1],n=y(r,`${e}-launcher.js`);await E(n,t,`utf-8`),o=[...i.slice(0,a),n,...i.slice(a+2)]}else o=[...i];let s={type:`stdio`,...t,args:o};await super.registerMcp(e,s,n)}getScaffoldRoot(e){return null}getInstructionsRoot(e){return null}getScaffoldPaths(e){return{agents:null,skills:null,prompts:null,flows:null,commands:null,instructions:null,manifest:null}}buildInstructionContent(e,t){return t}getCopilotConfigDir(){let e=j();return process.platform===`win32`?y(process.env.LOCALAPPDATA??y(e,`AppData`,`Local`),`github-copilot`,`intellij`):process.platform===`darwin`?y(e,`Library`,`Application Support`,`github-copilot`,`intellij`):y(process.env.XDG_CONFIG_HOME??y(e,`.config`),`github-copilot`,`intellij`)}getJetBrainsBaseDir(){let e=j();return process.platform===`win32`?y(process.env.APPDATA??y(e,`AppData`,`Roaming`),`JetBrains`):process.platform===`darwin`?y(e,`Library`,`Application Support`,`JetBrains`):y(process.env.XDG_CONFIG_HOME??y(e,`.config`),`JetBrains`)}},V=class extends I{id=`trae`;name=`Trae`;configKey=`servers`;configDirName=`Trae`;scaffoldBase=`.trae`;getMacAppPath(){return`/Applications/Trae.app`}getInstructionFilePath(){return this.getPathModule().resolve(j(),this.scaffoldBase,`rules`,`aikit.md`)}},H=class extends I{id=`vscode`;name=`VS Code`;configKey=`servers`;configDirName=`Code`;scaffoldBase=`.copilot`;hasInstructions=!0;getMacAppPath(){return`/Applications/Visual Studio Code.app`}getInstructionFilePath(){return this.hasInstructions?this.getPathModule().resolve(j(),this.scaffoldBase,`instructions`,`copilot-instructions.md`):null}buildInstructionContent(e,t){return`---\napplyTo: "**"\n---\n\n${e}\n---\n\n${t}`}},U=class extends I{id=`vscode-insiders`;name=`VS Code Insiders`;configKey=`servers`;configDirName=`Code - Insiders`;scaffoldBase=`.copilot`;hasInstructions=!0;getMacAppPath(){return`/Applications/Visual Studio Code - Insiders.app`}getInstructionFilePath(){return this.hasInstructions?this.getPathModule().resolve(j(),this.scaffoldBase,`instructions`,`copilot-instructions.md`):null}buildInstructionContent(e,t){return`---\napplyTo: "**"\n---\n\n${e}\n---\n\n${t}`}},W=class extends I{id=`vscodium`;name=`VSCodium`;configKey=`servers`;configDirName=`VSCodium`;scaffoldBase=`.copilot`;hasInstructions=!0;getMacAppPath(){return`/Applications/VSCodium.app`}getInstructionFilePath(){return this.hasInstructions?this.getPathModule().resolve(j(),this.scaffoldBase,`instructions`,`copilot-instructions.md`):null}buildInstructionContent(e,t){return`---\napplyTo: "**"\n---\n\n${e}\n---\n\n${t}`}},G=class extends I{id=`windsurf`;name=`Windsurf`;configKey=`servers`;configDirName=`Windsurf`;scaffoldBase=`.windsurf`;getMacAppPath(){return`/Applications/Windsurf.app`}getInstructionFilePath(){return this.getPathModule().resolve(j(),this.scaffoldBase,`rules`,`aikit.md`)}},K=class extends N{id=`gemini-cli`;name=`Gemini CLI`;family=`gemini`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`mcpServers`;async detect(){return this.isPlatformSupported()?u(this.getPathModule().resolve(j(),`.gemini`)):!1}getConfigPath(){return this.getPathModule().resolve(j(),`.gemini`,`settings.json`)}getScaffoldRoot(){return this.getPathModule().resolve(j(),`.gemini`)}getScaffoldPaths(){let e=this.getPathModule(),t=e.resolve(j(),`.gemini`);return{agents:e.resolve(t,`agents`),skills:e.resolve(t,`skills`),prompts:null,flows:e.resolve(t,`flows`),commands:null,instructions:e.resolve(t,`AGENTS.md`),manifest:e.resolve(t,`.aikit-scaffold.json`)}}getPathModule(){return process.platform===`win32`?b:v}},q=class extends N{id=`zed`;name=`Zed`;family=`zed`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`context_servers`;async detect(){return this.isPlatformSupported()?u(this.getConfigDir()):!1}getConfigPath(){return this.getPathModule().resolve(this.getConfigDir(),`settings.json`)}async registerMcp(e,t){let{type:n,...r}=t,i={...r,env:r.env??{}};await super.registerMcp(e,i)}getScaffoldRoot(){return this.getConfigDir()}getScaffoldPaths(){let e=this.getPathModule(),t=this.getConfigDir();return{agents:e.resolve(t,`prompts`),skills:null,prompts:e.resolve(t,`prompts`),flows:null,commands:null,instructions:null,manifest:e.resolve(t,`.aikit-scaffold.json`)}}getConfigDir(){let e=this.getPathModule();return process.platform===`win32`?e.resolve(process.env.APPDATA||j(),`Zed`):e.resolve(j(),`.config`,`zed`)}getPathModule(){return process.platform===`win32`?b:v}};const J=[new H,new U,new W,new R,new z,new G,new V,new L,new B,new P,new K,new F,new q];function Y(){return[...J]}async function X(e){let t=e?.scope?J.filter(t=>t.scope===e.scope):J;return(await Promise.allSettled(t.map(async e=>await e.detect()?e:null))).flatMap(e=>e.status!==`fulfilled`||e.value===null?[]:[e.value])}function Z(e){let t=e;for(let e=0;e<10;e++){try{let e=_(t,`package.json`);if(u(e)&&JSON.parse(f(e,`utf8`)).name===`@vpxa/aikit`)return t}catch{}let e=g(t);if(e===t)break;t=e}return y(e,`..`,`..`,`..`)}function Q(){let e={command:t.command,args:t.args?[...t.args]:void 0,type:t.type};if(process.platform!==`win32`){let t=process.env.PATH;t&&(e.env={PATH:t});try{let t=k(`which`,[`node`],{encoding:`utf-8`,timeout:3e3}).trim();t&&u(t)&&(e.command=t)}catch{}}return e}const $=new Set([`VS Code`,`VS Code Insiders`,`VSCodium`]);function te(t,n=!1){if(!$.has(t.name))return;let r=y(g(t.getConfigPath()),`settings.json`),i={};if(u(r))try{let e=f(r,`utf-8`);i=JSON.parse(e)}catch{console.log(` ${t.name}: skipped settings.json (invalid JSON)`);return}let a=!1;for(let[t,r]of Object.entries(e))if(typeof r==`object`&&r){let e=typeof i[t]==`object`&&i[t]!==null?i[t]:{},n={...e,...r};JSON.stringify(n)!==JSON.stringify(e)&&(i[t]=n,a=!0)}else (n||!(t in i))&&(i[t]=r,a=!0);a&&(h(r,`${JSON.stringify(i,null,2)}\n`,`utf-8`),console.log(` ${t.name}: updated settings.json`))}async function ne(e,t,n,f,p=!1){let m=new Map;for(let e of t)e.getScaffoldRoot()!==null&&m.set(e.id,e);if(t.some(e=>$.has(e.name))){let e=Y().find(e=>e.id===`copilot-cli`);e&&m.set(e.id,e)}if(m.size===0){console.log(` No IDEs with global scaffold support detected.`);return}let _=await a(e,`copilot`),v=new Map;for(let e of _){let t=e.path.indexOf(`/`);if(t===-1)continue;let n=e.path.substring(0,t);if(n!==`agents`&&n!==`prompts`)continue;let r=v.get(n)??[];r.push({path:e.path.substring(t+1),content:e.content}),v.set(n,r)}let b=await a(e,`skills`),x=new Set;for(let e of b){let t=e.path.indexOf(`/`);t!==-1&&x.add(e.path.substring(0,t))}let S=await a(e,`flows`),C=new Set;for(let e of S){let t=e.path.indexOf(`/`);t!==-1&&C.add(e.path.substring(0,t))}let w=await a(e,`claude-code`),T=new Set,E=new Set,D=new Set,O=(e,t,n)=>{let r=v.get(n);if(!e||!t||!r||r.length===0)return;let i=`${n}:${t}:${e}`;if(T.has(i))return;T.add(i);let a=o(t)??l(f);a.version=f,c(r,e,a,n,p),s(t,a),D.add(g(t))},k=(e,t,n,r)=>{if(!e||!t||r.length===0)return;let i=`${n}:${t}:${e}`;if(T.has(i))return;if(T.add(i),n===`flows`&&!E.has(e)){for(let n of C){let r=y(e,n,`skills`);u(r)&&(ee(r,{recursive:!0,force:!0}),console.log(` ${g(t)}: migrated ${n} flow to steps/ layout`))}E.add(e)}let a=o(t)??l(f);a.version=f,c(r,e,a,n,p),s(t,a),D.add(g(t))};for(let e of m.values()){let t=e.getScaffoldPaths();O(t.agents,t.manifest,`agents`),O(t.prompts,t.manifest,`prompts`),k(t.skills,t.manifest,`skills`,b),k(t.flows,t.manifest,`flows`,S),k(t.commands,t.manifest,`commands`,w)}for(let e of D)console.log(` ${e}: scaffold updated (${x.size} skills)`);let A=new Set,j=r(`aikit`,n),M=i(`aikit`,n);for(let e of m.values()){let t=e.getScaffoldPaths().instructions;!t||A.has(t)||(d(g(t),{recursive:!0}),h(t,e.buildInstructionContent(j,M),`utf-8`),A.add(t))}A.size>0&&console.log(` Instruction files: ${[...A].join(`, `)}`)}function re(e){let t=[];for(let n of e){let e=n.getScaffoldRoot();if(e)if($.has(n.name)){let r=n.getInstructionsRoot()??e;t.push(y(r,`kb.instructions.md`)),t.push(y(r,`aikit.instructions.md`))}else n.name===`Cursor`||n.name===`Cursor Nightly`?t.push(y(e,`rules`,`kb.mdc`)):n.name===`Windsurf`&&t.push(y(e,`rules`,`kb.md`))}for(let e of t)u(e)&&(m(e),console.log(` Removed legacy file: ${e}`))}async function ie(e){let t=n,r=Z(g(x(import.meta.url))),i=JSON.parse(f(y(r,`package.json`),`utf-8`)).version;console.log(`Initializing @vpxa/aikit v${i}...\n`);let a=D();d(a,{recursive:!0}),console.log(` Global data store: ${a}`),O({version:1,workspaces:{}}),console.log(` Created registry.json`);let o=await X({scope:`user`});if(o.length===0)console.log(`
|
|
3
|
-
No supported IDEs detected. You can manually add the MCP server config.`);else{console.log(`\n Detected ${o.length} IDE(s):`);let n=Q();for(let e of o)try{await e.registerMcp(t,n),console.log(` ${e.name}: configured ${t}`)}catch(t){console.log(` ${e.name}: failed to configure (${t.message})`)}for(let t of o)te(t,e.force)}console.log(`
|
|
4
|
-
Installing scaffold files:`),await ne(r,o,t,i,e.force),re(o),console.log(`
|
|
5
|
-
User-level AI Kit installation complete!`),console.log(`
|
|
6
|
-
Next steps:`),console.log(` 1. Open any workspace in your IDE`),console.log(` 2. The AI Kit server will auto-start and index the workspace`),console.log(` 3. Agents, prompts, skills & instructions are available globally`),console.log(` 4. No per-workspace init needed — just open a project and start coding`)}export{ie as initUser};
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as n,readFileSync as r,renameSync as i,unlinkSync as a,writeFileSync as o}from"node:fs";import{EMBEDDING_DEFAULTS as s,SEARCH_DEFAULTS as c,STORE_DEFAULTS as l,createLogger as u,serializeError as d,sourceTypeContentTypes as f}from"../../core/dist/index.js";import{dirname as p}from"node:path";var m=Object.defineProperty,h=(e,t)=>{let n={};for(var r in e)m(n,r,{get:e[r],enumerable:!0});return t||m(n,Symbol.toStringTag,{value:`Module`}),n},g=e(import.meta.url);const _=u(`sqlite-adapter`),v=e(import.meta.url);var y=class{type=`better-sqlite3`;vectorCapable=!1;db=null;stmtCache=new Map;async open(e){let t;try{t=v(`better-sqlite3`)}catch(e){throw Error(`better-sqlite3 native binding unavailable: ${e instanceof Error?e.message:String(e)}`)}this.db=new t(e),this.db.pragma(`journal_mode = WAL`),this.db.pragma(`foreign_keys = ON`),this.db.pragma(`synchronous = NORMAL`);try{v(`sqlite-vec`).load(this.db),this.vectorCapable=!0,_.info(`sqlite-vec extension loaded`)}catch(e){this.vectorCapable=!1,_.warn(`sqlite-vec extension failed to load; vector search disabled`,d(e))}}exec(e){this.getDb().exec(e)}pragma(e){this.getDb().pragma(e)}queryAll(e,t=[]){let n=this.prepareCached(e);return t.length>0?n.all(...t):n.all()}run(e,t=[]){let n=this.prepareCached(e);t.length>0?n.run(...t):n.run()}flush(){}close(){this.db&&=(this.stmtCache.clear(),this.db.close(),null)}getDb(){if(!this.db)throw Error(`BetterSqlite3Adapter: database not opened`);return this.db}prepareCached(e){let t=this.stmtCache.get(e);if(t)return t;let n=this.getDb().prepare(e);return this.stmtCache.set(e,n),n}};function b(e){return v.resolve(`sql.js/dist/${e}`)}var x=class{type=`sql.js`;vectorCapable=!1;db=null;dbPath=``;dirty=!1;inTransaction=!1;async open(e){this.dbPath=e;let n=(await import(`sql.js`)).default,i=await n({locateFile:e=>b(e)});if(t(e)){let t=r(e);this.db=new i.Database(t)}else this.db=new i.Database}exec(e){let t=e.trimStart().toUpperCase();this.getDb().run(e),t.startsWith(`BEGIN`)?this.inTransaction=!0:(t.startsWith(`COMMIT`)||t.startsWith(`ROLLBACK`))&&(this.inTransaction=!1),this.dirty=!0}pragma(e){this.getDb().exec(`PRAGMA ${e}`)}queryAll(e,t=[]){let n=this.getDb().prepare(e);try{t.length>0&&n.bind(t);let e=[];for(;n.step();)e.push(n.getAsObject());return e}finally{n.free()}}run(e,t=[]){let n=this.getDb(),r=e.trimStart().toUpperCase(),i=!this.inTransaction&&(r.startsWith(`INSERT`)||r.startsWith(`UPDATE`));i&&n.run(`SAVEPOINT fk_check`);try{if(t.length>0){let r=n.prepare(e);try{r.bind(t),r.step()}finally{r.free()}}else n.run(e);if(i){if(n.exec(`PRAGMA foreign_key_check`).length>0)throw n.run(`ROLLBACK TO fk_check`),n.run(`RELEASE fk_check`),Error(`FOREIGN KEY constraint failed`);n.run(`RELEASE fk_check`)}}catch(e){if(i)try{n.run(`ROLLBACK TO fk_check`),n.run(`RELEASE fk_check`)}catch{}throw e}this.dirty=!0}flush(){if(!this.dirty||!this.db)return;let e=this.db.export(),t=`${this.dbPath}.tmp`;o(t,Buffer.from(e)),i(t,this.dbPath),this.dirty=!1}close(){if(this.db){let e=this.db,t;try{this.flush()}catch(e){t=e;try{a(`${this.dbPath}.tmp`)}catch{}}try{e.close()}finally{this.db=null}if(t)throw t}}getDb(){if(!this.db)throw Error(`SqlJsAdapter: database not opened`);return this.db}};let S=!1;async function C(e){let t=new y;try{return await t.open(e),t}catch(e){S||(S=!0,_.warn(`[aikit] better-sqlite3 unavailable — falling back to sql.js (vector search disabled). Reinstall with prebuild support or rebuild from source to enable vector search.`,d(e)))}let n=new x;try{return await n.open(e),n}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`[aikit] SQLite adapter "sql.js" failed to load: ${t}`)}}async function w(e){let t=new x;return await t.open(e),t}var T=h({SqliteVecStore:()=>j});function E(e){let t=0;for(let n=0;n<e.length;n++){let r=e[n]<0?-e[n]:e[n];r>t&&(t=r)}let n=new Int8Array(e.length);if(t===0)return Buffer.from(n.buffer,n.byteOffset,n.byteLength);let r=127/t;for(let t=0;t<e.length;t++){let i=Math.round(e[t]*r);n[t]=i<-127?-127:i>127?127:i}return Buffer.from(n.buffer,n.byteOffset,n.byteLength)}const D=/^[\w.\-/ ]+$/,O=u(`sqlite-vec-store`);function k(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function A(e,t){if(!D.test(e))throw Error(`Invalid ${t} filter value: contains disallowed characters`);return e}var j=class{adapter=null;externalAdapter;dbPath;embeddingDim;coarseDim;vectorEnabled=!1;warnedVectorDisabled=!1;_draining=!1;_priorityQueue=[];_normalQueue=[];_onCloseHooks=[];constructor(e={}){this.embeddingDim=e.embeddingDim??s.dimensions,this.coarseDim=Math.min(128,this.embeddingDim),e.adapter?(this.adapter=e.adapter,this.externalAdapter=!0,this.dbPath=``):(this.dbPath=e.path??`${l.path}/aikit.db`,this.externalAdapter=!1)}async initialize(){if(!this.adapter){let e=p(this.dbPath);t(e)||n(e,{recursive:!0}),this.adapter=await C(this.dbPath)}this.vectorEnabled=this.adapter.vectorCapable,this.createKnowledgeTable(),this.createFtsTable(),this.vectorEnabled?this.ensureVecTable():this.warnedVectorDisabled||(this.warnedVectorDisabled=!0,O.warn(`SqliteVecStore: vector search disabled (sqlite-vec extension not loaded). Hybrid search will return FTS-only results.`))}getDiagnostics(){let e=this.adapter,t=e?e.constructor.name:`unknown`,n=null,r=this.externalAdapter?``:this.dbPath;if(r)try{let{statSync:e}=g(`node:fs`);n=e(r).size}catch{n=null}return{adapterType:t,vectorSearchEnabled:this.vectorEnabled,degradedMode:!this.vectorEnabled,dbPath:r,dbSizeBytes:n,embeddingDim:this.embeddingDim,vectorDtype:`int8`}}createKnowledgeTable(){let e=this.getAdapter();e.exec(`
|
|
2
|
-
CREATE TABLE IF NOT EXISTS knowledge (
|
|
3
|
-
id TEXT PRIMARY KEY,
|
|
4
|
-
content TEXT NOT NULL,
|
|
5
|
-
sourcePath TEXT NOT NULL,
|
|
6
|
-
contentType TEXT NOT NULL,
|
|
7
|
-
headingPath TEXT NOT NULL DEFAULT '',
|
|
8
|
-
chunkIndex INTEGER NOT NULL DEFAULT 0,
|
|
9
|
-
totalChunks INTEGER NOT NULL DEFAULT 1,
|
|
10
|
-
startLine INTEGER NOT NULL DEFAULT 0,
|
|
11
|
-
endLine INTEGER NOT NULL DEFAULT 0,
|
|
12
|
-
fileHash TEXT NOT NULL DEFAULT '',
|
|
13
|
-
content_hash TEXT NOT NULL DEFAULT '',
|
|
14
|
-
indexedAt TEXT NOT NULL,
|
|
15
|
-
origin TEXT NOT NULL DEFAULT 'indexed',
|
|
16
|
-
tags TEXT NOT NULL DEFAULT '[]',
|
|
17
|
-
category TEXT NOT NULL DEFAULT '',
|
|
18
|
-
version INTEGER NOT NULL DEFAULT 1
|
|
19
|
-
)
|
|
20
|
-
`),e.queryAll(`PRAGMA table_info(knowledge)`).some(e=>e.name===`content_hash`)||e.exec(`ALTER TABLE knowledge ADD COLUMN content_hash TEXT NOT NULL DEFAULT ''`),e.exec(`CREATE INDEX IF NOT EXISTS idx_knowledge_sourcePath ON knowledge(sourcePath)`),e.exec(`CREATE INDEX IF NOT EXISTS idx_knowledge_contentType ON knowledge(contentType)`),e.exec(`CREATE INDEX IF NOT EXISTS idx_knowledge_content_hash ON knowledge(content_hash)`),e.exec(`CREATE INDEX IF NOT EXISTS idx_knowledge_origin ON knowledge(origin)`)}createFtsTable(){this.getAdapter().exec(`
|
|
21
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_fts USING fts5(
|
|
22
|
-
id UNINDEXED,
|
|
23
|
-
content,
|
|
24
|
-
tokenize = 'unicode61 remove_diacritics 2'
|
|
25
|
-
)
|
|
26
|
-
`)}ensureVecTable(){let e=this.getAdapter(),t=e.queryAll(`SELECT name, sql FROM sqlite_master WHERE type='table' AND name='vec_knowledge'`);if(t.length>0){let n=t[0].sql??``,r=n.match(/(?:float|int8)\[(\d+)\]/i),i=r?Number(r[1]):null,a=/int8\[/i.test(n);(i!==null&&i!==this.embeddingDim||!a)&&(O.warn(`Vec table schema mismatch — dropping for recreation`,{existingDim:i,newDim:this.embeddingDim,wasInt8:a}),e.exec(`DROP TABLE vec_knowledge`))}e.exec(`
|
|
27
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS vec_knowledge USING vec0(
|
|
28
|
-
embedding int8[${this.embeddingDim}] distance_metric=cosine,
|
|
29
|
-
+knowledge_id TEXT
|
|
30
|
-
)
|
|
31
|
-
`),this.coarseDim<this.embeddingDim&&e.exec(`
|
|
32
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS vec_knowledge_coarse USING vec0(
|
|
33
|
-
embedding int8[${this.coarseDim}] distance_metric=cosine,
|
|
34
|
-
+knowledge_id TEXT
|
|
35
|
-
)
|
|
36
|
-
`)}enqueueWrite(e,t=!1){return new Promise((n,r)=>{let i=async()=>{try{n(await e())}catch(e){r(e)}};t?this._priorityQueue.push(i):this._normalQueue.push(i),this._drain()})}async _drain(){if(!this._draining){this._draining=!0;try{for(;this._priorityQueue.length>0||this._normalQueue.length>0;){let e=this._priorityQueue.shift()??this._normalQueue.shift();e&&await e()}}finally{this._draining=!1}}}async upsert(e,t){if(e.length!==0){if(e.length!==t.length)throw Error(`Record count (${e.length}) does not match vector count (${t.length})`);return this.enqueueWrite(()=>this._upsertImpl(e,t))}}async upsertInteractive(e,t){if(e.length!==0){if(e.length!==t.length)throw Error(`Record count (${e.length}) does not match vector count (${t.length})`);return this.enqueueWrite(()=>this._upsertImpl(e,t),!0)}}async upsertWithoutVector(e,t){return this.enqueueWrite(async()=>{let n=this.getAdapter(),r=e;n.exec(`BEGIN`);try{if(this.upsertKnowledgeRow(r),n.run(`DELETE FROM knowledge_fts WHERE id = ?`,[r.id]),n.run(`INSERT INTO knowledge_fts (id, content) VALUES (?, ?)`,[r.id,r.content]),this.vectorEnabled){let e=n.queryAll(`SELECT embedding FROM vec_knowledge WHERE knowledge_id = ?`,[t]);if(e.length>0){if(n.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[r.id]),n.run(`INSERT INTO vec_knowledge (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[e[0].embedding,r.id]),this.coarseDim<this.embeddingDim){let e=n.queryAll(`SELECT embedding FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[t]);e.length>0&&(n.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[r.id]),n.run(`INSERT INTO vec_knowledge_coarse (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[e[0].embedding,r.id]))}}else O.warn(`upsertWithoutVector: source vector not found, record will lack vector`,{recordId:r.id,sourceRecordId:t})}n.exec(`COMMIT`)}catch(e){try{n.exec(`ROLLBACK`)}catch{}throw e}n.flush()})}upsertKnowledgeRow(e){this.getAdapter().run(`INSERT INTO knowledge (id, content, sourcePath, contentType, headingPath, chunkIndex,
|
|
37
|
-
totalChunks, startLine, endLine, fileHash, content_hash, indexedAt, origin, tags, category, version)
|
|
38
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
39
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
40
|
-
content = excluded.content,
|
|
41
|
-
sourcePath = excluded.sourcePath,
|
|
42
|
-
contentType = excluded.contentType,
|
|
43
|
-
headingPath = excluded.headingPath,
|
|
44
|
-
chunkIndex = excluded.chunkIndex,
|
|
45
|
-
totalChunks = excluded.totalChunks,
|
|
46
|
-
startLine = excluded.startLine,
|
|
47
|
-
endLine = excluded.endLine,
|
|
48
|
-
fileHash = excluded.fileHash,
|
|
49
|
-
content_hash = excluded.content_hash,
|
|
50
|
-
indexedAt = excluded.indexedAt,
|
|
51
|
-
origin = excluded.origin,
|
|
52
|
-
tags = excluded.tags,
|
|
53
|
-
category = excluded.category,
|
|
54
|
-
version = excluded.version`,[e.id,e.content,e.sourcePath,e.contentType,e.headingPath??``,e.chunkIndex,e.totalChunks,e.startLine,e.endLine,e.fileHash,e.contentHash??``,e.indexedAt,e.origin,JSON.stringify(e.tags??[]),e.category??``,e.version])}async _upsertImpl(e,t){let n=this.getAdapter();n.exec(`BEGIN`);try{for(let r=0;r<e.length;r++){let i=e[r];if(this.upsertKnowledgeRow(i),n.run(`DELETE FROM knowledge_fts WHERE id = ?`,[i.id]),n.run(`INSERT INTO knowledge_fts (id, content) VALUES (?, ?)`,[i.id,i.content]),this.vectorEnabled&&(n.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[i.id]),n.run(`INSERT INTO vec_knowledge (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[E(t[r]),i.id]),this.coarseDim<this.embeddingDim)){let e=t[r].subarray(0,this.coarseDim);n.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[i.id]),n.run(`INSERT INTO vec_knowledge_coarse (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[E(e),i.id])}}n.exec(`COMMIT`)}catch(e){try{n.exec(`ROLLBACK`)}catch{}throw e}n.flush()}async search(e,t){if(!this.vectorEnabled)return this.warnedVectorDisabled||(this.warnedVectorDisabled=!0,O.warn(`search() called but vector backend is disabled — returning []`)),[];let n=this.getAdapter(),r=t?.limit??c.maxResults,i=t?.minScore??c.minScore,a=r*4,o=`
|
|
55
|
-
SELECT k.*, v.distance AS _distance
|
|
56
|
-
FROM (
|
|
57
|
-
SELECT knowledge_id, distance
|
|
58
|
-
FROM vec_knowledge
|
|
59
|
-
WHERE embedding MATCH vec_int8(?)
|
|
60
|
-
ORDER BY distance
|
|
61
|
-
LIMIT ?
|
|
62
|
-
) v
|
|
63
|
-
JOIN knowledge k ON k.id = v.knowledge_id
|
|
64
|
-
${this.buildFilterSqlSuffix(t)}
|
|
65
|
-
ORDER BY v.distance ASC
|
|
66
|
-
LIMIT ?
|
|
67
|
-
`,s;try{s=n.queryAll(o,[E(e),a,r])}catch(e){return O.warn(`vector search failed`,d(e)),[]}return s.map(e=>({record:this.fromRow(e),score:1-(e._distance??1)})).filter(e=>e.score>=i).slice(0,r)}async coarseSearch(e,t){if(!this.vectorEnabled||this.coarseDim>=this.embeddingDim)return this.search(e,t);let n=this.getAdapter(),r=t?.limit??c.maxResults,i=t?.minScore??c.minScore,a=r*4,o=e.subarray(0,this.coarseDim),s=`
|
|
68
|
-
SELECT k.*, v.distance AS _distance
|
|
69
|
-
FROM (
|
|
70
|
-
SELECT knowledge_id, distance
|
|
71
|
-
FROM vec_knowledge_coarse
|
|
72
|
-
WHERE embedding MATCH vec_int8(?)
|
|
73
|
-
ORDER BY distance
|
|
74
|
-
LIMIT ?
|
|
75
|
-
) v
|
|
76
|
-
JOIN knowledge k ON k.id = v.knowledge_id
|
|
77
|
-
${this.buildFilterSqlSuffix(t)}
|
|
78
|
-
ORDER BY v.distance ASC
|
|
79
|
-
LIMIT ?
|
|
80
|
-
`,l;try{l=n.queryAll(s,[E(o),a,r])}catch(n){return O.warn(`coarse vector search failed, falling back to full search`,d(n)),this.search(e,t)}return l.length===0?this.search(e,t):l.map(e=>({record:this.fromRow(e),score:1-(e._distance??1)})).filter(e=>e.score>=i).slice(0,r)}async ftsSearch(e,t){if(!e||e.trim().length===0)return[];let n=this.getAdapter(),r=t?.limit??c.maxResults,i=`
|
|
81
|
-
SELECT k.*, bm25(knowledge_fts) AS _bm25
|
|
82
|
-
FROM knowledge_fts
|
|
83
|
-
JOIN knowledge k ON k.id = knowledge_fts.id
|
|
84
|
-
WHERE knowledge_fts MATCH ?
|
|
85
|
-
${this.buildFilterSqlSuffix(t,!0)}
|
|
86
|
-
ORDER BY _bm25 ASC
|
|
87
|
-
LIMIT ?
|
|
88
|
-
`,a;try{let t=M(e);a=n.queryAll(i,[t,r])}catch(e){return O.warn(`fts search failed`,d(e)),[]}return a.map(e=>({record:this.fromRow(e),score:N(e._bm25)}))}async getById(e){let t=this.getAdapter().queryAll(`SELECT * FROM knowledge WHERE id = ? LIMIT 1`,[e]);return t.length===0?null:this.fromRow(t[0])}async deleteBySourcePath(e){return this.enqueueWrite(()=>this._deleteBySourcePathImpl(e))}async _deleteBySourcePathImpl(e){let t=this.getAdapter(),n=t.queryAll(`SELECT id FROM knowledge WHERE sourcePath = ?`,[e]);if(n.length===0)return 0;t.exec(`BEGIN`);try{for(let{id:e}of n)this.vectorEnabled&&(t.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[e]),this.coarseDim<this.embeddingDim&&t.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[e])),t.run(`DELETE FROM knowledge_fts WHERE id = ?`,[e]);t.run(`DELETE FROM knowledge WHERE sourcePath = ?`,[e]),t.exec(`COMMIT`)}catch(e){try{t.exec(`ROLLBACK`)}catch{}throw e}return t.flush(),n.length}async deleteById(e){return this.enqueueWrite(()=>this._deleteByIdImpl(e))}async deleteByIdInteractive(e){return this.enqueueWrite(()=>this._deleteByIdImpl(e),!0)}async _deleteByIdImpl(e){let t=this.getAdapter();if(t.queryAll(`SELECT id FROM knowledge WHERE id = ? LIMIT 1`,[e]).length===0)return!1;t.exec(`BEGIN`);try{this.vectorEnabled&&(t.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[e]),this.coarseDim<this.embeddingDim&&t.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[e])),t.run(`DELETE FROM knowledge_fts WHERE id = ?`,[e]),t.run(`DELETE FROM knowledge WHERE id = ?`,[e]),t.exec(`COMMIT`)}catch(e){try{t.exec(`ROLLBACK`)}catch{}throw e}return t.flush(),!0}async getBySourcePath(e){return this.getAdapter().queryAll(`SELECT * FROM knowledge WHERE sourcePath = ? ORDER BY chunkIndex ASC`,[e]).map(e=>this.fromRow(e))}async getStats(){let e=this.getAdapter(),t=e.queryAll(`SELECT COUNT(*) AS n FROM knowledge`)[0]?.n??0;if(t===0)return{totalRecords:0,totalFiles:0,contentTypeBreakdown:{},lastIndexedAt:null,storeBackend:`sqlite-vec`,embeddingModel:s.model};let n=e.queryAll(`SELECT COUNT(DISTINCT sourcePath) AS n FROM knowledge`),r=e.queryAll(`SELECT contentType, COUNT(*) AS n FROM knowledge GROUP BY contentType`),i=e.queryAll(`SELECT MAX(indexedAt) AS ts FROM knowledge`),a={};for(let e of r)a[e.contentType]=e.n;return{totalRecords:t,totalFiles:n[0]?.n??0,contentTypeBreakdown:a,lastIndexedAt:i[0]?.ts??null,storeBackend:`sqlite-vec`,embeddingModel:s.model}}async listSourcePaths(){return this.getAdapter().queryAll(`SELECT DISTINCT sourcePath FROM knowledge ORDER BY sourcePath`).map(e=>e.sourcePath)}async createFtsIndex(){this.createFtsTable()}async dropTable(){return this.enqueueWrite(()=>this._dropTableImpl())}async _dropTableImpl(){let e=this.getAdapter();e.exec(`DROP TABLE IF EXISTS knowledge_fts`),this.vectorEnabled&&(this.coarseDim<this.embeddingDim&&e.exec(`DROP TABLE IF EXISTS vec_knowledge_coarse`),e.exec(`DROP TABLE IF EXISTS vec_knowledge`)),e.exec(`DROP TABLE IF EXISTS knowledge`),e.flush(),this.createKnowledgeTable(),this.createFtsTable(),this.vectorEnabled&&this.ensureVecTable()}releaseMemory(){if(this.adapter)try{this.adapter.exec(`PRAGMA shrink_memory`),this.adapter.exec(`PRAGMA wal_checkpoint(TRUNCATE)`)}catch{}}onBeforeClose(e){this._onCloseHooks.push(e)}async close(){for(let e of this._onCloseHooks)try{e()}catch{}for(this._onCloseHooks.length=0;this._priorityQueue.length>0||this._normalQueue.length>0||this._draining;)await new Promise(e=>setTimeout(e,5));this.adapter&&!this.externalAdapter&&this.adapter.close(),this.adapter=null}getAdapter(){if(!this.adapter)throw Error(`SqliteVecStore: not initialized — call initialize() first`);return this.adapter}buildFilterSqlSuffix(e,t=!1){if(!e)return``;let n=[];if(e.contentType&&n.push(`k.contentType = '${A(e.contentType,`contentType`)}'`),e.sourceType){let t=f(e.sourceType);if(t.length>0){let e=t.map(e=>`'${A(e,`sourceType`)}'`).join(`, `);n.push(`k.contentType IN (${e})`)}}if(e.origin&&n.push(`k.origin = '${A(e.origin,`origin`)}'`),e.category&&n.push(`k.category = '${A(e.category,`category`)}'`),e.tags&&e.tags.length>0){let t=e.tags.map(e=>`k.tags LIKE '%' || '${A(e,`tag`)}' || '%'`);n.push(`(${t.join(` OR `)})`)}if(n.length===0)return``;let r=n.join(` AND `);return t?`AND ${r}`:`WHERE ${r}`}fromRow(e){return{id:e.id,content:e.content,sourcePath:e.sourcePath,contentType:e.contentType,headingPath:e.headingPath||void 0,chunkIndex:e.chunkIndex,totalChunks:e.totalChunks,startLine:e.startLine,endLine:e.endLine,fileHash:e.fileHash,contentHash:e.content_hash||void 0,indexedAt:e.indexedAt,origin:e.origin,tags:k(e.tags),category:e.category||void 0,version:e.version}}};function M(e){let t=e.replace(/["'()*:^-]/g,` `).trim();return t?t.split(/\s+/).filter(e=>e.length>0).map(e=>`"${e}"`).join(` OR `):`""`}function N(e){return e==null||Number.isNaN(e)?0:1-Math.exp(-Math.abs(e)/5)}export{C as i,T as n,w as r,j as t};
|