From 406dac2ef8bf63f38b1a70cf5341183cf71adb12 Mon Sep 17 00:00:00 2001 From: Steffen Zellmer <151627820+Steffen025@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:43:49 +0200 Subject: [PATCH 1/7] =?UTF-8?q?fix(core):=20OpenCode-native=20architecture?= =?UTF-8?q?=20=E2=80=94=20zero=20bootstrap,=20Zen=20free=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - opencode.json: Switch all agents to Zen free models (opencode/big-pickle as default), remove Anthropic dependency for out-of-box experience - PAI-Install: Remove provider selection from both steps-fresh.ts and install-wizard.ts — Zen free ships as default, /connect for upgrades - PAI-Install: .env template no longer writes empty API key entries - PAI/SKILL.md: Fix frontmatter (remove HTML comment blocking scanner) - PAI/Tools/GenerateSkillIndex.ts: Add PAI to ALWAYS_LOADED_SKILLS list - skills/PAI: Add symlink → ../PAI for native OpenCode skill discovery - skills/skill-index.json: Regenerate — PAI now tier:always (count: 3) - plugins/pai-unified.ts: Remove MINIMAL_BOOTSTRAP loading — PAI Core loaded via skill system; plugin now loads only user identity context - AGENTS.md: Slim to 14 lines (repo build commands only) — PAI system behavior lives exclusively in PAI/SKILL.md via skill system No content lost: OpenCode natively provides XML listing all skills with name+description+location. Bootstrap Discovery Index was redundant. PAI Core Skill (tier:always) replaces the thin Algorithm essence that was in MINIMAL_BOOTSTRAP.md with the full 479-line implementation. --- .opencode/PAI/SKILL.md | 1 - .opencode/PAI/Tools/GenerateSkillIndex.ts | 2 + .opencode/plugins/pai-unified.ts | 45 +-- .opencode/skills/PAI | 1 + .opencode/skills/skill-index.json | 18 +- AGENTS.md | 452 +--------------------- PAI-Install/cli/install-wizard.ts | 47 +-- PAI-Install/engine/provider-models.ts | 9 +- PAI-Install/engine/steps-fresh.ts | 63 ++- opencode.json | 34 +- 10 files changed, 97 insertions(+), 575 deletions(-) create mode 120000 .opencode/skills/PAI diff --git a/.opencode/PAI/SKILL.md b/.opencode/PAI/SKILL.md index 0977332d..8789fc7f 100644 --- a/.opencode/PAI/SKILL.md +++ b/.opencode/PAI/SKILL.md @@ -1,4 +1,3 @@ - --- name: PAI description: Personal AI Infrastructure core. The authoritative reference for how PAI works. diff --git a/.opencode/PAI/Tools/GenerateSkillIndex.ts b/.opencode/PAI/Tools/GenerateSkillIndex.ts index 115542be..64ed3afc 100644 --- a/.opencode/PAI/Tools/GenerateSkillIndex.ts +++ b/.opencode/PAI/Tools/GenerateSkillIndex.ts @@ -41,7 +41,9 @@ interface SkillIndex { } // Skills that should always be fully loaded (Tier 1) +// PAI = the core PAI skill (Algorithm, ISC, Capabilities) — MUST always be loaded const ALWAYS_LOADED_SKILLS = [ + 'PAI', 'CORE', 'Development', 'Research', diff --git a/.opencode/plugins/pai-unified.ts b/.opencode/plugins/pai-unified.ts index 47fd4455..abc06c62 100644 --- a/.opencode/plugins/pai-unified.ts +++ b/.opencode/plugins/pai-unified.ts @@ -248,41 +248,21 @@ async function readFileSafe(filePath: string): Promise { } /** - * Load minimal bootstrap context — WP2: Minimal Useful + * Load user context — Steering Rules + User Identity * - * Loads: - * 1. MINIMAL_BOOTSTRAP.md (core Algorithm + Steering Rules) - * 2. System AISTEERINGRULES.md (if exists) - * 3. User Identity files (ABOUTME, TELOS, DAIDENTITY) if exist + * The PAI Core Skill (Algorithm, ISC, Capabilities) is loaded via the + * skill system (tier: "always") — NOT here. This function loads only + * the dynamic, user-specific context that can't live in static skill files: * - * Target: ~15KB (not 2KB - must know the user!) + * 1. System AISTEERINGRULES.md (behavioral governance) + * 2. User Identity files (ABOUTME, TELOS, DAIDENTITY) if they exist */ async function loadMinimalBootstrap(): Promise { try { - // Resolve PAI directory relative to plugin location, not cwd. - // Plugin is at ~/.opencode/plugins/pai-unified.ts - // PAI is at ~/.opencode/PAI/ - // PLUGIN_DIR is resolved via fileURLToPath(import.meta.url) because - // .opencode/package.json declares "type": "module" — __filename is - // not defined in ESM contexts. const paiDir = path.join(PLUGIN_DIR, "..", "PAI"); - const bootstrapPath = path.join(paiDir, "MINIMAL_BOOTSTRAP.md"); - - // Check if bootstrap exists (async) - try { - await fs.promises.access(bootstrapPath); - } catch { - fileLog("MINIMAL_BOOTSTRAP.md not found, using fallback", "warn"); - return null; - } - const contextParts: string[] = []; - // 1. Core bootstrap (async) - const bootstrapContent = await fs.promises.readFile(bootstrapPath, "utf-8"); - contextParts.push(`--- PAI BOOTSTRAP ---\n${bootstrapContent}`); - - // 2. System Steering Rules (if exists) + // 1. System Steering Rules (if exists) const systemSteeringPath = path.join(paiDir, "AISTEERINGRULES.md"); const systemSteering = await readFileSafe(systemSteeringPath); if (systemSteering) { @@ -290,7 +270,7 @@ async function loadMinimalBootstrap(): Promise { fileLog("Loaded System AISTEERINGRULES.md"); } - // 3. User Identity Files (if exist) — CRITICAL: Must know the user! + // 2. User Identity Files (if exist) — CRITICAL: Must know the user! const userDir = path.join(paiDir, "USER"); const userFiles = [ { file: "ABOUTME.md", label: "User Profile" }, @@ -310,12 +290,17 @@ async function loadMinimalBootstrap(): Promise { } } + if (contextParts.length === 0) { + fileLog("No user context files found — PAI Core loaded via skill system"); + return null; + } + // Combine all context const fullContext = contextParts.join("\n\n"); const size = Buffer.byteLength(fullContext, "utf-8"); - fileLog(`Bootstrap loaded: ${size} bytes (${userContextLoaded} user files)`); + fileLog(`User context loaded: ${size} bytes (${userContextLoaded} user files)`); - return `\nPAI CONTEXT (Lazy Loading Bootstrap)\n\n${fullContext}\n\n---\nSkills load on-demand via OpenCode skill tool. User context auto-loaded if exists.\n`; + return `\nPAI CONTEXT (User Context)\n\n${fullContext}\n\n---\nPAI Core Skill loaded via skill system. Skills load on-demand via Skill tool.\n`; } catch (error) { fileLogError("Failed to load minimal bootstrap", error); // Return null to signal failure - caller should handle diff --git a/.opencode/skills/PAI b/.opencode/skills/PAI new file mode 120000 index 00000000..46bcd35c --- /dev/null +++ b/.opencode/skills/PAI @@ -0,0 +1 @@ +../PAI \ No newline at end of file diff --git a/.opencode/skills/skill-index.json b/.opencode/skills/skill-index.json index b2fb7c4f..5d71364e 100644 --- a/.opencode/skills/skill-index.json +++ b/.opencode/skills/skill-index.json @@ -1,10 +1,10 @@ { - "generated": "2026-03-19T23:43:15.603Z", - "totalSkills": 52, + "generated": "2026-04-13T09:19:28.749Z", + "totalSkills": 53, "categories": 7, - "flatSkills": 11, + "flatSkills": 12, "hierarchicalSkills": 41, - "alwaysLoadedCount": 2, + "alwaysLoadedCount": 3, "deferredCount": 50, "skills": { "agents": { @@ -606,6 +606,16 @@ "tier": "deferred", "isHierarchical": true }, + "pai": { + "name": "PAI", + "path": "PAI/SKILL.md", + "category": null, + "fullDescription": "Personal AI Infrastructure core. The authoritative reference for how PAI works.", + "triggers": [], + "workflows": [], + "tier": "always", + "isHierarchical": false + }, "paiupgrade": { "name": "PAIUpgrade", "path": "Utilities/PAIUpgrade/SKILL.md", diff --git a/AGENTS.md b/AGENTS.md index 40cd6962..e35a329a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,450 +1,14 @@ -# AGENTS.md — PAI-OpenCode +# AGENTS.md — PAI-OpenCode Repository -> Coding agent constitution for CI/CD compliance and branch protection workflows - -## Identity - -You are an AI coding assistant working on **PAI-OpenCode** — the community port of Daniel Miessler's PAI Infrastructure for OpenCode. - -**Your role:** Implement features, fix bugs, and maintain code quality while strictly adhering to CI/CD and branch protection workflows. - -**Tech Stack:** TypeScript, Bun (never npm), Biome (never ESLint/Prettier), GitHub Actions - -## OpenCode Bash Tool — CRITICAL Behavior - -**OpenCode's Bash tool is STATELESS. Every call spawns a fresh shell process.** - -| Behavior | OpenCode | Claude Code | -|----------|---------|------------| -| Working Directory | ❌ Does NOT persist | ✅ Persists | -| Environment Variables | ❌ Does NOT persist | ✅ Persists | -| `cd` commands | ❌ No effect on next call | ✅ Works | - -**ALWAYS use `workdir` parameter instead of `cd`:** +**Runtime:** Bun (never npm/yarn/pnpm) | **Linter:** Biome (never ESLint/Prettier) | **Lang:** TypeScript ESM ```bash -# ❌ WRONG — cd has no effect on next call -Bash({ command: "cd /some/path && ls" }) - -# ✅ CORRECT — use workdir parameter -Bash({ command: "ls", workdir: "/some/path" }) +bun install # Install dependencies +bun test # Run tests +biome check . # Lint + format check +biome check --write . # Auto-fix ``` ---- - -## CI/CD & Branch Protection Rules - -### Absolute Constraints (NEVER violate) - -| Constraint | Rule | Why | -|------------|------|-----| -| **Package Manager** | `bun` only — NEVER `npm install`, `npm ci`, `yarn`, `pnpm` | Per TECHSTACKPREFERENCES.md | -| **Linter/Formatter** | `biome` only — NEVER `eslint`, `prettier` | Per TECHSTACKPREFERENCES.md | -| **Commits to main** | NEVER commit directly to `main` | Branch protection enforced | -| **Force pushes** | NEVER `git push --force` to protected branches | Git history integrity | -| **Secrets** | NEVER hardcode API keys, passwords, tokens | Secret scan will fail | - -### Workflow: How to Make Changes - -```bash -# 1. Always branch from dev (not main) -git checkout -b feature/short-description origin/dev - -# 2. Make your changes -# ... edit files ... - -# 3. Verify locally (Bun only!) -bun install # Never npm install -bunx @biomejs/biome check . # If biome.json exists -bunx tsc --noEmit # If tsconfig.json exists - -# 4. Commit with conventional format -git commit -m "feat(scope): Description - -- Detail 1 -- Detail 2 - -Refs: #issue-number" - -# 5. Push to origin -git push origin feature/short-description - -# 6. Create PR targeting dev (not main!) -# - CI will run automatically -# - CodeRabbit will review automatically -# - Wait for green checks before requesting merge -``` - -### Branch Strategy - -| Branch | Purpose | Protection | Merge Policy | -|--------|---------|------------|--------------| -| `main` | Production releases | 1 approver required | PR from dev only | -| `dev` | Development/integration | CI must pass | PR from feature branches | -| `feature/*` | New features | No direct push | PR to dev | -| `fix/*` | Bug fixes | No direct push | PR to dev | - -### Conventional Commits (Required) - -``` -feat(skills): Add new Cloudflare skill -fix(tools): Resolve path issue in MigrationValidator -docs(readme): Update installation instructions -refactor(agents): Simplify agent registry logic -test(ci): Add secret scan workflow -ci(workflows): Update upstream sync schedule -``` - -**Format:** `type(scope): Description` - -**Types:** feat, fix, docs, refactor, test, ci, chore, perf - -**Scopes:** skills, agents, tools, workflows, docs, tests, core - ---- - -## CodeRabbit Integration - -**CodeRabbit reviews every PR automatically.** - -### When CodeRabbit Comments: - -**[BLOCKING] comments:** -- MUST be resolved before merge -- Reply with fix or justification - -**[SUGGESTION] comments:** -- Can be dismissed if you disagree -- But consider them — CodeRabbit catches real issues - -### How to Trigger Re-review - -``` -@coderabbit review -``` - -Or push new commits — CodeRabbit reviews automatically. - ---- - -## Secret Scanning - -**The CI scans for hardcoded secrets. If found, the build fails.** - -### Patterns that trigger failure: -- `sk-` followed by 20+ characters (OpenAI keys) -- `api_key` or `api-key` with values -- `client_secret`, `password`, `token` with values - -### Safe patterns: -- Environment variables: `process.env.API_KEY` -- Config files with placeholders: `"apiKey": "YOUR_KEY_HERE"` -- Documentation examples marked clearly - -**If you need to commit a secret pattern legitimately:** -Add comment: `# pragma: allowlist secret` on that line. - ---- - -## PR Checklist (Complete before requesting review) - -- [ ] Branched from `dev`, targeting `dev` -- [ ] Uses `bun` commands only (no npm) -- [ ] Follows conventional commit format -- [ ] No hardcoded secrets -- [ ] CodeRabbit review complete (no blocking issues) -- [ ] CI passes (green checkmark) -- [ ] Documentation updated if needed - ---- - -## File Structure Awareness - -### Critical Paths - -| Path | Purpose | Modification Rules | -|------|---------|-------------------| -| `.opencode/skills/**` | PAI Skills | Follow SKILL.md format, USE WHEN triggers | -| `.opencode/agents/**` | Agent definitions | Keep frontmatter valid, first-person voice | -| `.opencode/plugins/**` | Lifecycle plugins | No blocking operations in init hooks | -| `Tools/**` | CLI tools | Bun-only, pure execution (no LLM inside) | -| `docs/**` | Documentation | Obsidian format, ASCII + Mermaid diagrams | -| `.github/workflows/**` | CI/CD | NEVER use npm, ALWAYS use bun | - -### Never Modify (Unless Explicitly Asked) - -- `node_modules/**` — Auto-generated -- `bun.lock` — Auto-generated by bun install -- `.git/**` — Git internals -- `dist/**`, `coverage/**` — Build artifacts - ---- - -## Upstream Sync Awareness - -**PAI-OpenCode tracks two upstream repositories:** - -1. **danielmiessler/Personal_AI_Infrastructure** — Daily sync at 08:00 UTC -2. **anomalyco/opencode** — Daily sync at 08:30 UTC - -If an upstream sync Issue appears: -- Review the changes -- Assess impact on PAI-OpenCode -- Update code if necessary - -**Do NOT:** -- Modify upstream sync workflows unless fixing bugs -- Ignore upstream sync issues (they indicate drift) - ---- - -## OpenCode Action Commands - -When triggered via `/opencode` or `/oc` in a PR comment: - -1. Read the specific request -2. Check out the PR branch (not main/dev) -3. Make requested changes -4. Commit back to the PR branch -5. **NEVER merge — let humans decide** - -### Example Commands - -``` -/opencode Fix the Biome linting errors in this PR -/oc Add error handling to the migration tool -/opencode Review this PR for PAI v3.0 compliance -``` - ---- - -## Emergency Protocols - -### If CI fails on your PR: - -1. Read the CI logs (GitHub Actions tab) -2. Identify failure reason: - - **Secret scan:** Remove hardcoded secret or add `# pragma: allowlist secret` - - **Biome check:** Run `bunx @biomejs/biome check --write .` to auto-fix - - **Typecheck:** Fix TypeScript errors - - **Missing bun:** Ensure `bun install` not `npm install` -3. Push fixes to the same branch -4. CI re-runs automatically - -### If CodeRabbit flags something you disagree with: - -1. Reply to the comment explaining your reasoning -2. If it's a [SUGGESTION], you can dismiss after replying -3. If it's [BLOCKING], you MUST address it or escalate to Steffen - -### If you accidentally committed to main: - -**STOP.** Do not force push. - -1. Notify Steffen immediately -2. Main is protected — the push might have failed anyway -3. If it succeeded, a revert PR will be needed - ---- - -## OpenCode Session API - -After context compaction, subagent results are **NOT lost**. They are stored in OpenCode's SQLite database and accessible via custom tools. Use these tools to recover session context after compaction or to resume subagent work. - -### Custom Tools - -**`session_registry`** — List all subagent sessions spawned in this session. - -- **When to use:** After context compaction, or when you need to check what subagents were spawned -- **Returns:** Markdown table with session IDs, agent types, descriptions, and spawn times -- **Example output:** - ```text - ## Subagent Registry (2 sessions) - - | # | Agent Type | Session ID | Description | Spawned At | - |---|-----------|-----------|-------------|------------| - | 1 | Engineer | ses_abc123 | Refactor auth middleware | 2026-03-10T10:30:00Z | - | 2 | Research | ses_def456 | Investigate OpenCode API | 2026-03-10T10:35:00Z | - ``` - -**`session_results`** — Get registry metadata for a specific subagent session. - -- **When to use:** When you need details about a specific subagent's work -- **Args:** `{ session_id: string }` -- **Returns:** Agent type, full description, model tier, status, and resume instructions -- **Note:** The full conversation history is in OpenCode's database. Use Task tool with `session_id` to retrieve it. - -### Post-Compaction Recovery Pattern - -When the Algorithm says "subagent results are lost after compaction": - -1. **Call `session_registry`** to see what subagents exist - ```json - session_registry: {} - ``` - -2. **Call `session_results`** for any sessions you need context on - ```json - session_results: { "session_id": "ses_abc123" } - ``` - -3. **Resume the session** using Task tool if you need full conversation: - ```javascript - Task({ session_id: "ses_abc123", prompt: "Continue where you left off and summarize what you did" }) - ``` - -### Key Facts - -- Subagent data survives compaction — it's stored in OpenCode's SQLite with indexed `parent_id` -- The registry file lives in `.opencode/MEMORY/STATE/subagent-registry-{parentSessionId}.json` -- Registry is human-readable JSON for debugging -- Session data persists across restarts, not just compaction - ---- - -## Code Navigation (LSP Integration) - -OpenCode has 35+ Language Server Protocol (LSP) servers built-in. When enabled, they provide **type-aware code navigation** that goes beyond simple text matching. - -### Available LSP Tools - -| Tool | What It Does | When to Use | -|------|-------------|-------------| -| `goToDefinition` | Jump to symbol definition (type-aware, follows imports) | Find where a function/type is defined | -| `findReferences` | All usages of a function (semantic, not text-match) | Understand impact before refactoring | -| `hover` | Show type info and docs for a symbol | Quickly inspect unfamiliar APIs | -| `callHierarchy` | Incoming/outgoing call chains | Trace execution paths | - -### LSP vs. Grep — When to Use Which - -| Use Case | LSP | Grep | -|----------|-----|------| -| Find all callers of `myFunction()` | ✅ `findReferences` — semantic, exact | ⚠️ Misses renamed imports, aliases | -| Jump to type definition across files | ✅ `goToDefinition` — follows imports | ❌ Can't follow re-exports | -| Check TypeScript type of a variable | ✅ `hover` — live type info | ❌ Not possible | -| Find all files containing "TODO" | ❌ LSP can't do text search | ✅ Grep is correct tool | -| Find all uses of a string literal | ❌ LSP is symbol-only | ✅ Grep is correct tool | -| Quick pattern match in one file | ❌ Overhead not worth it | ✅ Grep is faster | - -**Rule of thumb:** Use LSP for **symbols** (functions, types, variables). Use Grep for **text** (strings, comments, patterns). - -### Activation - -LSP tools are **experimental** and must be explicitly enabled: - -```bash -# Enable LSP tools for the current session -export OPENCODE_EXPERIMENTAL_LSP_TOOL=true -opencode -``` - -Or add to your shell profile for permanent activation: - -```bash -echo 'export OPENCODE_EXPERIMENTAL_LSP_TOOL=true' >> ~/.zshrc -``` - -> [!NOTE] -> LSP tools are only available when `OPENCODE_EXPERIMENTAL_LSP_TOOL=true` is set. Without this flag, the tools are not registered and will not appear in the tool list. - ---- - -## Safe Experiments (Session Fork) - -> [!NOTE] -> Plan Mode is **not available** in OpenCode. Session Fork is the native equivalent — a checkpoint system for safe experimentation. - -OpenCode's Session Fork creates an **exact copy** of the current session up to a specific message. The original session is untouched. If the experiment fails, discard the fork and return to the original. - -### When to Fork - -| Situation | Action | -|-----------|--------| -| About to do a risky refactoring | Fork first, then refactor in the fork | -| Exploring multiple solution approaches | Fork once per approach, compare results | -| About to run destructive operations (delete, overwrite) | Fork → verify in fork → apply to original | -| Algorithm needs to "try something" without commitment | Fork, try, decide | -| Pre-BUILD checkpoint in the PAI Algorithm | Fork at end of PLAN phase | - -### API Reference - -```http -POST /session/{sessionID}/fork -Content-Type: application/json - -{ - "messageID": "msg_..." -} -``` - -**Response:** A new session ID pointing to an exact copy of the session at the specified message. - -**How to get the current messageID:** Available via the OpenCode Session API (same endpoint used by `session_registry`). - -### Fork Workflow - -```text -PLAN phase complete → identify last messageID - → POST /session/{id}/fork - → get forked_session_id - → work in forked session (BUILD / EXECUTE) - → if success: apply changes to original - → if failure: discard fork, original is safe -``` - -### Key Properties - -- **Atomic:** Fork creates a complete snapshot — no partial state -- **Non-destructive:** Original session is never modified by fork operations -- **Persistent:** Forked sessions survive restarts (stored in OpenCode SQLite) -- **Discardable:** Failed experiments leave no traces in the original session - ---- - -## Quick Reference - -### Commands - -```bash -# Install dependencies (BUN ONLY!) -bun install - -# Run Biome check -bunx @biomejs/biome check . - -# Auto-fix Biome issues -bunx @biomejs/biome check --write . - -# TypeScript check -bunx tsc --noEmit - -# Run tests -bun test -``` - -### Git Workflow - -```bash -# Start feature -git checkout -b feature/description origin/dev - -# Work... - -# Commit -git commit -m "feat(scope): Description" - -# Push -git push origin feature/description - -# Create PR via GitHub UI (target: dev) -``` - -### Links - -- **CI Status:** https://github.com/Steffen025/pai-opencode/actions -- **CodeRabbit:** https://app.coderabbit.ai -- **Setup Guide:** docs/CI-CD-SETUP.md - ---- +**Commits:** `feat(scope):`, `fix(scope):`, `docs:`, `chore:` — branch from `dev`, PR to `dev`, never commit to `main`. -**Remember:** Professionalism = following the workflow, not hacking around it. When in doubt, ask in the PR comments. +**PAI System:** Loaded via skill system (`PAI/SKILL.md`, tier: always). Not in this file. diff --git a/PAI-Install/cli/install-wizard.ts b/PAI-Install/cli/install-wizard.ts index 4e69b426..ccb38168 100644 --- a/PAI-Install/cli/install-wizard.ts +++ b/PAI-Install/cli/install-wizard.ts @@ -21,8 +21,7 @@ import { stepProviderConfig, stepVoice, } from "../engine/steps-fresh.ts"; -import { PROVIDER_LABELS, PROVIDER_MODELS } from "../engine/provider-models.ts"; -import type { ProviderName } from "../engine/provider-models.ts"; + import { stepCreateBackup, stepDetectMigration, @@ -48,15 +47,6 @@ const COLOR = { const PROGRESS_BAR_WIDTH = 24; -const API_KEY_REQUIRED = new Set(["openai", "anthropic", "openrouter"]); - -const PROVIDER_ENV_VARS: Record = { - anthropic: ["ANTHROPIC_API_KEY", "API_KEY"], - openai: ["OPENAI_API_KEY", "API_KEY"], - openrouter: ["OPENROUTER_API_KEY", "API_KEY"], - zen: ["ZEN_API_KEY", "API_KEY"], -}; - type Mode = "fresh" | "migrate" | "update"; const { values } = parseArgs({ @@ -362,39 +352,14 @@ async function runFreshWizard(): Promise { printInfo("OpenCode install is managed by the vanilla opencode.ai installer."); printInfo("If opencode is not on your PATH, run: curl -fsSL https://opencode.ai/install | bash"); - const providerChoices = (Object.keys(PROVIDER_MODELS) as ProviderName[]).map((provider) => ({ - label: PROVIDER_LABELS[provider].label, - value: provider, - description: PROVIDER_LABELS[provider].description, - })); - - const provider = (await askChoice("Choose AI provider:", providerChoices, 1)) as ProviderName; - const envCandidates = PROVIDER_ENV_VARS[provider]; - const envKey = envCandidates.map((key) => process.env[key]).find(Boolean)?.trim() || ""; - let apiKey = ""; - - if (API_KEY_REQUIRED.has(provider)) { - const hasEnvKey = envKey.length > 0; - if (hasEnvKey) { - printInfo(`Detected ${envCandidates.join("/")} in environment.`); - } - - apiKey = await askHidden( - `Enter ${provider} API key${hasEnvKey ? " (press Enter to use env key)" : ""}`, - "", - ); - } - - const chosenApiKey = apiKey.trim() || envKey; - if (API_KEY_REQUIRED.has(provider) && !chosenApiKey) { - printError(`Provider ${provider} requires API key.`); - printInfo(`Set one of: ${envCandidates.join(", ")} or enter it in wizard.`); - process.exit(1); - } + // Provider defaults to Zen free — no API key needed for out-of-box experience. + // Users configure premium providers AFTER install via `/connect` in OpenCode. + printInfo("Using OpenCode Zen (free models) — no API key required."); + printInfo("To connect premium providers later, run /connect inside OpenCode.\n"); await stepProviderConfig( state, - { provider, apiKey: chosenApiKey }, + { provider: "zen", apiKey: "" }, progress, ); diff --git a/PAI-Install/engine/provider-models.ts b/PAI-Install/engine/provider-models.ts index 69362643..b12edd76 100644 --- a/PAI-Install/engine/provider-models.ts +++ b/PAI-Install/engine/provider-models.ts @@ -26,10 +26,11 @@ export const PROVIDER_MODELS: Record = { advanced: "anthropic/claude-opus-4-6", }, zen: { - // OpenCode Zen — cost-optimised tiers (IDs verified against opencode.ai/docs/zen/) - quick: "zen/minimax-m2.5-free", // FREE - standard: "zen/gpt-5.1-codex-mini", // $0.25/M in+out - advanced: "zen/claude-3-5-haiku", // $0.80/M — catalog ID for Claude Haiku 3.5 + // OpenCode Zen — ALL FREE models (no API key required for out-of-box experience) + // Users upgrade to paid models post-install via /connect in OpenCode + quick: "opencode/gpt-5-nano", // FREE — fastest + standard: "opencode/qwen3.6-plus-free", // FREE — capable general/coding + advanced: "opencode/big-pickle", // FREE — Zen flagship, heaviest }, openrouter: { quick: "openrouter/google/gemini-flash-1.5", diff --git a/PAI-Install/engine/steps-fresh.ts b/PAI-Install/engine/steps-fresh.ts index 9ef8ec77..0a8d0c75 100644 --- a/PAI-Install/engine/steps-fresh.ts +++ b/PAI-Install/engine/steps-fresh.ts @@ -255,19 +255,34 @@ export async function stepInstallPAI( onProgress(94, "Generated settings.json..."); // Create .env file with API keys (restricted permissions) - const providerEnvVar = `${(state.collected.provider || "zen").toUpperCase()}_API_KEY`; const voiceEnvVar = state.collected.voiceProvider === "google" ? "GOOGLE_API_KEY" : state.collected.voiceProvider === "elevenlabs" ? "ELEVENLABS_API_KEY" : state.collected.voiceProvider === "macos" ? "" : ""; let envContent = `# PAI-OpenCode Environment Variables # Generated by installer - DO NOT COMMIT THIS FILE -${providerEnvVar}=${state.collected.apiKey || ""} +# +# Provider API keys are configured via /connect inside OpenCode. +# OpenCode Zen (free models) works without any API key. +# +# To add a premium provider, start OpenCode and run: +# /connect +# +# Or set keys here manually: +# ANTHROPIC_API_KEY=sk-ant-... +# OPENAI_API_KEY=sk-... # Optional: Enable experimental LSP code navigation tools # Uncomment to activate goToDefinition, findReferences, hover, callHierarchy # OPENCODE_EXPERIMENTAL_LSP_TOOL=true `; + + // Write provider API key if one was provided (e.g. via CLI flags) + const providerApiKey = state.collected.apiKey || ""; + if (providerApiKey) { + const providerEnvVar = `${(state.collected.provider || "zen").toUpperCase()}_API_KEY`; + envContent += `${providerEnvVar}=${providerApiKey}\n`; + } if (voiceEnvVar && state.collected.voiceApiKey) { envContent += `${voiceEnvVar}=${state.collected.voiceApiKey}\n`; @@ -515,43 +530,23 @@ export async function runFreshInstall( } await emit({ event: "step_complete", step: "prerequisites" }); - // Step 3: Provider Configuration (API Keys) + // Step 3: Provider — default to Zen free (no API key needed) + // + // Provider configuration is intentionally skipped during install. + // OpenCode ships pre-connected to OpenCode Zen with free models. + // Users configure premium providers AFTER install via `/connect` + // inside a running OpenCode session. await emit({ event: "step_start", step: "api-keys" }); - // Collect provider config via interactive callbacks - const providerChoices = Object.entries(PROVIDER_LABELS).map(([value, { label, description }]) => ({ - label, - value, - description, - })); - const provider = (await requestChoice("provider", "Choose your AI provider:", providerChoices)) as ProviderName || "zen"; - - const providerEnvMap: Record = { - anthropic: ["ANTHROPIC_API_KEY", "API_KEY"], - openai: ["OPENAI_API_KEY", "API_KEY"], - openrouter: ["OPENROUTER_API_KEY", "API_KEY"], - zen: ["ZEN_API_KEY", "API_KEY"], - }; - const requiredProviders = new Set(["openai", "anthropic", "openrouter"]); - - let apiKey = ""; - if (requiredProviders.has(provider)) { - apiKey = await requestInput("api-key", `Enter your ${provider} API key:`, "key", "sk-..."); - } - - const envCandidates = providerEnvMap[provider] || ["API_KEY"]; - const envKey = envCandidates.map((name) => process.env[name]).find(Boolean) || ""; - const selectedApiKey = apiKey.trim() || envKey; - - if (requiredProviders.has(provider) && !selectedApiKey) { - throw new Error(`Provider ${provider} requires an API key. Provide --api-key or set ${envCandidates.join("/")}.`); - } - await stepProviderConfig(state, { - provider, - apiKey: selectedApiKey, + provider: "zen", + apiKey: "", }, (percent, message) => { emit({ event: "progress", step: "api-keys", percent, detail: message }); }); + await emit({ + event: "message", + content: "Using OpenCode Zen (free models) — no API key required.\n\n💡 To connect premium providers later, run `/connect` inside OpenCode.", + }); await emit({ event: "step_complete", step: "api-keys" }); // Step 4: Identity diff --git a/opencode.json b/opencode.json index 33fdb826..d05db532 100644 --- a/opencode.json +++ b/opencode.json @@ -1,7 +1,7 @@ { "$schema": "https://opencode.ai/config.json", "theme": "dark", - "model": "anthropic/claude-sonnet-4-5", + "model": "opencode/big-pickle", "snapshot": true, "username": "User", "permission": { @@ -22,52 +22,52 @@ }, "agent": { "Algorithm": { - "model": "anthropic/claude-opus-4-6" + "model": "opencode/big-pickle" }, "Architect": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/qwen3.6-plus-free" }, "Engineer": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/qwen3.6-plus-free" }, "general": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/qwen3.6-plus-free" }, "explore": { - "model": "anthropic/claude-haiku-4-5" + "model": "opencode/gpt-5-nano" }, "Intern": { - "model": "anthropic/claude-haiku-4-5" + "model": "opencode/gpt-5-nano" }, "Writer": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/qwen3.6-plus-free" }, "DeepResearcher": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/nemotron-3-super-free" }, "GeminiResearcher": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/minimax-m2.5-free" }, "GrokResearcher": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/qwen3.6-plus-free" }, "PerplexityResearcher": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/big-pickle" }, "CodexResearcher": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/nemotron-3-super-free" }, "QATester": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/qwen3.6-plus-free" }, "Pentester": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/qwen3.6-plus-free" }, "Designer": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/minimax-m2.5-free" }, "Artist": { - "model": "anthropic/claude-sonnet-4-5" + "model": "opencode/minimax-m2.5-free" } } } From 9fad5e61fbfe157dfbb52fa4c20559326d6d0ac0 Mon Sep 17 00:00:00 2001 From: Steffen Zellmer <151627820+Steffen025@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:00:13 +0200 Subject: [PATCH 2/7] docs: update README, INSTALL, PAI-ADAPTATIONS + add ADR-020 for v3.0 architecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README.md: Zen free as default (no API key), /connect for providers, plugin section clarified (user identity only), PAI skill tier:always noted - INSTALL.md: Installer no longer asks for provider preset, Zen free default, /connect for premium providers, First Run updated (PAI tier:always) - docs/PAI-ADAPTATIONS.md: Add v3.0 version history row, new v3.0 Native OpenCode Architecture section (bootstrap removal, plugin simplification, AGENTS.md 450→14 LOC, skills/PAI symlink, big-pickle default) - docs/architecture/adr/ADR-020: Document the bootstrap removal decision — context, problem, decision, consequences, alternatives considered --- INSTALL.md | 62 ++++---- README.md | 55 ++++--- docs/PAI-ADAPTATIONS.md | 142 ++++++++++++++++-- ...ADR-020-native-opencode-context-loading.md | 111 ++++++++++++++ 4 files changed, 303 insertions(+), 67 deletions(-) create mode 100644 docs/architecture/adr/ADR-020-native-opencode-context-loading.md diff --git a/INSTALL.md b/INSTALL.md index 38f8e2af..149d9adf 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -49,14 +49,9 @@ opencode The installer will: 1. ✅ Check prerequisites (git, bun 1.3.9+) 2. ✅ **Install vanilla OpenCode** from opencode.ai -3. ✅ Ask you to choose a preset: - - **Anthropic Max** (recommended) — Best quality, full PAI experience - - **ZEN PAID** — Budget-friendly, paid tier models - - **OpenRouter** — Provider diversity, 100+ models - - **OpenAI** — GPT-4 models via OpenAI directly -4. ✅ Configure research agents (optional) -5. ✅ Set up your identity (name, AI assistant name, timezone) -6. ✅ Generate all configuration files +3. ✅ Uses OpenCode Zen free models by default — no API key required. To connect premium providers after install, run `/connect` inside OpenCode. +4. ✅ Set up your identity (name, AI assistant name, timezone) +5. ✅ Generate all configuration files **Takes ~2-3 minutes.** @@ -232,7 +227,7 @@ If you already have a PAI installation on Claude Code, see our [Migration Guide] After installation, verify everything works: 1. **Check Skills Loading** - - On first message, CORE skill should auto-load + - On first message, PAI skill auto-loads (tier: always) - Ask: "What skills do I have?" 2. **Test an Agent** @@ -322,27 +317,43 @@ Edit `.opencode/settings.json`: ## Provider Configuration -### The Four Presets +### Default: Zen Free (No Setup Required) -PAI-OpenCode uses a **preset system** for simplicity: +PAI-OpenCode ships with **OpenCode Zen free** as the default provider. Big Pickle — Zen's free flagship model — works out of the box with no API key or account required. Just install and start. -| Preset | Best For | Models | Cost | -|--------|----------|--------|------| -| **Anthropic Max** | Best quality | Claude Opus 4.6, Sonnet 4.5 | ~$75/1M tokens | -| **ZEN PAID** | Budget-friendly | GLM 4.7, Kimi K2.5, Gemini Flash | ~$1-15/1M tokens | -| **OpenRouter** | Provider diversity | 100+ models via OpenRouter | Varies by model | -| **OpenAI** | GPT-4 models | GPT-4o, GPT-4.1 | ~$10-30/1M tokens | +### Connecting Premium Providers -### Switching Presets +To use Anthropic Max, paid Zen models, or any other provider, run `/connect` inside OpenCode after install: + +``` +/connect +``` + +This opens OpenCode's provider connection flow. You can connect: + +| Provider | Best For | Cost | +|----------|----------|------| +| **Anthropic Max** | Best quality, Claude Opus 4.6 / Sonnet 4.5 | Claude Max subscription | +| **ZEN PAID** | Budget-friendly paid models (GLM 4.7, Kimi K2.5, Gemini Flash) | ~$1-15/1M tokens | +| **OpenRouter** | Provider diversity, 100+ models | Varies by model | +| **OpenAI** | GPT-4o, GPT-4.1 | ~$10-30/1M tokens | + +Alternatively, add API keys directly to `~/.opencode/.env`: +```bash +ANTHROPIC_API_KEY=your_key +OPENAI_API_KEY=your_key +``` + +### Changing Providers After Install ```bash -# Re-run the installer in headless mode to force a specific preset +# Re-run the installer in headless mode to reconfigure a specific preset bash PAI-Install/install.sh --headless --preset openai --name "Your Name" --ai-name "Jeremy" ``` ### Advanced Provider Setup -For custom provider configuration beyond the 4 presets, see [ADVANCED-SETUP.md](docs/ADVANCED-SETUP.md). +For custom provider configuration, see [ADVANCED-SETUP.md](docs/ADVANCED-SETUP.md). ### Multi-Provider Research (Optional) @@ -365,20 +376,11 @@ Check which keys you have: bun run .opencode/tools/switch-provider.ts --researchers ``` -### Authentication Options - -| Method | How | Best For | -|--------|-----|----------| -| **Subscription login** | Run `/login` in OpenCode | Claude Pro/Max, ChatGPT Plus users | -| **API key** | Add to `~/.opencode/.env` | Pay-per-use, multiple providers | - ### API Keys for Multi-Provider Research (Optional) -The 4-preset system covers most use cases. For multi-provider research or custom providers, see [ADVANCED-SETUP.md](docs/ADVANCED-SETUP.md). - | Provider | Where to Get Key | For | |----------|-----------------|-----| -| Anthropic | https://console.anthropic.com/ | Claude models (via Anthropic Max) | +| Anthropic | https://console.anthropic.com/ | Claude models | | Google | https://aistudio.google.com/apikey | GeminiResearcher | | xAI | https://console.x.ai/ | GrokResearcher | | Perplexity | https://perplexity.ai/settings/api | PerplexityResearcher | diff --git a/README.md b/README.md index ef17b020..a6290c62 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) > [!note] -> **v3.0 Release** — Plugin event bus, security hardening (prompt injection protection), terminal installer wizard + headless mode, DB health tooling, hierarchical skills structure, and 52 skills. See [CHANGELOG.md](CHANGELOG.md) and [UPGRADE.md](UPGRADE.md). +> **v3.0 Release** — Zero-bootstrap context loading, Zen free out-of-box (no API key required), PAI Core Skill always-loaded via OpenCode's native skill system, plugin event bus, security hardening (prompt injection protection), terminal installer wizard + headless mode, DB health tooling, hierarchical skills structure, and 52 skills. See [CHANGELOG.md](CHANGELOG.md) and [UPGRADE.md](UPGRADE.md). > **🎯 Scope Note:** PAI-OpenCode is a **community port** of PAI to OpenCode. For the future vision (Voice-to-Voice, Ambient AI, OMI integration), see **[Open Arc](https://github.com/jeremaiah-ai/openark)**. @@ -100,7 +100,7 @@ PAI-OpenCode is the complete port of **Daniel Miessler's Personal AI Infrastruct bash PAI-Install/install.sh # Headless example (optional) -bash PAI-Install/install.sh --headless --preset zen --name "Your Name" --ai-name "Jeremy" +bash PAI-Install/install.sh --headless --name "Your Name" --ai-name "Jeremy" ``` ### Manual Setup @@ -126,17 +126,19 @@ opencode The installer wizard will: - **Install vanilla OpenCode** from opencode.ai -- **Choose your preset** — zen (recommended), anthropic, openrouter, or openai +- **Uses Zen free models by default** — no API key required to start - Set your name and timezone - Name your AI assistant +To add premium providers (Anthropic, OpenAI, OpenRouter), run `/connect` inside a running OpenCode session after install. + **Takes ~2-3 minutes** and creates all necessary configuration files. --- ## Switch Providers Anytime -The installer configures one preset, but you can switch to **any provider** after installation: +After installation, you can switch to **any provider** at any time: ```bash # See all available provider profiles @@ -186,7 +188,7 @@ This **10-15 minute** interactive session will configure your complete TELOS fra ### 🎯 Skills System (52 Skills) Modular, reusable capabilities invoked by name: -- **CORE** — Identity, preferences, auto-loaded at session start (Algorithm v1.8.0) +- **PAI** — Core Algorithm, ISC, Capabilities — always-loaded via skill system (`tier:always`) - **Art** — Excalidraw-style visual diagrams - **Browser** — Code-first browser automation - **Security** — Pentesting, secret scanning @@ -231,7 +233,7 @@ Persistent context across sessions: ### 🔧 Plugin System (20 Handlers) TypeScript lifecycle plugins with comprehensive coverage: -- **Context injection** at session start +- **User identity context** loaded at session start (ABOUTME, TELOS, DAIDENTITY, Steering Rules) - **Security validation** before commands - **Voice notifications** (ElevenLabs + Google TTS + macOS say) - **Implicit sentiment** detection from user messages @@ -245,6 +247,9 @@ TypeScript lifecycle plugins with comprehensive coverage: - **Version check** — Algorithm version compatibility - **Integrity check** — Session-end validation +> [!NOTE] +> PAI Core (Algorithm, ISC, Capabilities) is loaded automatically by OpenCode's native skill system via `tier:always`. The plugin handles user identity context only. + ### 🌐 75+ AI Providers Use any AI provider: - Anthropic (Claude) @@ -255,15 +260,27 @@ Use any AI provider: --- -## Provider Preset System +## Provider Configuration + +PAI-OpenCode ships with **Zen free as the default** — OpenCode Zen is pre-connected with free models, so you can start immediately with no API key. + +| Provider | How to Enable | Cost | +|----------|---------------|------| +| **Zen free** (Default) | Works out of the box — no setup required | **FREE** | +| **Zen paid** | Run `/connect` in OpenCode → select Zen AI Gateway | ~$1-75/1M tokens depending on tier | +| **Anthropic** | Run `/connect` in OpenCode → select Anthropic | Per-token (Claude Max subscription or API) | +| **OpenRouter** | Run `/connect` in OpenCode → select OpenRouter | Varies by model | +| **Ollama (local)** | Run `/connect` in OpenCode → select Ollama | **FREE** (your hardware) | + +### Connecting Providers After Install -PAI-OpenCode offers **three presets** — each gives you access to 75+ providers with different routing strategies: +All provider configuration happens inside a running OpenCode session — no reinstall needed: -| Preset | Best For | Providers | Cost | -|--------|----------|-----------|------| -| **`zen-paid`** (Recommended) | Production use, privacy-conscious | 75+ providers via Zen AI Gateway | ~$1-75/1M tokens depending on tier | -| **`openrouter`** | Provider diversity, experimental models | OpenRouter routing to 100+ models | Varies by model | -| **`local-ollama`** | Full privacy, offline operation | Local Ollama instance | **FREE** (your hardware) | +``` +/connect +``` + +Follow the prompts to authenticate. OpenCode stores credentials securely and your PAI skills and agents work with any provider you connect. ### Why This Design? @@ -278,9 +295,9 @@ This is what PAI on OpenCode can do that PAI on Claude Code cannot — Claude Co **Easy to customize** later via [ADVANCED-SETUP.md](docs/ADVANCED-SETUP.md) -### Switching Presets +### Switching Provider Profiles -Use the provider switcher tool after install: +Use the provider switcher tool anytime after install: ```bash bun run .opencode/tools/switch-provider.ts --list @@ -367,6 +384,7 @@ PAI-OpenCode's design is documented through **Architecture Decision Records (ADR | [ADR-006](docs/architecture/adr/ADR-006-security-validation-preservation.md) | Security Patterns Preserved | Critical security validation unchanged | | [ADR-007](docs/architecture/adr/ADR-007-memory-system-structure-preserved.md) | Memory Structure Preserved | File-based MEMORY/ system unchanged | | [ADR-008](docs/architecture/adr/ADR-008-opencode-bash-workdir-parameter.md) | Bash workdir Parameter | Critical platform difference for multi-repo workflows | +| [ADR-020](docs/architecture/adr/ADR-020-native-opencode-context-loading.md) | Zero Bootstrap / tier:always | PAI Core loads via native skill system, no custom bootstrap | **Key Principles:** - **Preserve PAI's design** where possible @@ -390,11 +408,6 @@ PAI-OpenCode's design is documented through **Architecture Decision Records (ADR | [docs/MIGRATION.md](docs/MIGRATION.md) | Migration from Claude Code PAI | | [CONTRIBUTING.md](CONTRIBUTING.md) | Contribution guidelines | -**For Contributors:** -| Document | Description | -|----------|-------------| -| [PAI-to-OpenCode Mapping](.opencode/PAISYSTEM/PAI-TO-OPENCODE-MAPPING.md) | How to correctly import PAI components | - **Upstream Resources:** - [Daniel Miessler's PAI](https://github.com/danielmiessler/Personal_AI_Infrastructure) — Original PAI documentation - [OpenCode Documentation](https://docs.opencode.ai) — OpenCode official docs @@ -437,7 +450,7 @@ opencode For scripted installs, use `--headless`: ```bash -bash PAI-Install/install.sh --headless --preset zen --name "Your Name" --ai-name "Jeremy" +bash PAI-Install/install.sh --headless --name "Your Name" --ai-name "Jeremy" ``` **Welcome to Personal AI Infrastructure, your way.** diff --git a/docs/PAI-ADAPTATIONS.md b/docs/PAI-ADAPTATIONS.md index 66b32b95..073f8e1e 100644 --- a/docs/PAI-ADAPTATIONS.md +++ b/docs/PAI-ADAPTATIONS.md @@ -20,6 +20,7 @@ This document explains **what we changed** and **why**. | v1.1.0 | PAI 2.5 | v0.2.25 | 13 handlers, voice/sentiment | | v1.3.0 | PAI 2.5 | v0.2.25 | 16 agents, agent-based routing, 3 presets | | **v2.0.0** | **PAI 3.0** | **v1.8.0** | **8 effort levels, Verify Completion Gate, Wisdom Frames, 20 handlers, 39 skills** | +| **v3.0.0** | **PAI 3.0** | **v1.8.0** | **Vanilla OpenCode, zero bootstrap, Zen free default, PAI Core tier:always** | --- @@ -135,7 +136,8 @@ export function fileLog(message: string, level = "info") { ### Skills System -**100% unchanged:** +**100% unchanged** — PAI skill discovery now uses the native OpenCode scanner via symlink (`skills/PAI → ../PAI`). OpenCode discovers `PAI/SKILL.md` automatically without any bootstrap. + - All 29 skills work identically - SKILL.md format unchanged - Workflow files unchanged @@ -192,6 +194,110 @@ export function fileLog(message: string, level = "info") { --- +## v3.0 Native OpenCode Architecture + +> **Architecture Decision:** ADR-020 - Native OpenCode Context Loading (Bootstrap Removal) + +The v3.0 release represents a fundamental shift in how PAI Core loads into context. The custom bootstrap mechanism built for earlier versions became redundant once OpenCode's native skill system matured — so we removed it entirely. + +### What Changed + +**Context Loading: Bootstrap → Skill System (tier:always)** + +| Before (v2.x) | After (v3.0) | +|---------------|--------------| +| `MINIMAL_BOOTSTRAP.md` loaded explicitly by plugin | PAI Core Skill loads via native OpenCode `tier: always` | +| `pai-unified.ts` handled bootstrap + user context | `pai-unified.ts` handles user context only | +| `AGENTS.md` contained ~450 lines of PAI behavior | `AGENTS.md` contains 14 lines (repo build commands only) | +| Manual Skill Discovery Index in bootstrap | OpenCode natively provides `` XML listing | + +**Why:** +- OpenCode's native skill system (`tier: always` in `GenerateSkillIndex.ts`) reliably loads skills into every session context +- The `ALWAYS_LOADED_SKILLS` list now includes `'PAI'`, ensuring the full 479-line PAI Core Skill is always in context +- OpenCode natively provides `` XML with name + description + location for every skill — the bootstrap's Skill Discovery Index was a manual duplicate of this native capability +- Removing bootstrap reduces the attack surface, eliminates a maintenance burden, and makes the architecture easier to reason about + +### Plugin Simplification + +**`pai-unified.ts` — What it does now:** + +``` +Before: Bootstrap loading + User identity context +After: User identity context only +``` + +The plugin now loads only the pieces OpenCode's native system cannot provide: +- `USER/ABOUTME.md` — personal background +- `USER/TELOS/TELOS.md` — goals and mission +- `USER/DAIDENTITY.md` — DA name and personality +- `USER/AISTEERINGRULES.md` — behavioral steering rules + +PAI Core (Algorithm, ISC, Capabilities) loads automatically via the skill system. + +### AGENTS.md Simplification + +**From 450 LOC → 14 LOC** + +The old `AGENTS.md` contained the full PAI behavior specification — effectively duplicating what `PAI/SKILL.md` already held. This created a maintenance hazard: two sources of truth that could drift apart. + +The new `AGENTS.md` contains only what belongs there — repository-specific build commands: + +```markdown +# AGENTS.md +## Build, Test & Lint Commands +| Command | Purpose | +|---------|---------| +| `bun test` | Run all tests | +... +``` + +All PAI behavior lives in `PAI/SKILL.md` — the single source of truth. + +### PAI/SKILL.md Now tier:always + +**`GenerateSkillIndex.ts` — ALWAYS_LOADED_SKILLS:** + +```typescript +// Before +const ALWAYS_LOADED_SKILLS = []; + +// After +const ALWAYS_LOADED_SKILLS = ['PAI']; +``` + +This ensures the full 479-line PAI Core Skill (Algorithm, ISC, Capabilities) is always available in session context without any plugin intervention. + +### skills/PAI Symlink + +A new symlink `skills/PAI → ../PAI` enables the native OpenCode skill scanner to discover `PAI/SKILL.md`: + +``` +.opencode/ +├── skills/ +│ ├── PAI -> ../PAI ← symlink +│ ├── Blog/ +│ ├── Research/ +│ └── ... +└── PAI/ + └── SKILL.md ← the actual file +``` + +The skill scanner follows symlinks (supported since `upstream #620`), so PAI is discovered and loaded exactly like any other skill — no special handling required. + +### Default Model: Zen Free + +**`opencode.json` default model:** + +```json +{ + "model": "opencode/big-pickle" +} +``` + +Previously the default required configuring an API key. With `opencode/big-pickle` as default, new users get a working PAI installation out of the box — zero API key required. + +--- + ## v3.0 Adaptations ### Platform-Specific Adaptations @@ -432,19 +538,22 @@ See **ROADMAP.md** for detailed timeline. ## Version Compatibility -| Component | PAI v3.0 | PAI-OpenCode v2.0 | -|-----------|----------|-------------------| -| Skills | ✅ 39 skills | ✅ 39 skills (identical) | -| Agents | ✅ Content identical | ⚠️ Filename casing changed (PascalCase) | -| MEMORY | ✅ Identical | ✅ Identical (+ WISDOM/ directory) | -| Security Patterns | ✅ Identical | ✅ Identical | -| Hooks/Plugins | ❌ Different architecture | ✅ Plugin system (20 handlers) | -| Algorithm v1.8.0 | ✅ Full | ✅ Full | -| Wisdom Frames | ✅ Available | ✅ Available (5 seed domains) | -| Voice Server | ✅ Available | ✅ Available (3 backends) | -| Sentiment Detection | ✅ Available | ✅ Available | -| Verify Completion Gate | ✅ Available | ✅ Available | -| Observability Dashboard | ✅ Available | ✅ Available | +| Component | PAI v3.0 | PAI-OpenCode v2.0 | PAI-OpenCode v3.0 | +|-----------|----------|-------------------|-------------------| +| Skills | ✅ 39 skills | ✅ 39 skills (identical) | ✅ 39 skills (identical) | +| Agents | ✅ Content identical | ⚠️ Filename casing changed (PascalCase) | ⚠️ Filename casing changed (PascalCase) | +| MEMORY | ✅ Identical | ✅ Identical (+ WISDOM/ directory) | ✅ Identical (+ WISDOM/ directory) | +| Security Patterns | ✅ Identical | ✅ Identical | ✅ Identical | +| Hooks/Plugins | ❌ Different architecture | ✅ Plugin system (20 handlers) | ✅ Plugin system, simplified (user context only) | +| Algorithm v1.8.0 | ✅ Full | ✅ Full | ✅ Full | +| Wisdom Frames | ✅ Available | ✅ Available (5 seed domains) | ✅ Available (5 seed domains) | +| Voice Server | ✅ Available | ✅ Available (3 backends) | ✅ Available (3 backends) | +| Sentiment Detection | ✅ Available | ✅ Available | ✅ Available | +| Verify Completion Gate | ✅ Available | ✅ Available | ✅ Available | +| Observability Dashboard | ✅ Available | ✅ Available | ✅ Available | +| Bootstrap | N/A | ✅ MINIMAL_BOOTSTRAP.md | ❌ Removed — native skill system | +| PAI Core Loading | SKILL.md | Plugin-injected | Native tier:always | +| Default Model | N/A | Requires API key | `opencode/big-pickle` (free) | --- @@ -485,6 +594,7 @@ See **MIGRATION.md** for full guide. | **Logging** | stdout → file logging | TUI integrity | Debug workflow change | | **Deferred** | Voice/Observability | Focus on core first | Available in v1.x | | **Model Routing** | `.md` frontmatter → `opencode.json` exclusively (one model per agent) | Centralized configuration | Easier provider switching | +| **Context Loading** | Bootstrap → Native skill system (`tier:always`) | OpenCode native `` XML made bootstrap redundant | Zero bootstrap overhead, single source of truth | --- @@ -499,6 +609,6 @@ See **MIGRATION.md** for full guide. --- -*Last updated: 2026-03-06 (PR #42 — WP-A complete, shell.env hook, 6 new Bus events, 5 new handlers)* +*Last updated: 2026-04-13 (v3.0 native OpenCode architecture — bootstrap removed, PAI Core tier:always, AGENTS.md 450→14 LOC, Zen free default)* -**PAI-OpenCode v3.0-dev** — Full PAI v3.0, Algorithm v1.8.0, 39 Skills, 25 Handlers, Wisdom Frames, shell.env Hook +**PAI-OpenCode v3.0** — Full PAI v3.0, Algorithm v1.8.0, 39 Skills, Native OpenCode Skill Loading, Zero Bootstrap, Zen Free Default diff --git a/docs/architecture/adr/ADR-020-native-opencode-context-loading.md b/docs/architecture/adr/ADR-020-native-opencode-context-loading.md new file mode 100644 index 00000000..274ddc8f --- /dev/null +++ b/docs/architecture/adr/ADR-020-native-opencode-context-loading.md @@ -0,0 +1,111 @@ +# ADR-020: Native OpenCode Context Loading (Bootstrap Removal) + +**Status:** Accepted +**Date:** 2026-04-13 +**Deciders:** Steffen Zellmer + +--- + +## Context + +PAI-OpenCode v2.x used a custom bootstrap mechanism (`MINIMAL_BOOTSTRAP.md`) loaded by the `pai-unified.ts` plugin via the `experimental.chat.system.transform` hook. This bootstrap served three purposes: + +1. **Algorithm Essence** — A condensed summary of Algorithm v3.7.0 phases and ISC rules +2. **Skill Discovery Index** — A manual table listing all 50+ skills with their trigger phrases and paths +3. **User Identity Routing** — Instructions for loading ABOUTME, TELOS, DAIDENTITY files + +The bootstrap was introduced in early v3.0 development as an interim solution while we were porting PAI's Claude Code `contextFiles` mechanism to OpenCode. The intent was "minimal useful context" — provide just enough for the AI to understand the system without loading everything upfront. + +## Problem + +Three issues emerged with this approach: + +1. **Algorithm works poorly from the bootstrap alone.** The bootstrap contained only an "essence" (20-30 lines) of the full 479-line Algorithm. Models produced a degraded experience — following the Algorithm's structure but missing critical details like exact ISC minimums, capability audit format, Red Lines, and verification phase requirements. + +2. **Skill Discovery Index was redundant.** OpenCode natively generates an `` XML block for every session, listing all discovered skills with name, description, and location. The bootstrap's 90-line manual table was duplicating what OpenCode already provided — and falling out of sync as new skills were added. + +3. **Plugin used an experimental hook.** `experimental.chat.system.transform` is marked experimental. The PAI Core (Algorithm) is the most critical piece of PAI — anchoring it to an experimental hook was a reliability risk. + +## Decision + +Remove the bootstrap mechanism entirely. Shift PAI Core loading to OpenCode's native skill system. + +**Three changes:** + +### 1. PAI/SKILL.md → tier:always + +Add `'PAI'` to the `ALWAYS_LOADED_SKILLS` array in `GenerateSkillIndex.ts`. The PAI Core Skill (479 lines, full Algorithm + ISC + Capabilities) is now loaded by the native OpenCode skill system at session start — not by the plugin. + +```typescript +// PAI/Tools/GenerateSkillIndex.ts +const ALWAYS_LOADED_SKILLS = [ + 'PAI', // ← added + 'CORE', + 'Development', + // ... +]; +``` + +### 2. skills/PAI symlink + +Create `skills/PAI → ../PAI` symlink so OpenCode's native skill scanner discovers `PAI/SKILL.md`. The scanner already follows symlinks (established in upstream #620). No special handling required. + +``` +.opencode/ +├── skills/ +│ └── PAI -> ../PAI ← new symlink +└── PAI/ + └── SKILL.md +``` + +### 3. Plugin simplified to user context only + +`pai-unified.ts` no longer loads `MINIMAL_BOOTSTRAP.md`. It loads only: +- `PAI/AISTEERINGRULES.md` — behavioral governance (cannot be in a static skill file) +- `USER/ABOUTME.md` — personal background (user-specific, not in repo) +- `USER/TELOS/TELOS.md` — life goals and mission (user-specific) +- `USER/DAIDENTITY.md` — DA name and personality (user-specific) +- `USER/AISTEERINGRULES.md` — personal behavioral rules (user-specific) + +These files are genuinely dynamic — they vary per user installation and cannot live in a static committed skill file. Everything else (the Algorithm, ISC rules, Capabilities) belongs in `PAI/SKILL.md`. + +## Consequences + +### Positive + +- **Full Algorithm always available.** The complete 479-line PAI Core Skill is in context — not a condensed essence. Models follow the Algorithm correctly. +- **No redundant Skill Discovery Index.** OpenCode's native `` XML handles skill discovery. One source of truth. +- **Stable loading mechanism.** Native skill system (`tier:always`) is production-stable, not experimental. +- **Simpler plugin.** `pai-unified.ts` has a single clear responsibility: user identity context. +- **AGENTS.md simplified.** Reduced from 450 to 14 lines. PAI behavior has one home: `PAI/SKILL.md`. + +### Negative / Tradeoffs + +- **Larger always-loaded context.** PAI/SKILL.md (479 lines, ~18KB) vs. MINIMAL_BOOTSTRAP.md (203 lines, ~7KB). This is accepted — the degraded Algorithm experience from the thin bootstrap was worse than the token cost of loading it fully. +- **All context reaches model via user message (not system prompt).** The `cemalturkcan/opencode-anthropic-login-via-cli` plugin (used by most PAI-OpenCode users with Anthropic Max) relocates all system prompt blocks except the identity block to the first user message. This applies to both the old bootstrap and the new skill content equally — so the tradeoff is identical. + +### No Impact + +- Skill on-demand loading — unchanged. All 50+ non-PAI skills remain deferred, loaded via `Skill()` tool. +- Memory system, agents, security validation — unchanged. +- User identity loading — unchanged (still handled by plugin). + +## Alternatives Considered + +### Alternative A: Expand MINIMAL_BOOTSTRAP.md + +Expand the bootstrap to contain the full Algorithm. Rejected: still uses the experimental hook, still a second copy of content that belongs in SKILL.md. + +### Alternative B: Use opencode.json `instructions` field + +Load Algorithm via `opencode.json → instructions: [".opencode/PAI/Algorithm/v3.7.0.md"]`. Rejected: the Anthropic auth plugin relocates `instructions` content to user messages exactly like the existing bootstrap — same net result, but adds complexity without benefit for the target user base. + +### Alternative C: Inline Algorithm in AGENTS.md + +Add Algorithm content to AGENTS.md. Rejected: AGENTS.md is loaded on every message by every agent working in the repo. Putting PAI Core there would load it for Contributors fixing CI issues — work that has nothing to do with PAI. Wrong layer of concern. + +## Related ADRs + +- [ADR-001](ADR-001-hooks-to-plugins-architecture.md) — Hooks → Plugins (original plugin architecture) +- [ADR-003](ADR-003-skills-system-unchanged.md) — Skills system preservation +- [ADR-019](ADR-019-vanilla-opencode-migration.md) — Vanilla OpenCode migration (prerequisite for this ADR) From 43a25afb28e23650f257325443e18fa5c873aa24 Mon Sep 17 00:00:00 2001 From: Steffen Zellmer <151627820+Steffen025@users.noreply.github.com> Date: Mon, 13 Apr 2026 13:08:08 +0200 Subject: [PATCH 3/7] fix(review): address CodeRabbit PR #138 commentary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AGENTS.md: replace pipe-separated bullet with markdown table - pai-unified.ts: rename loadMinimalBootstrap() → loadUserSystemContext() (function only loads user/system context, no longer a bootstrap) - INSTALL.md: document two-step provider setup (connect + switch-provider) and add Anthropic Claude Max ToS warning with plugin reference - README.md: same two-step flow + ToS caveat in provider table - docs/ADVANCED-SETUP.md: overview updated, ToS note + links to INSTALL.md - ADR-020, PAI-ADAPTATIONS: add 'text' specifier to bare code fences Skipped findings verified as incorrect or already fixed: - AGENTS.md YAML frontmatter (verbatim to model, would appear as noise) - install-wizard.ts switch-provider ref (already uses /connect) - steps-fresh.ts OPENROUTER_API_KEY (.env comment not needed, post-install flow) - steps-fresh.ts model mapping refactor (already uses PROVIDER_MODELS centrally) - ADR-020 Algorithm v3.7 version (correct for pai-opencode v3.0) --- .opencode/plugins/pai-unified.ts | 6 +-- AGENTS.md | 4 +- INSTALL.md | 53 ++++++++++++++++--- README.md | 18 +++++-- docs/ADVANCED-SETUP.md | 5 +- docs/PAI-ADAPTATIONS.md | 4 +- ...ADR-020-native-opencode-context-loading.md | 2 +- 7 files changed, 72 insertions(+), 20 deletions(-) diff --git a/.opencode/plugins/pai-unified.ts b/.opencode/plugins/pai-unified.ts index abc06c62..54e574ee 100644 --- a/.opencode/plugins/pai-unified.ts +++ b/.opencode/plugins/pai-unified.ts @@ -53,7 +53,7 @@ import { fileURLToPath } from "node:url"; // ESM-equivalent of __dirname — .opencode/package.json has "type": "module" // so CommonJS globals (__dirname / __filename) are not defined. Used below -// by loadMinimalBootstrap() to resolve the PAI dir relative to this plugin +// by loadUserSystemContext() to resolve the PAI dir relative to this plugin // file regardless of the current working directory opencode is launched from. const PLUGIN_DIR = path.dirname(fileURLToPath(import.meta.url)); import type { Hooks, Plugin } from "@opencode-ai/plugin"; @@ -257,7 +257,7 @@ async function readFileSafe(filePath: string): Promise { * 1. System AISTEERINGRULES.md (behavioral governance) * 2. User Identity files (ABOUTME, TELOS, DAIDENTITY) if they exist */ -async function loadMinimalBootstrap(): Promise { +async function loadUserSystemContext(): Promise { try { const paiDir = path.join(PLUGIN_DIR, "..", "PAI"); const contextParts: string[] = []; @@ -393,7 +393,7 @@ export const PaiUnified: Plugin = async (_ctx) => { emitSessionStart({ model: (input as any).model }).catch(() => {}); // WP2: Use minimal bootstrap instead of full context loader - const bootstrap = await loadMinimalBootstrap(); + const bootstrap = await loadUserSystemContext(); if (bootstrap && bootstrap.length > 0) { output.system.push(bootstrap); diff --git a/AGENTS.md b/AGENTS.md index e35a329a..b270dcd6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,8 @@ # AGENTS.md — PAI-OpenCode Repository -**Runtime:** Bun (never npm/yarn/pnpm) | **Linter:** Biome (never ESLint/Prettier) | **Lang:** TypeScript ESM +| Runtime | Linter | Lang | +|---------|--------|------| +| Bun (never npm/yarn/pnpm) | Biome (never ESLint/Prettier) | TypeScript ESM | ```bash bun install # Install dependencies diff --git a/INSTALL.md b/INSTALL.md index 149d9adf..d1fe35e0 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -323,27 +323,64 @@ PAI-OpenCode ships with **OpenCode Zen free** as the default provider. Big Pickl ### Connecting Premium Providers -To use Anthropic Max, paid Zen models, or any other provider, run `/connect` inside OpenCode after install: +Provider setup is a **post-install step** — PAI-OpenCode ships with Zen free so you can start immediately, then connect additional providers whenever you're ready. -``` +Connecting a provider is a two-step process: + +#### Step 1 — Establish credentials via `/connect` + +Run `/connect` inside a running OpenCode session and follow the prompts: + +```text /connect ``` -This opens OpenCode's provider connection flow. You can connect: +OpenCode stores credentials securely in `~/.opencode/`. Supported providers: | Provider | Best For | Cost | |----------|----------|------| -| **Anthropic Max** | Best quality, Claude Opus 4.6 / Sonnet 4.5 | Claude Max subscription | -| **ZEN PAID** | Budget-friendly paid models (GLM 4.7, Kimi K2.5, Gemini Flash) | ~$1-15/1M tokens | +| **Zen paid** | Budget-friendly paid models (GLM 4.7, Kimi K2.5, Gemini Flash) | ~$1-15/1M tokens | +| **Anthropic (API key)** | Best quality, Claude Opus 4.6 / Sonnet 4.5 | Pay-per-token | | **OpenRouter** | Provider diversity, 100+ models | Varies by model | | **OpenAI** | GPT-4o, GPT-4.1 | ~$10-30/1M tokens | +| **Ollama** | Local, private, offline | Free (your hardware) | + +#### Step 2 — Update agent model assignments in `opencode.json` + +`/connect` stores your credentials, but the agent model strings in `opencode.json` still point to the Zen free defaults. Switch them to your newly connected provider: -Alternatively, add API keys directly to `~/.opencode/.env`: ```bash -ANTHROPIC_API_KEY=your_key -OPENAI_API_KEY=your_key +# Switch all agents to Anthropic models +bun run .opencode/tools/switch-provider.ts anthropic + +# Switch to paid Zen models +bun run .opencode/tools/switch-provider.ts zen-paid + +# Switch to OpenAI models +bun run .opencode/tools/switch-provider.ts openai + +# List all available profiles +bun run .opencode/tools/switch-provider.ts --list ``` +See [ADVANCED-SETUP.md](docs/ADVANCED-SETUP.md) for manual `opencode.json` editing and custom profiles. + +--- + +### ⚠️ Anthropic Claude Max — Important Note + +Anthropic's Claude Max subscription (the one used at claude.ai) is designed for direct human use. Routing it through a third-party tool like OpenCode via OAuth is a **grey area in Anthropic's Terms of Service** and carries a risk of account suspension. + +**How the connection works technically:** +OpenCode connects to Claude Max via a community plugin — [`cemalturkcan/opencode-anthropic-login-via-cli`](https://github.com/cemalturkcan/opencode-anthropic-login-via-cli) — that authenticates your Claude Max account and proxies requests through it. + +**Why PAI-OpenCode does not ship this plugin:** +We intentionally exclude it from the distribution. Installing and using it is your own decision and responsibility — we do not want to expose users to ToS risk without their explicit informed consent. + +**Alternatives that are clearly within Anthropic's ToS:** +- **Anthropic API key** — Pay-per-token access via [console.anthropic.com](https://console.anthropic.com/). Add `ANTHROPIC_API_KEY=sk-ant-...` to `~/.opencode/.env` and run `/connect` → Anthropic. +- **OpenCode Zen paid** — High-quality models at competitive rates, no ToS concerns. Includes access to models equivalent in capability to Sonnet/Opus tiers. + ### Changing Providers After Install ```bash diff --git a/README.md b/README.md index a6290c62..45dbcfb5 100644 --- a/README.md +++ b/README.md @@ -268,19 +268,29 @@ PAI-OpenCode ships with **Zen free as the default** — OpenCode Zen is pre-conn |----------|---------------|------| | **Zen free** (Default) | Works out of the box — no setup required | **FREE** | | **Zen paid** | Run `/connect` in OpenCode → select Zen AI Gateway | ~$1-75/1M tokens depending on tier | -| **Anthropic** | Run `/connect` in OpenCode → select Anthropic | Per-token (Claude Max subscription or API) | +| **Anthropic API** | Add `ANTHROPIC_API_KEY` to `~/.opencode/.env`, then `/connect` | Pay-per-token | | **OpenRouter** | Run `/connect` in OpenCode → select OpenRouter | Varies by model | | **Ollama (local)** | Run `/connect` in OpenCode → select Ollama | **FREE** (your hardware) | +> **Anthropic Claude Max:** Using a Claude Max subscription (claude.ai) with OpenCode requires a community plugin not shipped with PAI-OpenCode due to Anthropic ToS concerns. See [INSTALL.md](INSTALL.md#️-anthropic-claude-max--important-note) for details. + ### Connecting Providers After Install -All provider configuration happens inside a running OpenCode session — no reinstall needed: +Provider setup is a **two-step process** — no reinstall needed: -``` +1. **Run `/connect`** inside OpenCode to store credentials: + +```text /connect ``` -Follow the prompts to authenticate. OpenCode stores credentials securely and your PAI skills and agents work with any provider you connect. +2. **Update agent model assignments** in `opencode.json` to point at your new provider: + +```bash +bun run .opencode/tools/switch-provider.ts anthropic # or zen-paid, openai, etc. +``` + +See [INSTALL.md](INSTALL.md#connecting-premium-providers) for the full walkthrough. ### Why This Design? diff --git a/docs/ADVANCED-SETUP.md b/docs/ADVANCED-SETUP.md index babd8515..9ec08165 100644 --- a/docs/ADVANCED-SETUP.md +++ b/docs/ADVANCED-SETUP.md @@ -3,7 +3,10 @@ ## Overview -The installer configures a preset for your initial provider routing (`zen`, `anthropic`, `openrouter`, `openai`). See [INSTALL.md](../INSTALL.md) for installation details. + +The installer configures Zen free as the default provider. Connecting additional providers and updating agent model assignments are **post-install steps** — see [INSTALL.md — Connecting Premium Providers](../INSTALL.md#connecting-premium-providers) for the two-step process (`/connect` → `switch-provider`). + +> **Anthropic Claude Max note:** Routing a Claude Max subscription through OpenCode via OAuth requires a community plugin that PAI-OpenCode intentionally does not ship ([details in INSTALL.md](../INSTALL.md#️-anthropic-claude-max--important-note)). The ToS-safe alternative is an Anthropic API key or OpenCode Zen paid. ## Custom Model Configuration diff --git a/docs/PAI-ADAPTATIONS.md b/docs/PAI-ADAPTATIONS.md index 073f8e1e..a305ab06 100644 --- a/docs/PAI-ADAPTATIONS.md +++ b/docs/PAI-ADAPTATIONS.md @@ -221,7 +221,7 @@ The v3.0 release represents a fundamental shift in how PAI Core loads into conte **`pai-unified.ts` — What it does now:** -``` +```text Before: Bootstrap loading + User identity context After: User identity context only ``` @@ -271,7 +271,7 @@ This ensures the full 479-line PAI Core Skill (Algorithm, ISC, Capabilities) is A new symlink `skills/PAI → ../PAI` enables the native OpenCode skill scanner to discover `PAI/SKILL.md`: -``` +```text .opencode/ ├── skills/ │ ├── PAI -> ../PAI ← symlink diff --git a/docs/architecture/adr/ADR-020-native-opencode-context-loading.md b/docs/architecture/adr/ADR-020-native-opencode-context-loading.md index 274ddc8f..73ff54d7 100644 --- a/docs/architecture/adr/ADR-020-native-opencode-context-loading.md +++ b/docs/architecture/adr/ADR-020-native-opencode-context-loading.md @@ -50,7 +50,7 @@ const ALWAYS_LOADED_SKILLS = [ Create `skills/PAI → ../PAI` symlink so OpenCode's native skill scanner discovers `PAI/SKILL.md`. The scanner already follows symlinks (established in upstream #620). No special handling required. -``` +```text .opencode/ ├── skills/ │ └── PAI -> ../PAI ← new symlink From e33f7c8267f2eda426d4da9a61232b26bd173610 Mon Sep 17 00:00:00 2001 From: Steffen Zellmer <151627820+Steffen025@users.noreply.github.com> Date: Mon, 13 Apr 2026 13:15:47 +0200 Subject: [PATCH 4/7] fix(review): address CodeRabbit round 2 commentary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pai-unified.ts: - Fix stale error message: 'minimal bootstrap' → 'user system context' - Rename local variable bootstrap → userContext (matches loadUserSystemContext) - Update stale log messages and comments (WP2/minimal bootstrap refs removed) docs/ADVANCED-SETUP.md: - Convert plain blockquote → Obsidian callout (> [!important]) for Anthropic note docs/PAI-ADAPTATIONS.md: - Convert plain blockquote → Obsidian callout (> [!note]) for ADR-020 reference - Fix ALWAYS_LOADED_SKILLS snippet: show all 6 entries matching GenerateSkillIndex.ts (was incorrectly showing only ['PAI']) - Add v3.0 architecture ASCII flow diagram + collapsible Mermaid diagram --- .opencode/plugins/pai-unified.ts | 22 ++++++------- docs/ADVANCED-SETUP.md | 3 +- docs/PAI-ADAPTATIONS.md | 55 ++++++++++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/.opencode/plugins/pai-unified.ts b/.opencode/plugins/pai-unified.ts index 54e574ee..1dd90aff 100644 --- a/.opencode/plugins/pai-unified.ts +++ b/.opencode/plugins/pai-unified.ts @@ -302,7 +302,7 @@ async function loadUserSystemContext(): Promise { return `\nPAI CONTEXT (User Context)\n\n${fullContext}\n\n---\nPAI Core Skill loaded via skill system. Skills load on-demand via Skill tool.\n`; } catch (error) { - fileLogError("Failed to load minimal bootstrap", error); + fileLogError("Failed to load user system context", error); // Return null to signal failure - caller should handle return null; } @@ -370,8 +370,8 @@ export const PaiUnified: Plugin = async (_ctx) => { /** * CONTEXT INJECTION (SessionStart equivalent) * - * WP2: Injects minimal bootstrap (~7KB) instead of full 233KB context. - * Skills load on-demand via OpenCode native skill tool. + * Injects user system context (steering rules + identity files, ~7KB). + * PAI Core Skill loads via native skill system (tier:always). Other skills load on-demand. * * NOTE: Only loads when PAI_ENABLED=1 is set (via `pai` wrapper). * Plain `opencode` launches without PAI context by design. @@ -387,26 +387,26 @@ export const PaiUnified: Plugin = async (_ctx) => { return; } - fileLog("Injecting minimal bootstrap context (WP2 lazy loading)..."); + fileLog("Injecting user system context (steering rules + identity)..."); // Emit session start emitSessionStart({ model: (input as any).model }).catch(() => {}); - // WP2: Use minimal bootstrap instead of full context loader - const bootstrap = await loadUserSystemContext(); + // Load user-specific context (AISTEERINGRULES + USER/* identity files) + const userContext = await loadUserSystemContext(); - if (bootstrap && bootstrap.length > 0) { - output.system.push(bootstrap); - fileLog(`Context injected successfully (${bootstrap.length} chars)`); + if (userContext && userContext.length > 0) { + output.system.push(userContext); + fileLog(`Context injected successfully (${userContext.length} chars)`); // Emit context loaded emitContextLoaded({ files_loaded: 1, - total_size: bootstrap.length, + total_size: userContext.length, success: true, }).catch(() => {}); } else { - fileLog("Context injection skipped: empty bootstrap", "warn"); + fileLog("Context injection skipped: empty user context", "warn"); // Emit context load failure emitContextLoaded({ files_loaded: 0, diff --git a/docs/ADVANCED-SETUP.md b/docs/ADVANCED-SETUP.md index 9ec08165..1393470b 100644 --- a/docs/ADVANCED-SETUP.md +++ b/docs/ADVANCED-SETUP.md @@ -6,7 +6,8 @@ The installer configures Zen free as the default provider. Connecting additional providers and updating agent model assignments are **post-install steps** — see [INSTALL.md — Connecting Premium Providers](../INSTALL.md#connecting-premium-providers) for the two-step process (`/connect` → `switch-provider`). -> **Anthropic Claude Max note:** Routing a Claude Max subscription through OpenCode via OAuth requires a community plugin that PAI-OpenCode intentionally does not ship ([details in INSTALL.md](../INSTALL.md#️-anthropic-claude-max--important-note)). The ToS-safe alternative is an Anthropic API key or OpenCode Zen paid. +> [!important] +> **Anthropic Claude Max:** Routing a Claude Max subscription through OpenCode via OAuth requires a community plugin that PAI-OpenCode intentionally does not ship ([details in INSTALL.md](../INSTALL.md#️-anthropic-claude-max--important-note)). The ToS-safe alternative is an Anthropic API key or OpenCode Zen paid. ## Custom Model Configuration diff --git a/docs/PAI-ADAPTATIONS.md b/docs/PAI-ADAPTATIONS.md index a305ab06..ac280c39 100644 --- a/docs/PAI-ADAPTATIONS.md +++ b/docs/PAI-ADAPTATIONS.md @@ -196,7 +196,7 @@ export function fileLog(message: string, level = "info") { ## v3.0 Native OpenCode Architecture -> **Architecture Decision:** ADR-020 - Native OpenCode Context Loading (Bootstrap Removal) +> [!note] **Architecture Decision:** ADR-020 — Native OpenCode Context Loading (Bootstrap Removal) The v3.0 release represents a fundamental shift in how PAI Core loads into context. The custom bootstrap mechanism built for earlier versions became redundant once OpenCode's native skill system matured — so we removed it entirely. @@ -234,6 +234,50 @@ The plugin now loads only the pieces OpenCode's native system cannot provide: PAI Core (Algorithm, ISC, Capabilities) loads automatically via the skill system. +### v3.0 Architecture — Context Flow + +``` +OpenCode session start + │ + ├─► GenerateSkillIndex.ts (ALWAYS_LOADED_SKILLS) + │ └─► skills/PAI symlink → PAI/SKILL.md [tier:always] + │ Algorithm, ISC, Capabilities — always in context + │ + └─► pai-unified.ts plugin (loadUserSystemContext) + └─► PAI/AISTEERINGRULES.md [behavioral governance] + └─► USER/ABOUTME.md [personal background] + └─► USER/TELOS/TELOS.md [goals & mission] + └─► USER/DAIDENTITY.md [DA name & personality] + └─► USER/AISTEERINGRULES.md [personal steering rules] +``` + +
+Detailed Mermaid Diagram + +```mermaid +flowchart TD + A[OpenCode session start] --> B[GenerateSkillIndex.ts] + A --> C[pai-unified.ts] + + B --> D["ALWAYS_LOADED_SKILLS\n['PAI','CORE','Development',...]"] + D --> E["skills/PAI symlink\n→ PAI/SKILL.md"] + E --> F["PAI Core Skill\ntier: always\nAlgorithm · ISC · Capabilities"] + + C --> G["loadUserSystemContext()"] + G --> H["PAI/AISTEERINGRULES.md\nBehavioral governance"] + G --> I["USER/ABOUTME.md\nPersonal background"] + G --> J["USER/TELOS/TELOS.md\nGoals & mission"] + G --> K["USER/DAIDENTITY.md\nDA name & personality"] + G --> L["USER/AISTEERINGRULES.md\nPersonal steering rules"] + + F --> M[Session context] + H & I & J & K & L --> M + + N["opencode.json\nagent model assignments"] --> O[Agent routing per task] +``` + +
+ ### AGENTS.md Simplification **From 450 LOC → 14 LOC** @@ -262,7 +306,14 @@ All PAI behavior lives in `PAI/SKILL.md` — the single source of truth. const ALWAYS_LOADED_SKILLS = []; // After -const ALWAYS_LOADED_SKILLS = ['PAI']; +const ALWAYS_LOADED_SKILLS = [ + 'PAI', // ← PAI Core Skill (Algorithm, ISC, Capabilities) + 'CORE', + 'Development', + 'Research', + 'Blogging', + 'Art', +]; ``` This ensures the full 479-line PAI Core Skill (Algorithm, ISC, Capabilities) is always available in session context without any plugin intervention. From ba1f4b13af03fd00e552724be49805497a3e0da4 Mon Sep 17 00:00:00 2001 From: Steffen Zellmer <151627820+Steffen025@users.noreply.github.com> Date: Mon, 13 Apr 2026 13:25:51 +0200 Subject: [PATCH 5/7] fix(review): address CodeRabbit round 3 commentary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pai-unified.ts: - loadUserSystemContext() now returns { context, filesLoaded } instead of string so callers get an accurate file count including PAI/AISTEERINGRULES.md - Remove userContextLoaded counter; use contextParts.length (counts all files, not just USER/* — systemSteering was previously excluded) - Update caller: destructure result, pass filesLoaded to emitContextLoaded() (was hardcoded files_loaded: 1) - Fix log messages: 'Loaded USER/file' → 'Loaded PAI/USER/file' (correct path) docs/PAI-ADAPTATIONS.md: - Add 'text' language tag to bare ASCII diagram code fence (MD040) - Fix USER/... paths → PAI/USER/... throughout (bullets, ASCII diagram, Mermaid) matches actual file layout in code (paiDir = .../PAI, userDir = paiDir/USER) --- .opencode/plugins/pai-unified.ts | 24 +++++++++++++----------- docs/PAI-ADAPTATIONS.md | 27 ++++++++++++++------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/.opencode/plugins/pai-unified.ts b/.opencode/plugins/pai-unified.ts index 1dd90aff..4bd68ac2 100644 --- a/.opencode/plugins/pai-unified.ts +++ b/.opencode/plugins/pai-unified.ts @@ -257,7 +257,7 @@ async function readFileSafe(filePath: string): Promise { * 1. System AISTEERINGRULES.md (behavioral governance) * 2. User Identity files (ABOUTME, TELOS, DAIDENTITY) if they exist */ -async function loadUserSystemContext(): Promise { +async function loadUserSystemContext(): Promise<{ context: string; filesLoaded: number } | null> { try { const paiDir = path.join(PLUGIN_DIR, "..", "PAI"); const contextParts: string[] = []; @@ -267,7 +267,7 @@ async function loadUserSystemContext(): Promise { const systemSteering = await readFileSafe(systemSteeringPath); if (systemSteering) { contextParts.push(`--- System Steering Rules ---\n${systemSteering}`); - fileLog("Loaded System AISTEERINGRULES.md"); + fileLog("Loaded PAI/AISTEERINGRULES.md"); } // 2. User Identity Files (if exist) — CRITICAL: Must know the user! @@ -279,14 +279,12 @@ async function loadUserSystemContext(): Promise { { file: "AISTEERINGRULES.md", label: "User Steering Rules" }, ]; - let userContextLoaded = 0; for (const { file, label } of userFiles) { const filePath = path.join(userDir, file); const content = await readFileSafe(filePath); if (content) { contextParts.push(`--- ${label} ---\n${content}`); - fileLog(`Loaded USER/${file}`); - userContextLoaded++; + fileLog(`Loaded PAI/USER/${file}`); } } @@ -295,12 +293,15 @@ async function loadUserSystemContext(): Promise { return null; } - // Combine all context + // Combine all context — contextParts.length is the accurate total file count const fullContext = contextParts.join("\n\n"); const size = Buffer.byteLength(fullContext, "utf-8"); - fileLog(`User context loaded: ${size} bytes (${userContextLoaded} user files)`); + fileLog(`User context loaded: ${size} bytes (${contextParts.length} files)`); - return `\nPAI CONTEXT (User Context)\n\n${fullContext}\n\n---\nPAI Core Skill loaded via skill system. Skills load on-demand via Skill tool.\n`; + return { + context: `\nPAI CONTEXT (User Context)\n\n${fullContext}\n\n---\nPAI Core Skill loaded via skill system. Skills load on-demand via Skill tool.\n`, + filesLoaded: contextParts.length, + }; } catch (error) { fileLogError("Failed to load user system context", error); // Return null to signal failure - caller should handle @@ -393,15 +394,16 @@ export const PaiUnified: Plugin = async (_ctx) => { emitSessionStart({ model: (input as any).model }).catch(() => {}); // Load user-specific context (AISTEERINGRULES + USER/* identity files) - const userContext = await loadUserSystemContext(); + const userContextResult = await loadUserSystemContext(); - if (userContext && userContext.length > 0) { + if (userContextResult && userContextResult.context.length > 0) { + const { context: userContext, filesLoaded } = userContextResult; output.system.push(userContext); fileLog(`Context injected successfully (${userContext.length} chars)`); // Emit context loaded emitContextLoaded({ - files_loaded: 1, + files_loaded: filesLoaded, total_size: userContext.length, success: true, }).catch(() => {}); diff --git a/docs/PAI-ADAPTATIONS.md b/docs/PAI-ADAPTATIONS.md index ac280c39..8f6ff2f1 100644 --- a/docs/PAI-ADAPTATIONS.md +++ b/docs/PAI-ADAPTATIONS.md @@ -227,16 +227,17 @@ After: User identity context only ``` The plugin now loads only the pieces OpenCode's native system cannot provide: -- `USER/ABOUTME.md` — personal background -- `USER/TELOS/TELOS.md` — goals and mission -- `USER/DAIDENTITY.md` — DA name and personality -- `USER/AISTEERINGRULES.md` — behavioral steering rules +- `PAI/AISTEERINGRULES.md` — behavioral governance (system-level) +- `PAI/USER/ABOUTME.md` — personal background +- `PAI/USER/TELOS/TELOS.md` — goals and mission +- `PAI/USER/DAIDENTITY.md` — DA name and personality +- `PAI/USER/AISTEERINGRULES.md` — personal behavioral steering rules PAI Core (Algorithm, ISC, Capabilities) loads automatically via the skill system. ### v3.0 Architecture — Context Flow -``` +```text OpenCode session start │ ├─► GenerateSkillIndex.ts (ALWAYS_LOADED_SKILLS) @@ -245,10 +246,10 @@ OpenCode session start │ └─► pai-unified.ts plugin (loadUserSystemContext) └─► PAI/AISTEERINGRULES.md [behavioral governance] - └─► USER/ABOUTME.md [personal background] - └─► USER/TELOS/TELOS.md [goals & mission] - └─► USER/DAIDENTITY.md [DA name & personality] - └─► USER/AISTEERINGRULES.md [personal steering rules] + └─► PAI/USER/ABOUTME.md [personal background] + └─► PAI/USER/TELOS/TELOS.md [goals & mission] + └─► PAI/USER/DAIDENTITY.md [DA name & personality] + └─► PAI/USER/AISTEERINGRULES.md [personal steering rules] ```
@@ -265,10 +266,10 @@ flowchart TD C --> G["loadUserSystemContext()"] G --> H["PAI/AISTEERINGRULES.md\nBehavioral governance"] - G --> I["USER/ABOUTME.md\nPersonal background"] - G --> J["USER/TELOS/TELOS.md\nGoals & mission"] - G --> K["USER/DAIDENTITY.md\nDA name & personality"] - G --> L["USER/AISTEERINGRULES.md\nPersonal steering rules"] + G --> I["PAI/USER/ABOUTME.md\nPersonal background"] + G --> J["PAI/USER/TELOS/TELOS.md\nGoals & mission"] + G --> K["PAI/USER/DAIDENTITY.md\nDA name & personality"] + G --> L["PAI/USER/AISTEERINGRULES.md\nPersonal steering rules"] F --> M[Session context] H & I & J & K & L --> M From 428275c3fc5cd303a0d6136f648b77e7009c93db Mon Sep 17 00:00:00 2001 From: Steffen Zellmer <151627820+Steffen025@users.noreply.github.com> Date: Mon, 13 Apr 2026 13:38:26 +0200 Subject: [PATCH 6/7] fix(defaults): ship with big-pickle only + clarify two-step provider setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit opencode.json: - All agents standardised to opencode/big-pickle (was: 5 different free models) Reason: other free Zen models rotate periodically (qwen3.6-plus-free, gpt-5-nano, nemotron-3-super-free, minimax-m2.5-free have all been retired from the free tier in past rotations). big-pickle is the permanent Zen flagship — always available, always free. PAI-Install/engine/provider-models.ts: - Zen quick/standard/advanced all → opencode/big-pickle Fresh installs generate a consistent, always-working opencode.json. Users who want per-role optimisation apply zen-free profile post-install: bun run .opencode/tools/switch-provider.ts zen-free PAI-Install/engine/steps-fresh.ts: - Remove misleading ANTHROPIC_API_KEY/OPENAI_API_KEY comments from .env template Model provider auth goes through OpenCode's /connect flow (stored in OpenCode's own config, separate from this repo). The .env is for service keys that PAI tools read directly (e.g. voice API keys). PAI-Install/cli/install-wizard.ts: - Update post-install Next Steps to show the two-step provider setup: 1. /connect (in a running OpenCode session) — establishes credentials 2. switch-provider.ts — updates opencode.json agents Previously only mentioned 'switch-provider tool' without the /connect step. --- PAI-Install/cli/install-wizard.ts | 11 ++++++++++- PAI-Install/engine/provider-models.ts | 20 ++++++++++++++----- PAI-Install/engine/steps-fresh.ts | 12 ++++-------- opencode.json | 28 +++++++++++++-------------- 4 files changed, 43 insertions(+), 28 deletions(-) diff --git a/PAI-Install/cli/install-wizard.ts b/PAI-Install/cli/install-wizard.ts index ccb38168..95dc87a8 100644 --- a/PAI-Install/cli/install-wizard.ts +++ b/PAI-Install/cli/install-wizard.ts @@ -420,7 +420,16 @@ async function runFreshWizard(): Promise { print("Next steps:"); print(" 1. Open a new terminal or source your shell config file"); print(" 2. Run: pai"); - print(" 3. Optional: switch provider profiles via switch-provider tool"); + print(""); + print("To use a different model provider (optional):"); + print(" Step 1 — Connect the provider inside a running OpenCode session:"); + print(" /connect"); + print(" Step 2 — Update agent model assignments in opencode.json:"); + print(" bun run .opencode/tools/switch-provider.ts "); + print(" bun run .opencode/tools/switch-provider.ts --list (see available profiles)"); + print(""); + printInfo("Default: all agents use opencode/big-pickle (Zen free, no API key needed)."); + printInfo("Free model list: https://opencode.ai/docs/zen/"); } async function runMigrationWizard(): Promise { diff --git a/PAI-Install/engine/provider-models.ts b/PAI-Install/engine/provider-models.ts index b12edd76..c83be17b 100644 --- a/PAI-Install/engine/provider-models.ts +++ b/PAI-Install/engine/provider-models.ts @@ -26,11 +26,21 @@ export const PROVIDER_MODELS: Record = { advanced: "anthropic/claude-opus-4-6", }, zen: { - // OpenCode Zen — ALL FREE models (no API key required for out-of-box experience) - // Users upgrade to paid models post-install via /connect in OpenCode - quick: "opencode/gpt-5-nano", // FREE — fastest - standard: "opencode/qwen3.6-plus-free", // FREE — capable general/coding - advanced: "opencode/big-pickle", // FREE — Zen flagship, heaviest + // OpenCode Zen — big-pickle for ALL tiers at install time. + // + // Why big-pickle everywhere? Free model availability on Zen changes + // periodically (e.g. qwen3.6-plus-free and gpt-5-nano were removed in + // a past rotation). big-pickle is the permanent Zen flagship — always + // available, always free. Shipping a single model removes the risk of + // users hitting "model not found" errors out of the box. + // + // After install, users can optimise per-agent routing via: + // bun run .opencode/tools/switch-provider.ts zen-free + // which applies the zen-free profile with current model assignments. + // Check https://opencode.ai/docs/zen/ for the up-to-date free model list. + quick: "opencode/big-pickle", + standard: "opencode/big-pickle", + advanced: "opencode/big-pickle", }, openrouter: { quick: "openrouter/google/gemini-flash-1.5", diff --git a/PAI-Install/engine/steps-fresh.ts b/PAI-Install/engine/steps-fresh.ts index 0a8d0c75..4106dde0 100644 --- a/PAI-Install/engine/steps-fresh.ts +++ b/PAI-Install/engine/steps-fresh.ts @@ -262,15 +262,11 @@ export async function stepInstallPAI( let envContent = `# PAI-OpenCode Environment Variables # Generated by installer - DO NOT COMMIT THIS FILE # -# Provider API keys are configured via /connect inside OpenCode. -# OpenCode Zen (free models) works without any API key. +# Model provider authentication (Anthropic, Zen paid, OpenRouter, etc.) is +# managed by OpenCode via /connect — NOT via this file. OpenCode stores +# provider credentials in its own config, separate from this repository. # -# To add a premium provider, start OpenCode and run: -# /connect -# -# Or set keys here manually: -# ANTHROPIC_API_KEY=sk-ant-... -# OPENAI_API_KEY=sk-... +# This file is for service keys that PAI tools read directly (e.g. voice). # Optional: Enable experimental LSP code navigation tools # Uncomment to activate goToDefinition, findReferences, hover, callHierarchy diff --git a/opencode.json b/opencode.json index d05db532..56037a99 100644 --- a/opencode.json +++ b/opencode.json @@ -25,49 +25,49 @@ "model": "opencode/big-pickle" }, "Architect": { - "model": "opencode/qwen3.6-plus-free" + "model": "opencode/big-pickle" }, "Engineer": { - "model": "opencode/qwen3.6-plus-free" + "model": "opencode/big-pickle" }, "general": { - "model": "opencode/qwen3.6-plus-free" + "model": "opencode/big-pickle" }, "explore": { - "model": "opencode/gpt-5-nano" + "model": "opencode/big-pickle" }, "Intern": { - "model": "opencode/gpt-5-nano" + "model": "opencode/big-pickle" }, "Writer": { - "model": "opencode/qwen3.6-plus-free" + "model": "opencode/big-pickle" }, "DeepResearcher": { - "model": "opencode/nemotron-3-super-free" + "model": "opencode/big-pickle" }, "GeminiResearcher": { - "model": "opencode/minimax-m2.5-free" + "model": "opencode/big-pickle" }, "GrokResearcher": { - "model": "opencode/qwen3.6-plus-free" + "model": "opencode/big-pickle" }, "PerplexityResearcher": { "model": "opencode/big-pickle" }, "CodexResearcher": { - "model": "opencode/nemotron-3-super-free" + "model": "opencode/big-pickle" }, "QATester": { - "model": "opencode/qwen3.6-plus-free" + "model": "opencode/big-pickle" }, "Pentester": { - "model": "opencode/qwen3.6-plus-free" + "model": "opencode/big-pickle" }, "Designer": { - "model": "opencode/minimax-m2.5-free" + "model": "opencode/big-pickle" }, "Artist": { - "model": "opencode/minimax-m2.5-free" + "model": "opencode/big-pickle" } } } From 73be561ae8944db4d6fc5e7f187a1ca6cd7921a2 Mon Sep 17 00:00:00 2001 From: Steffen Zellmer <151627820+Steffen025@users.noreply.github.com> Date: Mon, 13 Apr 2026 13:55:54 +0200 Subject: [PATCH 7/7] fix(review): address CodeRabbit round 4 commentary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/PAI-ADAPTATIONS.md: - Plugin Simplification: clarify what 'before/after' means — the Algorithm content is what was removed, behavioral governance + user identity remain. 'User identity context only' was inaccurate; PAI/AISTEERINGRULES.md is behavioral governance, not user identity. - Skills System: replace '100% unchanged' + hardcoded '29 skills' with accurate prose. Content/format/triggers unchanged; discovery path DID change (bootstrap → native scanner via skills/PAI symlink). Removed hardcoded count (was 29, actual is 52+, number changes with each skill addition). PAI-Install/engine/steps-fresh.ts: - Headless install message: add Step 2 (switch-provider.ts) to the post- provider-config message so both install paths show the full two-step flow. - Remove providerApiKey → .env write block. Model provider auth goes through OpenCode's /connect credential store, not this repository's .env file. Voice service keys (read directly by PAI tools) are still written. --- PAI-Install/engine/steps-fresh.ts | 30 ++++++++++++++++++++++-------- docs/PAI-ADAPTATIONS.md | 13 +++++++------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/PAI-Install/engine/steps-fresh.ts b/PAI-Install/engine/steps-fresh.ts index 4106dde0..7bdd3b0f 100644 --- a/PAI-Install/engine/steps-fresh.ts +++ b/PAI-Install/engine/steps-fresh.ts @@ -273,13 +273,12 @@ export async function stepInstallPAI( # OPENCODE_EXPERIMENTAL_LSP_TOOL=true `; - // Write provider API key if one was provided (e.g. via CLI flags) - const providerApiKey = state.collected.apiKey || ""; - if (providerApiKey) { - const providerEnvVar = `${(state.collected.provider || "zen").toUpperCase()}_API_KEY`; - envContent += `${providerEnvVar}=${providerApiKey}\n`; - } - + // NOTE: Model provider API keys are NOT written to .env. + // Provider authentication is managed by OpenCode via /connect (stored in + // OpenCode's own credential store, outside this repository). + // Only voice service keys are written here — those are read directly by + // PAI tools, not by OpenCode's provider routing. + if (voiceEnvVar && state.collected.voiceApiKey) { envContent += `${voiceEnvVar}=${state.collected.voiceApiKey}\n`; if (state.collected.voiceProvider === "google") { @@ -541,7 +540,22 @@ export async function runFreshInstall( }); await emit({ event: "message", - content: "Using OpenCode Zen (free models) — no API key required.\n\n💡 To connect premium providers later, run `/connect` inside OpenCode.", + content: [ + "Using OpenCode Zen (free models) — no API key required.", + "", + "All agents default to opencode/big-pickle, which is always available on Zen free.", + "", + "To switch to a premium provider after install (two steps required):", + " Step 1 — Connect the provider inside a running OpenCode session:", + " /connect", + " (This stores credentials in OpenCode's own config — not in .env)", + " Step 2 — Update agent model assignments in opencode.json:", + " bun run .opencode/tools/switch-provider.ts ", + " bun run .opencode/tools/switch-provider.ts --list", + "", + "The default remains opencode/big-pickle (Zen free) until Step 2 is performed.", + "Free model list: https://opencode.ai/docs/zen/", + ].join("\n"), }); await emit({ event: "step_complete", step: "api-keys" }); diff --git a/docs/PAI-ADAPTATIONS.md b/docs/PAI-ADAPTATIONS.md index 8f6ff2f1..0b8fa4f2 100644 --- a/docs/PAI-ADAPTATIONS.md +++ b/docs/PAI-ADAPTATIONS.md @@ -136,12 +136,13 @@ export function fileLog(message: string, level = "info") { ### Skills System -**100% unchanged** — PAI skill discovery now uses the native OpenCode scanner via symlink (`skills/PAI → ../PAI`). OpenCode discovers `PAI/SKILL.md` automatically without any bootstrap. +**Skill content unchanged** — SKILL.md format, workflow files, and trigger logic are identical to v2.x. What changed is the *discovery path*: PAI now uses the native OpenCode scanner via the symlink `skills/PAI → ../PAI`, so OpenCode discovers `PAI/SKILL.md` automatically without any plugin intervention. -- All 29 skills work identically +- All skills work identically (invoke, trigger, and output behaviour unchanged) - SKILL.md format unchanged - Workflow files unchanged - Skill trigger logic unchanged +- Discovery path changed: bootstrap → native scanner via symlink ### Memory Structure @@ -222,12 +223,12 @@ The v3.0 release represents a fundamental shift in how PAI Core loads into conte **`pai-unified.ts` — What it does now:** ```text -Before: Bootstrap loading + User identity context -After: User identity context only +Before: Bootstrap loading (Algorithm + ISC + Capabilities) + behavioral governance + user identity +After: Behavioral governance + user identity context only (no Algorithm content) ``` -The plugin now loads only the pieces OpenCode's native system cannot provide: -- `PAI/AISTEERINGRULES.md` — behavioral governance (system-level) +The plugin now loads only the pieces OpenCode's native system cannot provide — files that are user-specific or cannot live in a committed skill file: +- `PAI/AISTEERINGRULES.md` — behavioral governance (system-level, applies to all sessions) - `PAI/USER/ABOUTME.md` — personal background - `PAI/USER/TELOS/TELOS.md` — goals and mission - `PAI/USER/DAIDENTITY.md` — DA name and personality