homonto

command module
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 2 Imported by: 0

README

homonto

Declarative config for your AI coding tools. Describe your MCP servers, skills, plugins, and settings once in homonto.toml; homonto apply projects them into Claude Code and OpenCode through a terraform-style plan/confirm/apply pipeline. Secrets are referenced, never stored — resolved only at apply time. The public v0.1 release gate is dual-binary: homonto remains the deterministic installer/projector, and onto will ship beside it as the spec-driven workflow operator.

Install

Install a tagged release directly:

go install github.com/noviopenworks/homonto@v0.1.0   # a tagged release

Tagged releases also ship prebuilt homonto and onto binaries for Linux/macOS/Windows (amd64 and arm64) with a SHA256SUMS file, attached to the GitHub release.

Or install/run the current source:

go install github.com/noviopenworks/homonto@main   # current main branch
go install .                                        # from a checked-out repo

Quickstart

homonto init            # scaffold homonto.toml, .gitignore, .env.example, homonto/skills/
$EDITOR homonto.toml    # declare your MCPs / skills / plugins / settings
homonto plan            # dry run: show the diff, write nothing
homonto apply           # plan → confirm [y/N] → write (use --yes to skip prompt)

New to homonto? docs/guides/getting-started.md is a hands-on walkthrough of both binaries with real command output and a supported / not-supported matrix.

Other commands:

Command What it does
homonto update Re-materialize this binary's embedded catalog and re-project it — bring installed frameworks/skills/commands/subagents up to the running version, printing the version transition. Does not self-update the binaries.
homonto status Show managed values that would be reset or recreated on apply
homonto doctor Health check: pass present? tool dirs present? owned skill content and both tool links present?
homonto --version Print the build version

After installing a newer homonto/onto binary, run homonto update to bring the projected content up to that version. State records the versions behind each apply (binary, catalog, per-framework), and onto doctor warns when the onto binary and the homonto that installed its framework have drifted apart.

--config <path> selects a different config file for plan/apply/status/doctor/import. init instead takes an optional target directory and always writes homonto.toml inside that directory.

Experimental adoption helper: homonto import bootstraps Claude global MCP servers into homonto.toml with best-effort env redaction. It is deliberately narrow and is not part of the main quickstart path.

homonto.toml

[mcps.codegraph]
command = ["codegraph", "serve", "--mcp"]
targets = ["claude", "opencode"]          # default: all tools

[mcps.brave]
command = ["npx", "-y", "@modelcontextprotocol/server-brave-search"]
env = { BRAVE_API_KEY = "${pass:ai/brave}" }

[skills.graphify]
source = "local:graphify"                 # local:<name> → homonto/skills/<name>
scope = "project"                         # required: user | project (no default)

[marketplaces.claude.official]            # → extraKnownMarketplaces (claude only)
source = "github"                         # github | url | git-subdir | directory
repo = "anthropics/claude-plugins"        # locator for the source type

[plugins.claude.claude-hud]
source = "claude-hud@official"            # name@marketplace (enabledPlugins key)
# enabled = false                        # optional; omit → enabled
# config = { compact = true }            # optional; → pluginConfigs.<source>.options (claude only)

[plugins.opencode.opencode-quota]
source = "@slkiser/opencode-quota"        # npm package (the `plugin` array entry)

[settings.claude]
model = "opus"
theme = "dark"                            # TUI settings are top-level settings.json keys

[settings.opencode]
model = "anthropic/claude-opus-4-8"

[tui.opencode]                            # → ~/.config/opencode/tui.json (separate file)
theme = "gruvbox"
scroll_speed = 3

[models.claude.architectural]             # required for every model-enabled tool
model = "opus"
variant = "max"

[subagents.review]                        # an agent definition, projected by apply
source = "builtin:review-agent"           # builtin:<name> | local:<name> | remote:<url>
scope = "project"                         # user | project (default project)
mode = "copy"                             # link (symlink, default) | copy (managed file)
targets = ["claude", "opencode"]          # optional; default both

