Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .opencode/lancedb-opencode-pro.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"provider": "lancedb-opencode-pro",
"dbPath": "~/.opencode/memory/lancedb",
"embedding": {
"provider": "ollama",
"model": "nomic-embed-text",
"baseUrl": "http://192.168.11.206:11434"
},
"retrieval": {
"mode": "hybrid",
"vectorWeight": 0.7,
"bm25Weight": 0.3,
"minScore": 0.2
},
"includeGlobalScope": true,
"minCaptureChars": 80,
"maxEntriesPerScope": 3000
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-21
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
## Context

The `lancedb-opencode-pro` plugin currently stores all memories scoped to `project:*`, derived from Git repository context. This design addresses the need for cross-project knowledge sharing by introducing a dual-scope architecture where certain memories can be promoted to `global` scope and automatically shared across all projects.

**Current State:**
- All memory entries are stored with `scope = "project:<repo-id>"`
- Recall queries are constrained to the active project scope
- No mechanism for knowledge that applies to multiple projects (e.g., "Alpine Linux find uses BusyBox")

**Constraints:**
- Must maintain backward compatibility with existing project-scoped behavior
- Must not pollute project recall with irrelevant global memories
- Must provide user control over scope decisions (promotion/demotion)

**Stakeholders:**
- OpenCode users working across multiple repositories
- Developers who want to share tool knowledge and workflow patterns

## Goals / Non-Goals

**Goals:**
- Enable cross-project memory sharing without manual copy-paste
- Automatically detect potential global knowledge during capture
- Provide clear user prompts for scope promotion/demotion decisions
- Keep project recall signal strong by discounting global scores

**Non-Goals:**
- Automatic memory deduplication across projects (out of scope)
- Automatic demotion without user confirmation (only suggestions)
- Global recall search without project context (always dual-scope)

## Decisions

### Decision 1: Dual-Scope Recall with Score Discount

**Choice:** Query both project and global scopes in parallel, merge results with global scores discounted by 0.7x.

**Rationale:**
- Ensures global knowledge is available when relevant
- Prevents global memories from drowning out project-specific context
- Allows users to understand which results came from global scope (via metadata)

**Alternatives Considered:**
- Query global only when project has no results → Rejected. Global knowledge should be available proactively.
- Always include global with equal weight → Rejected. Would dilute project recall signal.
- User-controlled global toggle → Adds friction. Automatic inclusion with discount is better default.

### Decision 2: Keyword-Based Global Detection

**Choice:** Use a predefined list of cross-project keywords (Linux distributions, Docker, Kubernetes, shells, cloud platforms) with a threshold of 2+ matches to trigger promotion prompt.

**Rationale:**
- Simple, predictable, and explainable
- Covers the most common cross-project knowledge types
- Avoids expensive LLM-based classification
- Threshold of 2 reduces false positives

**Alternatives Considered:**
- LLM-based semantic analysis → Rejected. Too expensive, adds latency, may be inconsistent.
- All memories global by default → Rejected. Would clutter global scope with project-specific noise.
- User manual tagging only → Rejected. Misses the automation goal.

**Keywords:**
```typescript
const GLOBAL_KEYWORDS = [
// Distributions
'alpine', 'debian', 'ubuntu', 'centos', 'fedora', 'arch',
// Containers
'docker', 'dockerfile', 'docker-compose', 'containerd',
// Orchestration
'kubernetes', 'k8s', 'helm', 'kubectl',
// Shells/Systems
'bash', 'shell', 'linux', 'unix', 'posix', 'busybox',
// Web servers
'nginx', 'apache', 'caddy',
// Databases
'postgres', 'postgresql', 'mysql', 'redis', 'mongodb', 'sqlite',
// Cloud
'aws', 'gcp', 'azure', 'digitalocean',
// VCS
'git', 'github', 'gitlab', 'bitbucket',
// Protocols
'api', 'rest', 'graphql', 'grpc', 'http', 'https',
// Tools
'npm', 'yarn', 'pnpm', 'pip', 'cargo', 'make', 'cmake',
];
```

### Decision 3: User Confirmation for Promotion

**Choice:** Prompt user for confirmation when heuristic detects global-worthy content, rather than automatically promoting.

**Rationale:**
- User context is required to determine if knowledge is truly cross-project
- Avoids polluting global scope with false positives
- Respects user agency over memory organization

**Alternatives Considered:**
- Automatic promotion → Rejected. Too risky for global scope pollution.
- No prompt, just label suggestion → Rejected. Misses the opportunity to confirm intent.

### Decision 4: Unused Global Memory Demotion

**Choice:** Track recall usage of global memories. After 30 days without recall, prompt user with demotion suggestions. User must confirm demotion.

**Rationale:**
- Prevents global scope from accumulating unused knowledge
- Gives user final say on scope changes
- Maintains global scope quality over time

**Alternatives Considered:**
- Automatic demotion → Rejected. Could remove genuinely useful memories that simply haven't been queried recently.
- No cleanup → Rejected. Global scope would grow unbounded.

### Decision 5: Scope Field Schema

**Choice:** Add `scope: "project" | "global"` field to `MemoryEntry` interface. Default is `"project"`.

