openpetswithchatandmcp/docs/phases/phase-19c-opencode-cli-project-setup.md
OpenPets Dev 6ab3bb64d8 feat(rebrand): rename OpenPets to FamiliarOS and pets to familiars
- 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.
2026-06-17 01:42:08 +00:00

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

  • parseConfigureArgs accepts --agent opencode and still accepts/keeps --agent claude behavior unchanged.
  • Unsupported agents still fail clearly.
  • familiaros configure --agent opencode --familiar <id> --cwd <project> runs without requiring FamiliarOS desktop.
  • familiaros configure --agent opencode without --familiar uses local IPC familiar listing/picker like Claude.
  • OpenCode setup does not require opencode binary on PATH; 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.familiaros or 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.
  • --force may 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 use node <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.md is 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.
  • 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.jsonc and .opencode/familiaros.md can be committed and contain the selected familiar id;
    • restart guidance for OpenCode.
  • 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.json
  • packages/cli/src/index.ts
  • packages/cli/src/check-cli-contract.ts
  • packages/opencode/src/opencode-config.ts
  • packages/opencode/src/opencode-previews.ts
  • packages/opencode/src/opencode-status.ts
  • packages/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:

  1. Validate project root.
  2. Select config write target.
  3. Read and classify all existing project config candidates because OpenCode can merge top-level and .opencode configs.
  4. Fail on custom/foreign/conflicting FamiliarOS entries anywhere in those candidates.
  5. Read the selected write target or {}.
  6. Classify selected-target FamiliarOS entries for idempotent updates.
  7. Add/update:
    • mcp.familiaros;
    • instructions containing .opencode/familiaros.md once;
    • plugin containing version-pinned @familiaros/opencode@<version> once with { familiar }.
  8. Plan .opencode/familiaros.md managed block upsert.
  9. Plan safe config writes via Phase 19A helpers.
  10. 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_say for 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_react for visual feedback.
  • Use familiaros_status only 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 --familiar validates syntax only and does not require desktop;
  • omitted --familiar queries installed familiars through local IPC.

Risks and tradeoffs

  • Project .opencode/opencode.jsonc and .opencode/familiaros.md can 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/opencode is published alongside CLI. This is correct for published mode; local plugin path setup is deferred.
  • --local-dev only 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 check
  • pnpm --filter @familiaros/cli check
  • pnpm --filter @familiaros/claude check
  • pnpm check after 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 --familiar still calls familiar picker/listing.
  • New project creates .opencode/opencode.jsonc and .opencode/familiaros.md.
  • Existing opencode.json is preferred over .opencode/opencode.jsonc as 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.familiaros refuses without writing.
  • Custom FamiliarOS-like plugin/instruction refuses without writing.
  • Existing .opencode/familiaros.md without 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:

  1. Run pnpm --filter @familiaros/cli check.
  2. Run pnpm check.
  3. 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
  1. Confirm .opencode/opencode.jsonc contains mcp.familiaros, .opencode/familiaros.md instruction path, and @familiaros/opencode plugin spec.
  2. Confirm .opencode/familiaros.md contains FamiliarOS managed markers.
  3. Re-run the command and confirm config remains idempotent.
  4. Add a custom mcp.familiaros entry and confirm setup refuses without overwriting.
  5. 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_update and should upsert the managed block while preserving existing content.