Skip to content

Commit 3461f86

Browse files
Add memory plugin with semantic search and session planning (#145)
* Add memory plugin with semantic search and session planning * Clarify Memory Plugin is optional in docs
1 parent da283a8 commit 3461f86

File tree

132 files changed

+11448
-2834
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+11448
-2834
lines changed

.gitguardian.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ paths-ignore:
1717
- '**/*.spec.tsx'
1818
- 'temp/**'
1919
- '**/*.md'
20-
- '**/test/**'
21-
- '**/__tests__/**'
2220

2321
# Specific files to ignore
2422
files-ignore:

Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ COPY --chown=node:node package.json pnpm-workspace.yaml pnpm-lock.yaml ./
4141
COPY --chown=node:node shared/package.json ./shared/
4242
COPY --chown=node:node backend/package.json ./backend/
4343
COPY --chown=node:node frontend/package.json ./frontend/
44+
COPY --chown=node:node packages/memory ./packages/memory/
4445

4546
RUN pnpm install --frozen-lockfile
4647

@@ -52,8 +53,10 @@ COPY backend ./backend
5253
COPY frontend/src ./frontend/src
5354
COPY frontend/public ./frontend/public
5455
COPY frontend/index.html frontend/vite.config.ts frontend/tsconfig*.json frontend/components.json frontend/eslint.config.js ./frontend/
56+
COPY packages/memory ./packages/memory
5557

5658
RUN pnpm --filter frontend build
59+
RUN pnpm --filter @opencode-manager/memory build
5760

5861
FROM base AS runner
5962

@@ -80,6 +83,7 @@ ENV PORT=5003
8083
ENV OPENCODE_SERVER_PORT=5551
8184
ENV DATABASE_PATH=/app/data/opencode.db
8285
ENV WORKSPACE_PATH=/workspace
86+
ENV NODE_PATH=/opt/opencode-plugins/node_modules
8387

8488
COPY --from=deps --chown=node:node /app/node_modules ./node_modules
8589
COPY --from=builder /app/shared ./shared
@@ -90,6 +94,16 @@ COPY package.json pnpm-workspace.yaml ./
9094
RUN mkdir -p /app/backend/node_modules/@opencode-manager && \
9195
ln -s /app/shared /app/backend/node_modules/@opencode-manager/shared
9296

97+
COPY --from=builder /app/packages/memory /opt/opencode-plugins/src
98+
99+
RUN cd /opt/opencode-plugins/src && npm install
100+
101+
RUN mkdir -p /opt/opencode-plugins/node_modules/@opencode-manager/memory && \
102+
cp -r /opt/opencode-plugins/src/dist/* /opt/opencode-plugins/node_modules/@opencode-manager/memory/ && \
103+
cp /opt/opencode-plugins/src/package.json /opt/opencode-plugins/node_modules/@opencode-manager/memory/ && \
104+
cp /opt/opencode-plugins/src/config.json /opt/opencode-plugins/node_modules/@opencode-manager/memory/config.json 2>/dev/null || true && \
105+
cp -r /opt/opencode-plugins/src/node_modules/* /opt/opencode-plugins/node_modules/ 2>/dev/null || true
106+
93107
COPY scripts/docker-entrypoint.sh /docker-entrypoint.sh
94108
RUN chmod +x /docker-entrypoint.sh
95109

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ On first launch, you'll be prompted to create an admin account. That's it!
7676
- **OAuth Support** - Secure OAuth login for Anthropic and GitHub Copilot
7777
- **Custom Agents** - Create agents with custom system prompts and tool permissions
7878
- **MCP Servers** - Add local or remote MCP servers with pre-built templates
79+
- **Memory Plugin** - Persistent project knowledge with semantic search, planning state, and compaction awareness
7980

8081
### Mobile & PWA
8182
- **Mobile-First Design** - Responsive UI optimized for mobile

backend/src/db/migration-runner.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { Database } from 'bun:sqlite'
2+
import { logger } from '../utils/logger'
3+
4+
export interface Migration {
5+
version: number
6+
name: string
7+
up(db: Database): void
8+
down(db: Database): void
9+
}
10+
11+
interface MigrationRecord {
12+
version: number
13+
name: string
14+
applied_at: number
15+
}
16+
17+
function ensureMigrationsTable(db: Database): void {
18+
db.run(`
19+
CREATE TABLE IF NOT EXISTS schema_migrations (
20+
version INTEGER PRIMARY KEY,
21+
name TEXT NOT NULL,
22+
applied_at INTEGER NOT NULL
23+
)
24+
`)
25+
}
26+
27+
function getAppliedVersions(db: Database): Set<number> {
28+
const rows = db.prepare('SELECT version FROM schema_migrations ORDER BY version').all() as MigrationRecord[]
29+
return new Set(rows.map(r => r.version))
30+
}
31+
32+
function markApplied(db: Database, migration: Migration): void {
33+
db.prepare('INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)')
34+
.run(migration.version, migration.name, Date.now())
35+
}
36+
37+
function markReverted(db: Database, version: number): void {
38+
db.prepare('DELETE FROM schema_migrations WHERE version = ?').run(version)
39+
}
40+
41+
export function migrate(db: Database, migrations: Migration[]): void {
42+
ensureMigrationsTable(db)
43+
44+
const applied = getAppliedVersions(db)
45+
const sorted = [...migrations].sort((a, b) => a.version - b.version)
46+
const pending = sorted.filter(m => !applied.has(m.version))
47+
48+
if (pending.length === 0) {
49+
logger.info('Database schema is up to date')
50+
return
51+
}
52+
53+
logger.info(`Running ${pending.length} pending migration(s)`)
54+
55+
for (const migration of pending) {
56+
logger.info(`Applying migration ${migration.version}: ${migration.name}`)
57+
db.run('BEGIN TRANSACTION')
58+
try {
59+
migration.up(db)
60+
markApplied(db, migration)
61+
db.run('COMMIT')
62+
logger.info(`Migration ${migration.version} applied successfully`)
63+
} catch (error) {
64+
db.run('ROLLBACK')
65+
logger.error(`Migration ${migration.version} failed:`, error)
66+
throw error
67+
}
68+
}
69+
70+
logger.info('All migrations applied successfully')
71+
}
72+
73+
export function rollback(db: Database, migrations: Migration[], targetVersion?: number): void {
74+
ensureMigrationsTable(db)
75+
76+
const applied = getAppliedVersions(db)
77+
const sorted = [...migrations]
78+
.filter(m => applied.has(m.version))
79+
.sort((a, b) => b.version - a.version)
80+
81+
if (sorted.length === 0) {
82+
logger.info('No migrations to rollback')
83+
return
84+
}
85+
86+
const latest = sorted[0]
87+
if (!latest) {
88+
logger.info('No migrations to rollback')
89+
return
90+
}
91+
const target = targetVersion ?? latest.version - 1
92+
93+
const toRevert = sorted.filter(m => m.version > target)
94+
95+
if (toRevert.length === 0) {
96+
logger.info('No migrations to rollback')
97+
return
98+
}
99+
100+
logger.info(`Rolling back ${toRevert.length} migration(s) to version ${target}`)
101+
102+
for (const migration of toRevert) {
103+
logger.info(`Reverting migration ${migration.version}: ${migration.name}`)
104+
db.run('BEGIN TRANSACTION')
105+
try {
106+
migration.down(db)
107+
markReverted(db, migration.version)
108+
db.run('COMMIT')
109+
logger.info(`Migration ${migration.version} reverted successfully`)
110+
} catch (error) {
111+
db.run('ROLLBACK')
112+
logger.error(`Rollback of migration ${migration.version} failed:`, error)
113+
throw error
114+
}
115+
}
116+
117+
logger.info('Rollback completed successfully')
118+
}

backend/src/db/migrations.ts

Lines changed: 0 additions & 188 deletions
This file was deleted.

0 commit comments

Comments
 (0)