Skip to content
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
7a62373
feat: finalize OCX v2 clean break
kdcokenny Jan 31, 2026
f774aab
chore: align registries to v2 schema
kdcokenny Jan 31, 2026
bd15388
chore: update examples to v2 types
kdcokenny Jan 31, 2026
32ea2ee
docs: clean up v2 usage notes
kdcokenny Feb 1, 2026
46e851f
docs: trim redundant parentheticals
kdcokenny Feb 1, 2026
82eb684
docs: refer to lock file
kdcokenny Feb 1, 2026
6dda045
docs: clarify default excludes
kdcokenny Feb 1, 2026
c0786ce
feat: use hash-based canonical ids
kdcokenny Feb 1, 2026
e53c9fe
fix: align instruction discovery with OpenCode and fix registry instr…
kdcokenny Feb 2, 2026
2201860
feat: implement profile layering (global + local merge)
kdcokenny Feb 3, 2026
e0dd0e5
fix: remove dead CLI options from init and registry commands
kdcokenny Feb 3, 2026
42b49ba
docs: remove orphaned registry version pinning references
kdcokenny Feb 3, 2026
466e82d
fix: remove dead version pinning code and fix component name refs
kdcokenny Feb 3, 2026
3f7bb65
fix: profile install registry resolution + remove/move --global
kdcokenny Feb 3, 2026
325ba3f
fix: address reviewer feedback + schema/URL bugs
kdcokenny Feb 3, 2026
735b348
fix: local kit profile URL + URL install namespace
kdcokenny Feb 3, 2026
98e346b
fix: error handling and path resolution bugs
kdcokenny Feb 3, 2026
8347db8
docs: fix receipt file path references
kdcokenny Feb 3, 2026
e1dc8eb
test: fix incorrect CLI flags in test files
kdcokenny Feb 3, 2026
3abdce9
test: remove invalid --force flag from init commands
kdcokenny Feb 3, 2026
27ed852
test: add --global flag to profile move/remove tests
kdcokenny Feb 3, 2026
d891e6b
test: fix init --registry argument order
kdcokenny Feb 3, 2026
2a16cc5
test: fix profile conflict detection scope mismatch
kdcokenny Feb 3, 2026
2bb840d
fix: remove unused profileDir from buildOpenCodeEnv
kdcokenny Feb 3, 2026
d85dc4b
fix: profile resolution and test mock registries
kdcokenny Feb 3, 2026
b9751ac
refactor(cli): align short-flag policy with Cargo conventions
kdcokenny Feb 4, 2026
2473ecb
feat(cli): remove diff command
kdcokenny Feb 4, 2026
1b8d472
fix(tests): correct plugin → plugins import paths in worktree tests
kdcokenny Feb 4, 2026
6a168ac
chore: cleanup
kdcokenny Feb 4, 2026
f2dc3b7
refactor(utils): Consolidate duplicate utilities (#123)
kdcokenny Feb 4, 2026
f77f1f3
fix(config): consolidate opencode config merging to single source of …
kdcokenny Feb 4, 2026
6915182
feat(cli): standardize --dry-run across all commands
kdcokenny Feb 4, 2026
fae9b2a
docs: comprehensive CLI.md rewrite and cross-file fixes
kdcokenny Feb 4, 2026
5d3c2b6
refactor(cli): use shared options helpers and fix exit codes
kdcokenny Feb 4, 2026
6e3f809
fix(cli): use accurate --force description for remove command
kdcokenny Feb 4, 2026
e057ff5
fix(profile): use fresh env reads for profilesDir
kdcokenny Feb 4, 2026
24f70c3
fix: address critical review issues from PR #119
kdcokenny Feb 4, 2026
bd684cf
refactor: replace ALLOWED_PREFIXES with BLOCKED_PATHS
kdcokenny Feb 5, 2026
a171275
fix: improve error handling in profile clone and path validation
kdcokenny Feb 5, 2026
ea5dfa5
refactor: improve type safety and fix flaky test
kdcokenny Feb 5, 2026
874b44a
fix: code quality improvements and cwd bug fix
kdcokenny Feb 5, 2026
5f57fee
fix: resolve PR #119 review items
kdcokenny Feb 5, 2026
2009aea
docs: fix PR #119 review items - path naming and section numbering
kdcokenny Feb 6, 2026
b90b1d7
docs: normalize plural directory paths across templates and docs
kdcokenny Feb 6, 2026
351d902
fix: harden path validation and remove safety
kdcokenny Feb 6, 2026
768e9bb
test: fix git-root and remove test setup
kdcokenny Feb 6, 2026
ea6f812
test: use path.relative for git-root test
kdcokenny Feb 6, 2026
0edf2dc
fix: align receipt versioning to v1
kdcokenny Feb 6, 2026
db3ece4
fix: add missing relative import to git-root test
kdcokenny Feb 6, 2026
f3cf927
docs: remove invalid global registry verification command
kdcokenny Feb 6, 2026
358e30c
docs: align npm plugin manual test expectations with runtime behavior
kdcokenny Feb 6, 2026
9990d56
docs: fix npm plugin verification steps in add manual tests
kdcokenny Feb 6, 2026
79903f9
docs: fix versioned npm plugin manual test command
kdcokenny Feb 6, 2026
ecca133
docs: clarify build output path in manual test 9.2
kdcokenny Feb 6, 2026
3078c53
test: strengthen CLI coverage and reduce test noise
kdcokenny Feb 6, 2026
d3b2035
feat(cli): standardize V2 install-source grammar
kdcokenny Feb 6, 2026
968fc90
docs(readme): simplify profile-first onboarding and demos
kdcokenny Feb 6, 2026
1ce38d4
chore(demo): harden vhs recording and suppress wrangler noise
kdcokenny Feb 6, 2026
529689b
docs(facades): normalize installation sections and OCX install guidance
kdcokenny Feb 6, 2026
e05bf37
fix: restore local .opencode install paths and harden target containment
kdcokenny Feb 7, 2026
a4817ac
docs: refine AGENTS.md for coding agents
kdcokenny Feb 7, 2026
69f0461
chore(cli): remove confirmed dead code and backup artifacts
kdcokenny Feb 7, 2026
9b0102c
chore(cli): remove unused shared force option helper
kdcokenny Feb 7, 2026
01e93dd
chore(cli): remove deprecated path-safety wrappers and tests
kdcokenny Feb 7, 2026
da1c10c
fix(cli): enforce strict profile fail-fast resolution
kdcokenny Feb 7, 2026
b8b12b8
fix(cli): enforce strict --json output contracts
kdcokenny Feb 7, 2026
a7f8731
docs: harden manual testing workflow and add runner script
kdcokenny Feb 7, 2026
397b5fc
fix(cli): preserve raw profile config bytes during clone
kdcokenny Feb 7, 2026
9c7d865
test(cli): isolate add and self-* suites to prevent CI coverage flaki…
kdcokenny Feb 7, 2026
262b0df
fix(cli): scope-aware profiles, flexible registry alias, and input va…
kdcokenny Feb 7, 2026
72c0822
docs: make manual testing guide deterministic and idempotent
kdcokenny Feb 7, 2026
b9242b1
fix: align OpenCode merge parity and manual testing expectations
kdcokenny Feb 9, 2026
da554e9
refactor(cli): require registry alias and align docs to alias-first u…
kdcokenny Feb 9, 2026
064a90c
test(cli): replace unnecessary dynamic imports in tests
kdcokenny Feb 9, 2026
62ac850
docs: align manual testing wording to singular plugin key
kdcokenny Feb 9, 2026
790e349
refactor(cli): make registry identity alias-first and align manual te…
kdcokenny Feb 11, 2026
05da960
fix(schema): require registry name and version in manifests
kdcokenny Feb 11, 2026
ce19d16
chore: align registry alias terminology and tighten schema validation…
kdcokenny Feb 11, 2026
235469b
docs: sync MANUAL_TESTING references and checklist counts
kdcokenny Feb 11, 2026
a35a659
fix(cli): replace non-null assertions with safe type narrowing in ded…
kdcokenny Feb 11, 2026
29bd24a
fix(cli): resolve inline config env/file tokens before launch
kdcokenny Feb 11, 2026
eaa3bb6
docs: migrate OCX documentation to Mintlify with QA artifacts
kdcokenny Feb 12, 2026
c588250
feat(worker): proxy /docs routes to Mintlify with safe redirect handling
kdcokenny Feb 12, 2026
50bf438
docs: fix agent naming, receipt references, and directory paths
kdcokenny Feb 12, 2026
9c10908
fix(cli): invalidate opencode node_modules when deps change
kdcokenny Feb 12, 2026
d1e1927
fix(worktree): avoid iTerm temp script race and clean orphaned script…
kdcokenny Feb 12, 2026
9732c44
chore(cli): bump version to 2.0.0
kdcokenny Feb 12, 2026
380cb90
docs: mark manual testing complete for v2.0.0 on 2026-02-12
kdcokenny Feb 12, 2026
c0be584
fix: align profile schema defaults and normalize schema endpoints
kdcokenny Feb 12, 2026
6e51905
refactor: enforce global-only profile UX and align docs/tests
kdcokenny Feb 13, 2026
3a80e15
docs: align profile docs with global-only command requirements
kdcokenny Feb 13, 2026
b21725d
refactor(cli): remove OPENCODE_CONFIG_CONTENT token-resolution shim
kdcokenny Feb 13, 2026
d5d3595
feat(cli): add ocx migrate command for v1.4.6 to v2 receipt migration
kdcokenny Feb 13, 2026
2014438
fix(cli): clarify migrate receipt terminology
kdcokenny Feb 13, 2026
575286f
feat(cli): add --global migrate scope and legacy registry normalization
kdcokenny Feb 13, 2026
5fbebcc
fix: resolve turbo bun lock workspace parsing and ci cache key
kdcokenny Feb 13, 2026
c0296dd
feat(cli): fan out --global migrate across root and all profiles
kdcokenny Feb 13, 2026
e04a6d8
chore(cli): add explicit preview_with_errors migrate status
kdcokenny Feb 13, 2026
e13f892
chore: mark main merged, keep v2 as canon
kdcokenny Feb 14, 2026
bb6be6e
test(cli): make self-update checksum fixture platform-agnostic
kdcokenny Feb 15, 2026
7e8acb6
test(self-update): harden module mock isolation across test files
kdcokenny Feb 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .opencode/profiles/work/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Profile Instructions

<!-- Add your custom instructions for this profile here -->
<!-- These will be included when running `ocx opencode -p work` -->
14 changes: 14 additions & 0 deletions .opencode/profiles/work/ocx.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "https://ocx.kdco.dev/schemas/ocx.json",
"registries": {},
"renameWindow": true,
"exclude": [
// "**/AGENTS.md",
"**/CLAUDE.md",
"**/CONTEXT.md",
"**/.opencode/**",
"**/opencode.jsonc",
"**/opencode.json"
],
"include": []
}
1 change: 1 addition & 0 deletions .opencode/profiles/work/opencode.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
42 changes: 21 additions & 21 deletions .opencode/worktree.jsonc
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
{
"$schema": "https://registry.kdco.dev/schemas/worktree.json",
"$schema": "https://registry.kdco.dev/schemas/worktree.json",

// Worktree plugin configuration
// Documentation: https://github.com/kdcokenny/ocx
// Worktree plugin configuration
// Documentation: https://github.com/kdcokenny/ocx

"sync": {
// Files to copy from main worktree to new worktrees
// Example: [".env", ".env.local", "dev.sqlite"]
"copyFiles": [],
"sync": {
// Files to copy from main worktree to new worktrees
// Example: [".env", ".env.local", "dev.sqlite"]
"copyFiles": [],

// Directories to symlink (saves disk space)
// Example: ["node_modules"]
"symlinkDirs": [],
// Directories to symlink (saves disk space)
// Example: ["node_modules"]
"symlinkDirs": [],

// Patterns to exclude from copying
"exclude": []
},
// Patterns to exclude from copying
"exclude": []
},

"hooks": {
// Commands to run after worktree creation
// Example: ["pnpm install", "docker compose up -d"]
"postCreate": [],
"hooks": {
// Commands to run after worktree creation
// Example: ["pnpm install", "docker compose up -d"]
"postCreate": [],

// Commands to run before worktree deletion
// Example: ["docker compose down"]
"preDelete": []
}
// Commands to run before worktree deletion
// Example: ["docker compose down"]
"preDelete": []
}
}
80 changes: 40 additions & 40 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ packages/cli/ # Main CLI tool (@ocx/cli)
update.ts # Update components
build.ts # Build components
diff.ts # Component diff
ghost/ # [TEMPORARY] Ghost mode migration
config/ # Config providers and resolution
profile/ # Profile management (manager, paths)
registry/ # Registry fetching/resolution
Expand Down Expand Up @@ -257,7 +256,6 @@ OCX provides a global profile system for managing multiple OpenCode configuratio
| Config commands | `src/commands/config/` | show, edit |
| OpenCode commands | `src/commands/opencode/` | Launch OpenCode with profile config |
| Init commands | `src/commands/init/` | Initialize global/local configs |
| Ghost commands | `src/commands/ghost/` | [TEMPORARY] Migration utilities |