[subagents.reviewer]                      # a remote, pinned agent
source = "remote:https://example.com/reviewer.tar.gz"
digest = "sha256:<64 hex>"                # REQUIRED content pin; verified before any write

Agents are just [subagents.<name>] resources — declarative, managed by plan/apply/status/doctor like skills and commands (there is no separate imperative agents command group). mode = "link" (the default) symlinks the agent into each tool's agent directory; mode = "copy" projects it as a real managed file you can edit locally — apply keeps it in sync, detects drift, prunes it when de-declared, and backs up a local edit to <path>.bak before overwriting. Sources are local:<name> (from homonto/subagents/), builtin:<name> (from the bundled catalog), or remote:<url> (fetched, pinned, and verified). The legacy [agents.<name>] table still parses but is folded into a copy-mode [subagents.<name>] at load.

A remote: source is pinned and fail-closed: it requires a digest = "sha256:…", and apply fetches → validates the archive (rejecting traversal, symlinks, and bombs) → matches the pin → checks revocation → caches, never writing a tool file until every check passes. Pins are recorded in .homonto/remote.lock.json; content is cached under .homonto/cache/remote/ (offline + reproducible). See docs/guides/remote-source-trust.md.

The example is abbreviated — a complete config must also define models.claude.coding and models.claude.trivial, and the same three levels apply to every model-enabled target tool.

Secrets — referenced, never stored

Secret values live outside the repo and are referenced by token:

  • ${pass:PATH} — resolved via pass.
  • ${ENV_VAR} — resolved from the environment (zero-dependency fallback).

Guarantees:

  • plan never resolves or prints a secret — it shows the ${...} token.
  • apply resolves secrets only after you confirm, all at once before any file is written; a missing reference aborts before touching anything.
  • .homonto/state.json stores only the unresolved token plus a sha256 hash of the applied value — never plaintext — so it is safe to share and a repeat apply on a secret-backed value is a no-op (idempotent), while an out-of-band change to that value is still detected as drift.

Owned content is symlinked

Skills you author live under homonto/skills/ (the local provider root, next to homonto.toml) and are symlinked into each tool, so editing homonto/skills/... is instantly live everywhere. apply ensures the links exist and point correctly; it never clobbers a file that isn't its own symlink (reported as a conflict instead). A skills-only apply leaves tool JSON files byte-for-byte untouched — adapters write a file only when a managed key inside it actually changes — so OpenCode JSONC comments survive link-only applies.

Skill scope — user or project

Each skill resource declares its own scope (scope is required; there is no default). scope chooses where that skill is linked (it affects skills only; MCP servers and settings always project into your global tool files):

  • scope = "user" — links into ~/.claude/skills/ and ~/.config/opencode/skills/.
  • scope = "project" — links into the project itself, next to homonto.toml: <repo>/.claude/skills/ and <repo>/.opencode/skills/. Use this to keep a project's skills in-repo instead of your global tool config.

Switching a skill's scope is clean: plan shows the link relocating from its old location to the new one, and apply removes the old link as it creates the new one, so no orphaned symlink is left behind. status and doctor report against whichever location each skill's scope selects.

Surgical merge

homonto writes only the keys it manages and preserves every unmanaged key in each tool's file. Removal is declarative too: keys you remove from homonto.toml are deleted from the tool files on the next apply (and owned-skill links removed) — state tracks what homonto manages. A skills-only apply leaves tool JSON files byte-for-byte untouched, since adapters write a file only when a managed key inside it actually changes.

Known limitations

