openpetswithchatandmcp/docs/phases/phase-17-openpets-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
Raw Permalink Blame History

Phase 17 — FamiliarOS CLI Project Setup

Goal

Add a real npm-distributed FamiliarOS CLI that lets users configure the current project to use a selected familiar with Claude, using one simple command.

Primary command:

npx @familiaros/cli configure --familiar fixer

This should configure both Claude MCP and Claude hooks for the current project. No extra MCP/hooks questions.

Non-goals

  • App-installed familiaros shim / Settings “Install CLI” button. Defer to a later phase.
  • Shared/team project config by default.
  • Supporting agents other than Claude.
  • Remote/custom catalog flags.
  • familiaros install <familiar-id>; defer familiar installation CLI to a later phase.
  • Exposing familiar install/remove/default controls through MCP tools.
  • Building a complex TUI. Keep interaction simple.

User-visible/manual outcome

From a project directory, users can run:

npx @familiaros/cli configure --familiar fixer

Expected result:

  • Claude MCP is configured locally for the current project with FamiliarOS --familiar fixer.
  • Claude project-local hooks are configured with --project-local --familiar fixer.
  • Starting Claude from that project makes MCP tools and hook speech/reactions target fixer.

Users can also run:

npx @familiaros/cli configure --agent claude --familiar fixer --cwd /path/to/project --yes

If --familiar is omitted, the CLI lists installed familiars and prompts for one.

Acceptance criteria

  • packages/cli exposes a real executable bin, familiaros.
  • familiaros configure --familiar <id> runs non-interactively for Claude using current working directory.
  • --agent claude is accepted; unsupported agents fail clearly.
  • --cwd <path> targets another project directory; default is process.cwd().
  • --yes is accepted as a compatibility/no-op flag; there is no final confirmation prompt in Phase 17.
  • If --familiar is missing, CLI queries installed familiars from the running FamiliarOS desktop app and prompts the user to choose.
  • CLI configures Claude MCP using local project scope, private to the current user.
  • CLI configures Claude hooks in <project>/.claude/settings.local.json, private to the project/user.
  • Claude MCP command is installed while spawning claude with cwd set to the resolved target project.
  • Long-lived generated Claude MCP/hook commands use a stable package command, not a temporary npx @familiaros/cli install path.
  • Hook commands include --familiar <id>.
  • Existing non-FamiliarOS hooks in .claude/settings.local.json are preserved.
  • Existing FamiliarOS-managed hooks are replaced safely.
  • CLI fails with clear instructions if FamiliarOS desktop app is not running.
  • CLI fails clearly if Claude Code is unavailable on PATH.
  • CLI validates selected familiar ids and rejects unsafe values.

Proposed files/directories

  • packages/cli/package.json
  • packages/cli/src/index.ts
  • packages/cli/src/check-cli-contract.ts
  • packages/client/src/index.ts
  • apps/desktop/src/local-ipc.ts
  • apps/desktop/src/local-ipc-protocol.ts
  • apps/desktop/src/check-local-ipc-protocol.ts
  • packages/claude/src/hook-settings.ts (reuse project-local hook writer by passing settings path)

Technical approach

CLI command shape

familiaros configure [--agent claude] [--familiar <id>] [--cwd <path>] [--yes]

Defaults:

  • --agent claude
  • --cwd process.cwd()
  • configure both MCP and hooks
  • local/private project config

Installed familiar discovery

Add a narrow local IPC/client method for the user-run CLI:

familiars.list

Return only safe display data:

{
  ok: true,
  familiars: [
    { id, displayName, installed: true, builtIn, broken }
  ],
  defaultPetId
}

The CLI uses this to validate --familiar and power the picker.

Stable generated commands

Do not write temp/cache paths from npx @familiaros/cli into Claude config. Instead make the CLI package self-contained and expose wrapper subcommands:

familiaros mcp --familiar fixer
familiaros hook --familiaros-managed --project-local --familiar fixer

Generated long-lived commands should use the package version being configured:

