feat: add opencode plugin and xdg defaults#9
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new OpenCode plugin export (createObsxaPlugin) with hooks for chat/tool/event/system; centralizes default DB resolution to an XDG-compliant path via getDefaultDbPath(); removes hardcoded "./obsxa.db" defaults across APIs and CLI; adds backup Changes
Sequence DiagramsequenceDiagram
participant Agent as Agent
participant Plugin as OpenCode Plugin
participant Obsxa as Obsxa API
participant DB as SQLite DB
participant LLM as LLM/System
Agent->>Plugin: chat.message(payload)
activate Plugin
Plugin->>Obsxa: computeInputHash(text)
Obsxa->>DB: SELECT WHERE (project_id,input_hash)
DB-->>Obsxa: row or null
alt not found
Obsxa->>DB: INSERT observation
DB-->>Obsxa: inserted row
else found
Obsxa->>DB: UPDATE frequency (optional)
DB-->>Obsxa: updated row
end
Obsxa-->>Plugin: observation result
deactivate Plugin
Agent->>Plugin: tool.execute.after(result)
activate Plugin
Plugin->>Obsxa: computeInputHash(tool output)
Obsxa->>DB: SELECT by (project_id,input_hash)
DB-->>Obsxa: row or null
alt not exists and tool writable
Obsxa->>DB: INSERT measurement
Obsxa->>DB: INSERT relation (derived_from -> message_obs)
end
deactivate Plugin
Agent->>Plugin: experimental.chat.system.transform(request)
activate Plugin
Plugin->>Obsxa: query recent observations (possibly cross-project)
Obsxa->>DB: SELECT recent observations
DB-->>Obsxa: matching observations
Obsxa-->>Plugin: formatted observations
Plugin->>LLM: inject observations into system prompt
deactivate Plugin
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
3 issues found across 13 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="test/opencode.test.ts">
<violation number="1" location="test/opencode.test.ts:101">
P2: Hook instances created in this suite are not tracked/destroyed, which can leak open DB handles and make temp directory cleanup flaky.</violation>
</file>
<file name="src/opencode.ts">
<violation number="1" location="src/opencode.ts:151">
P2: System-context injection uses a global last-message buffer, which can leak/mix context across concurrent sessions.</violation>
<violation number="2" location="src/opencode.ts:198">
P2: When a chat message dedupes, the session mapping is skipped, so subsequent tool observations may miss `derived_from` links.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 48f24d00ca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
🤖 Augment PR SummarySummary: Adds an OpenCode plugin export to capture hook-based observations and inject recent context into chat system prompts. Changes:
Technical Notes: 🤖 Was this summary useful? React with 👍 or 👎 |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Line 58: The devDependency "@opencode-ai/plugin" is pinned to the "latest" tag
which yields non-reproducible installs; update the package.json entry for
"@opencode-ai/plugin" to a specific semver version or a constrained range (e.g.,
a concrete version like "1.2.3" or a caret/tilde range) to ensure deterministic
CI and local installs and commit the updated package.json (and lockfile) so
installs remain reproducible.
In `@src/core/db-path.ts`:
- Around line 9-21: The current guard throws if home is empty before considering
environment roots; change the logic so you first check for an env-provided data
root (env.XDG_DATA_HOME on POSIX or env.LOCALAPPDATA on Windows) and only
require/validate home when no env data root exists and you need to build a
platform-specific fallback. Concretely: compute/trim xdgDataHome/localAppData
first, if that value exists use it as dataHome; otherwise validate that home is
non-empty and then compute fallbackDataHome (using platform, win32.join or join)
and assign dataHome accordingly — update the code around the variables env,
xdgDataHome, fallbackDataHome and dataHome to follow this flow.
- Around line 2-3: The code uses the OS-native join (imported as join) which
ignores the explicit platform argument; update getDefaultDbPath so Windows
branches use win32.join and all non-Windows branches use path.posix.join (import
posix from "node:path" or reference path.posix) instead of the generic join,
ensuring you choose the join implementation based on the platform parameter
(e.g., when platform === "win32" use win32.join, otherwise use posix.join) so
returned paths match the requested platform.
In `@src/index.ts`:
- Line 130: Move the raw CREATE INDEX statement for
idx_observations_project_input_hash out of runtime code and add it as a proper
drizzle-kit migration so schema changes are recorded in the drizzle journal and
SCHEMA_VERSION; create a new migration that runs CREATE INDEX IF NOT EXISTS
idx_observations_project_input_hash ON observations(project_id, input_hash) and
ensure the migration is applied during startup (via your existing drizzle
migration runner) instead of executing the index creation in src/index.ts at
runtime.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 391ccb12-38b8-4623-b64f-587d5747abfa
📒 Files selected for processing (13)
CHANGELOG.mdbuild.config.tspackage.jsonsrc/ai.tssrc/commands/_db.tssrc/commands/backup.tssrc/core/db-path.tssrc/core/observation.tssrc/index.tssrc/opencode.tssrc/types.tstest/db-path.test.tstest/opencode.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: cubic · AI code reviewer
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use TypeScript with strict mode enabled
Do not useas any,@ts-ignore, or@ts-expect-errortype assertions
Files:
src/types.tstest/db-path.test.tssrc/core/db-path.tssrc/core/observation.tssrc/opencode.tssrc/index.tsbuild.config.tssrc/ai.tstest/opencode.test.tssrc/commands/_db.tssrc/commands/backup.ts
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use oxlint + oxfmt for linting and formatting
Files:
src/types.tssrc/core/db-path.tssrc/core/observation.tssrc/opencode.tssrc/index.tssrc/ai.tssrc/commands/_db.tssrc/commands/backup.ts
src/types.ts
📄 CodeRabbit inference engine (AGENTS.md)
Place shared types in src/types.ts
Files:
src/types.ts
**/*.{ts,tsx,js,mjs,mts}
📄 CodeRabbit inference engine (AGENTS.md)
Use ESM only with "type": "module" in package.json
Files:
src/types.tstest/db-path.test.tssrc/core/db-path.tssrc/core/observation.tssrc/opencode.tssrc/index.tsbuild.config.tssrc/ai.tstest/opencode.test.tssrc/commands/_db.tssrc/commands/backup.ts
test/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use vitest for testing
Files:
test/db-path.test.tstest/opencode.test.ts
src/core/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/core/**/*.ts: Use SQLite via better-sqlite3 + drizzle-orm for database operations
Place database, schema, and CRUD operations in src/core/
Files:
src/core/db-path.tssrc/core/observation.ts
src/index.ts
📄 CodeRabbit inference engine (AGENTS.md)
Export public API from src/index.ts as a barrel export
Files:
src/index.ts
src/ai.ts
📄 CodeRabbit inference engine (AGENTS.md)
Export AI SDK tools from src/ai.ts
Files:
src/ai.ts
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Use
@mentionsyntax in documentation to reference agents (e.g.,@AGENTS.md)
Files:
CHANGELOG.md
src/commands/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/commands/**/*.ts: Place CLI command handlers in src/commands/ using citty framework
Support --json and --toon output formats for CLI commands
Files:
src/commands/_db.tssrc/commands/backup.ts
🧠 Learnings (15)
📚 Learning: 2026-03-10T19:06:40.289Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/observation.ts:375-393
Timestamp: 2026-03-10T19:06:40.289Z
Learning: In the obsxa project (`src/core/observation.ts` and related files), the database driver is `better-sqlite3` — a synchronous, single-connection SQLite driver. There is no concurrency possible, so read-modify-write patterns (e.g., read frequency, increment, write back) are safe and do not lose updates. Concurrency-based race condition comments are not applicable here.
Applied to files:
src/types.tssrc/index.tssrc/ai.tssrc/commands/_db.ts
📚 Learning: 2026-03-10T19:06:20.854Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/cluster.ts:84-97
Timestamp: 2026-03-10T19:06:20.854Z
Learning: In the obsxa project (`src/core/cluster.ts` and similar files), the database driver is `better-sqlite3`, which is fully synchronous and single-connection. Concurrent insert races (TOCTOU) cannot occur within the same process, so check-then-insert patterns are safe and do not need duplicate-key error handling.
Applied to files:
src/types.tssrc/index.tssrc/ai.tssrc/commands/_db.ts
📚 Learning: 2026-03-10T19:31:33.966Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T19:31:33.966Z
Learning: Applies to src/core/**/*.ts : Use SQLite via better-sqlite3 + drizzle-orm for database operations
Applied to files:
src/types.tssrc/core/db-path.tssrc/index.tspackage.jsonsrc/ai.tssrc/commands/_db.ts
📚 Learning: 2026-03-10T19:31:33.966Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T19:31:33.966Z
Learning: Applies to src/core/**/*.ts : Place database, schema, and CRUD operations in src/core/
Applied to files:
src/core/db-path.tssrc/index.ts
📚 Learning: 2026-03-10T19:06:24.096Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/db.ts:81-93
Timestamp: 2026-03-10T19:06:24.096Z
Learning: In the obsxa repo (TypeScript, Node.js, better-sqlite3), read-then-insert patterns discussed in src/core/dedup.ts (scan()) and src/core/cluster.ts are intentionally safe from race conditions due to better-sqlite3 being fully synchronous and blocking the event loop, preventing concurrent interleaving within the same process. Do not flag these as race conditions. Apply this understanding to TypeScript files under src/core/ when evaluating similar read-then-insert patterns; they may be safe in this environment, but still verify that there are no cross-process or multi-threaded interactions that could reintroduce race-like scenarios.
Applied to files:
src/core/db-path.tssrc/core/observation.ts
📚 Learning: 2026-03-10T19:06:20.854Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/cluster.ts:84-97
Timestamp: 2026-03-10T19:06:20.854Z
Learning: In the obsxa project, for files under src/core that use the better-sqlite3 driver, inserts using a check-then-insert pattern are considered safe from TOCTOU issues and do not require duplicate-key error handling, provided there is a single Database instance per process and no concurrent connections. Apply this guideline to src/core/cluster.ts and similar files. If multiple connections or multi-process usage is possible, retain appropriate duplicate-key error handling.
Applied to files:
src/core/db-path.tssrc/core/observation.ts
📚 Learning: 2026-03-10T19:31:33.966Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T19:31:33.966Z
Learning: Applies to src/backup.ts : Implement database backup/restore functionality in src/backup.ts
Applied to files:
src/index.tssrc/ai.tssrc/commands/_db.tssrc/commands/backup.ts
📚 Learning: 2026-03-10T19:31:33.966Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T19:31:33.966Z
Learning: Applies to drizzle/**/*.sql : Use drizzle-kit for schema migrations
Applied to files:
src/index.ts
📚 Learning: 2026-03-10T19:31:33.967Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T19:31:33.967Z
Learning: Applies to drizzle/**/*.sql : Route all SQLite schema changes through drizzle-kit migrations
Applied to files:
src/index.ts
📚 Learning: 2026-03-10T19:31:33.966Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T19:31:33.966Z
Learning: Applies to src/cli.ts : Use src/cli.ts as the CLI entrypoint
Applied to files:
build.config.ts
📚 Learning: 2026-03-10T19:31:33.966Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T19:31:33.966Z
Learning: Applies to src/index.ts : Export public API from src/index.ts as a barrel export
Applied to files:
build.config.ts
📚 Learning: 2026-03-10T19:31:33.966Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T19:31:33.966Z
Learning: Applies to src/ai.ts : Export AI SDK tools from src/ai.ts
Applied to files:
build.config.tspackage.jsonsrc/ai.ts
📚 Learning: 2026-03-10T19:31:33.966Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T19:31:33.966Z
Learning: Applies to **/*.{ts,tsx,js,mjs,mts} : Use ESM only with "type": "module" in package.json
Applied to files:
package.json
📚 Learning: 2026-03-10T19:06:24.096Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/db.ts:81-93
Timestamp: 2026-03-10T19:06:24.096Z
Learning: In the `obsxa` repository (TypeScript, Node.js, better-sqlite3), read-then-insert patterns (e.g., in `src/core/dedup.ts` scan() and `src/core/cluster.ts`) are intentionally safe without DB-level unique constraints because `better-sqlite3` is fully synchronous — it blocks the event loop on every call, making concurrent interleaving within the same process impossible. Do not flag these as race conditions.
Applied to files:
src/ai.tstest/opencode.test.ts
📚 Learning: 2026-03-10T19:31:33.967Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T19:31:33.967Z
Learning: Applies to src/commands/**/*.ts : Support --json and --toon output formats for CLI commands
Applied to files:
src/commands/backup.ts
🧬 Code graph analysis (7)
test/db-path.test.ts (1)
src/core/db-path.ts (1)
getDefaultDbPath(4-25)
src/core/observation.ts (4)
src/index.ts (1)
Observation(32-32)src/types.ts (1)
Observation(50-78)src/core/db.ts (1)
observations(15-45)src/core/mappers.ts (1)
toObservation(45-75)
src/opencode.ts (2)
src/index.ts (2)
ObsxaInstance(173-182)createObsxa(197-257)src/core/db-path.ts (1)
getDefaultDbPath(4-25)
src/index.ts (3)
src/types.ts (1)
ObsxaOptions(2-11)src/core/db-path.ts (1)
getDefaultDbPath(4-25)src/backup.ts (1)
backupDatabase(38-62)
src/ai.ts (1)
src/core/db-path.ts (1)
getDefaultDbPath(4-25)
test/opencode.test.ts (3)
src/opencode.ts (1)
createObsxaPlugin(138-388)src/index.ts (2)
createObsxaPlugin(55-55)createObsxa(197-257)src/core/db.ts (1)
observations(15-45)
src/commands/backup.ts (2)
src/core/db-path.ts (1)
getDefaultDbPath(4-25)src/commands/_db.ts (1)
output(23-25)
🔇 Additional comments (14)
src/core/observation.ts (1)
124-131: Looks good.Clean implementation following the existing
get(id)pattern. Query logic is correct. One minor note: there's no composite index on(projectId, inputHash)in the schema, so this could be slow on large tables. If this gets called frequently in hot paths (like the opencode plugin dedup), consider adding an index.build.config.ts (1)
8-13: LGTM.New entry point added correctly. Matches the
./opencodeexport in package.json.package.json (1)
33-36: Export looks correct.Paths align with build.config.ts output. Good to go.
src/opencode.ts (5)
64-89: LRU cache implementation is correct.The delete-then-set pattern on access moves the key to the end, and eviction removes the oldest (first) key. This works because Map maintains insertion order.
170-229: chat.message hook looks solid.Good dedup logic, appropriate guards for null/short messages, and proper context capture. The 20-char minimum threshold is reasonable for filtering noise.
279-286: Good defensive handling of duplicate relations.Silently dropping UNIQUE constraint violations here is the right call - prevents noise when the same derived_from relation is attempted twice.
362-385: System transform injection looks correct.Always pushes the instruction, conditionally appends observations. The cross-project search (undefined projectId) matches the comment intent.
147-149:⚠️ Potential issue | 🟡 MinorProject creation could silently succeed on race.
If two plugin instances start simultaneously with the same projectId, both call
get()→ null, both calladd(). The second add will throw a UNIQUE constraint error. Might want to wrap this in try/catch or use INSERT OR IGNORE semantics.That said, this is unlikely in practice since plugins are typically singleton per process.
Possible fix
- if (!obsxa.project.get(projectId)) { - obsxa.project.add({ id: projectId, name: projectName }); - } + if (!obsxa.project.get(projectId)) { + try { + obsxa.project.add({ id: projectId, name: projectName }); + } catch (err) { + // Ignore if another process created it first + const msg = err instanceof Error ? err.message : String(err); + if (!msg.includes("UNIQUE constraint") && !msg.includes("SQLITE_CONSTRAINT")) { + throw err; + } + } + }⛔ Skipped due to learnings
Learnt from: oritwoen Repo: oritwoen/obsxa PR: 2 File: src/core/db.ts:81-93 Timestamp: 2026-03-10T19:06:24.096Z Learning: In the `obsxa` repository (TypeScript, Node.js, better-sqlite3), read-then-insert patterns (e.g., in `src/core/dedup.ts` scan() and `src/core/cluster.ts`) are intentionally safe without DB-level unique constraints because `better-sqlite3` is fully synchronous — it blocks the event loop on every call, making concurrent interleaving within the same process impossible. Do not flag these as race conditions.Learnt from: oritwoen Repo: oritwoen/obsxa PR: 2 File: src/core/cluster.ts:84-97 Timestamp: 2026-03-10T19:06:20.854Z Learning: In the obsxa project (`src/core/cluster.ts` and similar files), the database driver is `better-sqlite3`, which is fully synchronous and single-connection. Concurrent insert races (TOCTOU) cannot occur within the same process, so check-then-insert patterns are safe and do not need duplicate-key error handling.Learnt from: oritwoen Repo: oritwoen/obsxa PR: 2 File: src/core/observation.ts:375-393 Timestamp: 2026-03-10T19:06:40.289Z Learning: In the obsxa project (`src/core/observation.ts` and related files), the database driver is `better-sqlite3` — a synchronous, single-connection SQLite driver. There is no concurrency possible, so read-modify-write patterns (e.g., read frequency, increment, write back) are safe and do not lose updates. Concurrency-based race condition comments are not applicable here.Learnt from: aeitwoen Repo: oritwoen/obsxa PR: 2 File: src/core/observation.ts:94-117 Timestamp: 2026-03-10T19:17:03.432Z Learning: In the obsxa project (src/core/observation.ts), `addMany` is intentionally designed for partial success on large batch imports (e.g., agent pipelines with messy data). The design intent is: if some records fail validation, keep the ones that succeeded and report the ones that failed. Do NOT suggest wrapping the entire addMany loop in a transaction. However, note that the current implementation using Array.prototype.map with throws does not actually achieve partial success — a throw inside .map() propagates and the caller receives an exception rather than partial results.src/types.ts (1)
3-4: Type change aligns with implementation.Making
dboptional here matchescreateObsxanow usinggetDefaultDbPath()as fallback. Doc comment is accurate.CHANGELOG.md (1)
3-9: Good breaking change documentation.Clear migration path for users who want the old behavior. This is exactly what changelog entries should look like.
src/commands/_db.ts (1)
6-12: Clean delegation of default path.Removing the hardcoded default here and letting
createObsxahandle it viagetDefaultDbPath()is the right approach. Single source of truth.src/ai.ts (2)
3-3: Import change looks correct.Centralizing the default path logic is good. Consistent with changes in other files.
7-12: Remove this comment - the premise is factually incorrect.This file is new (first commit 48f24d0), so there was never an
isAbsolutecheck to remove. The review appears based on inaccurate summary information. The current code is fine:getDefaultDbPath()handles safe XDG paths, user input gets validated for..(path traversal) and.dbextension. No issue here.test/opencode.test.ts (1)
77-82:⚠️ Potential issue | 🟠 MajorOnly track file-backed DB tests; skip
:memory:cases.The test at lines 77-82 uses
:memory:, so untracked hooks there don't cause rmSync failures. The real issue is in "createObsxaPlugin factory" (lines 99-205), where file-backed tests callawait plugin(...)directly without pushing totrackedHooks. This leavesbetter-sqlite3handles open, causingrmSync(dbDir)to fail withEBUSYon Windows.Tests like "ensures project exists after factory call" (line 111), "returns hooks object with expected keys" (line 130+), and others in that block don't track hooks and don't manually call
destroy(). They need:const plugin = createObsxaPlugin({ db: dbPath, projectId: "test-project" }); -await plugin({ project: { id: "test-project" }, directory: "/tmp", worktree: "/tmp" }); +trackedHooks.push(await plugin({ project: { id: "test-project" }, directory: "/tmp", worktree: "/tmp" }));or manually call
hooks.destroy()before the test ends. Skipping this leaves the DB locked during cleanup.⛔ Skipped due to learnings
Learnt from: oritwoen Repo: oritwoen/obsxa PR: 2 File: src/core/db.ts:81-93 Timestamp: 2026-03-10T19:06:24.096Z Learning: In the `obsxa` repository (TypeScript, Node.js, better-sqlite3), read-then-insert patterns (e.g., in `src/core/dedup.ts` scan() and `src/core/cluster.ts`) are intentionally safe without DB-level unique constraints because `better-sqlite3` is fully synchronous — it blocks the event loop on every call, making concurrent interleaving within the same process impossible. Do not flag these as race conditions.Learnt from: oritwoen Repo: oritwoen/obsxa PR: 2 File: src/core/cluster.ts:84-97 Timestamp: 2026-03-10T19:06:20.854Z Learning: In the obsxa project (`src/core/cluster.ts` and similar files), the database driver is `better-sqlite3`, which is fully synchronous and single-connection. Concurrent insert races (TOCTOU) cannot occur within the same process, so check-then-insert patterns are safe and do not need duplicate-key error handling.Learnt from: oritwoen Repo: oritwoen/obsxa PR: 2 File: src/core/observation.ts:375-393 Timestamp: 2026-03-10T19:06:40.289Z Learning: In the obsxa project (`src/core/observation.ts` and related files), the database driver is `better-sqlite3` — a synchronous, single-connection SQLite driver. There is no concurrency possible, so read-modify-write patterns (e.g., read frequency, increment, write back) are safe and do not lose updates. Concurrency-based race condition comments are not applicable here.Learnt from: CR Repo: oritwoen/obsxa PR: 0 File: AGENTS.md:0-0 Timestamp: 2026-03-10T19:31:33.966Z Learning: Applies to src/core/**/*.ts : Use SQLite via better-sqlite3 + drizzle-orm for database operations
|
CodeAnt AI is reviewing your PR. |
Sequence DiagramThis PR adds an OpenCode plugin that opens obsxa using an XDG-compliant default database path, captures observations from chat messages, tools, and events, and then injects relevant observations back into the model system prompt. sequenceDiagram
participant OpenCode
participant ObsxaPlugin
participant ObsxaCore
OpenCode->>ObsxaPlugin: Initialize plugin with project info
ObsxaPlugin->>ObsxaCore: Open or create database using XDG default path and ensure project
OpenCode->>ObsxaPlugin: chat.message hook with user message
ObsxaPlugin->>ObsxaCore: Deduplicate and record pattern observation for session
OpenCode->>ObsxaPlugin: tool.execute.after hook with tool result
ObsxaPlugin->>ObsxaCore: Record measurement observation and link to chat observation
OpenCode->>ObsxaPlugin: system.transform hook before model call
ObsxaPlugin->>ObsxaCore: Search recent observations relevant to query
ObsxaCore-->>ObsxaPlugin: Return matching observations
ObsxaPlugin-->>OpenCode: Inject observation instruction and context into system messages
Generated by CodeAnt AI |
|
CodeAnt AI finished reviewing your PR. |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/core/db-path.ts`:
- Around line 12-35: In getDefaultDbPath, currently XDG_DATA_HOME and
localAppData are only checked for non-empty strings, allowing relative paths;
update the checks to ensure the selected base is absolute using the
platform-appropriate isAbsolute (use win32.isAbsolute when platform === "win32",
otherwise posix.isAbsolute) and treat relative values as absent (ignore them and
continue to the next fallback) or throw a clear error; modify the validation for
XDG_DATA_HOME, localAppData, and home to use the isAbsolute checks before
constructing paths like win32.join(..., "obsxa", "obsxa.db") or posix.join(...).
In `@src/opencode.ts`:
- Around line 422-427: The search call is currently unscoped
(obsxa.search.search(query, undefined, maxObs)) which allows cross-project
recall; change the lookup to default to the current project scope by passing the
current projectId (from the surrounding context) as the scope argument instead
of undefined, e.g. use obsxa.search.search(query, projectId, maxObs), and only
allow cross-project/global recall when an explicit flag is passed; update the
call sites that build `query` (using latestMessageBufferBySession,
latestMessageBuffer, projectName and _sysInput.sessionID) to use the projectId
parameter so recalls are confined to the current project unless explicitly
requested.
- Around line 120-123: Sanitize observation titles before embedding them into
the obsxa-context wrapper: in the mapping that produces the lines constant (the
lines variable that uses r.observation.title) and the code path that injects the
title as system text (the obsxa-context insertion around lines ~430-431),
collapse any newlines to a single space and escape HTML/tag delimiters (e.g.,
replace '<' with '<' and '>' with '>' or otherwise neutralize sequences
like '</obsxa-context>') so titles cannot break out of the wrapper or inject
instructions.
- Around line 292-295: The dedupe branch currently increments the existing
observation's frequency and returns, which omits adding the new derived_from
relation; update the branch where findByHash(...) returns an existingId so that
after calling obsxa.observation.incrementFrequency(existingId) you also add the
same derived_from relation the non-dedupe path creates (use the same
relation-method and parameters used later in the non-dedup path) before
returning, ensuring the new message/session is linked to the reused observation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7491fdd9-8ed5-46b8-8b16-de3829417227
📒 Files selected for processing (4)
src/ai.tssrc/core/db-path.tssrc/opencode.tstest/opencode.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/ai.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: cubic · AI code reviewer
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.ts: Use TypeScript with strict mode enabled (all strict checks enabled)
Do not useas any,@ts-ignore, or@ts-expect-errorTypeScript escape hatches
Files:
src/opencode.tssrc/core/db-path.ts
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use ESM only with
"type": "module"configuration
Files:
src/opencode.tssrc/core/db-path.ts
src/core/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use drizzle-orm with SQLite via libsql as the database layer
Files:
src/core/db-path.ts
🧠 Learnings (6)
📚 Learning: 2026-03-10T19:06:28.832Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/db.ts:81-93
Timestamp: 2026-03-10T19:06:28.832Z
Learning: In the `obsxa` repository (TypeScript, Node.js, better-sqlite3), read-then-insert patterns (e.g., in `src/core/dedup.ts` scan() and `src/core/cluster.ts`) are intentionally safe without DB-level unique constraints because `better-sqlite3` is fully synchronous — it blocks the event loop on every call, making concurrent interleaving within the same process impossible. Do not flag these as race conditions.
Applied to files:
test/opencode.test.tssrc/opencode.ts
📚 Learning: 2026-03-10T19:06:35.925Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/cluster.ts:84-97
Timestamp: 2026-03-10T19:06:35.925Z
Learning: In the obsxa project (`src/core/cluster.ts` and similar files), the database driver is `better-sqlite3`, which is fully synchronous and single-connection. Concurrent insert races (TOCTOU) cannot occur within the same process, so check-then-insert patterns are safe and do not need duplicate-key error handling.
Applied to files:
src/opencode.ts
📚 Learning: 2026-03-10T19:06:48.549Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/observation.ts:375-393
Timestamp: 2026-03-10T19:06:48.549Z
Learning: In the obsxa project (`src/core/observation.ts` and related files), the database driver is `better-sqlite3` — a synchronous, single-connection SQLite driver. There is no concurrency possible, so read-modify-write patterns (e.g., read frequency, increment, write back) are safe and do not lose updates. Concurrency-based race condition comments are not applicable here.
Applied to files:
src/opencode.ts
📚 Learning: 2026-03-11T17:33:57.939Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 11
File: src/core/observation.ts:94-126
Timestamp: 2026-03-11T17:33:57.939Z
Learning: In the obsxa project (`src/core/observation.ts`), `addMany` is intentionally fail-fast: if any record fails validation (e.g., promoted status missing `promotedTo`) or any awaited call (`this.add`, `this.dismiss`, `this.archive`, `this.promote`) throws, the entire batch aborts and the error propagates to the caller. Do NOT suggest changing this to partial success, as it would alter the established contract and caller expectations.
Applied to files:
src/opencode.ts
📚 Learning: 2026-03-10T19:06:24.096Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/db.ts:81-93
Timestamp: 2026-03-10T19:06:24.096Z
Learning: In the obsxa repo (TypeScript, Node.js, better-sqlite3), read-then-insert patterns discussed in src/core/dedup.ts (scan()) and src/core/cluster.ts are intentionally safe from race conditions due to better-sqlite3 being fully synchronous and blocking the event loop, preventing concurrent interleaving within the same process. Do not flag these as race conditions. Apply this understanding to TypeScript files under src/core/ when evaluating similar read-then-insert patterns; they may be safe in this environment, but still verify that there are no cross-process or multi-threaded interactions that could reintroduce race-like scenarios.
Applied to files:
src/core/db-path.ts
📚 Learning: 2026-03-10T19:06:20.854Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/cluster.ts:84-97
Timestamp: 2026-03-10T19:06:20.854Z
Learning: In the obsxa project, for files under src/core that use the better-sqlite3 driver, inserts using a check-then-insert pattern are considered safe from TOCTOU issues and do not require duplicate-key error handling, provided there is a single Database instance per process and no concurrent connections. Apply this guideline to src/core/cluster.ts and similar files. If multiple connections or multi-process usage is possible, retain appropriate duplicate-key error handling.
Applied to files:
src/core/db-path.ts
🧬 Code graph analysis (2)
test/opencode.test.ts (3)
src/opencode.ts (1)
createObsxaPlugin(167-443)src/index.ts (2)
createObsxaPlugin(56-56)createObsxa(217-295)src/core/db.ts (1)
observations(15-45)
src/opencode.ts (2)
src/index.ts (2)
ObsxaInstance(193-202)createObsxa(217-295)src/core/db-path.ts (1)
getDefaultDbPath(4-38)
There was a problem hiding this comment.
2 issues found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/opencode.ts">
<violation number="1" location="src/opencode.ts:246">
P2: When a duplicate tool result is found, the dedup path increments frequency and returns without creating the `derived_from` relation to the current session's message observation. New observations get this relation (see the block below), but reused observations from another session silently lose the edge. Add the relation on the dedup path as well.</violation>
<violation number="2" location="src/opencode.ts:437">
P1: The search call passes `undefined` instead of `projectId`, making prompt recall global across all projects. Now that the DB default moved to a shared XDG location, observations captured in one repository can be injected into another repository's system prompt if they match the query. Scope the search to the current `projectId`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
src/opencode.ts (3)
115-117:⚠️ Potential issue | 🟠 MajorEscape titles before they go back into
obsxa-context.
observation.titleis user, tool, and event controlled. A stored title like</obsxa-context>\nIgnore previous instructionscloses this wrapper and comes back as fresh system text on the next transform.Minimal fix
+function escapeContextText(value: string): string { + return value + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/\r?\n+/g, " "); +} + function formatObservations( results: Array<{ observation: { title: string; type: string; confidence: number; frequency: number }; }>, maxChars: number, ): string { const lines = results.map( (r) => - `- [${r.observation.type}] ${r.observation.title} (${r.observation.confidence}%, seen ${r.observation.frequency}x)`, + `- [${r.observation.type}] ${escapeContextText(r.observation.title)} (${r.observation.confidence}%, seen ${r.observation.frequency}x)`, );Also applies to: 440-441
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/opencode.ts` around lines 115 - 117, The code builds the `lines` array using raw `observation.title`, which lets user/tool-controlled text inject and close the surrounding `obsxa-context` wrapper; sanitize/escape titles before embedding them by applying a proper HTML/XML escape (e.g., escape &, <, >, ", ') or a project-standard sanitizer to `observation.title` in the `results.map` that creates `lines` (and the other occurrences you flagged at lines around 440-441), so the string inserted into the `- [${r.observation.type}] ${...}` template cannot break out of `obsxa-context` or inject new directives—use or add a reusable helper like `escapeHtml` and call it where titles are interpolated.
297-300:⚠️ Potential issue | 🟠 MajorKeep the
derived_fromlink on the dedup path.When the same tool output shows up again, this branch bumps frequency and returns. The relation is only added below for newly inserted observations, so every later repeat loses the message that produced it.
Minimal fix
const existingId = await findByHash(obsxa, projectId, hash, hashCache); if (existingId !== undefined) { await obsxa.observation.incrementFrequency(existingId); + const msgObsId = getCacheValue(sessionMessageObs, toolInput.sessionID); + if (msgObsId !== undefined) { + try { + await obsxa.relation.add({ + fromObservationId: existingId, + toObservationId: msgObsId, + type: "derived_from", + }); + } catch (err) { + if (!isSqliteConstraintError(err)) { + logHookError("tool.execute.after.relation", err); + } + } + } return; }Also applies to: 325-341
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/opencode.ts` around lines 297 - 300, The dedup branch increments frequency but omits adding the derived_from relation; update the branch where existingId is returned (the code using findByHash, existingId, and obsxa.observation.incrementFrequency) to also create or ensure a derived_from relation from the new message/observation to existingId (using the same relation creation call used for new inserts on the non-duplicate path), passing projectId and the source message id/hash so repeated occurrences retain the derived_from link.
432-437:⚠️ Potential issue | 🟠 MajorDo not search every project by default here.
The DB now defaults to a shared XDG location. Passing
undefinedhere pulls hits from every project and injects them into this project's system prompt, so repo B can see repo A's observations if the query overlaps.Minimal fix
- const results = await obsxa.search.search(query, undefined, maxObs); + const results = await obsxa.search.search(query, projectId, maxObs);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/opencode.ts` around lines 432 - 437, The call to obsxa.search.search(query, undefined, maxObs) leaves the project/workspace filter undefined, which causes searches to return hits from every project; replace the undefined workspace/project argument with the current project's identifier (e.g., projectName or the appropriate project-scoped ID available in this module) so obsxa.search.search(query, projectName, maxObs) (or the equivalent project-scoped variable) is used; ensure the changed call is made where query is computed (using _sysInput.sessionID, latestMessageBufferBySession, latestMessageBuffer, projectName) so results are restricted to this project only.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/ai.ts`:
- Around line 9-14: The current guard only rejects filesystem absolute paths but
allows DB URLs like "file:///..." or "https://..." — update the validation
around dbPath/path so any string that looks like a URL scheme is rejected before
falling back to getDefaultDbPath(); e.g., check the original path variable (not
just the fallback dbPath) against a URL-scheme pattern such as
/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\// (or explicitly test for file: and http(s):) and
throw the same "Database path must be relative" error if it matches, then keep
the existing absolute, ".." and ".db" checks on dbPath. Ensure you reference and
modify the existing validation block that uses path, dbPath, isAbsolute(...) and
the following includes/endsWith checks.
---
Duplicate comments:
In `@src/opencode.ts`:
- Around line 115-117: The code builds the `lines` array using raw
`observation.title`, which lets user/tool-controlled text inject and close the
surrounding `obsxa-context` wrapper; sanitize/escape titles before embedding
them by applying a proper HTML/XML escape (e.g., escape &, <, >, ", ') or a
project-standard sanitizer to `observation.title` in the `results.map` that
creates `lines` (and the other occurrences you flagged at lines around 440-441),
so the string inserted into the `- [${r.observation.type}] ${...}` template
cannot break out of `obsxa-context` or inject new directives—use or add a
reusable helper like `escapeHtml` and call it where titles are interpolated.
- Around line 297-300: The dedup branch increments frequency but omits adding
the derived_from relation; update the branch where existingId is returned (the
code using findByHash, existingId, and obsxa.observation.incrementFrequency) to
also create or ensure a derived_from relation from the new message/observation
to existingId (using the same relation creation call used for new inserts on the
non-duplicate path), passing projectId and the source message id/hash so
repeated occurrences retain the derived_from link.
- Around line 432-437: The call to obsxa.search.search(query, undefined, maxObs)
leaves the project/workspace filter undefined, which causes searches to return
hits from every project; replace the undefined workspace/project argument with
the current project's identifier (e.g., projectName or the appropriate
project-scoped ID available in this module) so obsxa.search.search(query,
projectName, maxObs) (or the equivalent project-scoped variable) is used; ensure
the changed call is made where query is computed (using _sysInput.sessionID,
latestMessageBufferBySession, latestMessageBuffer, projectName) so results are
restricted to this project only.
🪄 Autofix (Beta)
✅ Autofix completed
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: cd36d306-b3fc-4f22-893c-cfa6d7ecb570
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
CHANGELOG.mdsrc/ai.tssrc/index.tssrc/opencode.tstest/opencode.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- CHANGELOG.md
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.ts: Use TypeScript with strict mode enabled (all strict checks enabled)
Do not useas any,@ts-ignore, or@ts-expect-errorTypeScript escape hatches
Files:
src/ai.tssrc/index.tssrc/opencode.ts
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use ESM only with
"type": "module"configuration
Files:
src/ai.tssrc/index.tssrc/opencode.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 9
File: package.json:58-58
Timestamp: 2026-03-11T19:03:02.039Z
Learning: In `package.json` of `oritwoen/obsxa`, `opencode-ai/plugin` is intentionally pinned to `"latest"` in devDependencies to track plugin API movement. Do not flag this as a non-reproducible build issue.
📚 Learning: 2026-03-10T19:06:35.925Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/cluster.ts:84-97
Timestamp: 2026-03-10T19:06:35.925Z
Learning: In the obsxa project (`src/core/cluster.ts` and similar files), the database driver is `better-sqlite3`, which is fully synchronous and single-connection. Concurrent insert races (TOCTOU) cannot occur within the same process, so check-then-insert patterns are safe and do not need duplicate-key error handling.
Applied to files:
src/ai.tssrc/index.tssrc/opencode.ts
📚 Learning: 2026-03-10T19:06:48.549Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/observation.ts:375-393
Timestamp: 2026-03-10T19:06:48.549Z
Learning: In the obsxa project (`src/core/observation.ts` and related files), the database driver is `better-sqlite3` — a synchronous, single-connection SQLite driver. There is no concurrency possible, so read-modify-write patterns (e.g., read frequency, increment, write back) are safe and do not lose updates. Concurrency-based race condition comments are not applicable here.
Applied to files:
src/ai.tssrc/index.tssrc/opencode.ts
📚 Learning: 2026-03-10T19:06:28.832Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/db.ts:81-93
Timestamp: 2026-03-10T19:06:28.832Z
Learning: In the `obsxa` repository (TypeScript, Node.js, better-sqlite3), read-then-insert patterns (e.g., in `src/core/dedup.ts` scan() and `src/core/cluster.ts`) are intentionally safe without DB-level unique constraints because `better-sqlite3` is fully synchronous — it blocks the event loop on every call, making concurrent interleaving within the same process impossible. Do not flag these as race conditions.
Applied to files:
test/opencode.test.tssrc/index.tssrc/opencode.ts
📚 Learning: 2026-03-11T19:03:02.039Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 9
File: package.json:58-58
Timestamp: 2026-03-11T19:03:02.039Z
Learning: In `package.json` of `oritwoen/obsxa`, `opencode-ai/plugin` is intentionally pinned to `"latest"` in devDependencies to track plugin API movement. Do not flag this as a non-reproducible build issue.
Applied to files:
src/index.tssrc/opencode.ts
📚 Learning: 2026-03-11T18:13:51.875Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-11T18:13:51.875Z
Learning: Applies to src/core/**/*.ts : Use drizzle-orm with SQLite via libsql as the database layer
Applied to files:
src/index.ts
📚 Learning: 2026-03-11T18:13:51.875Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-11T18:13:51.875Z
Learning: Applies to drizzle/**/*.ts : Database schema changes must go through drizzle-kit migrations
Applied to files:
src/index.ts
📚 Learning: 2026-03-11T17:33:57.939Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 11
File: src/core/observation.ts:94-126
Timestamp: 2026-03-11T17:33:57.939Z
Learning: In the obsxa project (`src/core/observation.ts`), `addMany` is intentionally fail-fast: if any record fails validation (e.g., promoted status missing `promotedTo`) or any awaited call (`this.add`, `this.dismiss`, `this.archive`, `this.promote`) throws, the entire batch aborts and the error propagates to the caller. Do NOT suggest changing this to partial success, as it would alter the established contract and caller expectations.
Applied to files:
src/opencode.ts
🧬 Code graph analysis (4)
src/ai.ts (1)
src/core/db-path.ts (1)
getDefaultDbPath(4-38)
test/opencode.test.ts (3)
src/opencode.ts (1)
createObsxaPlugin(162-453)src/index.ts (2)
createObsxaPlugin(56-56)createObsxa(217-298)src/core/db.ts (1)
observations(15-45)
src/index.ts (2)
src/types.ts (1)
ObsxaOptions(2-11)src/core/db-path.ts (1)
getDefaultDbPath(4-38)
src/opencode.ts (2)
src/index.ts (2)
ObsxaInstance(193-202)createObsxa(217-298)src/core/db-path.ts (1)
getDefaultDbPath(4-38)
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Fixes Applied SuccessfullyFixed 1 file(s) based on 1 unresolved review comment. Files modified:
Commit: The changes have been pushed to the Time taken: |
Fixed 1 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/opencode.ts`:
- Around line 128-132: The truncation branch for output when output.length >
maxChars currently always appends "..." which violates tiny caps
(maxInjectedChars/maxChars < 3); update the logic in the block using variables
output and maxChars so that if maxChars <= 0 return empty string, if maxChars is
1 or 2 return a trimmed ellipsis-safe string (e.g., "." or ".." up to maxChars)
rather than "..." and only append "..." when maxChars >= 3; ensure you still
prefer cutting at the last newline (lastNewline) when available before applying
the tiny-limit handling.
- Around line 379-386: The code incorrectly casts props.file and props.name to
string (in the evt.type === "file.edited" and "command.executed" branches),
which yields "[object Object]" for non-string payloads; update these branches in
the handler for evt.type to perform runtime checks (e.g., typeof props.file ===
"string" ? props.file : extract a useful field like props.file.path if present,
or JSON.stringify(props.file) or "unknown") and similarly for props.name, then
set title and source from the sanitized value (refer to evt.type, props.file,
props.name, title, and source to locate and change the logic).
- Around line 14-38: The inline PluginInput and Hooks types (PluginInput, Hooks,
chat.message, event, destroy) are out of sync with `@opencode-ai/plugin`@1.2.24;
replace the handwritten contract by importing types from the real package using
a type-only import (e.g., import type { PluginInput, Hooks } from
"@opencode-ai/plugin") or update the inline definitions to match the published
declarations (add client, serverUrl, $,
config/auth/tool/chat.params/shell.env/experimental.* hooks and correct
signatures for event and chat.message including model { providerID, modelID,
variant }), and add a CI type-check that fails if the local inline types diverge
from the package types.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9a5ecc00-0b0f-4bfc-83c7-4e060f994ef1
📒 Files selected for processing (3)
src/ai.tssrc/core/db-path.tssrc/opencode.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/core/db-path.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.ts: Use TypeScript with strict mode enabled (all strict checks enabled)
Do not useas any,@ts-ignore, or@ts-expect-errorTypeScript escape hatches
Files:
src/ai.tssrc/opencode.ts
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use ESM only with
"type": "module"configuration
Files:
src/ai.tssrc/opencode.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 9
File: package.json:58-58
Timestamp: 2026-03-11T19:03:02.039Z
Learning: In `package.json` of `oritwoen/obsxa`, `opencode-ai/plugin` is intentionally pinned to `"latest"` in devDependencies to track plugin API movement. Do not flag this as a non-reproducible build issue.
📚 Learning: 2026-03-10T19:06:28.832Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/db.ts:81-93
Timestamp: 2026-03-10T19:06:28.832Z
Learning: In the `obsxa` repository (TypeScript, Node.js, better-sqlite3), read-then-insert patterns (e.g., in `src/core/dedup.ts` scan() and `src/core/cluster.ts`) are intentionally safe without DB-level unique constraints because `better-sqlite3` is fully synchronous — it blocks the event loop on every call, making concurrent interleaving within the same process impossible. Do not flag these as race conditions.
Applied to files:
src/ai.tssrc/opencode.ts
📚 Learning: 2026-03-10T19:06:35.925Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/cluster.ts:84-97
Timestamp: 2026-03-10T19:06:35.925Z
Learning: In the obsxa project (`src/core/cluster.ts` and similar files), the database driver is `better-sqlite3`, which is fully synchronous and single-connection. Concurrent insert races (TOCTOU) cannot occur within the same process, so check-then-insert patterns are safe and do not need duplicate-key error handling.
Applied to files:
src/ai.tssrc/opencode.ts
📚 Learning: 2026-03-10T19:06:48.549Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/observation.ts:375-393
Timestamp: 2026-03-10T19:06:48.549Z
Learning: In the obsxa project (`src/core/observation.ts` and related files), the database driver is `better-sqlite3` — a synchronous, single-connection SQLite driver. There is no concurrency possible, so read-modify-write patterns (e.g., read frequency, increment, write back) are safe and do not lose updates. Concurrency-based race condition comments are not applicable here.
Applied to files:
src/ai.tssrc/opencode.ts
📚 Learning: 2026-03-11T19:03:02.039Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 9
File: package.json:58-58
Timestamp: 2026-03-11T19:03:02.039Z
Learning: In `package.json` of `oritwoen/obsxa`, `opencode-ai/plugin` is intentionally pinned to `"latest"` in devDependencies to track plugin API movement. Do not flag this as a non-reproducible build issue.
Applied to files:
src/opencode.ts
📚 Learning: 2026-03-11T17:33:57.939Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 11
File: src/core/observation.ts:94-126
Timestamp: 2026-03-11T17:33:57.939Z
Learning: In the obsxa project (`src/core/observation.ts`), `addMany` is intentionally fail-fast: if any record fails validation (e.g., promoted status missing `promotedTo`) or any awaited call (`this.add`, `this.dismiss`, `this.archive`, `this.promote`) throws, the entire batch aborts and the error propagates to the caller. Do NOT suggest changing this to partial success, as it would alter the established contract and caller expectations.
Applied to files:
src/opencode.ts
🧬 Code graph analysis (2)
src/ai.ts (1)
src/core/db-path.ts (1)
getDefaultDbPath(8-42)
src/opencode.ts (2)
src/index.ts (2)
ObsxaInstance(193-202)createObsxa(217-298)src/core/db-path.ts (1)
getDefaultDbPath(8-42)
🔇 Additional comments (2)
src/ai.ts (2)
8-18: URL scheme validation looks good — past review concern addressed.The regex on line 10 now catches
file://,https://,libsql://, etc. before they can reachcreateObsxa(). The logic is sound:
- User provides path → must pass URL scheme check AND absolute path check
- User provides nothing →
getDefaultDbPath()returns an absolute XDG path, which is allowed since validation gates only fire whenpathis truthyOne small note:
file:obsxa.db(no double slash) still passes since the regex requires://. In practice this resolves to local fileobsxa.dbanyway, so it's not a bypass — just worth knowing if you ever tighten this further.
4-4: Import aligns with the new XDG default path strategy.Clean delegation to the centralized
getDefaultDbPath()utility.
|
CodeAnt AI is running Incremental review |
Sequence DiagramThis PR adds an OpenCode plugin that records sessions into the obsxa database and injects recent observations back into system prompts, while centralizing database location handling via XDG-compliant default paths used by both the core library and CLI backup/restore commands. sequenceDiagram
participant CLI
participant OpenCode
participant ObsxaPlugin
participant ObsxaCore as Obsxa core
participant Database
CLI->>ObsxaCore: Backup or restore using db arg or default XDG path
ObsxaCore->>Database: Open SQLite file and read or write data
OpenCode->>ObsxaPlugin: Initialize plugin with project and optional db path
ObsxaPlugin->>ObsxaCore: createObsxa using provided or default XDG db path
OpenCode->>ObsxaPlugin: Send chat, tool, and event hooks for session
ObsxaPlugin->>ObsxaCore: Deduplicate and upsert observations and relations
OpenCode->>ObsxaPlugin: Request system prompt transform for model
ObsxaPlugin->>ObsxaCore: Search observations by project and session context
ObsxaCore-->>ObsxaPlugin: Return matching observations
ObsxaPlugin-->>OpenCode: Return system messages including agent guidance and recent observations
Generated by CodeAnt AI |
|
CodeAnt AI Incremental review completed. |
|
CodeAnt AI is running Incremental review |
Sequence DiagramThis PR adds an OpenCode plugin flow that records chat, tool, and event activity as observations, then injects recalled context into system prompts. It also routes plugin and core initialization through an XDG-based default database path so local runs no longer default to project-root database files. sequenceDiagram
participant OpenCode
participant ObsxaPlugin
participant DbPathResolver
participant ObsxaCore
participant Database
OpenCode->>ObsxaPlugin: Initialize plugin for project
ObsxaPlugin->>DbPathResolver: Resolve default database path
ObsxaPlugin->>ObsxaCore: Open obsxa instance
ObsxaCore->>Database: Ensure project and open stores
OpenCode->>ObsxaPlugin: Send chat tool and event hook data
ObsxaPlugin->>Database: Upsert deduplicated observations and relations
OpenCode->>ObsxaPlugin: Request system prompt transform
ObsxaPlugin->>Database: Search recent project observations
ObsxaPlugin-->>OpenCode: Return instruction and recalled context
Generated by CodeAnt AI |
…efaults # Conflicts: # src/commands/backup.ts
What
I added the
obsxa/opencodeplugin hooks for observation capture and context injection, then moved default DB paths to XDG locations across API and CLI. The push for this was practical - local runs kept droppingobsxa.dbinto project roots.There is one runtime caveat for local OpenCode plugin tests: Bun cannot load
better-sqlite3, so this plugin path is verified in Node runtime.Closes
Closes #8