- Rename all user-facing and technical identifiers from OpenPets/Pet to FamiliarOS/Familiar. - Rename packages from @open-pets/* to @familiaros/*; rename install-pet/pet-format packages. - Rename plugin IDs and directories from openpets.* to familiaros.*. - Rename IPC namespace from openpets:* to familiaros:* and state filenames from openpets-* to familiaros-* with legacy migration. - Rename source files (pet-window, built-in-pet, default-pet-controller, etc.) to familiar equivalents. - Update locales (en, es-419, ja, ko, pt-BR, zh-Hans, zh-Hant) and tray/pet context menu strings. - Add Familiar naming feature: preference, settings input, tray menu display. - Update assets and packaging config; all desktop tests pass.
12 KiB
Phase 19C — OpenCode CLI Project Setup
Goal
Extend the FamiliarOS CLI so users can configure a project for OpenCode with one command:
npx @familiaros/cli configure --agent opencode --familiar fixer
This writes project-local OpenCode config for FamiliarOS MCP, FamiliarOS instructions, and the FamiliarOS OpenCode plugin.
Non-goals
- No Desktop Integrations OpenCode UI.
- No global OpenCode setup from desktop.
- No project directory picker UI.
- No OpenCode runtime plugin changes beyond using Phase 19B's plugin package/export.
- No new public MCP tools.
- No OpenCode source changes under
v1/opencode/. - No real user config writes in tests.
User-visible/manual outcome
From a project directory, users can run:
npx @familiaros/cli configure --agent opencode --familiar fixer
Expected result:
- OpenCode project config contains
mcp.familiaros. - OpenCode project config includes FamiliarOS instruction file
.opencode/familiaros.md. - OpenCode project config includes FamiliarOS plugin spec/options targeting
fixer. - Starting OpenCode from that project gives FamiliarOS MCP tools and plugin-driven reactions for
fixer.
If --familiar is omitted, the CLI uses the same installed-familiar picker as Claude setup and therefore requires FamiliarOS desktop to be running.
Acceptance criteria
parseConfigureArgsaccepts--agent opencodeand still accepts/keeps--agent claudebehavior unchanged.- Unsupported agents still fail clearly.
familiaros configure --agent opencode --familiar <id> --cwd <project>runs without requiring FamiliarOS desktop.familiaros configure --agent opencodewithout--familiaruses local IPC familiar listing/picker like Claude.- OpenCode setup does not require
opencodebinary onPATH; if detection is added, it is warning-only. - Project path validation rejects symlinked project roots.
- OpenCode config writes use Phase 19A helpers:
- project config candidate selection;
- all existing project config candidate scanning before writing;
- JSON/JSONC parsing/updating;
- backup/temp/atomic write safety;
- symlink and escape rejection.
- If no project OpenCode config exists, create
.opencode/opencode.jsonc. - Existing unrelated OpenCode config keys are preserved.
- Existing non-FamiliarOS MCP/plugin/instruction entries are preserved.
- Existing matching FamiliarOS entries are idempotent.
- Existing stale managed FamiliarOS entries are updated.
- Custom/foreign
mcp.familiarosor FamiliarOS-like plugin/instruction entries must not be overwritten. In this phase, fail clearly and tell the user to edit/remove the custom entry manually. --forcemay replace stale managed FamiliarOS entries, but must not overwrite custom/foreign entries.- Written project config uses published mode by default:
{
"mcp": {
"familiaros": {
"type": "local",
"command": ["npx", "-y", "@familiaros/cli@0.0.0", "mcp", "--familiar", "fixer"],
"enabled": true
}
},
"instructions": [".opencode/familiaros.md"],
"plugin": [["@familiaros/opencode@0.0.0", { "familiar": "fixer" }]]
}
- With
--local-dev, generated MCP config may usenode <current cli dist/index.js> mcp --familiar fixer, but the plugin spec should remain package-based unless a safe local plugin file path policy is implemented in a later phase. - The package-based plugin spec must be version-pinned to the same package version used for generated MCP commands, e.g.
@familiaros/opencode@<version>. - The managed instruction file
.opencode/familiaros.mdis written with FamiliarOS managed markers. - Instruction file writes must be safe:
- reject symlinked
.opencode/familiaros.md; - reject oversized instruction files;
- preserve user content outside managed markers;
- upsert the managed FamiliarOS block if the expected path exists without a managed block;
- backup before destructive update;
- temp-file + rename atomic write;
- no instruction write if config planning fails.
- reject symlinked
- CLI output prints:
- configured agent (
OpenCode); - target project path;
- selected familiar id/name;
- config file path changed;
- instruction file path changed;
- a warning that
.opencode/opencode.jsoncand.opencode/familiaros.mdcan be committed and contain the selected familiar id; - restart guidance for OpenCode.
- configured agent (
- Tests cover offline explicit-familiar setup, idempotency, preserving unrelated config, custom/foreign conflict refusal, symlink rejection, and Claude regression.
Proposed files/directories
Likely changed files:
packages/cli/package.jsonpackages/cli/src/index.tspackages/cli/src/check-cli-contract.tspackages/opencode/src/opencode-config.tspackages/opencode/src/opencode-previews.tspackages/opencode/src/opencode-status.tspackages/opencode/src/check-opencode-foundation.ts
Possible new file:
packages/opencode/src/opencode-project-setup.ts
Technical approach
CLI flow
Keep existing Claude flow intact and branch in configureProject by options.agent:
if (options.agent === "claude") return configureClaudeProject(options)
if (options.agent === "opencode") return configureOpenCodeProject(options)
Update ConfigureOptions.agent to "claude" | "opencode".
OpenCode project setup helper
Prefer putting most OpenCode-specific write logic in packages/opencode, not in the CLI, so desktop Phase 19D can reuse the same primitives.
Potential API:
prepareOpenCodeProjectSetup({
projectDir,
petId,
cliVersion,
commandMode,
cliEntryPath,
}): PreparedOpenCodeProjectSetup
writePreparedOpenCodeProjectSetup(prepared): OpenCodeProjectSetupResult
The helper should:
- Validate project root.
- Select config write target.
- Read and classify all existing project config candidates because OpenCode can merge top-level and
.opencodeconfigs. - Fail on custom/foreign/conflicting FamiliarOS entries anywhere in those candidates.
- Read the selected write target or
{}. - Classify selected-target FamiliarOS entries for idempotent updates.
- Add/update:
mcp.familiaros;instructionscontaining.opencode/familiaros.mdonce;plugincontaining version-pinned@familiaros/opencode@<version>once with{ familiar }.
- Plan
.opencode/familiaros.mdmanaged block upsert. - Plan safe config writes via Phase 19A helpers.
- Execute writes only after all config and instruction write plans have succeeded.
The setup must be two-phase: validate/classify/plan all writes first, then execute. If any plan fails, write nothing.
Instruction file content
Use the same guidance as Claude memory, adapted for OpenCode:
- FamiliarOS MCP tools may be available.
- Use
familiaros_sayfor meaningful short status/personality messages. - Keep messages brief, user-facing, and non-sensitive.
- Do not include code, logs, secrets, URLs, or file paths.
- Use
familiaros_reactfor visual feedback. - Use
familiaros_statusonly when checking availability or target familiar. - Do not spam every internal step.
Conflict policy
Status helpers from Phase 19A distinguish installed, needs_update, custom, and conflict.
For Phase 19C:
installed: leave as-is unless generated content differs only in managed block, then refresh instruction block.needs_update: update managed entries.not_installed: install entries.custom/conflict: fail clearly and do not write config.
This avoids overwriting user-owned familiaros entries.
If the expected instruction path is present but lacks the managed block, treat it as an instruction needs_update: upsert the managed block while preserving existing file content outside managed markers.
Offline behavior
Reuse existing resolveConfiguredPet behavior:
- explicit
--familiarvalidates syntax only and does not require desktop; - omitted
--familiarqueries installed familiars through local IPC.
Risks and tradeoffs
- Project
.opencode/opencode.jsoncand.opencode/familiaros.mdcan be committed. CLI must warn clearly. - Direct JSONC editing risks data loss. Use existing parse guards, backups, temp writes, and no-write-on-error policy.
- Package-based plugin spec assumes
@familiaros/opencodeis published alongside CLI. This is correct for published mode; local plugin path setup is deferred. --local-devonly affects MCP command in this phase. Plugin local-dev path is deferred to avoid unsafe path/config churn.
Security/privacy notes
- Do not write outside the project root.
- Reject symlinked project roots and unsafe config paths.
- Preserve unrelated OpenCode config.
- Do not overwrite custom/foreign FamiliarOS-like entries.
- Do not perform partial writes; if any plan fails, no config or instruction file should be written.
- Do not expose prompts, code, logs, URLs, paths, or secrets in instruction text beyond generic warnings.
- Tests must use temp directories only.
Test/check plan
pnpm --filter @familiaros/opencode checkpnpm --filter @familiaros/cli checkpnpm --filter @familiaros/claude checkpnpm checkafter implementation review fixes.
Specific tests:
parseConfigureArgs(["--agent", "opencode", "--familiar", "fixer"])works.- Unsupported agent still throws.
- Offline explicit-familiar OpenCode setup writes config without calling local IPC.
- Missing
--familiarstill calls familiar picker/listing. - New project creates
.opencode/opencode.jsoncand.opencode/familiaros.md. - Existing
opencode.jsonis preferred over.opencode/opencode.jsoncas write target. - Existing unrelated config keys/MCP/plugin/instructions are preserved.
- Conflicts across multiple project config candidate files are detected before writing.
- Re-running setup is idempotent.
- Stale managed FamiliarOS entries are updated.
- Custom
mcp.familiarosrefuses without writing. - Custom FamiliarOS-like plugin/instruction refuses without writing.
- Existing
.opencode/familiaros.mdwithout managed block gets managed block added while preserving user text. - Instruction symlink/oversized file is rejected without config writes.
- Symlink project/config paths are rejected.
- Claude CLI tests still pass.
Manual verification guide
After implementation and review:
- Run
pnpm --filter @familiaros/cli check. - Run
pnpm check. - In a temporary project, run:
node /path/to/packages/cli/dist/index.js configure --agent opencode --familiar fixer --cwd /tmp/familiaros-opencode-test --local-dev
- Confirm
.opencode/opencode.jsonccontainsmcp.familiaros,.opencode/familiaros.mdinstruction path, and@familiaros/opencodeplugin spec. - Confirm
.opencode/familiaros.mdcontains FamiliarOS managed markers. - Re-run the command and confirm config remains idempotent.
- Add a custom
mcp.familiarosentry and confirm setup refuses without overwriting. - Confirm real user OpenCode config was not touched.
Oracle plan review
Oracle reviewed the initial Phase 19C spec and found blockers:
- Must scan all project config candidates before writing, not only selected write target.
- Instruction file write safety was under-specified.
- Partial-write/data-loss sequencing was undefined.
- Published plugin spec should be version-pinned.
- Existing expected instruction path without managed block needed a preserve-and-upsert policy.
Oracle feedback disposition
- Fixed: Required scanning/classifying all existing project config candidates before selecting a write target.
- Fixed: Added safe instruction file write requirements: symlink/size rejection, preserve user content, backup, temp+rename, no write if config planning fails.
- Fixed: Required two-phase plan-all-then-execute sequencing to avoid partial writes.
- Fixed: Required version-pinned
@familiaros/opencode@<version>plugin spec. - Fixed: Clarified expected instruction path without managed block is
needs_updateand should upsert the managed block while preserving existing content.