npx -y @familiaros/cli@0.0.0 mcp --familiar fixer
npx -y @familiaros/cli@0.0.0 hook --familiaros-managed --project-local --familiar fixer

The mcp wrapper starts the existing FamiliarOS MCP server implementation. The hook wrapper delegates to the existing Claude hook handler.

This avoids storing an absolute path into npm's temporary npx cache.

Claude MCP configuration

Use Claudes own CLI for local project MCP setup when available:

claude mcp add-json familiaros '<json>' --scope local

JSON shape:

{
  "type": "stdio",
  "command": "npx",
  "args": ["-y", "@familiaros/cli@0.0.0", "mcp", "--familiar", "fixer"],
  "env": {}
}

Spawn claude mcp add-json with cwd set to the resolved target project directory so Claude's local MCP scope attaches to the intended project.

Do not directly mutate ~/.claude.json in this phase; delegate that to claude mcp add-json.

Claude project-local hooks

Write hooks directly to:

<project>/.claude/settings.local.json

Use existing installClaudeHooks(settingsPath, commandMode, selectedPetId) with a CLI-appropriate command mode/path if possible. If needed, add a small command builder option so the npm CLI writes hook commands that call the installed npm Claude CLI:

npx -y @familiaros/cli@0.0.0 hook --familiaros-managed --project-local --familiar fixer

Project-local hooks are private and should not require committing repo files.

Before writing hooks:

  • resolve and validate the target project directory;
  • reject symlinked <project>/.claude directories;
  • ensure <project>/.claude/settings.local.json resolves inside the target project;
  • preserve existing non-FamiliarOS settings and hooks.

Interactive picker

If --familiar is missing:

  • list usable installed non-broken familiars, including built-in unless explicitly disallowed later
  • use a simple stdin/stdout numbered picker
  • no extra MCP/hooks questions

Preflight and idempotency

Before writing anything:

  • verify FamiliarOS desktop is reachable and supports familiars.list;
  • verify the selected familiar is installed and usable;
  • verify Claude Code is available on PATH;
  • verify target project path and hook path safety;
  • build the version-pinned wrapper commands.

If any preflight fails, do not write MCP or hooks.

If a local Claude MCP entry named familiaros already exists, configure should replace it by invoking Claude's add/update flow for the same server name when supported by claude mcp add-json; if Claude refuses, fail clearly before writing hooks.

Risks and tradeoffs

  • Adding public local IPC familiar listing is a new surface. Keep it read-only and minimal.
  • Invoking claude CLI can fail if Claude Code is unavailable on PATH. CLI should print the exact failure and next step.
  • Project-local hooks and Claude local MCP scope are stored in different places by Claude design.
  • Npm npx hook commands may run package resolution at hook time. This is acceptable for npm-first CLI but may be slower than future app-installed shim.
  • Generated commands are version-pinned for stability; users may need to rerun configure after upgrading FamiliarOS CLI.

Security/privacy notes

  • Do not expose filesystem install/remove/default mutation over MCP.
  • Validate project path and familiar id before writes.
  • Only write inside <project>/.claude/settings.local.json for hooks.
  • Reject unsafe project-local hook paths, including symlinked .claude directories.
  • Preserve non-FamiliarOS settings/hooks.
  • Do not log prompt/hook payloads or secrets.

Test/check plan

  • Unit/contract check for CLI arg parsing and generated Claude config.
  • Contract check that existing hooks are preserved and FamiliarOS-managed hooks are replaced.
  • Mocked claude binary check for exact add-json argv, --scope local, JSON shape, and spawned cwd.
  • Noninteractive --familiar check; missing --familiar non-TTY failure/picker behavior.
  • Generated command version pinning check.
  • Project hook writer rejects symlinked .claude / unsafe cwd.
  • IPC protocol check for familiars.list validation/result shape.
  • MCP public tool list remains exactly familiaros_status, familiaros_say, familiaros_react.
  • Package build/test:
