mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85a178a6b8 | |||
| e4b32d7bc9 | |||
| 9590b934cf | |||
| 735f239f9b | |||
| 3ee30e1f36 |
@@ -1,69 +1,95 @@
|
||||
# /beta - Create Beta Release
|
||||
|
||||
Create a new beta release for the current version with automated quality checks and tagging.
|
||||
Create a new beta release using the automated justfile target with quality checks and tagging.
|
||||
|
||||
## Usage
|
||||
```
|
||||
/beta [version]
|
||||
/beta <version>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `version` (optional): Beta version like `v0.13.0b4`. If not provided, auto-increments from latest beta tag.
|
||||
- `version` (required): Beta version like `v0.13.2b1` or `v0.13.2rc1`
|
||||
|
||||
## Implementation
|
||||
|
||||
You are an expert release manager for the Basic Memory project. When the user runs `/beta`, execute the following steps:
|
||||
|
||||
### Step 1: Pre-flight Checks
|
||||
1. Check current git status for uncommitted changes
|
||||
2. Verify we're on the `main` branch
|
||||
3. Get the latest beta tag to determine next version if not provided
|
||||
### Step 1: Pre-flight Validation
|
||||
1. Verify version format matches `v\d+\.\d+\.\d+(b\d+|rc\d+)` pattern
|
||||
2. Check current git status for uncommitted changes
|
||||
3. Verify we're on the `main` branch
|
||||
4. Confirm no existing tag with this version
|
||||
|
||||
### Step 2: Quality Assurance
|
||||
1. Run `just check` to ensure code quality
|
||||
2. If any checks fail, report issues and stop
|
||||
3. Run `just update-deps` to ensure latest dependencies
|
||||
4. Commit any dependency updates with proper message
|
||||
### Step 2: Use Justfile Automation
|
||||
Execute the automated beta release process:
|
||||
```bash
|
||||
just beta <version>
|
||||
```
|
||||
|
||||
### Step 3: Version Determination
|
||||
If version not provided:
|
||||
1. Get latest git tags with `git tag -l "v*b*" --sort=-version:refname | head -1`
|
||||
2. Auto-increment beta number (e.g., `v0.13.0b2` → `v0.13.0b3`)
|
||||
3. Confirm version with user before proceeding
|
||||
The justfile target handles:
|
||||
- ✅ Beta version format validation (supports b1, b2, rc1, etc.)
|
||||
- ✅ Git status and branch checks
|
||||
- ✅ Quality checks (`just check` - lint, format, type-check, tests)
|
||||
- ✅ Version update in `src/basic_memory/__init__.py`
|
||||
- ✅ Automatic commit with proper message
|
||||
- ✅ Tag creation and pushing to GitHub
|
||||
- ✅ Beta release workflow trigger
|
||||
|
||||
### Step 4: Release Creation
|
||||
1. Commit any remaining changes
|
||||
2. Push to main: `git push origin main`
|
||||
3. Create tag: `git tag {version}`
|
||||
4. Push tag: `git push origin {version}`
|
||||
|
||||
### Step 5: Monitor Release
|
||||
### Step 3: Monitor Beta Release
|
||||
1. Check GitHub Actions workflow starts successfully
|
||||
2. Provide installation instructions for beta
|
||||
3. Report status and next steps
|
||||
2. Monitor workflow at: https://github.com/basicmachines-co/basic-memory/actions
|
||||
3. Verify PyPI pre-release publication
|
||||
4. Test beta installation: `uv tool install basic-memory --pre`
|
||||
|
||||
### Step 4: Beta Testing Instructions
|
||||
Provide users with beta testing instructions:
|
||||
|
||||
```bash
|
||||
# Install/upgrade to beta
|
||||
uv tool install basic-memory --pre
|
||||
|
||||
# Or upgrade existing installation
|
||||
uv tool upgrade basic-memory --prerelease=allow
|
||||
```
|
||||
|
||||
## Version Guidelines
|
||||
- **First beta**: `v0.13.2b1`
|
||||
- **Subsequent betas**: `v0.13.2b2`, `v0.13.2b3`, etc.
|
||||
- **Release candidates**: `v0.13.2rc1`, `v0.13.2rc2`, etc.
|
||||
- **Final release**: `v0.13.2` (use `/release` command)
|
||||
|
||||
## Error Handling
|
||||
- If quality checks fail, provide specific fix instructions
|
||||
- If git operations fail, provide manual recovery steps
|
||||
- If GitHub Actions fail, provide debugging guidance
|
||||
- If `just beta` fails, examine the error output for specific issues
|
||||
- If quality checks fail, fix issues and retry
|
||||
- If version format is invalid, correct and retry
|
||||
- If tag already exists, increment version number
|
||||
|
||||
## Success Output
|
||||
```
|
||||
✅ Beta Release v0.13.0b4 Created Successfully!
|
||||
✅ Beta Release v0.13.2b1 Created Successfully!
|
||||
|
||||
🏷️ Tag: v0.13.0b4
|
||||
🏷️ Tag: v0.13.2b1
|
||||
🚀 GitHub Actions: Running
|
||||
📦 PyPI: Will be available in ~5 minutes
|
||||
📦 PyPI: Will be available in ~5 minutes as pre-release
|
||||
|
||||
Install with:
|
||||
uv tool upgrade basic-memory --prerelease=allow
|
||||
Install/test with:
|
||||
uv tool install basic-memory --pre
|
||||
|
||||
Monitor release: https://github.com/basicmachines-co/basic-memory/actions
|
||||
```
|
||||
|
||||
## Beta Testing Workflow
|
||||
1. **Create beta**: Use `/beta v0.13.2b1`
|
||||
2. **Test features**: Install and validate new functionality
|
||||
3. **Fix issues**: Address bugs found during testing
|
||||
4. **Iterate**: Create `v0.13.2b2` if needed
|
||||
5. **Release candidate**: Create `v0.13.2rc1` when stable
|
||||
6. **Final release**: Use `/release v0.13.2` when ready
|
||||
|
||||
## Context
|
||||
- Use the existing justfile targets (`just check`, `just update-deps`)
|
||||
- Follow semantic versioning for beta releases
|
||||
- Maintain release notes in CHANGELOG.md
|
||||
- Use conventional commit messages
|
||||
- Leverage uv-dynamic-versioning for version management
|
||||
- Beta releases are pre-releases for testing new features
|
||||
- Automatically published to PyPI with pre-release flag
|
||||
- Uses the automated justfile target for consistency
|
||||
- Version is automatically updated in `__init__.py`
|
||||
- Ideal for validating changes before stable release
|
||||
- Supports both beta (b1, b2) and release candidate (rc1, rc2) versions
|
||||
@@ -1,6 +1,6 @@
|
||||
# /release - Create Stable Release
|
||||
|
||||
Create a stable release from the current main branch with comprehensive validation.
|
||||
Create a stable release using the automated justfile target with comprehensive validation.
|
||||
|
||||
## Usage
|
||||
```
|
||||
@@ -8,7 +8,7 @@ Create a stable release from the current main branch with comprehensive validati
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `version` (required): Release version like `v0.13.0`
|
||||
- `version` (required): Release version like `v0.13.2`
|
||||
|
||||
## Implementation
|
||||
|
||||
@@ -20,53 +20,54 @@ You are an expert release manager for the Basic Memory project. When the user ru
|
||||
3. Verify we're on the `main` branch
|
||||
4. Confirm no existing tag with this version
|
||||
|
||||
### Step 2: Comprehensive Quality Checks
|
||||
1. Run `just check` (lint, format, type-check, full test suite)
|
||||
2. Verify test coverage meets minimum requirements (95%+)
|
||||
3. Check that CHANGELOG.md contains entry for this version
|
||||
4. Validate all high-priority issues are closed
|
||||
### Step 2: Use Justfile Automation
|
||||
Execute the automated release process:
|
||||
```bash
|
||||
just release <version>
|
||||
```
|
||||
|
||||
### Step 3: Release Preparation
|
||||
1. Update any version references if needed
|
||||
2. Commit any final changes with message: `chore: prepare for ${version} release`
|
||||
3. Push to main: `git push origin main`
|
||||
The justfile target handles:
|
||||
- ✅ Version format validation
|
||||
- ✅ Git status and branch checks
|
||||
- ✅ Quality checks (`just check` - lint, format, type-check, tests)
|
||||
- ✅ Version update in `src/basic_memory/__init__.py`
|
||||
- ✅ Automatic commit with proper message
|
||||
- ✅ Tag creation and pushing to GitHub
|
||||
- ✅ Release workflow trigger
|
||||
|
||||
### Step 4: Release Creation
|
||||
1. Create annotated tag: `git tag -a ${version} -m "Release ${version}"`
|
||||
2. Push tag: `git push origin ${version}`
|
||||
3. Monitor GitHub Actions for release automation
|
||||
### Step 3: Monitor Release Process
|
||||
1. Check that GitHub Actions workflow starts successfully
|
||||
2. Monitor workflow completion at: https://github.com/basicmachines-co/basic-memory/actions
|
||||
3. Verify PyPI publication
|
||||
4. Test installation: `uv tool install basic-memory`
|
||||
|
||||
### Step 5: Post-Release Validation
|
||||
### Step 4: Post-Release Validation
|
||||
1. Verify GitHub release is created automatically
|
||||
2. Check PyPI publication
|
||||
3. Validate release assets
|
||||
4. Test installation: `uv tool install basic-memory`
|
||||
|
||||
### Step 6: Documentation Update
|
||||
1. Update any post-release documentation
|
||||
2. Create follow-up tasks if needed
|
||||
4. Update any post-release documentation
|
||||
|
||||
## Pre-conditions Check
|
||||
Before starting, verify:
|
||||
- [ ] All beta testing is complete
|
||||
- [ ] Critical bugs are fixed
|
||||
- [ ] Breaking changes are documented
|
||||
- [ ] CHANGELOG.md is updated
|
||||
- [ ] CHANGELOG.md is updated (if needed)
|
||||
- [ ] Version number follows semantic versioning
|
||||
|
||||
## Error Handling
|
||||
- If any quality check fails, stop and provide fix instructions
|
||||
- If changelog entry missing, prompt to create one
|
||||
- If tests fail, provide debugging guidance
|
||||
- If GitHub Actions fail, provide manual release steps
|
||||
- If `just release` fails, examine the error output for specific issues
|
||||
- If quality checks fail, fix issues and retry
|
||||
- If changelog entry missing, update CHANGELOG.md and commit before retrying
|
||||
- If GitHub Actions fail, check workflow logs for debugging
|
||||
|
||||
## Success Output
|
||||
```
|
||||
🎉 Stable Release v0.13.0 Created Successfully!
|
||||
🎉 Stable Release v0.13.2 Created Successfully!
|
||||
|
||||
🏷️ Tag: v0.13.0
|
||||
📋 GitHub Release: https://github.com/basicmachines-co/basic-memory/releases/tag/v0.13.0
|
||||
📦 PyPI: https://pypi.org/project/basic-memory/0.13.0/
|
||||
🏷️ Tag: v0.13.2
|
||||
📋 GitHub Release: https://github.com/basicmachines-co/basic-memory/releases/tag/v0.13.2
|
||||
📦 PyPI: https://pypi.org/project/basic-memory/0.13.2/
|
||||
🚀 GitHub Actions: Completed
|
||||
|
||||
Install with:
|
||||
@@ -79,6 +80,7 @@ uv tool upgrade basic-memory
|
||||
## Context
|
||||
- This creates production releases used by end users
|
||||
- Must pass all quality gates before proceeding
|
||||
- Follows the release workflow documented in CLAUDE.md
|
||||
- Uses uv-dynamic-versioning for automatic version management
|
||||
- Triggers automated GitHub release with changelog
|
||||
- Uses the automated justfile target for consistency
|
||||
- Version is automatically updated in `__init__.py`
|
||||
- Triggers automated GitHub release with changelog
|
||||
- Leverages uv-dynamic-versioning for package version management
|
||||
@@ -1,5 +1,20 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v0.13.1 (2025-06-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **CLI**: Fixed `basic-memory project` project management commands that were not working in v0.13.0 (#129)
|
||||
- **Projects**: Resolved case sensitivity issues when switching between projects that caused "Project not found" errors (#127)
|
||||
- **API**: Standardized CLI project command endpoints and improved error handling
|
||||
- **Core**: Implemented consistent project name handling using permalinks to avoid case-related conflicts
|
||||
|
||||
### Changes
|
||||
|
||||
- Renamed `basic-memory project sync` command to `basic-memory project sync-config` for clarity
|
||||
- Improved project switching reliability across different case variations
|
||||
- Removed redundant server status messages from CLI error outputs
|
||||
|
||||
## v0.13.0 (2025-06-11)
|
||||
|
||||
### Overview
|
||||
|
||||
@@ -58,6 +58,125 @@ check: lint format type-check test
|
||||
migration message:
|
||||
cd src/basic_memory/alembic && alembic revision --autogenerate -m "{{message}}"
|
||||
|
||||
# Create a stable release (e.g., just release v0.13.2)
|
||||
release version:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Validate version format
|
||||
if [[ ! "{{version}}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "❌ Invalid version format. Use: v0.13.2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract version number without 'v' prefix
|
||||
VERSION_NUM=$(echo "{{version}}" | sed 's/^v//')
|
||||
|
||||
echo "🚀 Creating stable release {{version}}"
|
||||
|
||||
# Pre-flight checks
|
||||
echo "📋 Running pre-flight checks..."
|
||||
if [[ -n $(git status --porcelain) ]]; then
|
||||
echo "❌ Uncommitted changes found. Please commit or stash them first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $(git branch --show-current) != "main" ]]; then
|
||||
echo "❌ Not on main branch. Switch to main first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if tag already exists
|
||||
if git tag -l "{{version}}" | grep -q "{{version}}"; then
|
||||
echo "❌ Tag {{version}} already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run quality checks
|
||||
echo "🔍 Running quality checks..."
|
||||
just check
|
||||
|
||||
# Update version in __init__.py
|
||||
echo "📝 Updating version in __init__.py..."
|
||||
sed -i.bak "s/__version__ = \".*\"/__version__ = \"$VERSION_NUM\"/" src/basic_memory/__init__.py
|
||||
rm -f src/basic_memory/__init__.py.bak
|
||||
|
||||
# Commit version update
|
||||
git add src/basic_memory/__init__.py
|
||||
git commit -m "chore: update version to $VERSION_NUM for {{version}} release"
|
||||
|
||||
# Create and push tag
|
||||
echo "🏷️ Creating tag {{version}}..."
|
||||
git tag "{{version}}"
|
||||
|
||||
echo "📤 Pushing to GitHub..."
|
||||
git push origin main
|
||||
git push origin "{{version}}"
|
||||
|
||||
echo "✅ Release {{version}} created successfully!"
|
||||
echo "📦 GitHub Actions will build and publish to PyPI"
|
||||
echo "🔗 Monitor at: https://github.com/basicmachines-co/basic-memory/actions"
|
||||
|
||||
# Create a beta release (e.g., just beta v0.13.2b1)
|
||||
beta version:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Validate version format (allow beta/rc suffixes)
|
||||
if [[ ! "{{version}}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(b[0-9]+|rc[0-9]+)$ ]]; then
|
||||
echo "❌ Invalid beta version format. Use: v0.13.2b1 or v0.13.2rc1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract version number without 'v' prefix
|
||||
VERSION_NUM=$(echo "{{version}}" | sed 's/^v//')
|
||||
|
||||
echo "🧪 Creating beta release {{version}}"
|
||||
|
||||
# Pre-flight checks
|
||||
echo "📋 Running pre-flight checks..."
|
||||
if [[ -n $(git status --porcelain) ]]; then
|
||||
echo "❌ Uncommitted changes found. Please commit or stash them first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $(git branch --show-current) != "main" ]]; then
|
||||
echo "❌ Not on main branch. Switch to main first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if tag already exists
|
||||
if git tag -l "{{version}}" | grep -q "{{version}}"; then
|
||||
echo "❌ Tag {{version}} already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run quality checks
|
||||
echo "🔍 Running quality checks..."
|
||||
just check
|
||||
|
||||
# Update version in __init__.py
|
||||
echo "📝 Updating version in __init__.py..."
|
||||
sed -i.bak "s/__version__ = \".*\"/__version__ = \"$VERSION_NUM\"/" src/basic_memory/__init__.py
|
||||
rm -f src/basic_memory/__init__.py.bak
|
||||
|
||||
# Commit version update
|
||||
git add src/basic_memory/__init__.py
|
||||
git commit -m "chore: update version to $VERSION_NUM for {{version}} beta release"
|
||||
|
||||
# Create and push tag
|
||||
echo "🏷️ Creating tag {{version}}..."
|
||||
git tag "{{version}}"
|
||||
|
||||
echo "📤 Pushing to GitHub..."
|
||||
git push origin main
|
||||
git push origin "{{version}}"
|
||||
|
||||
echo "✅ Beta release {{version}} created successfully!"
|
||||
echo "📦 GitHub Actions will build and publish to PyPI as pre-release"
|
||||
echo "🔗 Monitor at: https://github.com/basicmachines-co/basic-memory/actions"
|
||||
echo "📥 Install with: uv tool install basic-memory --pre"
|
||||
|
||||
# List all available recipes
|
||||
default:
|
||||
@just --list
|
||||
@@ -1,4 +1,7 @@
|
||||
"""basic-memory - Local-first knowledge management combining Zettelkasten with knowledge graphs"""
|
||||
|
||||
# Package version - updated by release automation
|
||||
__version__ = "0.13.2"
|
||||
|
||||
# API version for FastAPI - independent of package version
|
||||
__version__ = "v0"
|
||||
__api_version__ = "v0"
|
||||
|
||||
@@ -9,7 +9,6 @@ from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
from basic_memory.cli.app import app
|
||||
from basic_memory.config import config
|
||||
from basic_memory.mcp.project_session import session
|
||||
from basic_memory.mcp.resources.project_info import project_info
|
||||
import json
|
||||
@@ -24,6 +23,7 @@ from basic_memory.mcp.tools.utils import call_post
|
||||
from basic_memory.schemas.project_info import ProjectStatusResponse
|
||||
from basic_memory.mcp.tools.utils import call_delete
|
||||
from basic_memory.mcp.tools.utils import call_put
|
||||
from basic_memory.utils import generate_permalink
|
||||
|
||||
console = Console()
|
||||
|
||||
@@ -44,11 +44,8 @@ def format_path(path: str) -> str:
|
||||
def list_projects() -> None:
|
||||
"""List all configured projects."""
|
||||
# Use API to list projects
|
||||
|
||||
project_url = config.project_url
|
||||
|
||||
try:
|
||||
response = asyncio.run(call_get(client, f"{project_url}/project/projects"))
|
||||
response = asyncio.run(call_get(client, "/projects/projects"))
|
||||
result = ProjectList.model_validate(response.json())
|
||||
|
||||
table = Table(title="Basic Memory Projects")
|
||||
@@ -65,7 +62,6 @@ def list_projects() -> None:
|
||||
console.print(table)
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error listing projects: {str(e)}[/red]")
|
||||
console.print("[yellow]Note: Make sure the Basic Memory server is running.[/yellow]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@@ -80,16 +76,14 @@ def add_project(
|
||||
resolved_path = os.path.abspath(os.path.expanduser(path))
|
||||
|
||||
try:
|
||||
project_url = config.project_url
|
||||
data = {"name": name, "path": resolved_path, "set_default": set_default}
|
||||
|
||||
response = asyncio.run(call_post(client, f"{project_url}/project/projects", json=data))
|
||||
response = asyncio.run(call_post(client, "/projects/projects", json=data))
|
||||
result = ProjectStatusResponse.model_validate(response.json())
|
||||
|
||||
console.print(f"[green]{result.message}[/green]")
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error adding project: {str(e)}[/red]")
|
||||
console.print("[yellow]Note: Make sure the Basic Memory server is running.[/yellow]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Display usage hint
|
||||
@@ -105,15 +99,13 @@ def remove_project(
|
||||
) -> None:
|
||||
"""Remove a project from configuration."""
|
||||
try:
|
||||
project_url = config.project_url
|
||||
|
||||
response = asyncio.run(call_delete(client, f"{project_url}/project/projects/{name}"))
|
||||
project_name = generate_permalink(name)
|
||||
response = asyncio.run(call_delete(client, f"/projects/{project_name}"))
|
||||
result = ProjectStatusResponse.model_validate(response.json())
|
||||
|
||||
console.print(f"[green]{result.message}[/green]")
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error removing project: {str(e)}[/red]")
|
||||
console.print("[yellow]Note: Make sure the Basic Memory server is running.[/yellow]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Show this message regardless of method used
|
||||
@@ -126,20 +118,16 @@ def set_default_project(
|
||||
) -> None:
|
||||
"""Set the default project and activate it for the current session."""
|
||||
try:
|
||||
project_url = config.project_url
|
||||
project_name = generate_permalink(name)
|
||||
|
||||
response = asyncio.run(call_put(client, f"{project_url}/project/projects/{name}/default"))
|
||||
response = asyncio.run(call_put(client, f"projects/{project_name}/default"))
|
||||
result = ProjectStatusResponse.model_validate(response.json())
|
||||
|
||||
console.print(f"[green]{result.message}[/green]")
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error setting default project: {str(e)}[/red]")
|
||||
console.print("[yellow]Note: Make sure the Basic Memory server is running.[/yellow]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Always activate it for the current session
|
||||
os.environ["BASIC_MEMORY_PROJECT"] = name
|
||||
|
||||
# Reload configuration to apply the change
|
||||
from importlib import reload
|
||||
from basic_memory import config as config_module
|
||||
@@ -149,21 +137,18 @@ def set_default_project(
|
||||
console.print("[green]Project activated for current session[/green]")
|
||||
|
||||
|
||||
@project_app.command("sync")
|
||||
@project_app.command("sync-config")
|
||||
def synchronize_projects() -> None:
|
||||
"""Synchronize projects between configuration file and database."""
|
||||
"""Synchronize project config between configuration file and database."""
|
||||
# Call the API to synchronize projects
|
||||
|
||||
project_url = config.project_url
|
||||
|
||||
try:
|
||||
response = asyncio.run(call_post(client, f"{project_url}/project/sync"))
|
||||
response = asyncio.run(call_post(client, "/projects/sync"))
|
||||
result = ProjectStatusResponse.model_validate(response.json())
|
||||
|
||||
console.print(f"[green]{result.message}[/green]")
|
||||
except Exception as e: # pragma: no cover
|
||||
console.print(f"[red]Error synchronizing projects: {str(e)}[/red]")
|
||||
console.print("[yellow]Note: Make sure the Basic Memory server is running.[/yellow]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ from basic_memory.mcp.server import mcp
|
||||
from basic_memory.mcp.tools.utils import call_get, call_put, call_post, call_delete
|
||||
from basic_memory.schemas import ProjectInfoResponse
|
||||
from basic_memory.schemas.project_info import ProjectList, ProjectStatusResponse, ProjectInfoRequest
|
||||
from basic_memory.utils import generate_permalink
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@@ -77,6 +78,7 @@ async def switch_project(project_name: str, ctx: Context | None = None) -> str:
|
||||
if ctx: # pragma: no cover
|
||||
await ctx.info(f"Switching to project: {project_name}")
|
||||
|
||||
project_permalink = generate_permalink(project_name)
|
||||
current_project = session.get_current_project()
|
||||
try:
|
||||
# Validate project exists by getting project list
|
||||
@@ -84,13 +86,13 @@ async def switch_project(project_name: str, ctx: Context | None = None) -> str:
|
||||
project_list = ProjectList.model_validate(response.json())
|
||||
|
||||
# Check if project exists
|
||||
project_exists = any(p.name == project_name for p in project_list.projects)
|
||||
project_exists = any(p.permalink == project_permalink for p in project_list.projects)
|
||||
if not project_exists:
|
||||
available_projects = [p.name for p in project_list.projects]
|
||||
return f"Error: Project '{project_name}' not found. Available projects: {', '.join(available_projects)}"
|
||||
|
||||
# Switch to the project
|
||||
session.set_current_project(project_name)
|
||||
session.set_current_project(project_permalink)
|
||||
current_project = session.get_current_project()
|
||||
project_config = get_project_config(current_project)
|
||||
|
||||
@@ -99,11 +101,11 @@ async def switch_project(project_name: str, ctx: Context | None = None) -> str:
|
||||
response = await call_get(
|
||||
client,
|
||||
f"{project_config.project_url}/project/info",
|
||||
params={"project_name": project_name},
|
||||
params={"project_name": project_permalink},
|
||||
)
|
||||
project_info = ProjectInfoResponse.model_validate(response.json())
|
||||
|
||||
result = f"✓ Switched to {project_name} project\n\n"
|
||||
result = f"✓ Switched to {project_permalink} project\n\n"
|
||||
result += "Project Summary:\n"
|
||||
result += f"• {project_info.statistics.total_entities} entities\n"
|
||||
result += f"• {project_info.statistics.total_observations} observations\n"
|
||||
@@ -329,4 +331,4 @@ async def delete_project(project_name: str, ctx: Context | None = None) -> str:
|
||||
result += "Files remain on disk but project is no longer tracked by Basic Memory.\n"
|
||||
result += "Re-add the project to access its content again.\n"
|
||||
|
||||
return add_project_metadata(result, session.get_current_project())
|
||||
return add_project_metadata(result, session.get_current_project())
|
||||
@@ -6,6 +6,8 @@ from typing import Dict, List, Optional, Any
|
||||
|
||||
from pydantic import Field, BaseModel
|
||||
|
||||
from basic_memory.utils import generate_permalink
|
||||
|
||||
|
||||
class ProjectStatistics(BaseModel):
|
||||
"""Statistics about the current project."""
|
||||
@@ -183,6 +185,10 @@ class ProjectItem(BaseModel):
|
||||
name: str
|
||||
path: str
|
||||
is_default: bool = False
|
||||
|
||||
@property
|
||||
def permalink(self) -> str: # pragma: no cover
|
||||
return generate_permalink(self.name)
|
||||
|
||||
|
||||
class ProjectList(BaseModel):
|
||||
|
||||
@@ -207,7 +207,7 @@ class ProjectService:
|
||||
|
||||
# Get all projects from database
|
||||
db_projects = await self.repository.get_active_projects()
|
||||
db_projects_by_name = {p.name: p for p in db_projects}
|
||||
db_projects_by_permalink = {p.permalink: p for p in db_projects}
|
||||
|
||||
# Get all projects from configuration and normalize names if needed
|
||||
config_projects = config_manager.projects.copy()
|
||||
@@ -235,7 +235,7 @@ class ProjectService:
|
||||
|
||||
# Add projects that exist in config but not in DB
|
||||
for name, path in config_projects.items():
|
||||
if name not in db_projects_by_name:
|
||||
if name not in db_projects_by_permalink:
|
||||
logger.info(f"Adding project '{name}' to database")
|
||||
project_data = {
|
||||
"name": name,
|
||||
@@ -247,7 +247,7 @@ class ProjectService:
|
||||
await self.repository.create(project_data)
|
||||
|
||||
# Add projects that exist in DB but not in config to config
|
||||
for name, project in db_projects_by_name.items():
|
||||
for name, project in db_projects_by_permalink.items():
|
||||
if name not in config_projects:
|
||||
logger.info(f"Adding project '{name}' to configuration")
|
||||
config_manager.add_project(name, project.path)
|
||||
@@ -668,4 +668,4 @@ class ProjectService:
|
||||
database_size=db_size_readable,
|
||||
watch_status=watch_status,
|
||||
timestamp=datetime.now(),
|
||||
)
|
||||
)
|
||||
@@ -93,8 +93,6 @@ def test_project_default_command(mock_reload, mock_run, cli_env):
|
||||
|
||||
# Just verify it runs without exception and environment is set
|
||||
assert result.exit_code == 0
|
||||
assert "BASIC_MEMORY_PROJECT" in os.environ
|
||||
assert os.environ["BASIC_MEMORY_PROJECT"] == "test-project"
|
||||
|
||||
|
||||
@patch("basic_memory.cli.commands.project.asyncio.run")
|
||||
@@ -111,7 +109,7 @@ def test_project_sync_command(mock_run, cli_env):
|
||||
mock_run.return_value = mock_response
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli_app, ["project", "sync"])
|
||||
result = runner.invoke(cli_app, ["project", "sync-config"])
|
||||
|
||||
# Just verify it runs without exception
|
||||
assert result.exit_code == 0
|
||||
@@ -134,7 +132,6 @@ def test_project_failure_exits_with_error(mock_run, cli_env):
|
||||
# All should exit with code 1 and show error message
|
||||
assert list_result.exit_code == 1
|
||||
assert "Error listing projects" in list_result.output
|
||||
assert "Make sure the Basic Memory server is running" in list_result.output
|
||||
|
||||
assert add_result.exit_code == 1
|
||||
assert "Error adding project" in add_result.output
|
||||
|
||||
@@ -105,7 +105,6 @@ def config_manager(
|
||||
)
|
||||
|
||||
# Patch the project config that CLI commands import (only modules that actually import config)
|
||||
monkeypatch.setattr("basic_memory.cli.commands.project.config", project_config)
|
||||
monkeypatch.setattr("basic_memory.cli.commands.sync.config", project_config)
|
||||
monkeypatch.setattr("basic_memory.cli.commands.status.config", project_config)
|
||||
monkeypatch.setattr("basic_memory.cli.commands.import_memory_json.config", project_config)
|
||||
|
||||
Reference in New Issue
Block a user