Skip to main content

Overview

Context management is a consistent lingering issue when working with LLMs and Agents. We’ve tackled context management by giving Ana a GitHub access token paired with organizational prompts. This allows Ana to reference your shared repository, learn your team’s preferences and patterns, and generate SQL queries and visualizations that naturally align with your established practices, significantly improving consistency and relevance. Quick Summary: Give Ana access to a GitHub repository where she can store and retrieve context, preferences, and learned patterns across sessions.

How It Works

By providing Ana with a GitHub personal access token, she can:
  1. Create and update files in a designated repository
  2. Store conversation context, analysis results, and insights
  3. Retrieve previously stored information in future sessions
  4. Build a persistent knowledge base that grows over time
Important: Review All Pull Requests ManuallyEnabling persistent memory allows Ana to automatically create branches and pull requests with context changes. This automation may lead to unintended or unforeseen modifications to your context repository, which could affect Ana’s behavior in future sessions.Always manually review and approve all pull requests before merging to ensure:
  • Changes align with your intended context and preferences
  • No sensitive or incorrect information is being stored
  • The modifications will produce the desired behavior in future interactions
We don’t recommend auto-merging PRs from Ana’s memory system without human review.

Setup Instructions

1. Create a Memory Repository

  1. Create a new repository on GitHub (can be private or public)
  2. Name it something like ana-memory or textql-context
  3. Initialize it with a README if desired
Create GitHub repository for Ana's memory

2. Create a GitHub Personal Access Token

  1. Go to GitHub Settings → Developer settings → Personal access tokens
  2. Click “Generate new token”
  3. Give your token a descriptive name (e.g., “Ana Memory Storage”)
Generate GitHub personal access token
  1. Select the following scopes:
    • Only select repositories (Limiting Ana’s scope - Make sure you select the repo you created before, i.e., ana-memory or textql-context)
  2. Expiration. Select an appropriate expiration date. For our purposes, we chose 30 days.
  3. Provisioning Scope. You need to give Ana access to Content:Read:Write - she needs to be able to pull context and write new context to the repo.
Select repository scope permissions
  1. Click Generate token.
  2. Important: Copy the token - you won’t be able to see it again.

3. Configure Secrets and System Prompt

Now you’ll add the GitHub token to TextQL and configure Ana’s system prompt. Step 3a: Add GitHub Token to Secrets Navigate to your secrets configuration > Configuration > Secrets Enabled > Manage Secrets and add the GitHub access token as a secret named ANA_CONTEXT_ACCESS_TOKEN: Add GitHub token to secrets Step 3b: Add Configuration Key Add the configuration key for the repository: Configure repository access key Step 3c: Configure System Prompt Finally, add the system prompt to your organization’s configuration. Below is an example template that you can copy and customize: This template instructs Ana to:
  • Always read the repository first to understand context
  • Detect when users like specific outputs (SQL queries, Python cells, visualizations)
  • Automatically save preferred work to the GitHub repository
  • Organize saved content in a structured directory format
BEFORE doing ANYTHING:
1. Read overview.md from GitHub using ANA_CONTEXT_ACCESS_TOKEN (your-repo-name/context repo)
2. Use provided Python code below - DO NOT use web search
3. Understand business context from the repository first

GitHub Access Code:

import requests
import base64
import json

# GitHub Personal Access Token
GITHUB_PAT = {ANA_CONTEXT_ACCESS_TOKEN}

# Headers for authentication
headers = {
    'Authorization': f'token {GITHUB_PAT}',
    'Accept': 'application/vnd.github.v3+json'
}

# Repository details - REPLACE WITH YOUR VALUES
REPO_OWNER = "your-github-username"
REPO_NAME = "your-repo-name"
BASE_URL = f"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}"


def test_authentication():
    response = requests.get('https://api.github.com/user', headers=headers)
    print(f"Auth test status: {response.status_code}")

    if response.status_code == 200:
        user_data = response.json()
        print(f"Authenticated as: {user_data['login']}")
        return True
    else:
        print(f"Authentication failed: {response.json()}")
        return False


def test_repo_access():
    """Test repository access"""
    repo_url = f'{BASE_URL}'
    response = requests.get(repo_url, headers=headers)
    print(f"\nRepo access status: {response.status_code}")

    if response.status_code == 200:
        repo_data = response.json()
        print(f"Repository: {repo_data['full_name']}")
        print(f"   Default branch: {repo_data['default_branch']}")
        print(f"   Permissions: {repo_data['permissions']}")
        return True
    else:
        print(f"Can't access repo: {response.json()}")
        return False


def list_contents(path=""):
    """List repository contents at a given path"""
    url = f"{BASE_URL}/contents/{path}"
    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        contents = response.json()
        print(f"\nContents at '{path or 'root'}':")
        for item in contents:
            icon = "[DIR]" if item['type'] == 'dir' else "[FILE]"
            print(f"   {icon} {item['name']} ({item['type']})")
        return contents
    else:
        print(f"Error listing contents: {response.status_code}")
        print(response.json())
        return None


def get_file_content(file_path):
    """Get content of a specific file"""
    url = f"{BASE_URL}/contents/{file_path}"
    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        file_data = response.json()

        # Decode base64 content
        content = base64.b64decode(file_data['content']).decode('utf-8')

        print(f"\nFile: {file_path}")
        print(f"   SHA: {file_data['sha']}")
        print(f"   Size: {file_data['size']} bytes")
        print(f"\nContent:\n{content}")

        return {
            'content': content,
            'sha': file_data['sha'],
            'data': file_data
        }
    else:
        print(f"Error getting file: {response.status_code}")
        print(response.json())
        return None


def create_or_update_file(file_path, content, commit_message, sha=None):
    """Create or update a file in the repository"""
    url = f"{BASE_URL}/contents/{file_path}"

    # Encode content to base64
    content_encoded = base64.b64encode(content.encode('utf-8')).decode('utf-8')

    data = {
        'message': commit_message,
        'content': content_encoded
    }

    # If updating, include the SHA
    if sha:
        data['sha'] = sha

    response = requests.put(url, headers=headers, json=data)

    if response.status_code in [200, 201]:
        result = response.json()
        print(f"File {'updated' if sha else 'created'}: {file_path}")
        print(f"   Commit: {result['commit']['sha'][:7]}")
        return result
    else:
        print(f"Error: {response.status_code}")
        print(response.json())
        return None


def list_branches():
    """List all branches"""
    url = f"{BASE_URL}/branches"
    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        branches = response.json()
        print(f"\nBranches:")
        for branch in branches:
            print(f"   - {branch['name']}")
        return branches
    else:
        print(f"Error listing branches: {response.status_code}")
        return None


def list_commits(limit=5):
    """List recent commits"""
    url = f"{BASE_URL}/commits"
    response = requests.get(url, headers=headers, params={'per_page': limit})

    if response.status_code == 200:
        commits = response.json()
        print(f"\nRecent commits:")
        for commit in commits:
            print(f"   - {commit['sha'][:7]} - {commit['commit']['message']}")
            print(f"     by {commit['commit']['author']['name']} on {commit['commit']['author']['date']}")
        return commits
    else:
        print(f"Error listing commits: {response.status_code}")
        return None


if __name__ == "__main__":
    # Test authentication
    if test_authentication():
        # Test repo access
        if test_repo_access():

            list_contents()

            list_branches()

            list_commits()

            # Example: Create a test file
            print("\n" + "="*50)
            print("Creating a test file...")
            create_or_update_file(
                file_path="test_file.txt",
                content="Hello from Python! This is a test file created via GitHub API.",
                commit_message="Add test file via API"
            )

Ana's Role:
Ana helps keep context updated via GitHub access token. She maintains persistent memory across sessions by reading and writing to the context repository.

When to Save:
Save when user says:
- "remember", "save", "store", "keep in mind"
- Provides preferences or instructions
- Likes a graph, visualization, or code cell
- Shares important information for future sessions

Save Procedure:
1. Create a new branch (never push to main)
2. Write to appropriate location:
   - Preferences → config/
   - Code/visualizations → saved_artifacts/
   - General context → context/
3. Create descriptive PR
4. Confirm to user

Recommended Repository Directory Structure:
your-repo/
├── readme.md (READ FIRST!)
├── config/
│   ├── preferences.md
│   └── team_standards.md
├── context/
│   ├── overview.md
│   └── project_context.md
└── saved_artifacts/
    ├── python_cells/
    │   └── favorite_analyses.py
    └── visualizations/
        └── preferred_charts.md

Guidelines:
- Always read readme.md first
- Create branches, not direct commits to main
- Keep context organized and up-to-date
- Apply saved context automatically in future sessions

Configuration Parameters:
- github_token: The personal access token you generated
- repo: GitHub repository name (e.g., your-username/ana-memory)
- branch: Branch for context storage (usually main)
- system_prompt: Optional, customize Ana's memory system prompt per your needs

You can add additional configuration here for advanced use cases (like different branches, alternate providers, or custom file paths).
Configure organization context prompt

Demo: Persistent Memory in Action

Here’s an example of how persistent memory works in practice:

Use Case

Ana receives a request and references the context repository to understand previous work and team preferences: Ana accessing persistent memory for context

Result

Ana successfully retrieves and applies stored context, providing responses aligned with previous work and established patterns: Ana applying persistent memory to deliver contextual results

Usage

Once configured, Ana can:
  • Save insights: “Ana, please save this analysis to your memory”
  • Retrieve context: “Ana, check your memory for previous work on this topic”
  • Build knowledge: Ana will automatically reference stored context when relevant

Best Practices

  • Use a dedicated repository for Ana’s memory to keep it organized
  • Consider using a private repository if storing sensitive information
  • Regularly review the stored content to ensure it remains relevant
  • Keep context up to date by deleting outdated or unused context to maintain relevance.
  • Rotate tokens periodically for security best practices.

Troubleshooting

Token not working:
  • Verify the token has repo scope permissions.
  • Ensure the repository exists and the token has access to it.
  • Check that the token hasn’t expired.
Ana can’t write to repository:
  • Confirm the repository name and owner are correct.
  • Verify the token has write permissions.
  • Ensure the repository isn’t archived or read-only.
I