feat: sync OpenCode references#50
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:
📝 WalkthroughWalkthroughThe PR adds OpenCode configuration detection, consent, JSONC reference synchronization, ownership persistence, lock metadata, frozen drift validation, atomic writes, CLI/init integration, tests, and documentation. ChangesOpenCode synchronization
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant SyncCommand
participant ReferencePlanner
participant OpenCodeConfig
participant OwnershipStore
participant LockFile
CLI->>SyncCommand: resolve OpenCode consent
SyncCommand->>OwnershipStore: read ownership
SyncCommand->>ReferencePlanner: plan references
ReferencePlanner->>OpenCodeConfig: parse and compare JSONC
ReferencePlanner-->>SyncCommand: return drift and changes
SyncCommand->>OpenCodeConfig: apply managed references
SyncCommand->>OwnershipStore: persist ownership
SyncCommand->>LockFile: write OpenCode lock metadata
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Adds first-class OpenCode integration to docs-cache: detect existing OpenCode configs, store a consent decision in docs.config.json, and keep OpenCode references in sync with the canonical cache directory while tracking ownership for safe updates/removals.
Changes:
- Introduces OpenCode config detection + consent persistence during
initand interactivesync. - Plans/applies managed OpenCode reference updates (JSONC-preserving) and records managed aliases in
docs-lock.json. - Adds local ownership tracking and atomic file writes; expands schema/docs and adds comprehensive tests.
Reviewed changes
Copilot reviewed 22 out of 25 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Adds #opencode/* TS path alias. |
| tests/schema.test.js | Verifies schema includes opencode and no unintended per-source fields. |
| tests/opencode-references.test.js | End-to-end tests for managed reference creation/update/removal and safety cases. |
| tests/opencode-detection.test.js | Tests OpenCode config detection precedence behavior. |
| tests/init.test.js | Tests init-time OpenCode consent persistence. |
| tests/config-validation.test.js | Tests conditional validation for OpenCode alias constraints. |
| src/opencode/references.ts | Implements planning/applying OpenCode reference reconciliation with drift detection. |
| src/opencode/ownership.ts | Adds local ownership state read/write/restore helpers. |
| src/opencode/detection.ts | Adds OpenCode config candidate discovery + detection. |
| src/opencode/consent.ts | Persists user consent decision back into docs-cache config. |
| src/config/schema.ts | Extends config schema/types with optional opencode. |
| src/config/io.ts | Ensures opencode is preserved when merging config base. |
| src/config/index.ts | Threads opencode through validation/stripping/types. |
| src/commands/sync.ts | Integrates reference planning/apply/rollback + lock and ownership persistence. |
| src/commands/init.ts | Detects OpenCode config and prompts/stores consent during init. |
| src/cli/index.ts | Adds interactive sync-time consent prompt when appropriate. |
| src/cache/lock.ts | Adds opencode section to lock + writes lock atomically. |
| src/atomic-write.ts | New atomic write helper used for lock/OpenCode writes. |
| src/api.ts | Exposes new OpenCode helpers via public API. |
| README.md | Documents OpenCode reference syncing behavior and config field. |
| pnpm-lock.yaml | Locks new dependency additions. |
| package.json | Adds jsonc-parser dependency and #opencode/* export mapping. |
| docs.config.schema.json | Adds opencode to published JSON schema. |
| build.config.ts | Adds build alias mapping for #opencode/*. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4d8af00d18
ℹ️ 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".
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
src/opencode/detection.ts (1)
76-85: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winConsider early return in
detectOpenCodeConfigto reduce unnecessary filesystem access.The function iterates all candidates and keeps the last match. Since candidate order is already prioritized (later candidates have higher priority), you could iterate in reverse and return on the first match, avoiding unnecessary
accesscalls for remaining candidates.♻️ Proposed optimization
export const detectOpenCodeConfig = async (startDir: string) => { const candidates = await getOpenCodeConfigCandidates(startDir); - let detected: string | null = null; - for (const candidate of candidates) { - if (await exists(candidate)) { - detected = candidate; - } + for (let i = candidates.length - 1; i >= 0; i--) { + if (await exists(candidates[i])) { + return candidates[i]; + } } - return detected; + return null; };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/opencode/detection.ts` around lines 76 - 85, Update detectOpenCodeConfig to iterate candidates in reverse priority order and immediately return the first candidate for which exists(candidate) succeeds. Remove the detected accumulator and preserve the null return when no candidates exist.src/cache/lock.ts (1)
16-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
DocsCacheOpenCodeLockis duplicated across multiple files.The same interface is defined in
src/cache/lock.ts,src/commands/sync.ts,src/opencode/references.ts, andsrc/opencode/ownership.ts. Consider importing from a single source to avoid drift.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/cache/lock.ts` around lines 16 - 25, Use the canonical DocsCacheOpenCodeLock definition from src/cache/lock.ts and remove duplicate declarations in the sync and OpenCode modules. Update affected references to import and reuse the shared interface while preserving its existing configPath and aliases contract.README.md (1)
109-123: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winClarify that
docs-lock.json'sopencode.aliasesis informational, not the enforcement source.The text says aliases are "recorded... in docs-lock.json so removing a source removes only its own reference," but per the implementation (and the "does not trust lock aliases as OpenCode ownership" test), the actual ownership authority is a separate local state file, not
docs-lock.json. Worth a short clarification so readers don't assume editing/tracking the lock file affects collision handling.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@README.md` around lines 109 - 123, Clarify the README description of docs-lock.json so opencode.aliases is identified as informational only, while OpenCode reference ownership and collision handling rely on the separate local state file. State that editing or tracking docs-lock.json does not change ownership enforcement or managed-reference cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/commands/sync.ts`:
- Around line 1069-1090: Update the catch block surrounding
writeOpenCodeOwnership and writeLock so failures from restoreOpenCodeOwnership
and openCodeReferences.rollback are contained without replacing the original
caught error. Preserve both rollback attempts where enabled, then rethrow the
original error after rollback cleanup completes.
- Around line 982-993: Gate the readOpenCodeOwnership and planOpenCodeReferences
calls in the sync command so they run only for modes that consume OpenCode
state, excluding install and non-frozen lockOnly. Preserve the existing planning
behavior for applicable modes, and add a regression test covering install when
the saved OpenCode configPath is missing.
In `@src/opencode/references.ts`:
- Around line 163-176: Update the apply function’s rollback handling so failures
while restoring files cannot replace the original write error. Preserve the
initial error from the forward write loop, attempt all reversions in
written.reverse(), and ensure any rollback errors are suppressed or otherwise
handled without changing the error ultimately rethrown.
In `@tests/init.test.js`:
- Around line 194-209: Update the initConfig test around the OpenCode detection
assertion to isolate environment-dependent home-directory lookup by temporarily
setting HOME and OPENCODE_CONFIG_DIR to the test fixture context, matching the
environment management used in opencode-detection.test.js. Restore both
environment variables after the test, while preserving the existing project
config setup and assertion.
---
Nitpick comments:
In `@README.md`:
- Around line 109-123: Clarify the README description of docs-lock.json so
opencode.aliases is identified as informational only, while OpenCode reference
ownership and collision handling rely on the separate local state file. State
that editing or tracking docs-lock.json does not change ownership enforcement or
managed-reference cleanup.
In `@src/cache/lock.ts`:
- Around line 16-25: Use the canonical DocsCacheOpenCodeLock definition from
src/cache/lock.ts and remove duplicate declarations in the sync and OpenCode
modules. Update affected references to import and reuse the shared interface
while preserving its existing configPath and aliases contract.
In `@src/opencode/detection.ts`:
- Around line 76-85: Update detectOpenCodeConfig to iterate candidates in
reverse priority order and immediately return the first candidate for which
exists(candidate) succeeds. Remove the detected accumulator and preserve the
null return when no candidates exist.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 985b873d-b08c-426f-af16-fd735eb43563
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (23)
README.mdbuild.config.tsdocs.config.schema.jsonpackage.jsonsrc/api.tssrc/atomic-write.tssrc/cache/lock.tssrc/cli/index.tssrc/commands/init.tssrc/commands/sync.tssrc/config/index.tssrc/config/io.tssrc/config/schema.tssrc/opencode/consent.tssrc/opencode/detection.tssrc/opencode/ownership.tssrc/opencode/references.tstests/config-validation.test.jstests/init.test.jstests/opencode-detection.test.jstests/opencode-references.test.jstests/schema.test.jstsconfig.json
commit: |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 094b2e1c38
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 489e2496c4
ℹ️ 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".
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/install.test.js (1)
204-265: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd explicit assertions to strengthen the test.
The test relies entirely on
runSyncnot throwing — there are zero explicit assertions. IfrunSyncsilently swallows errors, the test would pass even with bugs. Adding at least one assertion (e.g., verifying the lock file was written or that installed files exist) would make the test more robust and self-documenting.♻️ Suggested additions
await runSync( { configPath, cacheDirOverride: cacheDir, json: false, lockOnly: true, offline: false, failOnMiss: false, }, { resolveRemoteCommit }, ); + + const lockPath = path.join(cacheDir, "docs-lock.json"); + assert.ok(await readFile(lockPath, "utf8").then(JSON.parse).then((l) => l.opencode?.configPath), "lock should record opencode.configPath"); await runSync( { configPath, cacheDirOverride: cacheDir, json: false, lockOnly: false, offline: false, failOnMiss: false, install: true, }, { fetchSource: async () => ({ repoDir, cleanup: async () => undefined, }), materializeSource: async ({ cacheDir: outputRoot, sourceId }) => { const outputDir = path.join(outputRoot, sourceId); await mkdir(outputDir, { recursive: true }); await writeFile(path.join(outputDir, "README.md"), "hello", "utf8"); await writeFile( path.join(outputDir, ".manifest.jsonl"), `${JSON.stringify({ path: "README.md", size: 5 })}\n`, ); return { bytes: 5, fileCount: 1, manifestSha256: "manifest" }; }, }, ); + + assert.ok( + await readFile(path.join(cacheDir, "local", "README.md"), "utf8").then((c) => c === "hello"), + "installed README.md should exist with expected content", + );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/install.test.js` around lines 204 - 265, Add explicit postcondition assertions to the test “install and lock-only ignore a missing OpenCode config” after the runSync calls, verifying the lock-only operation writes the expected lock file and the install operation produces the expected installed README.md. Keep the assertions focused on successful observable outcomes rather than relying on runSync not throwing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/install.test.js`:
- Around line 204-265: Add explicit postcondition assertions to the test
“install and lock-only ignore a missing OpenCode config” after the runSync
calls, verifying the lock-only operation writes the expected lock file and the
install operation produces the expected installed README.md. Keep the assertions
focused on successful observable outcomes rather than relying on runSync not
throwing.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e00fffe6-8512-4bea-bca2-5027bc98b240
📒 Files selected for processing (12)
src/cache/lock.tssrc/cli/index.tssrc/commands/sync.tssrc/config/index.tssrc/opencode/consent.tssrc/opencode/detection.tssrc/opencode/ownership.tssrc/opencode/references.tstests/config-validation.test.jstests/init.test.jstests/install.test.jstests/opencode-references.test.js
🚧 Files skipped from review as they are similar to previous changes (11)
- src/opencode/consent.ts
- src/config/index.ts
- tests/config-validation.test.js
- src/opencode/ownership.ts
- src/cli/index.ts
- tests/init.test.js
- src/opencode/detection.ts
- src/opencode/references.ts
- src/cache/lock.ts
- tests/opencode-references.test.js
- src/commands/sync.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/opencode-detection.test.js`:
- Line 52: Update the test named “OpenCode detection disables project config
only for true or 1” to include an assertion covering the `"true"` value, while
preserving the existing `"false"`, `"0"`, and `"1"` cases so the test title
accurately reflects its coverage.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: bfbee646-25e0-44f8-9be6-80150b95a8fc
📒 Files selected for processing (13)
src/cache/materialize.tssrc/cli/index.tssrc/commands/init.tssrc/commands/sync.tssrc/commands/verify.tssrc/config/schema.tssrc/git/fetch-source.tssrc/opencode/consent.tssrc/opencode/detection.tssrc/opencode/references.tstests/init.test.jstests/opencode-detection.test.jstests/opencode-references.test.js
🚧 Files skipped from review as they are similar to previous changes (8)
- src/opencode/detection.ts
- src/cli/index.ts
- src/opencode/consent.ts
- tests/init.test.js
- tests/opencode-references.test.js
- src/opencode/references.ts
- src/commands/init.ts
- src/commands/sync.ts
Summary
Sync configured documentation sources into detected OpenCode configs as managed local references, without exposing cached files to Git.
Changes
targetDiroptional and independent from OpenCode reference paths.Testing
pnpm typecheckbiome check .pnpm schema:buildnode --test --test-concurrency=1(206 passed, 3 skipped)Summary by CodeRabbit
--frozennow blocks both lock drift and OpenCode reference drift.targetDirbehavior, and the updated frozen workflow.