### Directory Structure

Expand Down Expand Up @@ -288,20 +286,19 @@ Profiles are resolved in this order:
3. `default` profile (if it exists)
4. No profile (base configs only)

### Configuration Isolation
### Configuration Merging

OCX configs (`ocx.jsonc`) are **ISOLATED per scope** - they do NOT merge.
OCX configs (`ocx.jsonc`) **merge when using profiles**:

**Registry Isolation (Security Model):**
- **Global registries** (in `~/.config/opencode/ocx.jsonc`) - ONLY used for downloading global settings like profiles
- **Profile registries** (in profile's `ocx.jsonc`) - ONLY available when using that profile
- **Local registries** (in `.opencode/ocx.jsonc`) - ONLY for that project
**Profile Layering:**
- Global profile + local profile (if both exist with same name) = merged config
- Deep merge with local winning on conflicts
- Registries merge (local adds to global)
- Arrays replace (local wins)

This prevents global registries from injecting components into all projects.

**What DOES merge:**
- OpenCode config files (`opencode.jsonc`) merge: profile → local (if not excluded by patterns)
- Profile's exclude/include patterns control which project files OpenCode can see
**Registry Isolation:**
Global base config registries are ONLY used for downloading profiles, not components.
When using a profile, registries come from merged profile config.

### How `ocx opencode` Works

Expand All @@ -318,26 +315,38 @@ This prevents global registries from injecting components into all projects.

### Instruction File Discovery

By default, all project instruction files are excluded so only your profile's files are used.
OCX doesn't exclude anything by default. A clean ocx.jsonc includes all project instruction files. The default profile template ships an exclude list for security.

**Default exclude patterns:**
- `**/AGENTS.md`
- `**/CLAUDE.md`
- `**/CONTEXT.md`
- `**/.opencode/**`
- `**/opencode.jsonc`
- `**/opencode.json`
**The default profile template uses this exclude list:**

```jsonc
{
"exclude": [
"**/AGENTS.md",
"**/CLAUDE.md",
"**/CONTEXT.md",
"**/.opencode/**",
"**/opencode.jsonc",
"**/opencode.json"
]
}
```

**To include project files**, modify your profile's `ocx.jsonc`:

```jsonc
{
// Include all project AGENTS.md files
"exclude": ["**/CLAUDE.md", "**/CONTEXT.md", "**/.opencode/**", "**/opencode.jsonc", "**/opencode.json"],

// Or exclude all but include specific ones (TypeScript/Vite style)
"exclude": ["**/AGENTS.md"],
"include": ["./docs/AGENTS.md"]
// Include all project AGENTS.md files by removing from exclude
"exclude": ["**/CLAUDE.md", "**/CONTEXT.md", "**/.opencode/**", "**/opencode.jsonc", "**/opencode.json"]
}
```

Or use include patterns to override excludes (TypeScript/Vite style):

```jsonc
{
"exclude": ["**/AGENTS.md", "**/CLAUDE.md", "**/CONTEXT.md", "**/.opencode/**", "**/opencode.jsonc", "**/opencode.json"],
"include": ["./docs/AGENTS.md"] // Include only specific file
}
```

Expand Down Expand Up @@ -395,15 +404,6 @@ Use profile commands to manage multiple configurations:
| `ocx init` | - | Initialize local .opencode/ |
| `ocx init --global` | - | Initialize global profiles |

#### Ghost Commands (TEMPORARY)

| Command | Alias | Description |
|---------|-------|-------------|
| `ocx ghost migrate` | - | Migrate ghost.jsonc to ocx.jsonc in profiles |
| `ocx ghost migrate --dry-run` | - | Preview migration without making changes |

**Note:** The ghost command group is temporary and will be removed in the next minor version. It helps users migrate from the legacy "ghost mode" configuration format (`ghost.jsonc`) to the unified profile system (`ocx.jsonc`).

#### Self Commands

| Command | Description |
Expand Down Expand Up @@ -435,13 +435,13 @@ Use profile commands to manage multiple configurations:
# Initialize global profiles
ocx init --global

# Create and use a work profile
ocx profile add work
# Create and use a global work profile
ocx profile add work --global
ocx config edit -p work # Edit profile settings

# Install profile from registry (requires global registry config)
ocx registry add https://ocx-kit.kdco.dev --name kit --global
ocx profile add ws --from kit/ws
ocx profile add ws --from kit/ws --global

# Force overwrite existing profile
ocx profile add ws --from kit/ws --force
Expand All @@ -453,7 +453,7 @@ ocx opencode -p work
OCX_PROFILE=work ocx opencode

# Clone settings from existing profile
ocx profile add client-x --from work
ocx profile add client-x --from work --global

# View configuration from current scope
ocx config show
Expand Down
9 changes: 3 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ Your `registry.jsonc` defines a namespace for all components:
"components": [
{
"name": "component",
"type": "ocx:plugin",
"type": "plugin",
"description": "What it does",
"files": [
{
"path": "plugin/my-plugin.ts",
"target": ".opencode/plugin/component.ts"
"target": "plugin/component.ts"
}
],
"dependencies": []
Expand Down Expand Up @@ -94,10 +94,8 @@ Use the `ocx-dev` profile to keep testing completely separate.
#### Setup ocx-dev Profile (One-time)

```bash
# Create the ocx-dev profile
# Create a LOCAL profile for isolated testing (not --global, intentionally project-scoped)
./packages/cli/dist/index.js profile add ocx-dev

# Profile is automatically available for use
```

#### Quick Feature Testing
Expand Down Expand Up @@ -198,7 +196,6 @@ Key test scenarios:
- `--all` flag updates all components
- `--registry` flag scopes to registry
- `--dry-run` previews without changes
- `@version` syntax pins to specific version
- Error cases (conflicts, missing components)

> **Note:** For quick manual testing or AI-driven testing, prefer Profile Mode Testing above.
Expand Down
33 changes: 19 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ Your OpenCode config, anywhere.

## Why OCX?

- 👻 **Profiles** — Work in any repo with YOUR config. Control exactly what OpenCode sees.
- 📁 **Profiles** — Work in any repo with YOUR config. Control exactly what OpenCode sees.
- 📦 **Registries** — npm plugins, MCP servers, components from curated registries.
- 🔒 **Auditable** — SHA-256 verified, version-pinned, code you own.
- 🔒 **Auditable** — SHA-verified, code you own.

![OCX Profiles Demo](./assets/profiles-demo.gif)

Expand All @@ -38,11 +38,11 @@ Work in any repo without modifying it. Your config, their code.
```bash
# One-time setup
ocx init --global # Initialize global profiles
ocx profile add work # Create a work profile
ocx profile add work --global # Create a global work profile

# Install pre-configured profile (optional)
ocx registry add https://ocx-kit.kdco.dev --name kit --global
ocx profile add work --from kit/omo
ocx profile add work --from kit/omo --global

# Use in any repo
cd ~/oss/some-project
Expand All @@ -55,7 +55,7 @@ ocx oc # Uses work profile automatically

Profile settings control what OpenCode sees through `exclude`/`include` patterns. Registries are isolated per profile for security. OpenCode config merges safely between profile and local settings.

> **Security Note:** By default, profiles include project `AGENTS.md` files. For untrusted repos, uncomment `**/AGENTS.md` in your profile's exclude list. See [Lock Down Recipe](./docs/PROFILES.md#lock-down-recipe).
> **Security Note:** An empty exclude list includes all project instruction files; the default profile template ships a secure exclude list. For trusted repos, edit your profile to loosen the template's exclude list. See [Lock Down Recipe](./docs/PROFILES.md#lock-down-recipe).

**[Full Profile Guide →](./docs/PROFILES.md)**

Expand All @@ -66,26 +66,30 @@ Add components to local projects with automatic dependency resolution.
![OCX Components Demo](./assets/components-demo.gif)

```bash
# Initialize local config
# Initialize local config if not present
ocx init

# Add a registry
ocx registry add https://registry.kdco.dev --name kdco

# Install components
ocx add kdco/workspace
# One-command install with ephemeral registry (not saved)
ocx add kdco/workspace --from https://registry.kdco.dev

# Or install npm plugins directly
ocx add npm:@franlol/opencode-md-table-formatter
```

After installation, components live in `.opencode/` where you can customize freely. OCX handles npm dependencies, MCP servers, and config merging automatically.

To add a registry permanently for your project:

```bash
ocx registry add https://registry.kdco.dev --name kdco
ocx add kdco/workspace
```

## Philosophy

OCX follows the **ShadCN model**: components are copied into your project (`.opencode/`), not hidden in `node_modules`. You own the code—customize freely.

Like **Cargo**, OCX resolves dependencies, pins versions, and verifies integrity. Every component is SHA-256 verified and version-pinned. See changes before updating:
Like **Cargo**, OCX resolves dependencies and verifies integrity. Every component is SHA-256 verified. See changes before updating:

```bash
ocx diff kdco/workspace
Expand All @@ -98,13 +102,14 @@ ocx diff kdco/workspace
| Command | Description |
|---------|-------------|
| `ocx add <component>` | Add components or npm plugins (`npm:package`) |
| `ocx add <component> --from <url>` | One-command install with ephemeral registry (not saved) |
| `ocx update [component]` | Update to latest version |
| `ocx diff [component]` | Show upstream changes before updating |
| `ocx profile <cmd>` | Manage global profiles (`add`, `list`, `remove`, `show`) |
| `ocx opencode` / `ocx oc` | Launch OpenCode with profile |
| `ocx registry add <url>` | Add a component registry (`--global` for global, `-p` for profile) |
| `ocx registry add <url>` | Add a component registry (local-first; use `--global` for global) |
| `ocx config show` | View config from current scope |
| `ocx config edit` | Edit local or global config (`--global`) |
| `ocx config edit` | Edit local config (use `--global` for global) |
| `ocx self update` | Update OCX to latest version |
| `ocx self uninstall` | Remove OCX config and binary |

Expand Down
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ OCX uses SHA-256 cryptographic hashes to ensure installed components haven't bee

### How it works

1. **Locking**: When a component is first installed, its content is hashed and stored in `ocx.lock`.
1. **Receipt**: When a component is first installed, its content is hashed and stored in `.ocx/receipt.jsonc`.
2. **Verification**: On subsequent installs or updates, OCX re-hashes the incoming content.
3. **Protection**: If the new hash doesn't match the one in `ocx.lock`, the installation is aborted with an `INTEGRITY_ERROR`.
3. **Protection**: If the new hash doesn't match the one in `.ocx/receipt.jsonc`, the installation is aborted with an `INTEGRITY_ERROR`.

This ensures that once a version is approved and locked by your team, it cannot be silently swapped for different content, even if the registry itself is compromised.

Expand Down
Loading