Skip to main content

Memory Tool (save_memory)

The memory tool allows Qwen Code to save and recall information across sessions, enabling personalized and context-aware assistance.

Overview

Tool Name: save_memory Display Name: Memory Kind: Edit Description: Saves specific facts or information to long-term memory that persists across sessions.

Parameters

interface SaveMemoryParams {
  fact: string;                       // Required: Fact to remember
  scope?: 'global' | 'project';      // Optional: Where to save
}

Usage

Save to Global Memory

save_memory({
  fact: 'My preferred programming language is TypeScript',
  scope: 'global',
});

Save to Project Memory

save_memory({
  fact: 'This project uses Jest for testing',
  scope: 'project',
});

Auto-select Scope

// User will be prompted to choose
save_memory({
  fact: 'Use tabs for indentation, not spaces',
});

Memory Scopes

Global Memory

Location: ~/.qwen/QWEN.md Purpose: User-level preferences and information shared across all projects Use for:
  • Personal preferences
  • Coding style
  • Frequently used patterns
  • General instructions
Example:
# ~/.qwen/QWEN.md

## Qwen Added Memories

- My preferred programming language is TypeScript
- I prefer functional programming patterns
- Use JSDoc comments for all public APIs
- My timezone is America/New_York

Project Memory

Location: <project-root>/QWEN.md Purpose: Project-specific information Use for:
  • Project conventions
  • Architecture decisions
  • Team preferences
  • Build instructions
Example:
# <project>/QWEN.md

## Project Context

This is a web application built with Next.js and TypeScript.

## Qwen Added Memories

- This project uses Jest for testing
- API routes are in pages/api/
- Use Prisma for database operations
- Deploy using Vercel

When to Use save_memory

Good Use Cases

User preferences
save_memory({
  fact: 'I prefer React over Vue.js',
  scope: 'global',
});
Project conventions
save_memory({
  fact: 'All API endpoints must have input validation',
  scope: 'project',
});
Important facts
save_memory({
  fact: 'The main database is PostgreSQL on port 5432',
  scope: 'project',
});
Coding standards
save_memory({
  fact: 'Maximum line length is 100 characters',
  scope: 'project',
});

When NOT to Use

Conversational context
// This is ephemeral, don't save
save_memory({
  fact: 'We discussed the login bug in the last message',
});
Long, complex text
// Too much information
save_memory({
  fact: '[Entire file contents or long documentation]',
});
Temporary information
// This will change
save_memory({
  fact: 'Currently working on the authentication feature',
});
Uncertain information
// Only save confirmed facts
save_memory({
  fact: 'Maybe the API uses REST, not sure',
});

How It Works

Memory Storage

Facts are appended to a markdown file under a special section:
# Context File

## Project Overview

... existing content ...

## Qwen Added Memories

- Fact 1
- Fact 2
- Fact 3

Memory Loading

Memory files are automatically loaded:
  1. On startup: Contents included in initial context
  2. Every session: Available to the model
  3. Project-specific: Project memories only load in that project
  4. Global always: Global memories load in all projects

Memory Format

Memories are stored as:
- [fact] (added: YYYY-MM-DD HH:mm:ss)
Example:
## Qwen Added Memories

- My preferred programming language is TypeScript (added: 2026-03-10 15:30:45)
- Use tabs for indentation (added: 2026-03-10 15:32:10)

Configuration

Custom Memory Files

Change the memory file name:
{
  "context": {
    "contextFileNames": [
      "QWEN.md",
      "PROJECT.md",
      "CUSTOM.md"
    ]
  }
}
All specified files will be loaded as context.

Multiple Context Files

You can have multiple context files:
<project>/
├── QWEN.md           # Primary context
├── AGENTS.md         # Agent configurations
├── CODING_STANDARDS.md  # Custom context
└── src/
All are loaded if specified in contextFileNames.

User Interaction

Scope Selection

If scope is not provided, the user is prompted:
Where should this memory be saved?

Fact: "Use TypeScript for all new files"

[G] Global (~/.qwen/QWEN.md)
    - Available in all projects
    
[P] Project (./QWEN.md)
    - Only available in this project
    
[C] Cancel

Choice:

Confirmation

Memory saves show confirmation:
Memory saved to global context:

"Use TypeScript for all new files"

Saved to: ~/.qwen/QWEN.md

Managing Memories

View Memories

Read the memory file:
# Global memories
cat ~/.qwen/QWEN.md

# Project memories
cat QWEN.md

Edit Memories

Manually edit the markdown file:
# Edit global memories
vim ~/.qwen/QWEN.md

# Edit project memories
vim QWEN.md

Remove Memories

Delete specific lines from the file or remove the entire section.

Organize Memories

You can reorganize the file structure:
# QWEN.md

## User Preferences

- Preferred language: TypeScript
- Coding style: Functional

## Project Setup

- Testing framework: Jest
- Database: PostgreSQL

## Qwen Added Memories

- [New memories added here by tool]

Best Practices

1. Be Specific and Clear

Good:
save_memory({
  fact: 'Use React Router v6 for navigation',
  scope: 'project',
});
Bad:
save_memory({
  fact: 'We use routing',  // Too vague
});

