@@ -3,7 +3,6 @@ import { randomUUID } from 'crypto';
33import type {
44 SkillsStore ,
55 SkillCreate ,
6- SkillImport ,
76 SkillPatch ,
87 SkillRecord ,
98 SkillDetail ,
@@ -69,21 +68,51 @@ export class SqliteSkillsStore implements SkillsStore {
6968
7069 create ( data : SkillCreate , embedding : number [ ] ) : SkillRecord {
7170 assertEmbeddingDim ( embedding , this . embeddingDim ) ;
72- const slug = randomUUID ( ) ;
71+ const slug = data . slug ?? randomUUID ( ) ;
7372 const ts = now ( ) ;
7473 const authorId = data . authorId ?? null ;
74+ const version = data . version ?? 1 ;
75+ const createdAt = data . createdAt ?? ts ;
76+ const updatedAt = data . updatedAt ?? ts ;
77+
78+ // Upsert when slug is provided and already exists
79+ if ( data . slug ) {
80+ const existing = this . db . prepare ( 'SELECT * FROM skills WHERE slug = ? AND project_id = ?' ) . get ( slug , this . projectId ) as Record < string , unknown > | undefined ;
81+ if ( existing ) {
82+ const id = num ( existing . id as bigint ) ;
83+ this . db . prepare ( `
84+ UPDATE skills SET title = ?, description = ?,
85+ steps_json = ?, triggers_json = ?, input_hints_json = ?, file_patterns_json = ?,
86+ source = ?, confidence = ?, usage_count = ?, last_used_at = ?,
87+ version = ?, updated_at = ?
88+ WHERE id = ? AND project_id = ?
89+ ` ) . run (
90+ data . title , data . description ?? '' ,
91+ JSON . stringify ( data . steps ?? [ ] ) , JSON . stringify ( data . triggers ?? [ ] ) ,
92+ JSON . stringify ( data . inputHints ?? [ ] ) , JSON . stringify ( data . filePatterns ?? [ ] ) ,
93+ data . source ?? 'user' , data . confidence ?? 1.0 ,
94+ data . usageCount ?? 0 , data . lastUsedAt ?? null ,
95+ version , updatedAt , id , this . projectId ,
96+ ) ;
97+ this . db . prepare ( 'DELETE FROM skills_vec WHERE rowid = ?' ) . run ( BigInt ( id ) ) ;
98+ this . db . prepare ( 'INSERT INTO skills_vec (rowid, embedding) VALUES (?, ?)' ) . run ( BigInt ( id ) , Buffer . from ( new Float32Array ( embedding ) . buffer ) ) ;
99+ if ( data . tags ) this . helpers . setTags ( GRAPH , id , data . tags ) ;
100+ return this . toRecord ( this . db . prepare ( 'SELECT * FROM skills WHERE id = ? AND project_id = ?' ) . get ( id , this . projectId ) as Record < string , unknown > ) ;
101+ }
102+ }
75103
76104 const result = this . db . prepare ( `
77- INSERT INTO skills (project_id, slug, title, description, steps_json, triggers_json, input_hints_json, file_patterns_json, source, confidence, version, created_by_id, updated_by_id, created_at, updated_at)
78- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1 , ?, ?, ?, ?)
105+ INSERT INTO skills (project_id, slug, title, description, steps_json, triggers_json, input_hints_json, file_patterns_json, source, confidence, usage_count, last_used_at, version, created_by_id, updated_by_id, created_at, updated_at)
106+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? , ?, ?, ?, ?)
79107 ` ) . run (
80108 this . projectId , slug , data . title , data . description ?? '' ,
81109 JSON . stringify ( data . steps ?? [ ] ) ,
82110 JSON . stringify ( data . triggers ?? [ ] ) ,
83111 JSON . stringify ( data . inputHints ?? [ ] ) ,
84112 JSON . stringify ( data . filePatterns ?? [ ] ) ,
85113 data . source ?? 'user' , data . confidence ?? 1.0 ,
86- authorId , authorId , ts , ts ,
114+ data . usageCount ?? 0 , data . lastUsedAt ?? null ,
115+ version , authorId , authorId , createdAt , updatedAt ,
87116 ) ;
88117 const id = result . lastInsertRowid ;
89118
@@ -199,56 +228,6 @@ export class SqliteSkillsStore implements SkillsStore {
199228 return row ? num ( row . updated_at ) : null ;
200229 }
201230
202- importRecord ( data : SkillImport , embedding : number [ ] ) : SkillRecord {
203- assertEmbeddingDim ( embedding , this . embeddingDim ) ;
204-
205- const existing = this . db . prepare ( 'SELECT * FROM skills WHERE slug = ? AND project_id = ?' ) . get ( data . slug , this . projectId ) as Record < string , unknown > | undefined ;
206-
207- if ( existing ) {
208- const id = num ( existing . id as bigint ) ;
209- this . db . prepare ( `
210- UPDATE skills SET title = ?, description = ?,
211- steps_json = ?, triggers_json = ?, input_hints_json = ?, file_patterns_json = ?,
212- source = ?, confidence = ?, usage_count = ?, last_used_at = ?,
213- version = ?, updated_at = ?
214- WHERE id = ? AND project_id = ?
215- ` ) . run (
216- data . title , data . description ,
217- JSON . stringify ( data . steps ?? [ ] ) , JSON . stringify ( data . triggers ?? [ ] ) ,
218- JSON . stringify ( data . inputHints ?? [ ] ) , JSON . stringify ( data . filePatterns ?? [ ] ) ,
219- data . source ?? 'user' , data . confidence ?? 1.0 ,
220- data . usageCount ?? 0 , data . lastUsedAt ?? null ,
221- data . version , data . updatedAt , id , this . projectId ,
222- ) ;
223-
224- this . db . prepare ( 'DELETE FROM skills_vec WHERE rowid = ?' ) . run ( BigInt ( id ) ) ;
225- this . db . prepare ( 'INSERT INTO skills_vec (rowid, embedding) VALUES (?, ?)' ) . run ( BigInt ( id ) , Buffer . from ( new Float32Array ( embedding ) . buffer ) ) ;
226-
227- if ( data . tags ) this . helpers . setTags ( GRAPH , id , data . tags ) ;
228-
229- return this . toRecord ( this . db . prepare ( 'SELECT * FROM skills WHERE id = ? AND project_id = ?' ) . get ( id , this . projectId ) as Record < string , unknown > ) ;
230- }
231-
232- const result = this . db . prepare ( `
233- INSERT INTO skills (project_id, slug, title, description, steps_json, triggers_json, input_hints_json, file_patterns_json, source, confidence, usage_count, last_used_at, version, created_at, updated_at)
234- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
235- ` ) . run (
236- this . projectId , data . slug , data . title , data . description ,
237- JSON . stringify ( data . steps ?? [ ] ) , JSON . stringify ( data . triggers ?? [ ] ) ,
238- JSON . stringify ( data . inputHints ?? [ ] ) , JSON . stringify ( data . filePatterns ?? [ ] ) ,
239- data . source ?? 'user' , data . confidence ?? 1.0 ,
240- data . usageCount ?? 0 , data . lastUsedAt ?? null ,
241- data . version , data . createdAt , data . updatedAt ,
242- ) ;
243- const id = num ( result . lastInsertRowid ) ;
244-
245- this . db . prepare ( 'INSERT INTO skills_vec (rowid, embedding) VALUES (?, ?)' ) . run ( BigInt ( id ) , Buffer . from ( new Float32Array ( embedding ) . buffer ) ) ;
246-
247- if ( data . tags && data . tags . length > 0 ) this . helpers . setTags ( GRAPH , id , data . tags ) ;
248-
249- return this . toRecord ( this . db . prepare ( 'SELECT * FROM skills WHERE id = ? AND project_id = ?' ) . get ( id , this . projectId ) as Record < string , unknown > ) ;
250- }
251-
252231 getMeta ( key : string ) : string | null { return this . meta . getMeta ( key ) ; }
253232 setMeta ( key : string , value : string ) : void { this . meta . setMeta ( key , value ) ; }
254233 deleteMeta ( key : string ) : void { this . meta . deleteMeta ( key ) ; }
0 commit comments