11import { readFile , readdir , writeFile , mkdir , rm , stat } from "node:fs/promises" ;
2- import { join , basename } from "node:path" ;
2+ import { join , basename , resolve , dirname } from "node:path" ;
33import { existsSync } from "node:fs" ;
44import { createHash } from "node:crypto" ;
55import * as lancedb from "@lancedb/lancedb" ;
@@ -15,6 +15,35 @@ import {
1515 MemoryMCPError ,
1616} from "./types.js" ;
1717
18+ interface SteeringTarget {
19+ dir : string ;
20+ buildContent : ( body : string ) => string ;
21+ }
22+
23+ function detectSteeringTargets ( workspaceRoot : string ) : SteeringTarget [ ] {
24+ const targets : SteeringTarget [ ] = [ ] ;
25+
26+ const kiroSteering = join ( workspaceRoot , ".kiro" , "steering" ) ;
27+ if ( existsSync ( kiroSteering ) ) {
28+ targets . push ( {
29+ dir : kiroSteering ,
30+ buildContent : ( body ) =>
31+ `---\ninclusion: always\nname: team-memories-index\n---\n\n${ body } \n` ,
32+ } ) ;
33+ }
34+
35+ const cursorRules = join ( workspaceRoot , ".cursor" , "rules" ) ;
36+ if ( existsSync ( cursorRules ) ) {
37+ targets . push ( {
38+ dir : cursorRules ,
39+ buildContent : ( body ) =>
40+ `---\ndescription: "Team memories index — active knowledge base"\nalwaysApply: true\n---\n\n${ body } \n` ,
41+ } ) ;
42+ }
43+
44+ return targets ;
45+ }
46+
1847function vectorRecord ( entry : MemoryEntry , vector : number [ ] , contentHash : string ) : Record < string , unknown > {
1948 return {
2049 id : entry . id ,
@@ -31,6 +60,7 @@ function vectorRecord(entry: MemoryEntry, vector: number[], contentHash: string)
3160
3261export class MemoryStore {
3362 private memoriesPath : string ;
63+ private workspaceRoot : string ;
3464 private catalog : MemoryEntry [ ] = [ ] ;
3565 private embeddings : EmbeddingEngine ;
3666 private db : lancedb . Connection | null = null ;
@@ -40,9 +70,19 @@ export class MemoryStore {
4070
4171 constructor ( memoriesPath : string , embeddingModel ?: string ) {
4272 this . memoriesPath = memoriesPath ;
73+ this . workspaceRoot = this . resolveWorkspaceRoot ( memoriesPath ) ;
4374 this . embeddings = new EmbeddingEngine ( embeddingModel ) ;
4475 }
4576
77+ private resolveWorkspaceRoot ( memoriesPath : string ) : string {
78+ const abs = resolve ( memoriesPath ) ;
79+ const parent = dirname ( abs ) ;
80+ if ( existsSync ( join ( parent , ".kiro" ) ) || existsSync ( join ( parent , ".cursor" ) ) || existsSync ( join ( parent , ".git" ) ) ) {
81+ return parent ;
82+ }
83+ return parent ;
84+ }
85+
4686 async load ( ) : Promise < void > {
4787 this . loadingPromise = this . doLoad ( ) ;
4888 return this . loadingPromise ;
@@ -93,6 +133,58 @@ export class MemoryStore {
93133 }
94134
95135 this . loaded = true ;
136+
137+ await this . syncSteeringIndex ( ) ;
138+ }
139+
140+ async syncSteeringIndex ( ) : Promise < void > {
141+ try {
142+ const targets = detectSteeringTargets ( this . workspaceRoot ) ;
143+ if ( targets . length === 0 ) return ;
144+
145+ const active = this . catalog . filter ( ( m ) => m . status === "active" ) ;
146+ const grouped = new Map < string , MemoryEntry [ ] > ( ) ;
147+
148+ for ( const entry of active ) {
149+ const list = grouped . get ( entry . category ) || [ ] ;
150+ list . push ( entry ) ;
151+ grouped . set ( entry . category , list ) ;
152+ }
153+
154+ const lines : string [ ] = [
155+ "# Team Memories Index" ,
156+ "" ,
157+ `Total: ${ active . length } active memories. Use \`get_memory(id)\` to read full content.` ,
158+ "" ,
159+ ] ;
160+
161+ for ( const category of VALID_CATEGORIES ) {
162+ const entries = grouped . get ( category ) ;
163+ if ( ! entries || entries . length === 0 ) continue ;
164+
165+ lines . push ( `## ${ category } (${ entries . length } )` ) ;
166+ lines . push ( "" ) ;
167+
168+ for ( const entry of entries ) {
169+ const tags = entry . tags . length > 0 ? ` [${ entry . tags . join ( ", " ) } ]` : "" ;
170+ lines . push ( `- **${ entry . title } **${ tags } → \`${ entry . id } \`` ) ;
171+ }
172+
173+ lines . push ( "" ) ;
174+ }
175+
176+ const body = lines . join ( "\n" ) ;
177+
178+ for ( const target of targets ) {
179+ const filePath = join ( target . dir , "team-memories-index.md" ) ;
180+ const content = target . buildContent ( body ) ;
181+ await writeFile ( filePath , content , "utf-8" ) ;
182+ }
183+
184+ console . error ( `Synced steering index (${ active . length } memories, ${ targets . length } target(s))` ) ;
185+ } catch ( error ) {
186+ console . error ( `Failed to sync steering index: ${ error instanceof Error ? error . message : error } ` ) ;
187+ }
96188 }
97189
98190 private async syncVectorStore ( ) : Promise < void > {
@@ -364,6 +456,8 @@ export class MemoryStore {
364456 }
365457 }
366458
459+ await this . syncSteeringIndex ( ) ;
460+
367461 return entry ;
368462 }
369463
@@ -385,6 +479,8 @@ export class MemoryStore {
385479 await this . table . update ( { where : `id = '${ id } '` , values : { status : "archived" } } ) ;
386480 }
387481
482+ await this . syncSteeringIndex ( ) ;
483+
388484 return entry ;
389485 }
390486
@@ -400,6 +496,8 @@ export class MemoryStore {
400496 if ( this . table ) {
401497 await this . table . delete ( `id = '${ id } '` ) ;
402498 }
499+
500+ await this . syncSteeringIndex ( ) ;
403501 }
404502
405503 private toCatalogEntry ( entry : MemoryEntry ) : MemoryCatalogEntry {
0 commit comments