2. Choose Appropriate Scope

// Personal preference → global
save_memory({
  fact: 'I prefer functional components',
  scope: 'global',
});

// Project-specific → project
save_memory({
  fact: 'API base URL is https://api.example.com',
  scope: 'project',
});

3. Keep Facts Concise

Good:
save_memory({
  fact: 'Database: PostgreSQL on localhost:5432',
});
Bad:
save_memory({
  fact: `The database we're using is PostgreSQL and it's 
         running on localhost and the port is 5432 and we 
         connect using username 'admin' and...`,  // Too long
});

4. Avoid Duplicates

Check existing memories before adding:
// First, read the memory file
read_file({ absolute_path: '/path/to/QWEN.md' });

// Then save if not duplicate
if (!alreadyStored) {
  save_memory({ fact: 'New fact' });
}

5. Update Instead of Add

If information changes, edit the file manually rather than adding a new fact.

Implementation

Location: packages/core/src/tools/memoryTool.ts

Tool Class

export class MemoryTool
  extends BaseDeclarativeTool<SaveMemoryParams, ToolResult>
  implements ModifiableDeclarativeTool<SaveMemoryParams>
{
  static readonly Name = ToolNames.SAVE_MEMORY;

  async execute(signal: AbortSignal): Promise<ToolResult> {
    const scope = params.scope || await this.promptForScope();
    const filePath = getMemoryFilePath(scope);
    
    // Read current content
    const currentContent = await readMemoryFileContent(filePath);
    
    // Append new memory
    const timestamp = new Date().toISOString();
    const memoryEntry = `- ${params.fact} (added: ${timestamp})\n`;
    const newContent = appendToMemorySection(
      currentContent,
      memoryEntry,
    );
    
    // Write updated content
    await fs.writeFile(filePath, newContent, 'utf-8');
    
    return {
      llmContent: `Saved memory to ${scope} context: "${params.fact}"`,
      returnDisplay: `Memory saved to ${filePath}`,
    };
  }
}

Memory File Functions

// Get path for scope
function getMemoryFilePath(scope: 'global' | 'project'): string {
  if (scope === 'global') {
    return path.join(Storage.getGlobalQwenDir(), 'QWEN.md');
  }
  return path.join(process.cwd(), 'QWEN.md');
}

// Append to memory section
function appendToMemorySection(
  content: string,
  memoryEntry: string,
): string {
  const sectionHeader = '## Qwen Added Memories';
  
  if (content.includes(sectionHeader)) {
    // Append to existing section
    return content.replace(
      sectionHeader,
      `${sectionHeader}\n\n${memoryEntry}`,
    );
  } else {
    // Create new section
    return `${content}\n\n${sectionHeader}\n\n${memoryEntry}`;
  }
}

Security

No Sensitive Data

⚠️ Warning: Don’t save sensitive information:
// ❌ DON'T save secrets
save_memory({
  fact: 'API key: sk-abc123xyz789',  // NEVER do this
});

// ❌ DON'T save passwords
save_memory({
  fact: 'Database password: mypassword',  // NEVER do this
});
Memory files are plain text and may be committed to version control.

.gitignore

Consider ignoring memory files:
# .gitignore
QWEN.md
.qwen/QWEN.md
Or commit them if they only contain non-sensitive project information.

Troubleshooting

Memory Not Loaded

Problem: Saved memory doesn’t appear in context Solutions:
  1. Restart Qwen Code
  2. Check file location:
    ls ~/.qwen/QWEN.md
    ls ./QWEN.md
    
  3. Verify contextFileNames setting
  4. Check file permissions

Wrong Scope

Problem: Saved to wrong scope Solution: Manually move the fact between files:
# Move from global to project
grep "fact text" ~/.qwen/QWEN.md
# Copy the line
vim QWEN.md
# Paste it
# Remove from global file
vim ~/.qwen/QWEN.md

Duplicate Memories

Problem: Same fact saved multiple times Solution: Edit the file and remove duplicates:
vim ~/.qwen/QWEN.md
# Delete duplicate lines

Examples

Development Preferences

// Code style
save_memory({
  fact: 'Use async/await instead of .then() chains',
  scope: 'global',
});

// Testing preference
save_memory({
  fact: 'Write tests using describe/it pattern',
  scope: 'global',
});

// Documentation
save_memory({
  fact: 'Add JSDoc comments to all exported functions',
  scope: 'global',
});

Project Setup

// Environment
save_memory({
  fact: 'Development server runs on port 3000',
  scope: 'project',
});

// Build process
save_memory({
  fact: 'Run npm run build before deploying',
  scope: 'project',
});

// Dependencies
save_memory({
  fact: 'Use npm, not yarn, for this project',
  scope: 'project',
});

Architectural Decisions

// Design patterns
save_memory({
  fact: 'Use Repository pattern for data access',
  scope: 'project',
});

// Structure
save_memory({
  fact: 'Feature-based folder structure under src/features',
  scope: 'project',
});

// Conventions
save_memory({
  fact: 'Name test files as *.test.ts, not *.spec.ts',
  scope: 'project',
});

Next Steps