homonto is a young, deliberately narrow tool. For the v0.1.0 beta line:

  • v0.1.0 is the first public tag. Both binaries ship: homonto provides deterministic projection and agent lifecycle management, while onto provides init, new, status, advance, close, and doctor with phase, dependency, archive, and dirty-worktree gates. The release pipeline packages both binaries behind the CI gate (gofmt, mod-tidy, vet, build, -race, govulncheck, dual-binary Docker E2E).
  • Framework skill, command, and subagent projection are all implemented. [frameworks.X] resolves through the bundled builtin catalog (onto, comet, superpowers, openspec), expands dependencies, and materializes/links skills into Claude Code and OpenCode. [commands.X] (builtin or local, single-file materialization to .homonto/catalog/commands/) and framework-declared [commands] tables project the same way, into Claude Code (.claude/commands/<name>.md) and OpenCode (.opencode/command/<name>.md project-scoped, or the equivalent user-scope directories). [subagents.X] (builtin or local, single-file materialization to .homonto/catalog/subagents/) and framework-declared [subagents] tables project verbatim the same way, into Claude Code (.claude/agents/<name>.md) and OpenCode (.opencode/agent/<name>.md project-scoped, or the equivalent user-scope directories), with doctor verifying the links. Three real subagents ship in the catalog: code-reviewer, codebase-explorer, and comet-navigator.
  • OpenCode JSONC comments are not preserved. Claude's files are plain JSON, but OpenCode's opencode.jsonc supports comments. Any apply that writes opencode.jsonc rewrites it as normalized JSON, so all comments in that file are removed. (A skills-only or otherwise no-op apply does not write the file, so comments survive those.) Accepted for beta.
  • import is a narrow Claude MCP bootstrap. It reads Claude's global MCP servers only, redacts values that look like secrets into ${pass:...} references (best-effort, not exhaustive), and preserves command/args verbatim. It does not import skills, plugins, settings, or OpenCode config. Treat its output as a starting point to review, not a complete migration.
  • Two full adapters + a Codex pilot. Claude Code and OpenCode are the full adapters. Codex (OpenAI Codex CLI) is a pilot third adapter built on the shared adapter contract (internal/adapter/structproj + the tomlutil TOML codec): it projects MCP servers only, into ~/.codex/config.toml [mcp_servers.<name>] tables, and is opt-in — a resource must list codex in its targets (the default target set stays Claude + OpenCode). The contract lets a new adapter supply just a file path, key mapping, and format codec instead of re-implementing the projection control flow.
  • Secrets need pass or an env var. ${pass:...} references require pass on PATH; ${ENV_VAR} references require the variable to be set at apply time. homonto doctor flags a missing pass.

For contributors

Everything below is about developing homonto itself — users don't need it.

How it works

homonto.toml is parsed into one tool-agnostic desired-state model; each tool is an adapter (ReadPlanApply) wired by the engine. Adding a new tool requires a new adapter plus engine/config wiring. Writes are atomic (temp + rename); state is persisted after each successful adapter so a later adapter failure does not lose earlier records.

Development workflow

The source of truth for shipped behavior is the code and its tests. Prior capability specs, change workspaces, and design/verify docs were cleared from the tree; that history remains in git (git log) if you need it.

  • docs/personas.md — which workflow to use (homonto vs onto vs Comet/OpenSpec/Superpowers), and why we build with Comet but ship onto
  • docs/adr/ — accepted / superseded architecture decisions (the durable rationale)
  • docs/guides/ — user-facing guides
  • docs/release-checklist.md — how to cut a release

