Skip to content

feat(memory): auto-sync steering index on memory changes#46

Merged
Joao208 merged 1 commit into
mainfrom
joaobarros-/-memory-mcp-steering-index
Apr 15, 2026
Merged

feat(memory): auto-sync steering index on memory changes#46
Joao208 merged 1 commit into
mainfrom
joaobarros-/-memory-mcp-steering-index

Conversation

@Joao208

@Joao208 Joao208 commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

O que muda

O MCP de memory agora gera e mantém automaticamente um steering file (team-memories-index.md) com o índice de todas as memórias ativas.

Motivação

Antes, o model precisava chamar search_memories via MCP toda interação pra descobrir quais memórias existiam — gastando tokens e latência em round-trips. Agora o índice já está no contexto do model via steering file, e ele só precisa chamar get_memory(id) quando quiser o conteúdo completo.

Como funciona

  • O índice é sincronizado automaticamente em:
    • store.load() (startup do MCP)
    • add_memory (nova memória)
    • archive_memory (arquivou)
    • remove_memory (deletou)
  • Detecção automática de editor — escreve em .kiro/steering/ e/ou .cursor/rules/ baseado em quais diretórios existem
  • Fallback graceful — se não conseguir escrever, loga o erro e continua funcionando

Formato do steering

# Team Memories Index

Total: 130 active memories. Use \`get_memory(id)\` to read full content.

## conventions (12)
- **Slack eng-prs channel ID** [slack]\`2026-03-02-slack-eng-prs-channel-id\`
...

Bump

@arvoretech/memory-mcp 1.2.0 → 1.3.0

Summary by CodeRabbit

  • New Features

    • Added automatic generation of memory index files to steering directories (.kiro/steering and .cursor/rules).
    • Indexes are now automatically synchronized whenever memories are added, archived, or removed.
  • Chores

    • Bumped package version to 1.3.0.

@coderabbitai

coderabbitai Bot commented Apr 15, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Joao208 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 51 minutes and 57 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 51 minutes and 57 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 234752e8-e0b6-4d28-8550-98f84998a475

📥 Commits

Reviewing files that changed from the base of the PR and between 402a8a6 and 2a92e24.

📒 Files selected for processing (2)
  • packages/memory/package.json
  • packages/memory/src/store.ts
📝 Walkthrough

Walkthrough

The package version increments from 1.2.0 to 1.3.0. The memory store adds workspace root detection and automatic steering-index generation that identifies writable targets, filters active catalog entries, builds markdown indices grouped by category, and synchronizes output on load completion and catalog mutations.

Changes

Cohort / File(s) Summary
Package Version Bump
packages/memory/package.json
Version updated from 1.2.0 to 1.3.0.
Steering-Index Generation
packages/memory/src/store.ts
Added SteeringTarget type and detectSteeringTargets() to discover writable targets (.kiro/steering, .cursor/rules). Extended MemoryStore with resolveWorkspaceRoot() logic and new syncSteeringIndex() method that filters active entries, groups by category, generates markdown indices with target-specific wrappers, and writes to detected target directories. Mutations (add, archive, remove) now trigger index synchronization.

Sequence Diagram

sequenceDiagram
    actor User
    participant Store as MemoryStore
    participant Detector as SteeringDetector
    participant FS as File System
    
    User->>Store: Load memories
    activate Store
    
    Store->>Store: doLoad() completes
    Store->>Detector: detectSteeringTargets(workspaceRoot)
    activate Detector
    Detector->>FS: Scan .kiro/steering and .cursor/rules
    Detector-->>Store: Return writable targets
    deactivate Detector
    
    Store->>Store: Filter entries (status === "active")
    Store->>Store: Group by category
    Store->>Store: Build markdown index
    
    loop For each target directory
        Store->>FS: Write team-memories-index.md
        FS-->>Store: Success/Failure
    end
    
    Store-->>User: Load complete, index synced
    deactivate Store
    
    User->>Store: Mutate catalog (add/archive/remove)
    activate Store
    Store->>Store: syncSteeringIndex()
    Store->>FS: Update team-memories-index.md
    FS-->>Store: Written
    Store-->>User: Mutation complete
    deactivate Store
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Hop, skip, and a bound through the memories we've found!
New steering targets gently guide the way,
Active entries grouped and indexed each day,
Workspace roots discovered, indices synchronized true—
Version 1.3 brings organization anew! 🎯✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(memory): auto-sync steering index on memory changes' is concise, specific, and directly summarizes the main feature added in the changeset. It clearly indicates the primary change: automatic synchronization of a steering index when memory operations occur.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch joaobarros-/-memory-mcp-steering-index

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/memory/src/store.ts`:
- Around line 77-83: The resolveWorkspaceRoot implementation is effectively a
no-op because it always returns dirname(resolve(memoriesPath)) and doesn't walk
ancestors; update resolveWorkspaceRoot to start from the resolved absolute path
(abs) and walk upward through parent directories (using dirname) until the
filesystem root, checking at each level with existsSync(join(dir, ".kiro")) ||
existsSync(join(dir, ".cursor")) || existsSync(join(dir, ".git")); return the
first directory where a marker is found, otherwise return the original resolved
path (abs). Use the existing resolve/dirname/join/existsSync utilities and keep
this logic inside resolveWorkspaceRoot.
- Around line 18-20: Add a target-specific filename to SteeringTarget and use it
when writing steering files: update the SteeringTarget interface to include
filename (string), set the Kiro target to "team-memories-index.md" and the
Cursor target to "team-memories-index.mdc", and change the file write logic that
currently writes to ".kiro/steering" and ".cursor/rules" to use target.filename
so the Cursor rule is written with the .mdc extension (ensuring the existing
frontmatter alwaysApply: true is preserved).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 383c8fcb-90b6-4370-86b4-548fbbe4ca6a

📥 Commits

Reviewing files that changed from the base of the PR and between 82cb711 and 402a8a6.

📒 Files selected for processing (2)
  • packages/memory/package.json
  • packages/memory/src/store.ts

Comment on lines +18 to +20
interface SteeringTarget {
dir: string;
buildContent: (body: string) => string;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/memory/src/store.ts | head -50

Repository: arvoreeducacao/arvore-mcp-servers

Length of output: 1928


🏁 Script executed:

cat -n packages/memory/src/store.ts | sed -n '15,190p'

Repository: arvoreeducacao/arvore-mcp-servers

Length of output: 6612


🌐 Web query:

Cursor rules .mdc extension .cursor/rules file format

💡 Result:

Cursor rules are stored in the .cursor/rules directory as Markdown files with .md or .mdc extensions. The .mdc extension (standing for "Markdown Cursor") is used for files with YAML frontmatter metadata to control application scope, enabling a custom editor in Cursor. File format: - YAML frontmatter (between --- lines) with keys like: - description: Brief summary for intelligent application. - globs: Array of glob patterns (e.g., [".ts", ".tsx"]) for file/path matching. - alwaysApply: boolean (true applies to every chat; default false uses description/globs). - Followed by Markdown content with the actual rule instructions, examples, etc. Example .mdc file: --- description: "Standards for React components" globs: ["src/components//*.tsx"] alwaysApply: false --- # React Component Standards - Use functional components with hooks - Prefer TypeScript interfaces Official Cursor documentation confirms .mdc files support frontmatter for description and globs. Community sources and Cursor engineers note .mdc signals Cursor-specific Markdown rules.

Citations:


Use target-specific filenames; Cursor rules require .mdc extension.

The code currently writes team-memories-index.md to both .kiro/steering and .cursor/rules directories. However, Cursor documents project rules in .cursor/rules as .mdc files (Markdown Cursor format). Writing .md files there means the Cursor target is ignored. The frontmatter already includes alwaysApply: true, indicating intent to use Cursor's rule format, so the extension mismatch breaks this feature.

Add a filename property to SteeringTarget and use target-specific extensions: team-memories-index.md for Kiro and team-memories-index.mdc for Cursor.

Proposed fix
 interface SteeringTarget {
   dir: string;
+  filename: string;
   buildContent: (body: string) => string;
 }
 
 function detectSteeringTargets(workspaceRoot: string): SteeringTarget[] {
   const targets: SteeringTarget[] = [];
 
   const kiroSteering = join(workspaceRoot, ".kiro", "steering");
   if (existsSync(kiroSteering)) {
     targets.push({
       dir: kiroSteering,
+      filename: "team-memories-index.md",
       buildContent: (body) =>
         `---\ninclusion: always\nname: team-memories-index\n---\n\n${body}\n`,
     });
   }
 
   const cursorRules = join(workspaceRoot, ".cursor", "rules");
   if (existsSync(cursorRules)) {
     targets.push({
       dir: cursorRules,
+      filename: "team-memories-index.mdc",
       buildContent: (body) =>
         `---\ndescription: "Team memories index — active knowledge base"\nalwaysApply: true\n---\n\n${body}\n`,
     });
   }
 
   return targets;
 }

 async syncSteeringIndex(): Promise<void> {
   try {
     const targets = detectSteeringTargets(this.workspaceRoot);
     if (targets.length === 0) return;

     // ... build body ...

     for (const target of targets) {
-      const filePath = join(target.dir, "team-memories-index.md");
+      const filePath = join(target.dir, target.filename);
       const content = target.buildContent(body);
       await writeFile(filePath, content, "utf-8");
     }
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/memory/src/store.ts` around lines 18 - 20, Add a target-specific
filename to SteeringTarget and use it when writing steering files: update the
SteeringTarget interface to include filename (string), set the Kiro target to
"team-memories-index.md" and the Cursor target to "team-memories-index.mdc", and
change the file write logic that currently writes to ".kiro/steering" and
".cursor/rules" to use target.filename so the Cursor rule is written with the
.mdc extension (ensuring the existing frontmatter alwaysApply: true is
preserved).

Comment on lines +77 to +83
private resolveWorkspaceRoot(memoriesPath: string): string {
const abs = resolve(memoriesPath);
const parent = dirname(abs);
if (existsSync(join(parent, ".kiro")) || existsSync(join(parent, ".cursor")) || existsSync(join(parent, ".git"))) {
return parent;
}
return parent;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Walk ancestors here; the current root resolution is effectively a no-op.

This always returns dirname(resolve(memoriesPath)), so it misses the actual workspace root whenever memoriesPath is the root itself or is nested deeper than one level. In those layouts detectSteeringTargets() never sees .kiro / .cursor, and the steering index silently stops syncing.

Proposed fix
  private resolveWorkspaceRoot(memoriesPath: string): string {
-    const abs = resolve(memoriesPath);
-    const parent = dirname(abs);
-    if (existsSync(join(parent, ".kiro")) || existsSync(join(parent, ".cursor")) || existsSync(join(parent, ".git"))) {
-      return parent;
-    }
-    return parent;
+    const start = resolve(memoriesPath);
+    let current = start;
+
+    while (true) {
+      if (
+        existsSync(join(current, ".kiro")) ||
+        existsSync(join(current, ".cursor")) ||
+        existsSync(join(current, ".git"))
+      ) {
+        return current;
+      }
+
+      const parent = dirname(current);
+      if (parent === current) {
+        return dirname(start);
+      }
+      current = parent;
+    }
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private resolveWorkspaceRoot(memoriesPath: string): string {
const abs = resolve(memoriesPath);
const parent = dirname(abs);
if (existsSync(join(parent, ".kiro")) || existsSync(join(parent, ".cursor")) || existsSync(join(parent, ".git"))) {
return parent;
}
return parent;
private resolveWorkspaceRoot(memoriesPath: string): string {
const start = resolve(memoriesPath);
let current = start;
while (true) {
if (
existsSync(join(current, ".kiro")) ||
existsSync(join(current, ".cursor")) ||
existsSync(join(current, ".git"))
) {
return current;
}
const parent = dirname(current);
if (parent === current) {
return dirname(start);
}
current = parent;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/memory/src/store.ts` around lines 77 - 83, The resolveWorkspaceRoot
implementation is effectively a no-op because it always returns
dirname(resolve(memoriesPath)) and doesn't walk ancestors; update
resolveWorkspaceRoot to start from the resolved absolute path (abs) and walk
upward through parent directories (using dirname) until the filesystem root,
checking at each level with existsSync(join(dir, ".kiro")) ||
existsSync(join(dir, ".cursor")) || existsSync(join(dir, ".git")); return the
first directory where a marker is found, otherwise return the original resolved
path (abs). Use the existing resolve/dirname/join/existsSync utilities and keep
this logic inside resolveWorkspaceRoot.

The memory MCP now automatically generates and maintains a steering
file (team-memories-index.md) with an index of all active memories.

The index is synced on:
- Store load (MCP startup)
- add_memory
- archive_memory
- remove_memory

Editor detection is automatic — writes to .kiro/steering/ and/or
.cursor/rules/ based on which directories exist.

Bumps @arvoretech/memory-mcp to 1.3.0
@Joao208 Joao208 force-pushed the joaobarros-/-memory-mcp-steering-index branch from 402a8a6 to 2a92e24 Compare April 15, 2026 15:55
@Joao208 Joao208 merged commit ed8675e into main Apr 15, 2026
4 checks passed
@Joao208 Joao208 deleted the joaobarros-/-memory-mcp-steering-index branch April 15, 2026 16:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant