From 89b27a85a0f5cc1a0f9599de20a582e5f5b7a7a0 Mon Sep 17 00:00:00 2001 From: Bot Date: Sun, 19 Apr 2026 03:12:10 +0100 Subject: [PATCH] opencode: add portable phase 1 port Add repo-backed OpenCode commands and installer wiring so Cavekit can be used honestly in OpenCode without claiming Claude/Codex runtime parity. --- install.sh | 51 ++++++++++++++++ opencode/AGENTS.md | 9 +++ opencode/README.md | 95 +++++++++++++++++++++++++++++ opencode/commands/ck-check.md | 55 +++++++++++++++++ opencode/commands/ck-help.md | 45 ++++++++++++++ opencode/commands/ck-init.md | 107 +++++++++++++++++++++++++++++++++ opencode/commands/ck-make.md | 60 ++++++++++++++++++ opencode/commands/ck-map.md | 76 +++++++++++++++++++++++ opencode/commands/ck-sketch.md | 70 +++++++++++++++++++++ opencode/commands/ck-status.md | 51 ++++++++++++++++ tests/opencode-port.test.cjs | 74 +++++++++++++++++++++++ 11 files changed, 693 insertions(+) create mode 100644 opencode/AGENTS.md create mode 100644 opencode/README.md create mode 100644 opencode/commands/ck-check.md create mode 100644 opencode/commands/ck-help.md create mode 100644 opencode/commands/ck-init.md create mode 100644 opencode/commands/ck-make.md create mode 100644 opencode/commands/ck-map.md create mode 100644 opencode/commands/ck-sketch.md create mode 100644 opencode/commands/ck-status.md create mode 100644 tests/opencode-port.test.cjs diff --git a/install.sh b/install.sh index 27e6a70..684b97e 100755 --- a/install.sh +++ b/install.sh @@ -10,6 +10,10 @@ set -euo pipefail INSTALL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CLAUDE_DIR="$HOME/.claude" SETTINGS_FILE="$CLAUDE_DIR/settings.json" +OPENCODE_DIR="$HOME/.config/opencode" +OPENCODE_COMMANDS_DIR="$OPENCODE_DIR/commands" +OPENCODE_USER_STATE_DIR="$HOME/.opencode" +LOCAL_BIN_DIR="$HOME/.local/bin" BIN_DIR="/usr/local/bin" MARKETPLACE_NAME="cavekit-local" MARKETPLACE_DIR="$CLAUDE_DIR/plugins/local/cavekit-marketplace" @@ -152,6 +156,38 @@ info "Configuring Codex bundle..." chmod +x "$INSTALL_DIR/scripts/sync-codex-plugin.sh" "$INSTALL_DIR/scripts/sync-codex-plugin.sh" +# ─── Sync OpenCode commands ───────────────────────────────────────────────── + +if [[ -d "$OPENCODE_DIR" || -d "$OPENCODE_USER_STATE_DIR" ]] || command -v opencode &>/dev/null; then + info "Configuring OpenCode commands..." + mkdir -p "$OPENCODE_COMMANDS_DIR" + + for command_file in "$INSTALL_DIR"/opencode/commands/*.md; do + command_name="$(basename "$command_file")" + ln -sfn "$command_file" "$OPENCODE_COMMANDS_DIR/$command_name" + done + + for command_path in "$OPENCODE_COMMANDS_DIR"/ck-*.md; do + [[ -L "$command_path" ]] || continue + target_path="$(readlink "$command_path" || true)" + [[ "$target_path" == "$INSTALL_DIR/opencode/commands/"* ]] || continue + [[ -e "$target_path" ]] || rm -f "$command_path" + done + + if [[ -f "$INSTALL_DIR/opencode/AGENTS.md" ]]; then + if [[ ! -e "$OPENCODE_DIR/AGENTS.md" ]]; then + cp "$INSTALL_DIR/opencode/AGENTS.md" "$OPENCODE_DIR/AGENTS.md" + ok "Copied OpenCode AGENTS template to $OPENCODE_DIR/AGENTS.md" + else + warn "Leaving existing OpenCode AGENTS.md unchanged at $OPENCODE_DIR/AGENTS.md" + fi + fi + + ok "Linked OpenCode commands at $OPENCODE_COMMANDS_DIR" +else + warn "OpenCode not detected. Skipping OpenCode command install." +fi + # ─── Install cavekit CLI ───────────────────────────────────────────────── info "Installing cavekit command..." @@ -182,6 +218,12 @@ fi if [[ -w "$BIN_DIR" ]]; then ln -sf "$INSTALL_DIR/scripts/cavekit" "$BIN_DIR/cavekit" ok "Installed cavekit to $BIN_DIR/cavekit" +elif mkdir -p "$LOCAL_BIN_DIR" && [[ -w "$LOCAL_BIN_DIR" ]]; then + ln -sf "$INSTALL_DIR/scripts/cavekit" "$LOCAL_BIN_DIR/cavekit" + ok "Installed cavekit to $LOCAL_BIN_DIR/cavekit" + if [[ ":$PATH:" != *":$LOCAL_BIN_DIR:"* ]]; then + warn "$LOCAL_BIN_DIR is not on PATH. Add it to use 'cavekit' directly." + fi else info "Need sudo to install cavekit to $BIN_DIR" sudo ln -sf "$INSTALL_DIR/scripts/cavekit" "$BIN_DIR/cavekit" @@ -213,4 +255,13 @@ printf " ${B}Codex:${R}\n" printf " Linked local bundle via ~/plugins/ck and ~/.agents/plugins/marketplace.json\n" printf " Linked prompts into ~/.codex/prompts (for /prompts:ck-... commands)\n" printf "\n" +printf " ${B}OpenCode:${R}\n" +printf " /ck-help Show portable-phase guide\n" +printf " /ck-init Bootstrap context/ for OpenCode\n" +printf " /ck-sketch Draft kits with approval gate\n" +printf " /ck-map Generate build-site.md\n" +printf " /ck-make Sequential scoped implementation\n" +printf " /ck-check Read-only verification\n" +printf " /ck-status Snapshot progress report\n" +printf "\n" printf " Restart Claude Code and Codex to load the plugin changes.\n\n" diff --git a/opencode/AGENTS.md b/opencode/AGENTS.md new file mode 100644 index 0000000..6bd4b19 --- /dev/null +++ b/opencode/AGENTS.md @@ -0,0 +1,9 @@ +# Cavekit OpenCode Port + +Use the OpenCode Cavekit commands from `~/.config/opencode/commands/` when the repo follows the Cavekit workflow. + +Hard rules: +- This is the **Portable Phase 1 OpenCode port**, not upstream Claude/Codex runtime parity. +- Do not claim autonomous loops, stop hooks, worktree fan-out, team mode, or hidden background orchestration. +- Prefer the normal workflow: kits → build-site → scoped implementation → read-only verification. +- Keep requirements implementation-agnostic in kits. Track execution in plans and impl docs. diff --git a/opencode/README.md b/opencode/README.md new file mode 100644 index 0000000..399ee3c --- /dev/null +++ b/opencode/README.md @@ -0,0 +1,95 @@ +# Cavekit for OpenCode — Portable Phase 1 + +This directory contains the **OpenCode-native Cavekit port** for the portable phases only: + +- `/ck-init` +- `/ck-sketch` +- `/ck-map` +- `/ck-make` +- `/ck-check` +- `/ck-status` +- `/ck-help` + +## What this port is + +- Real OpenCode command files, stored in-repo and installable via `install.sh` +- Truthful adaptation of Cavekit's **spec → plan → build → verify** workflow +- Sequential, tool-driven execution using OpenCode's normal file and shell tools + +## What this port is not + +- Not the Claude Code plugin runtime +- Not the Codex prompt/runtime bundle +- No autonomous stop-hook loop +- No `Agent()` fan-out or worktree orchestration +- No `.cavekit/tasks.json` task registry +- No `make-parallel`, team mode, visual companion, or auto-backprop runtime + +## Install into OpenCode + +Run: + +```bash +~/.cavekit/install.sh +``` + +If OpenCode is detected, the installer symlinks `opencode/commands/*.md` into: + +```text +~/.config/opencode/commands/ +``` + +It also links `opencode/AGENTS.md` **only when** `~/.config/opencode/AGENTS.md` does not already exist. + +## Command contract + +### `/ck-init` +- Bootstraps `context/` directories and minimal CLAUDE docs +- Optional `--tools-only` capability summary +- Does **not** create the upstream `.cavekit/` runtime + +### `/ck-sketch` +- Design-first kit drafting +- Requires explicit approval before writing kit files +- Supports interactive and `--from-code` brownfield drafting + +### `/ck-map` +- Reads kits and writes `context/plans/build-site.md` +- Produces tier tables, a coverage matrix, and a Mermaid dependency graph +- Does **not** initialize `.cavekit/tasks.json` + +### `/ck-make` +- Sequential, scoped implementation for one task or requirement at a time +- Runs tests/builds with OpenCode's normal shell tool +- Does **not** auto-commit, auto-loop, or spawn subagents + +### `/ck-check` +- Read-only verification against kits and code +- Reports gaps and prioritized review findings +- Does **not** auto-fix or auto-revise kits + +### `/ck-status` +- Single-snapshot status report from kits, plan, impl docs, and git +- `--watch` is intentionally unsupported in this port + +## Verification + +Automated check: + +```bash +node tests/run-tests.cjs +``` + +Manual smoke test in a target repo: + +1. `/ck-init` +2. `/ck-sketch --from-code` +3. Approve domain proposal and write kits +4. `/ck-map` +5. `/ck-status` +6. `/ck-make T-001` or similar target +7. `/ck-check` + +## Port honesty rule + +Do not claim parity with upstream Cavekit runtime. Describe this as an **OpenCode portable port**. diff --git a/opencode/commands/ck-check.md b/opencode/commands/ck-check.md new file mode 100644 index 0000000..c6ae410 --- /dev/null +++ b/opencode/commands/ck-check.md @@ -0,0 +1,55 @@ +--- +description: Read-only verification against kits and code for the OpenCode Cavekit portable workflow +argument-hint: [--filter PATTERN] +--- + +You are running **/ck-check** for the OpenCode Cavekit portable port. + +Hard rules: +- Read-only analysis only. +- Do not edit kits, plans, code, or impl docs. +- Do not claim upstream inspection/runtime parity. +- Inspect files directly. Do not guess from summaries alone. + +Interpret `$ARGUMENTS` as an optional filter for kits, domains, or task ranges. + +## Inspect +Use Read/Glob/Grep and, when useful, Bash for git/test commands. Inspect: +- `AGENTS.md` if present +- relevant `context/kits/cavekit-*.md` +- `context/kits/cavekit-overview.md` if present +- `context/plans/build-site.md` if present +- `context/impl/` artifacts if present +- relevant source files +- relevant tests +- `git status` +- recent diff or recent commits when useful + +## Pass 1: Gap analysis +For each relevant requirement and acceptance criterion, classify: +- COMPLETE +- PARTIAL +- MISSING +- OVER-BUILT + +Ground each finding in actual code or tests with file references. + +## Pass 2: Code review +Look for: +- logic bugs +- edge-case misses +- poor error handling +- security issues +- performance issues +- maintainability problems +- drift from repo rules in `AGENTS.md` + +## Output +Return a concise report with: +- coverage summary +- top requirement gaps +- prioritized findings (P0/P1/P2/P3) +- verdict: APPROVE / REVISE / REJECT +- exact next step, usually `/ck-make ` or `/ck-sketch` + +Do not auto-fix anything. Recommend fixes only. diff --git a/opencode/commands/ck-help.md b/opencode/commands/ck-help.md new file mode 100644 index 0000000..caa00b4 --- /dev/null +++ b/opencode/commands/ck-help.md @@ -0,0 +1,45 @@ +--- +description: Show the OpenCode Cavekit portable workflow and recommend the next command +--- + +You are serving the **Cavekit OpenCode portable port**. + +Hard rules: +- Be explicit that this is **not** the upstream Claude/Codex Cavekit runtime. +- No autonomous loop, no hook-driven re-entry, no subagent fan-out, no worktree orchestration, no hidden runtime state. +- Use OpenCode's normal tools only. + +First inspect the current repo with read-only tools: +- project root +- `README.md` if present +- `AGENTS.md` if present +- `context/kits/`, `context/plans/`, `context/impl/` if present + +Then return a concise guide with these sections: + +## Workflow +- `/ck-init` → bootstrap `context/` +- `/ck-sketch` → write or refine kits after explicit approval +- `/ck-map` → generate `context/plans/build-site.md` +- `/ck-make ` → implement one bounded task or requirement at a time +- `/ck-check` → read-only verification against kits and code +- `/ck-status` → snapshot of kit/plan/impl/git state + +## Supported in OpenCode port +- Sequential file-based workflow +- Kit drafting and refinement +- Build-site generation with tier tables + coverage matrix +- Scoped implementation with normal test/build commands +- Read-only verification and status reporting + +## Not supported in OpenCode port +- parallel build orchestration +- hook-driven autonomous loop +- subagent dispatch APIs +- `.cavekit/tasks.json` runtime registry +- team mode, visual companion, auto-backprop, live dashboards + +## Recommended next step +- Recommend exactly one command for the current repo: `/ck-init`, `/ck-sketch`, `/ck-map`, `/ck-make `, or `/ck-check`. + +Keep it short and operational. diff --git a/opencode/commands/ck-init.md b/opencode/commands/ck-init.md new file mode 100644 index 0000000..85b3b56 --- /dev/null +++ b/opencode/commands/ck-init.md @@ -0,0 +1,107 @@ +--- +description: Bootstrap context directories for the OpenCode Cavekit portable workflow +argument-hint: [--tools-only] +--- + +You are running **/ck-init** for the OpenCode Cavekit portable port. + +Hard rules: +- Do not claim this initializes the upstream `.cavekit/` runtime. +- Do not call upstream runtime scripts, router scripts, hook scripts, tmux helpers, or subagent APIs. +- Idempotent only. Create missing files. Do not overwrite existing project docs. + +Interpret `$ARGUMENTS`: +- `--tools-only` → capability summary only, no file edits +- empty → bootstrap `context/` and minimal CLAUDE docs + +## If `--tools-only` +Use the Bash tool to check common tools with `command -v` and summarize availability for: +- git +- gh +- node +- npm +- python3 +- docker +- opencode +- codex + +Return a short table and a brief note that capability discovery is advisory only. + +## Default mode + +1. Inspect repo root first with Read/Glob: + - existing `context/` + - `README.md` + - `AGENTS.md` + - `.gitignore` + +2. Create these directories if missing: + - `context/` + - `context/refs/` + - `context/kits/` + - `context/designs/` + - `context/plans/` + - `context/impl/` + - `context/impl/archive/` + +3. Create these files only if missing: + +### `context/CLAUDE.md` +```markdown +# Context Hierarchy + +This repo uses the Cavekit OpenCode portable workflow. + +- refs/ — source material and external references +- kits/ — implementation-agnostic requirements +- designs/ — visual system and design references +- plans/ — build-site and task graphs +- impl/ — execution notes, progress, dead ends +``` + +### `context/refs/CLAUDE.md` +```markdown +# References + +Source material used to derive kits. Read-only inputs. +``` + +### `context/kits/CLAUDE.md` +```markdown +# Kits + +Kits describe what must be true, not how to implement it. +Use `cavekit-overview.md` as the entry point. +``` + +### `context/plans/CLAUDE.md` +```markdown +# Plans + +Plans translate kits into tasks, tiers, and dependencies. +Use `build-site.md` as the primary execution plan. +``` + +### `context/impl/CLAUDE.md` +```markdown +# Implementation Tracking + +Record what was changed, what passed, what failed, and what remains. +``` + +### `context/designs/CLAUDE.md` +```markdown +# Design System + +Store design references here or point to a root DESIGN.md. +``` + +4. If source directories like `src/`, `app/`, `lib/`, `tests/`, or `scripts/` exist and already contain no `CLAUDE.md`, you may add a minimal local `CLAUDE.md` only when it helps navigation. Keep it tiny. + +5. Report: + - directories created + - files created + - files left untouched + - exact next step: usually `/ck-sketch` + +Keep the bootstrap minimal and honest. diff --git a/opencode/commands/ck-make.md b/opencode/commands/ck-make.md new file mode 100644 index 0000000..f3919af --- /dev/null +++ b/opencode/commands/ck-make.md @@ -0,0 +1,60 @@ +--- +description: Sequential scoped implementation from a build-site task or requirement for the OpenCode Cavekit portable workflow +argument-hint: +--- + +You are running **/ck-make** for the OpenCode Cavekit portable port. + +Hard rules: +- This is sequential, bounded implementation only. +- No autonomous loop, no subagents, no worktrees, no hidden batching. +- Do not commit unless the user explicitly asks for a commit. +- Do not edit kit requirements as part of implementation. Track execution in plan/impl docs instead. + +## Step 1: Resolve target +Interpret `$ARGUMENTS` as one of: +- a task ID like `T-001` +- a requirement reference like `cavekit-auth.md:R3` +- a narrow filter string that identifies one coherent task slice + +If no clear target is provided: +- inspect `context/plans/build-site.md` +- if exactly one task is clearly READY, use it +- otherwise stop and ask the user to choose a target + +## Step 2: Gather only relevant context +Use Read/Glob/Grep to load: +- `context/plans/build-site.md` +- the specific kit files tied to the target +- `context/impl/` notes relevant to that target, if present +- `DESIGN.md` if the target affects UI +- the exact source/test files likely involved + +Use `lsp_diagnostics` before editing when supported. + +## Step 3: Implement the smallest coherent slice +- Satisfy the target's acceptance criteria +- Keep changes scoped +- Add or update tests when the target changes behavior +- Prefer surgical edits over broad refactors + +Use the Bash tool to run the smallest relevant validation commands for the repo (tests, build, lint, typecheck as appropriate). + +## Step 4: Update execution tracking +If the repo uses `context/impl/`, update one relevant tracking file with: +- target implemented +- files changed +- validation commands and pass/fail status +- remaining blockers or follow-up work + +Do not mark kits as complete by editing their acceptance-criteria checkboxes unless the repo already treats kits as live progress trackers and the user expects that behavior. + +## Step 5: Report +Return: +- target implemented +- files changed +- validation run and results +- remaining gaps, if any +- exact next step: another `/ck-make `, `/ck-status`, or `/ck-check` + +If validation fails, stop with a precise blocker report instead of claiming success. diff --git a/opencode/commands/ck-map.md b/opencode/commands/ck-map.md new file mode 100644 index 0000000..cc94049 --- /dev/null +++ b/opencode/commands/ck-map.md @@ -0,0 +1,76 @@ +--- +description: Generate a build site from kits for the OpenCode Cavekit portable workflow +argument-hint: [--filter PATTERN] +--- + +You are running **/ck-map** for the OpenCode Cavekit portable port. + +Hard rules: +- Do not claim subagent dispatch, runtime task registries, or hook-driven loop behavior. +- Do not call upstream runtime scripts, router scripts, or subagent APIs. +- The result is a file-based build plan only. + +## Step 1: Validate inputs +Use Read/Glob to inspect `context/kits/`. + +If no kit files exist, stop and say: +> No kits found. Run `/ck-sketch` first. + +Apply `$ARGUMENTS` if it contains `--filter`. + +## Step 2: Read kits completely +Read: +- `context/kits/cavekit-overview.md` if present +- all relevant `context/kits/cavekit-*.md` +- `DESIGN.md` if present for UI-related requirements + +Catalog every requirement and every acceptance criterion. + +## Step 3: Decompose into tasks +Create small, implementable tasks: +- simple requirement → 1 task +- multi-part requirement → multiple tasks +- use `T-001`, `T-002`, ... across all kits +- every acceptance criterion must map to at least one task +- dependencies must reflect real blockers, not preferences + +## Step 4: Build the site +Write `context/plans/build-site.md` with: +- frontmatter (`created`, `last_edited`) +- summary of kits, tasks, and tiers +- tier tables with `blockedBy` where needed +- coverage matrix covering every acceptance criterion +- Mermaid dependency graph + +Use this structure: + +```markdown +# Build Site + +## Tier 0 — No Dependencies +| Task | Title | Kit | Requirement | Effort | + +## Tier 1 — Depends on Tier 0 +| Task | Title | Kit | Requirement | blockedBy | Effort | + +## Coverage Matrix +| Kit | Req | Criterion | Task(s) | Status | + +## Dependency Graph +```mermaid +graph LR + T-001 --> T-003 +``` +``` + +Status in the coverage matrix must be `COVERED` or `GAP`. If any criterion is `GAP`, add tasks before finishing. + +## Step 5: Report +Return: +- kits read +- tasks generated +- tiers generated +- whether coverage reached 100% +- exact next step: `/ck-make ` or `/ck-check` + +Keep it concrete. This is the execution plan for sequential OpenCode work. diff --git a/opencode/commands/ck-sketch.md b/opencode/commands/ck-sketch.md new file mode 100644 index 0000000..6b3c25c --- /dev/null +++ b/opencode/commands/ck-sketch.md @@ -0,0 +1,70 @@ +--- +description: Design-first drafting of kits for the OpenCode Cavekit portable workflow +argument-hint: [--from-code | REFS_PATH] +--- + +You are running **/ck-sketch** for the OpenCode Cavekit portable port. + +Hard rules: +- Do not claim upstream runtime behavior. +- Do not write kit files until you have presented the proposed domain structure and the user has explicitly approved it. +- Keep kits implementation-agnostic: describe **what** must be true, never **how** to code it. +- Do not use subagent APIs or deep-research fan-out. Work directly with normal OpenCode tools. + +Interpret `$ARGUMENTS`: +- `--from-code` → brownfield drafting/refinement from the current repo +- any other non-empty value → treat as a reference-material hint to inspect first +- empty → interactive drafting mode + +## Step 1: Inspect current state before asking questions +Use Read/Glob/Grep and, when useful, Bash for `git log --oneline -10` to inspect: +- `README.md` +- `AGENTS.md` +- existing `context/kits/` +- existing `context/refs/` +- major source directories +- recent git history +- `DESIGN.md` if present + +## Step 2: Frame current situation +- If kits already exist, explain whether you are refining, extending, or replacing them. +- If no kits exist, explain that you will propose a domain decomposition first. +- If the request spans multiple unrelated systems, propose a decomposition before drafting files. + +## Step 3: Design conversation +- Ask 1-2 scoped questions at a time. +- Prefer multiple choice when possible. +- Focus on scope, users, constraints, success criteria, and boundaries. +- If existing kits already cover most domains, focus on drift, missing domains, or changed priorities. + +## Step 4: Approval gate +Before writing any file, present: +- proposed kit/domain list +- brief scope of each kit +- notable cross-domain dependencies + +Ask for explicit approval. + +## Step 5: After approval, write kits +Ensure `context/kits/` exists, then create or update: +- `context/kits/cavekit-overview.md` +- `context/kits/cavekit-{domain}.md` + +Each kit file should contain: +- YAML frontmatter with `created` and `last_edited` +- short scope statement +- R-numbered requirements +- testable acceptance criteria with unchecked checkboxes +- out-of-scope section +- cross-references when domains interact + +Each requirement should be concrete enough that `/ck-map` can turn it into tasks. + +## Step 6: Report +Return: +- domains touched +- files created/updated +- estimated requirement count +- exact next step: usually `/ck-map` + +Be collaborative before approval. Precise after approval. diff --git a/opencode/commands/ck-status.md b/opencode/commands/ck-status.md new file mode 100644 index 0000000..fc48c35 --- /dev/null +++ b/opencode/commands/ck-status.md @@ -0,0 +1,51 @@ +--- +description: Snapshot progress report from kits, plans, impl docs, and git for the OpenCode Cavekit portable workflow +argument-hint: [--filter PATTERN] +--- + +You are running **/ck-status** for the OpenCode Cavekit portable port. + +Hard rules: +- Read-only only. +- No autonomous runtime claims. +- `--watch` is unsupported in this port. If requested, say so plainly and return one snapshot anyway. + +Inspect: +- project root +- `AGENTS.md` if present +- `context/kits/cavekit-overview.md` if present +- all relevant `context/kits/cavekit-*.md` +- `context/plans/build-site.md` if present +- `context/impl/` contents if present +- current git status + +Return: + +## Repo +- project name +- short description + +## Kits +- count of kit files +- key domains +- whether kits look draft / active / stale + +## Plan +- whether `context/plans/build-site.md` exists +- if present: total tasks, tiers, and current READY frontier +- if absent: say `/ck-map` is next + +## Implementation Tracking +- whether `context/impl/` contains active tracking docs +- summarize recent execution notes if present + +## Git +- concise changed/untracked summary + +## Risks / Gaps +- top 3 blockers or missing artifacts + +## Recommended next step +- recommend exactly one of: `/ck-sketch`, `/ck-map`, `/ck-make `, `/ck-check` + +Keep it concise and operational. diff --git a/tests/opencode-port.test.cjs b/tests/opencode-port.test.cjs new file mode 100644 index 0000000..565daaf --- /dev/null +++ b/tests/opencode-port.test.cjs @@ -0,0 +1,74 @@ +"use strict"; + +const assert = require("assert"); +const fs = require("fs"); +const path = require("path"); + +const root = path.resolve(__dirname, ".."); +const opencodeDir = path.join(root, "opencode"); +const commandsDir = path.join(opencodeDir, "commands"); +const installPath = path.join(root, "install.sh"); + +const expectedCommands = [ + "ck-help.md", + "ck-init.md", + "ck-sketch.md", + "ck-map.md", + "ck-make.md", + "ck-check.md", + "ck-status.md", +]; + +const bannedPatterns = [ + /CLAUDE_PLUGIN_ROOT/, + /Agent\s*\(/, + /subagent_type/, + /cavekit-tools\.cjs/, + /cavekit-router\.cjs/, + /stop-hook/i, + /make-parallel/, +]; + +function read(filePath) { + return fs.readFileSync(filePath, "utf8"); +} + +module.exports = { + "opencode port files exist"() { + assert.ok(fs.existsSync(opencodeDir), "missing opencode/"); + assert.ok(fs.existsSync(commandsDir), "missing opencode/commands/"); + for (const file of expectedCommands) { + assert.ok(fs.existsSync(path.join(commandsDir, file)), `missing ${file}`); + } + assert.ok(fs.existsSync(path.join(opencodeDir, "README.md")), "missing opencode/README.md"); + assert.ok(fs.existsSync(path.join(opencodeDir, "AGENTS.md")), "missing opencode/AGENTS.md"); + }, + + "opencode commands do not leak upstream runtime tokens"() { + for (const file of expectedCommands) { + const body = read(path.join(commandsDir, file)); + for (const pattern of bannedPatterns) { + assert.ok(!pattern.test(body), `${file} leaked banned pattern ${pattern}`); + } + assert.ok(/OpenCode Cavekit portable port|OpenCode Cavekit portable workflow/.test(body), `${file} missing portability scope marker`); + } + }, + + "install script wires opencode commands"() { + const install = read(installPath); + assert.match(install, /OPENCODE_COMMANDS_DIR/, "install.sh missing OpenCode commands dir variable"); + assert.match(install, /LOCAL_BIN_DIR/, "install.sh missing local bin fallback variable"); + assert.match(install, /\.local\/bin/, "install.sh missing local bin fallback path"); + assert.match(install, /opencode\/commands\//, "install.sh missing opencode commands sync"); + assert.match(install, /Configuring OpenCode commands/, "install.sh missing OpenCode sync step"); + assert.match(install, /ck-help/, "install.sh missing OpenCode command summary"); + }, + + "opencode readme states honest limitations"() { + const readme = read(path.join(opencodeDir, "README.md")); + assert.match(readme, /Portable Phase 1/, "README missing phase label"); + assert.match(readme, /not the Claude Code plugin runtime/i, "README missing non-parity warning"); + assert.match(readme, /No autonomous stop-hook loop/i, "README missing runtime limitation"); + assert.match(readme, /\/ck-make/i, "README missing command docs"); + }, +};