Read the relevant ADRs and the nearby implementation before changing behavior; add or update focused tests for behavior changes and run the narrowest useful verification command.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
Package catalog embeds the bundled framework metadata and skill content.
Package catalog embeds the bundled framework metadata and skill content.
cmd
onto command
internal
adapter/codex
Package codex is the pilot third-party adapter built on the adapter contract (internal/adapter/structproj) and the TOML codec (internal/tomlutil).
Package codex is the pilot third-party adapter built on the adapter contract (internal/adapter/structproj) and the TOML codec (internal/tomlutil).
adapter/copyproj
Package copyproj is the copy-mode projection contract: the content-file analogue of internal/adapter/structproj and internal/adapter/fileproj.
Package copyproj is the copy-mode projection contract: the content-file analogue of internal/adapter/structproj and internal/adapter/fileproj.
adapter/fileproj
Package fileproj is the file-projection contract: the symlink analogue of internal/adapter/structproj.
Package fileproj is the file-projection contract: the symlink analogue of internal/adapter/structproj.
adapter/jsoncodec
Package jsoncodec is the shared structproj.Codec for JSON config documents.
Package jsoncodec is the shared structproj.Codec for JSON config documents.
adapter/registry
Package registry is the tool-id-keyed adapter registry: the engine sources its tool adapters from here instead of a hardcoded slice, so adding a built-in adapter is a single registration in Builtins() rather than an edit to the engine's composition root.
Package registry is the tool-id-keyed adapter registry: the engine sources its tool adapters from here instead of a hardcoded slice, so adding a built-in adapter is a single registration in Builtins() rather than an edit to the engine's composition root.
adapter/structproj
Package structproj is the adapter contract: a format-agnostic managed-key projection engine.
Package structproj is the adapter contract: a format-agnostic managed-key projection engine.
agentfm
Package agentfm renders per-tool subagent frontmatter from one neutral source.
Package agentfm renders per-tool subagent frontmatter from one neutral source.
applylock
Package applylock provides a project-scoped exclusive lock so two concurrent `homonto apply` runs cannot plan from the same snapshot and race to a last-writer-wins outcome on the state and tool files.
Package applylock provides a project-scoped exclusive lock so two concurrent `homonto apply` runs cannot plan from the same snapshot and race to a last-writer-wins outcome on the state and tool files.
buildinfo
Package buildinfo resolves the effective binary version.
Package buildinfo resolves the effective binary version.
catalog
Package catalog loads and expands the embedded framework/skill catalog.
Package catalog loads and expands the embedded framework/skill catalog.
cli
commandpath
Package commandpath is the single source of truth for where each tool's owned commands are linked, as a function of the install scope.
Package commandpath is the single source of truth for where each tool's owned commands are linked, as a function of the install scope.
copyfile
Package copyfile is a conflict-safe reconciler for homonto-managed CONTENT files — the copy-mode analogue of internal/link (which reconciles symlinks).
Package copyfile is a conflict-safe reconciler for homonto-managed CONTENT files — the copy-mode analogue of internal/link (which reconciles symlinks).
fsutil
Package fsutil holds shared filesystem helpers used by adapters and state.
Package fsutil holds shared filesystem helpers used by adapters and state.
merge
Package merge is a pure, dependency-free line-based three-way merge.
Package merge is a pure, dependency-free line-based three-way merge.
ontostate
Package ontostate models the onto-state.yaml file used by the onto binary's workflow tracking.
Package ontostate models the onto-state.yaml file used by the onto binary's workflow tracking.
remote
Package remote implements homonto's remote-source trust boundary: pinned, verified, fail-closed installation of remote resources.
Package remote implements homonto's remote-source trust boundary: pinned, verified, fail-closed installation of remote resources.
skillpath
Package skillpath is the single source of truth for where each tool's owned skills are linked, as a function of the install scope.
Package skillpath is the single source of truth for where each tool's owned skills are linked, as a function of the install scope.
subagentpath
Package subagentpath is the single source of truth for where each tool's owned subagents are linked, as a function of the install scope.
Package subagentpath is the single source of truth for where each tool's owned subagents are linked, as a function of the install scope.
tomlutil
Package tomlutil is a TOML codec for the adapter projection core: it reads, sets, and deletes a value at a dotted key path in a TOML document while preserving unmanaged tables and keys, bridging TOML values to canonical JSON so state hashing stays format-independent.
Package tomlutil is a TOML codec for the adapter projection core: it reads, sets, and deletes a value at a dotted key path in a TOML document while preserving unmanaged tables and keys, bridging TOML values to canonical JSON so state hashing stays format-independent.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL