feat(memory): auto-sync steering index on memory changes#46
Conversation
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
packages/memory/package.jsonpackages/memory/src/store.ts
| interface SteeringTarget { | ||
| dir: string; | ||
| buildContent: (body: string) => string; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n packages/memory/src/store.ts | head -50Repository: 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:
- 1: https://cursor.sh/docs/rules
- 2: https://forum.cursor.com/t/what-is-a-mdc-file/50417
- 3: https://design.dev/guides/cursor-rules/
- 4: https://cursor.com/docs/rules
- 5: https://www.datacamp.com/tutorial/cursor-rules
- 6: https://www.cursor.com/docs/context/rules
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).
| 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; |
There was a problem hiding this comment.
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.
| 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
402a8a6 to
2a92e24
Compare
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_memoriesvia 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 chamarget_memory(id)quando quiser o conteúdo completo.Como funciona
store.load()(startup do MCP)add_memory(nova memória)archive_memory(arquivou)remove_memory(deletou).kiro/steering/e/ou.cursor/rules/baseado em quais diretórios existemFormato do steering
Bump
@arvoretech/memory-mcp1.2.0 → 1.3.0Summary by CodeRabbit
New Features
.kiro/steeringand.cursor/rules).Chores
1.3.0.