**Rationale:**
- Minimal schema change
- Backward compatible (existing entries implicitly have `scope: "project"`)
- Easy to query and filter

## Risks / Trade-offs

| Risk | Mitigation |
|------|------------|
| Global scope accumulates low-quality knowledge | User confirmation required; demotion flow available |
| Global recall dilutes project recall signal | Score discount (0.7x) keeps project results prioritized |
| Too many prompts annoy users | Only prompt when keyword threshold met; batch unused detection |
| User forgets to promote useful knowledge | Auto-detection acts as reminder; low friction to confirm |
| Global memories become stale | Demotion flow encourages periodic review |

## Migration Plan

### Phase 1: Schema and Storage
1. Add `scope` field to `MemoryEntry` with default `"project"`
2. Update LanceDB schema to include `scope` column
3. Add `readGlobalMemories()` method to store

### Phase 2: Dual-Scope Recall
1. Modify `search()` to accept scope filter
2. Implement parallel query of project + global
3. Implement score merge with global discount
4. Add `source` metadata to results (`"project"` or `"global"`)

### Phase 3: Detection and Promotion
1. Implement `detectGlobalWorthiness()` heuristic
2. Add promotion prompt in capture flow
3. Create `memory_scope_promote()` tool

### Phase 4: Demotion Flow
1. Track recall usage per global memory
2. Implement unused detection background task
3. Create demotion prompt and `memory_scope_demote()` tool

### Rollback Strategy
- Disable global inclusion via `includeGlobalScope: false` config
- Existing global memories remain but are excluded from recall

## Open Questions

1. **Should global memories be editable?** Currently memories are immutable. Extending to global scope may require edit capability.

2. **How to handle conflicting global vs project memories?** If a project has a memory about "use npm" but global says "avoid npm for large projects", which wins? Currently project wins due to score discount, but explicit conflict resolution may be needed.

3. **Should global memories have a separate retention policy?** Project memories are pruned at 3000 per scope. Should global have same or different limits?
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
## Why

Currently all long-term memories in `lancedb-opencode-pro` are scoped to `project:*` only. Knowledge types like tool limitations (e.g., "Alpine Linux find uses BusyBox, does not support -empty"), workflow patterns, and platform-specific insights should be shared across projects to avoid re-learning. However, there is no mechanism to automatically detect and promote cross-project knowledge.

## What Changes

### New Capabilities

1. **Memory scope field** — Every memory entry gains a `scope` field (`"project"` or `"global"`). Default is `"project"`.

2. **Global detection heuristic** — When storing a memory, the system analyzes content against known cross-project keywords (Linux distributions, Docker, Kubernetes, shells, cloud platforms, common services). If threshold is met, prompt user to confirm promotion.

3. **Scope promotion flow** — New tool `memory_scope_promote` that allows users to promote project-scoped memories to global, or confirm auto-detected promotions.

4. **Dual-scope recall** — When `memory_search` executes, it queries both the active project scope and the global scope in parallel, then merges results with global scores discounted by 0.7x to avoid drowning project-specific context.

5. **Unused global detection** — Background analysis periodically checks global memories. If a global memory has not been recalled in the past 30 days, prompt user with demotion suggestions.

6. **Scope management tools** — New tools for viewing and managing global memories: `memory_global_list`, `memory_scope_promote`, `memory_scope_demote`.

## Capabilities

### New Capabilities

- `memory-scope-field`: Add `scope` metadata field to memory entries with values `"project"` or `"global"`.
- `memory-global-detection`: Heuristic analysis of memory content to detect cross-project worthy knowledge based on keyword matching.
- `memory-scope-promotion`: Promotion flow with user confirmation for detected global candidates and manual promotion tool.
- `memory-scope-demotion`: Demotion flow for unused global memories with user confirmation.
- `memory-dual-scope-recall`: Parallel query of project + global scopes with score merge and global discount.
- `memory-global-list`: Tool to view and search all global-scoped memories.

### Modified Capabilities

- `memory-management-commands`: Add `memory_scope_promote`, `memory_scope_demote`, and `memory_global_list` tools to the existing command set.

## Impact

### Code Changes

- `src/types.ts`: Add `scope` field to `MemoryEntry` interface
- `src/store.ts`: Modify `search()` to support dual-scope query with merge; add `getGlobalMemories()` method
- `src/index.ts`: Add new tools (`memory_scope_promote`, `memory_scope_demote`, `memory_global_list`); add heuristic detection in capture flow
- `src/extract.ts`: Pass scope metadata during memory storage

### Configuration Changes

- New config: `includeGlobalScope` (default: `true`)
- New config: `global_detection_threshold` (default: `2` keywords)
- New config: `global_discount_factor` (default: `0.7`)
- New config: `unused_days_threshold` (default: `30` days)

### Behavior Changes

- `memory_search` now queries both project and global scopes (if `includeGlobalScope: true`)
- Global memories appear with `source: "global"` metadata to distinguish from project memories
- Users are prompted when storing memories that may be globally relevant
- Users are periodically notified about unused global memories

### Breaking Changes

