@@ -353,6 +353,51 @@ export function initializeSmritiTables(db: Database): void {
353353 PRIMARY KEY (language, version, framework)
354354 );
355355
356+ -- Artifacts from Claude.ai conversations (code, documents, diagrams)
357+ CREATE TABLE IF NOT EXISTS smriti_artifacts (
358+ id INTEGER PRIMARY KEY AUTOINCREMENT,
359+ message_id INTEGER NOT NULL,
360+ session_id TEXT NOT NULL,
361+ artifact_id TEXT,
362+ type TEXT,
363+ title TEXT,
364+ command TEXT,
365+ language TEXT,
366+ content TEXT,
367+ created_at TEXT NOT NULL
368+ );
369+
370+ -- Thinking blocks (Claude's internal reasoning)
371+ CREATE TABLE IF NOT EXISTS smriti_thinking (
372+ id INTEGER PRIMARY KEY AUTOINCREMENT,
373+ message_id INTEGER NOT NULL,
374+ session_id TEXT NOT NULL,
375+ thinking TEXT NOT NULL,
376+ created_at TEXT NOT NULL
377+ );
378+
379+ -- File attachments with extracted content
380+ CREATE TABLE IF NOT EXISTS smriti_attachments (
381+ id INTEGER PRIMARY KEY AUTOINCREMENT,
382+ message_id INTEGER NOT NULL,
383+ session_id TEXT NOT NULL,
384+ file_name TEXT,
385+ file_type TEXT,
386+ file_size INTEGER,
387+ content TEXT,
388+ created_at TEXT NOT NULL
389+ );
390+
391+ -- Voice note transcripts
392+ CREATE TABLE IF NOT EXISTS smriti_voice_notes (
393+ id INTEGER PRIMARY KEY AUTOINCREMENT,
394+ message_id INTEGER NOT NULL,
395+ session_id TEXT NOT NULL,
396+ title TEXT,
397+ transcript TEXT,
398+ created_at TEXT NOT NULL
399+ );
400+
356401 -- Indexes (original)
357402 CREATE INDEX IF NOT EXISTS idx_smriti_session_meta_agent
358403 ON smriti_session_meta(agent_id);
@@ -390,6 +435,18 @@ export function initializeSmritiTables(db: Database): void {
390435 ON smriti_git_operations(operation);
391436 CREATE INDEX IF NOT EXISTS idx_smriti_rule_cache_language
392437 ON smriti_rule_cache(language);
438+
439+ -- Indexes (claude-web sidecar tables)
440+ CREATE INDEX IF NOT EXISTS idx_smriti_artifacts_session
441+ ON smriti_artifacts(session_id);
442+ CREATE INDEX IF NOT EXISTS idx_smriti_artifacts_type
443+ ON smriti_artifacts(type);
444+ CREATE INDEX IF NOT EXISTS idx_smriti_thinking_session
445+ ON smriti_thinking(session_id);
446+ CREATE INDEX IF NOT EXISTS idx_smriti_attachments_session
447+ ON smriti_attachments(session_id);
448+ CREATE INDEX IF NOT EXISTS idx_smriti_voice_notes_session
449+ ON smriti_voice_notes(session_id);
393450 ` ) ;
394451}
395452
@@ -429,6 +486,18 @@ const DEFAULT_AGENTS = [
429486 log_pattern : "*/chatSessions/*.json" ,
430487 parser : "copilot" ,
431488 } ,
489+ {
490+ id : "generic" ,
491+ display_name : "Generic Import" ,
492+ log_pattern : null ,
493+ parser : "generic" ,
494+ } ,
495+ {
496+ id : "claude-web" ,
497+ display_name : "Claude.ai" ,
498+ log_pattern : null ,
499+ parser : "claude-web" ,
500+ } ,
432501] as const ;
433502
434503/** Default category taxonomy */
@@ -503,6 +572,87 @@ export function seedDefaults(db: Database): void {
503572 }
504573}
505574
575+ // =============================================================================
576+ // FTS Migration (sidecar content search)
577+ // =============================================================================
578+
579+ /**
580+ * Migrate memory_fts to v2: adds thinking, artifacts, attachments, voice_notes columns.
581+ * Drops old FTS table + triggers, rebuilds index from existing data.
582+ * Idempotent — skips if already migrated.
583+ */
584+ export function migrateFTSToV2 ( db : Database ) : void {
585+ // Check if migration needed by looking for the 'thinking' column
586+ const cols = db . prepare ( "PRAGMA table_info(memory_fts)" ) . all ( ) as { name : string } [ ] ;
587+ if ( cols . some ( ( c ) => c . name === "thinking" ) ) return ;
588+
589+ console . log ( "Migrating memory_fts to include sidecar content..." ) ;
590+
591+ // 1. Drop old triggers
592+ db . exec ( `DROP TRIGGER IF EXISTS memory_messages_ai` ) ;
593+ db . exec ( `DROP TRIGGER IF EXISTS memory_messages_ad` ) ;
594+
595+ // 2. Drop old FTS table
596+ db . exec ( `DROP TABLE IF EXISTS memory_fts` ) ;
597+
598+ // 3. Create new FTS table with sidecar columns
599+ db . exec ( `
600+ CREATE VIRTUAL TABLE memory_fts USING fts5(
601+ session_title, role, content,
602+ thinking, artifacts, attachments, voice_notes,
603+ tokenize='porter unicode61'
604+ )
605+ ` ) ;
606+
607+ // 4. Rebuild index from existing messages + sidecar data
608+ db . exec ( `
609+ INSERT INTO memory_fts(
610+ rowid, session_title, role, content,
611+ thinking, artifacts, attachments, voice_notes
612+ )
613+ SELECT
614+ mm.id,
615+ COALESCE(ms.title, ''),
616+ mm.role,
617+ mm.content,
618+ COALESCE((SELECT GROUP_CONCAT(thinking, ' ') FROM smriti_thinking WHERE message_id = mm.id), ''),
619+ COALESCE((SELECT GROUP_CONCAT(content, ' ') FROM smriti_artifacts WHERE message_id = mm.id), ''),
620+ COALESCE((SELECT GROUP_CONCAT(content, ' ') FROM smriti_attachments WHERE message_id = mm.id), ''),
621+ COALESCE((SELECT GROUP_CONCAT(transcript, ' ') FROM smriti_voice_notes WHERE message_id = mm.id), '')
622+ FROM memory_messages mm
623+ LEFT JOIN memory_sessions ms ON ms.id = mm.session_id
624+ ` ) ;
625+
626+ // 5. Create new triggers with sidecar columns
627+ db . exec ( `
628+ CREATE TRIGGER memory_messages_ai AFTER INSERT ON memory_messages
629+ BEGIN
630+ INSERT INTO memory_fts(
631+ rowid, session_title, role, content,
632+ thinking, artifacts, attachments, voice_notes
633+ )
634+ SELECT
635+ new.id,
636+ COALESCE((SELECT title FROM memory_sessions WHERE id = new.session_id), ''),
637+ new.role,
638+ new.content,
639+ COALESCE((SELECT GROUP_CONCAT(thinking, ' ') FROM smriti_thinking WHERE message_id = new.id), ''),
640+ COALESCE((SELECT GROUP_CONCAT(content, ' ') FROM smriti_artifacts WHERE message_id = new.id), ''),
641+ COALESCE((SELECT GROUP_CONCAT(content, ' ') FROM smriti_attachments WHERE message_id = new.id), ''),
642+ COALESCE((SELECT GROUP_CONCAT(transcript, ' ') FROM smriti_voice_notes WHERE message_id = new.id), '');
643+ END
644+ ` ) ;
645+
646+ db . exec ( `
647+ CREATE TRIGGER memory_messages_ad AFTER DELETE ON memory_messages
648+ BEGIN
649+ DELETE FROM memory_fts WHERE rowid = old.id;
650+ END
651+ ` ) ;
652+
653+ console . log ( "Migration complete." ) ;
654+ }
655+
506656// =============================================================================
507657// Convenience
508658// =============================================================================
@@ -514,6 +664,7 @@ export function initSmriti(dbPath?: string): Database {
514664 // so we just need to initialize Smriti tables
515665 initializeSmritiTables ( db ) ;
516666 seedDefaults ( db ) ;
667+ migrateFTSToV2 ( db ) ;
517668 return db ;
518669}
519670
@@ -802,3 +953,68 @@ export function insertGitOperation(
802953 VALUES(?, ?, ?, ?, ?, ?, ?, ?)`
803954 ) . run ( messageId , sessionId , operation , branch , prUrl , prNumber , details , createdAt ) ;
804955}
956+
957+ // =============================================================================
958+ // Claude-Web Sidecar Insert Helpers
959+ // =============================================================================
960+
961+ export function insertArtifact (
962+ db : Database ,
963+ messageId : number ,
964+ sessionId : string ,
965+ artifactId : string | null ,
966+ type : string | null ,
967+ title : string | null ,
968+ command : string | null ,
969+ language : string | null ,
970+ content : string | null ,
971+ createdAt : string
972+ ) : void {
973+ db . prepare (
974+ `INSERT INTO smriti_artifacts (message_id, session_id, artifact_id, type, title, command, language, content, created_at)
975+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
976+ ) . run ( messageId , sessionId , artifactId , type , title , command , language , content , createdAt ) ;
977+ }
978+
979+ export function insertThinking (
980+ db : Database ,
981+ messageId : number ,
982+ sessionId : string ,
983+ thinking : string ,
984+ createdAt : string
985+ ) : void {
986+ db . prepare (
987+ `INSERT INTO smriti_thinking (message_id, session_id, thinking, created_at)
988+ VALUES (?, ?, ?, ?)`
989+ ) . run ( messageId , sessionId , thinking , createdAt ) ;
990+ }
991+
992+ export function insertAttachment (
993+ db : Database ,
994+ messageId : number ,
995+ sessionId : string ,
996+ fileName : string | null ,
997+ fileType : string | null ,
998+ fileSize : number | null ,
999+ content : string | null ,
1000+ createdAt : string
1001+ ) : void {
1002+ db . prepare (
1003+ `INSERT INTO smriti_attachments (message_id, session_id, file_name, file_type, file_size, content, created_at)
1004+ VALUES (?, ?, ?, ?, ?, ?, ?)`
1005+ ) . run ( messageId , sessionId , fileName , fileType , fileSize , content , createdAt ) ;
1006+ }
1007+
1008+ export function insertVoiceNote (
1009+ db : Database ,
1010+ messageId : number ,
1011+ sessionId : string ,
1012+ title : string | null ,
1013+ transcript : string ,
1014+ createdAt : string
1015+ ) : void {
1016+ db . prepare (
1017+ `INSERT INTO smriti_voice_notes (message_id, session_id, title, transcript, created_at)
1018+ VALUES (?, ?, ?, ?, ?)`
1019+ ) . run ( messageId , sessionId , title , transcript , createdAt ) ;
1020+ }
0 commit comments