pnpm --filter @familiaros/cli build
pnpm --filter @familiaros/cli test
pnpm --filter @familiaros/client test
pnpm --filter @familiaros/desktop build
pnpm --filter @familiaros/desktop test
pnpm --filter @familiaros/claude test

Manual verification guide

  1. Run FamiliarOS desktop.

  2. In a test project, run:

    npx @familiaros/cli configure --familiar fixer
    
  3. Confirm command succeeds without MCP/hooks prompts.

  4. Confirm Claude local MCP entry for the project targets --familiar fixer.

  5. Confirm <project>/.claude/settings.local.json contains FamiliarOS hooks with --familiar fixer.

  6. Start Claude in that project.

  7. Confirm /mcp shows FamiliarOS connected.

  8. Submit a prompt and confirm hook bubble appears on fixer.

  9. Call familiaros_status and confirm actual target is fixer.

Oracle plan review

Oracle reviewed the Phase 17 plan and recommended revision before implementation.

Oracle feedback disposition

  • Fixed: Changed long-lived generated commands from temporary absolute package paths to self-contained, version-pinned npx -y @familiaros/cli@<version> mcp/hook ... wrappers.
  • Fixed: Removed final confirmation prompt from Phase 17; --yes is accepted as no-op compatibility only.
  • Fixed: Spec now requires spawning claude mcp add-json with cwd set to the target project.
  • Fixed: Added project-local hook path safety requirements for symlinked .claude and inside-project resolution.
  • Fixed: Deferred familiaros install <familiar-id> out of Phase 17 to avoid expanding mutation/security scope.
  • Fixed: Added preflight/idempotency requirements before writing MCP/hooks.
  • Fixed: Added version pinning, mocked Claude CLI tests, project hook safety tests, noninteractive CLI tests, and MCP public tool-list regression to test plan.

Implementation notes

  • Added familiaros bin in @familiaros/cli.
  • Added configure, mcp, and hook CLI command paths.
  • Added read-only familiars.list local IPC/client method.
  • configure --familiar <id> writes config without requiring the desktop app to be running; omitting --familiar still queries the running desktop app for interactive familiar selection.
  • Claude MCP config is written through claude mcp add-json familiaros <json> --scope local with cwd set to the target project.
  • Project-local hooks are prepared before MCP mutation and written only after MCP configuration succeeds.
  • Generated MCP/hook commands use version-pinned npx -y @familiaros/cli@<version> wrappers.
  • CLI-generated npm hook timeout is 10 seconds to tolerate npx startup.
  • Project-local hooks include the internal --project-local marker so global FamiliarOS hooks can detect project-specific FamiliarOS hooks and skip themselves, preventing duplicate default/project familiar reactions.
  • --force / --replace removes any existing local Claude MCP familiaros entry before adding the new one.
  • Hidden maintainer flag --local-dev writes local node <repo>/packages/cli/dist/index.js ... commands for pre-release testing; it is intentionally omitted from user help.

Oracle implementation review

Oracle approved the implementation after one revision pass.

  • Fixed: Avoid partial config by parsing/merging hook settings before MCP mutation and writing hooks only after MCP succeeds.
  • Fixed: Reject symlinked .claude, non-file settings.local.json, malformed hook event arrays, and unsafe project-local hook paths.
  • Fixed: Added publishConfig.access = public for npm-distributed packages.
  • Fixed: Added familiars.list response shape validation.
  • Fixed: Added mocked Claude CLI cwd/argv/JSON test.
  • Fixed: Increased CLI-generated hook timeout and added MCP wrapper signal forwarding.
  • Fixed: Added global-hook duplicate prevention using explicit project-local hook marker detection.
  • Fixed: Hardened project-local hook detection to reject symlinks/non-files, cap settings size, and ensure the settings path stays under the Claude project dir.
  • Fixed: Hook CLI boundaries reject --familiar with a missing value.
  • Accepted: Generated commands are pinned to the package version; release must publish @familiaros/cli, @familiaros/client, @familiaros/claude, and @familiaros/mcp at the same version.