- None. All changes are additive. Existing project-scoped behavior is preserved by default.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# memory-dual-scope-recall Specification

## Purpose

When retrieving memories, automatically include both project-scoped and relevant global-scoped memories, with appropriate score weighting to prioritize project context.

## Requirements

### Requirement: Dual-scope parallel query

The system MUST query both the active project scope and the global scope in parallel when executing `memory_search`.

#### Scenario: Dual-scope search
- **WHEN** user executes `memory_search` with query "docker alpine"
- **THEN** the system queries memories in both `project:<current-repo>` and `global` scopes
- **AND** results are merged into a single ranked list

### Requirement: Global score discount

The system MUST apply a configurable discount factor (default: 0.7) to global scope scores during merge to prevent drowning out project-specific context.

#### Scenario: Score calculation
- **WHEN** a project memory scores 0.9 and a global memory scores 0.9
- **THEN** the project memory retains 0.9
- **AND** the global memory is discounted to 0.63 (0.9 × 0.7)

### Requirement: Scope metadata in results

The system MUST include scope information in recall results so users can identify the source of each memory.

#### Scenario: Result metadata
- **WHEN** recall results are returned
- **THEN** each result includes `metadata.scope: "project"` or `metadata.scope: "global"`
- **AND** each result includes `metadata.source: "global"` for global memories (distinct from project source)

### Requirement: Global scope inclusion toggle

The system MUST respect a configuration option `includeGlobalScope` (default: `true`) to control whether global memories are included in recall.

#### Scenario: Global inclusion disabled
- **WHEN** `includeGlobalScope` is set to `false`
- **THEN** `memory_search` only queries the project scope
- **AND** no global memories appear in results

### Requirement: Dual-scope recall for auto-recall

The system MUST also apply dual-scope recall during automatic system-transform recall, not just for manual `memory_search`.

#### Scenario: Auto-recall includes global
- **WHEN** the system performs automatic context injection during system.transform
- **THEN** global memories relevant to the query are included with appropriate discounting
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# memory-global-detection Specification

## Purpose

Automatically detect memory content that may be applicable across projects using heuristic keyword matching, and prompt the user to confirm promotion to global scope.

## Requirements

### Requirement: Global keyword detection

The system MUST analyze memory content against a predefined list of cross-project keywords and calculate a match score.

#### Scenario: High keyword match triggers promotion prompt
- **WHEN** memory content matches 2 or more global keywords
- **THEN** the system presents a promotion prompt to the user

#### Scenario: Low keyword match does not trigger prompt
- **WHEN** memory content matches fewer than 2 global keywords
- **THEN** no promotion prompt is shown and memory is stored as project-scoped

### Requirement: Keyword list coverage

The system MUST check for keywords from these categories:
- Linux distributions (alpine, debian, ubuntu, centos, fedora, arch)
- Containers (docker, dockerfile, docker-compose, containerd)
- Orchestration (kubernetes, k8s, helm, kubectl)
- Shells/Systems (bash, shell, linux, unix, posix, busybox)
- Web servers (nginx, apache, caddy)
- Databases (postgres, postgresql, mysql, redis, mongodb, sqlite)
- Cloud platforms (aws, gcp, azure, digitalocean)
- Version control (git, github, gitlab, bitbucket)
- Protocols (api, rest, graphql, grpc, http, https)
- Package managers (npm, yarn, pnpm, pip, cargo, make, cmake)

### Requirement: Detection does not block storage

The system MUST NOT block memory storage while awaiting promotion confirmation.

#### Scenario: Memory stored while awaiting confirmation
- **WHEN** detection triggers promotion prompt
- **THEN** the memory is stored as project-scoped immediately
- **AND** the promotion prompt is presented asynchronously

### Requirement: Keyword detection configurable

The system MUST allow configuration of the global detection threshold via `global_detection_threshold` config (default: 2).
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# memory-global-list Specification

## Purpose

Provide a tool for users to view and search all global-scoped memories across projects.

## Requirements

### Requirement: List global memories tool

The system MUST provide a `memory_global_list` tool that returns all memories with `scope: "global"`.

#### Scenario: List all global memories
- **WHEN** user invokes `memory_global_list`
- **THEN** the system returns all global-scoped memories with their IDs, content, and timestamps

#### Scenario: Search within global memories
- **WHEN** user invokes `memory_global_list` with a search query
- **THEN** the system returns global memories matching the query, ranked by relevance

### Requirement: Global memory details

The system MUST include usage statistics for each global memory.

#### Scenario: Memory usage tracking
- **WHEN** global memories are returned
- **THEN** each entry includes:
- `lastRecalled`: timestamp of most recent recall
- `recallCount`: total number of times recalled
- `projectCount`: number of distinct projects that have recalled this memory

### Requirement: Global memory filtering

The system MUST support filtering global memories by usage status.

#### Scenario: Filter unused memories
- **WHEN** user invokes `memory_global_list` with `filter: "unused"`
- **THEN** only memories not recalled in the past 30 days are returned

#### Scenario: Filter frequently used memories
- **WHEN** user invokes `memory_global_list` with `filter: "frequently_used"`
- **THEN** memories with high recall counts are prioritized in results
Loading
Loading