Dual-PAI:把 AI 模型变成个人助理的开源项目
johansenmeister/Dual-PAI
Dual-PAI 是一个基于 PAI(Personal AI Infrastructure)的开源项目,通过技能、智能体、规则和记忆把 AI 模型变成个人助理,可在 OpenCode 和 Claude Code 两个终端智能体程序间切换并共享技能、记忆与设置。
Install · Your computer, your data · Make it yours · Skills · Agents · The Algorithm · Memory · Security · Self-test · Keeping it current · For developers
An AI assistant that is truly yours. It knows your goals and your way of working, it remembers what it learned from you, it checks its own work before it says it is done, and every instruction it follows is a plain text file you can read and change. It runs only when you start it, and it cannot see your screen or follow what you do. You talk to it in ordinary language, and you do not need to be a programmer to start: one command installs it, and the setup is a conversation.
Some of what you can ask it, from the first day:
- "Research this for me." It sends several researchers out at once, checks every link they bring back, and tells you where the sources agree and where they do not.
- "Help me think this through." Take a decision apart from first principles, let a council of agents debate it, or have one attack your plan before reality does.
- "Turn this into a document." Notes become a Word document, a spreadsheet, a presentation or a PDF.
- "How is my home lab doing?" Once it has their keys, ask what runs on your Proxmox, TrueNAS or Docker hosts, and how they are doing.
- "Does this fit what I want?" Tell it your goals once, and it weighs advice against them.
- "3 - you missed the point." Rate an answer, and it notes what went wrong, so the next one is better.
PAI (Personal AI Infrastructure) is a set of skills, agents, rules and memory that turn an AI model into a personal assistant. Dual-PAI runs it in two agent programs in the terminal, and you can switch between them: OpenCode (any model provider, including free models) and Claude Code (Anthropic's, with a Claude subscription). Both share the same skills, memory and settings.
Everything PAI is lives in one git repository: this one, once you have cloned it. Git keeps the history, so every change can be seen and undone.
Tip
This is meant to become your AI. PAI ships with 52 ready-to-use skills and 17 agents, and every one of them is a plain text file you can read. Open them, change them, perfect them, write your own: see Make it yours.
The idea
PAI follows Daniel Miessler's view that the scaffolding around a model matters more than the model: skills it can call by name, memory that carries over from one session to the next, checks that stop what must not happen, and code wherever code can do the job. The six parts around the core in the banner each have a section below: Skills, Agents, The Algorithm, Memory, Security and Self-test.
Your computer, your data
Many AI apps want to live on your desktop: watch the screen, follow what you do, keep running in the background. PAI is built the other way round.
- It runs only when you start it. PAI is a command you type in a terminal.
When you end the session, it is gone: no background service, no autostart,
nothing that keeps running and watching. The one exception is the browser the
Browserskill uses for web pages. It can only be reached from your own computer, with a key that changes every session, and it closes itself 30 minutes after it was last used. - It cannot see your screen. There is no screen capture, no keylogger, and no access to your clipboard, microphone or camera. When a skill takes a screenshot, it is of a web page in its own browser.
- It acts as you, only while you are there. Inside a session it can read and
write files and run commands in your user account; that is what lets it do
real work. It never runs as administrator: the installer refuses to run as
root, and uses
sudoonly to install git and a few tools. Every command passes the security guard first. Claude Code asks you before it acts; OpenCode works without asking, and the guard blocks the dangerous commands and asks you about the risky ones. - You choose who sees what. What you write, and what the assistant reads to
answer you, goes to the model provider you chose, as with any AI assistant. The
files that describe you (
.opencode/PAI/USER/) go along at the start of each session, so keep them short and free of secrets. With thelocalprofile and Ollama, the model runs on your own computer and nothing leaves it. - PAI itself reports nothing. Its own code sends data only to the model
provider and to services you set up yourself. The engines are programs by their
own makers. Claude Code sends Anthropic usage metrics and crash reports by
default; by Anthropic's account they never include your code, prompts or file
paths. You can turn them off with
DISABLE_TELEMETRY=1andDISABLE_ERROR_REPORTING=1, in your shell or underenvin~/.claude/settings.json; the first also turns off Claude Code's Remote Control (Anthropic's page on data usage). OpenCode has no such setting, and we found no usage reporting in it. - Your memory stays with you. It is plain files in your copy of the repository. Nothing is uploaded unless you set up your own server and push to it.
Install
PAI runs on Linux, on macOS (tested on an Intel Mac with macOS 15; Apple Silicon is not tested yet), and on Windows through WSL2.
On Windows, get WSL2 first. WSL2, the Windows Subsystem for Linux, is
Microsoft's own way to run Linux inside Windows, and it takes a few minutes. Open
PowerShell as administrator, run wsl --install, and restart the computer. Then
open Ubuntu from the Start menu and choose a user name and password. That
Ubuntu window is where you run the install command below, not PowerShell.
Microsoft's guide: Install WSL.
You need no account and no API key to start. In a terminal (on macOS the Terminal app, on Windows the Ubuntu window):
curl -fsSL https://raw.githubusercontent.com/johansenmeister/dual-pai/main/install.sh | bashor, on a system with wget and no curl:
wget -qO- https://raw.githubusercontent.com/johansenmeister/dual-pai/main/install.sh | bashTo clone from somewhere else, such as your own fork, add -s -- --repo <git url>
after bash. If you have cloned already, run ./install.sh in the clone.
install.sh does the parts that have one right answer: it checks the system,
installs git, Bun and a few tools, clones PAI to ~/repos/pai (--dir picks
another folder), installs the two engines at the versions PAI is tested with,
and picks a free model. Then it opens OpenCode with the setup conversation.
The setup conversation
SETUP.md is a runbook the assistant follows with you, one step at a time:
your language, your assistant's name and personality, which engine and models
you want, your machines, the services it may use and their keys, where faults
and ideas go, and access from another computer. Each choice comes with a short
text on what it is, what it needs, what it costs and whether you can change it
later. You can skip steps and come back.
The free model is only for the setup; you choose your own in step 5. Your keys
go in ~/.opencode/.env, which you fill in yourself: the assistant is told never
to read it.
Every day
| Command | What it does |
|---|---|
pai |
starts PAI in OpenCode |
pai --claude |
starts PAI in Claude Code |
pai keys |
shows which keys are set, never their values |
pai doctor |
checks the installation and says what to fix; changes nothing |
guide/ has short guides for you: where things are, API keys, faults and
improvements, your own Gitea, engine and model updates, remote access and your
goals (TELOS). Start with guide/README.md.
Make it yours
The setup conversation tells PAI who it is and who you are. This is about what comes after: PAI works from the first session, but it is not meant to stay the way it ships. Nothing is hidden in a binary or behind a service: the instructions, the skills, the agents and the Algorithm are Markdown, and the hooks and tools are TypeScript, all in this repository. Read them to see how your assistant thinks, and change them where it does not think the way you do.
- Read a skill before you lean on it. Each one is a
SKILL.mdand a few workflows in.opencode/skills/. Where it works differently from how you work, change it, or put aPREFERENCES.mdin.opencode/PAI/USER/SKILLCUSTOMIZATIONS/<Skill>/to adjust it without touching the skill (41 of the skills read it). - Write your own. Ask your assistant to "create a skill" for something you do
often;
CreateSkillwrites it in the same structure. A personal skill gets a leading underscore (_MYSKILL), so it stands apart from the ones PAI ships. An agent is one Markdown file in.opencode/agents/. - Let it learn, and help it. Rate answers, and now and then read what it
wrote in
.opencode/MEMORY/LEARNING/. A lesson that keeps coming back belongs inAISTEERINGRULES.mdor in a skill. - Experiment without fear. It is all in git: commit before you try something,
and any change can be seen and undone. If you change PAI's own files (outside
USER/) and latergit pullan update, the two can meet as a merge conflict, so for what you want to keep, prefer a skill of your own or the files underUSER/. guide/where-things-are.md shows which files are which.
After you change skills or agents, pai claude sync brings them to Claude Code.
Skills
PAI ships with 52 skills, ready to use from the first session, in
.opencode/skills/. A skill is a folder the assistant loads when a task matches
it: a SKILL.md that says when to use it ("USE WHEN …") and what to do,
workflows for the steps, and tools written as code. Two are always
loaded, PAI (how the system itself works) and Research; the other 50 are
loaded only when a task calls for them, so they cost no context until then.
Some of them:
| Group | Skills |
|---|---|
| Thinking | FirstPrinciples, Council (agents debate a question), RedTeam, SystemsThinking, RootCauseAnalysis, Science, BeCreative, IterativeDepth |
| Security | ThreatModel, WebAssessment (with OSINT), PromptInjection, Recon, SECUpdates |
| Research and content | Research, ExtractWisdom, Fabric (over 200 prompt patterns), Parser |
| Documents | Docx, Xlsx, Pptx, Pdf |
| Building | CreateSkill, CreateCLI, CodeReview, Evals, Hardening, Prompting, Browser, Cloudflare |
| Infrastructure | Proxmox, TrueNAS, Technitium, DockerPortainer |
| PAI itself | Agents, Telos (your goals), Delegation, System, HarnessUpdate, ModelUpdate |
Under Claude Code the same skills appear through a generated mirror
(pai claude sync). To change one or write your own, see
Make it yours.
Agents
PAI hands parts of a task to agents: specialists with their own instructions,
which the assistant starts in parallel and whose results it checks. There are
17. In OpenCode each one has its own model, set by a profile in
.opencode/profiles/ (zen, fireworks, anthropic, openai, local and
more), so a coding agent can run on a coding model and a quick lookup on a cheap,
fast one: bun .opencode/tools/switch-provider.ts <profile>.
| Agent | What it does | |
|---|---|---|
| Build | Architect | system design, specs and implementation plans |
| Engineer | implementation, test first | |
| Designer | UX and UI design | |
| Artist | images: the prompt and the image model | |
| Writer | documentation and technical writing | |
| Check | QATester | checks that it actually works before anything is called done |
| UIReviewer | runs user stories in a browser, with screenshots, and reports pass or fail | |
| Pentester | security assessments, for the WebAssessment skill |
|
| General | Algorithm | writes and sharpens the Algorithm's Ideal State Criteria |
| Intern | a high-agency generalist for broad problems | |
| BrowserAgent | headless browser work in parallel: scraping, forms, screenshots | |
| Research | DeepResearcher | thorough investigations, on the main profile's model |
| ClaudeResearcher, GrokResearcher, CodexResearcher, GeminiResearcher, PerplexityResearcher | one model family each, see Multi-research |
The Agents skill composes new agents from traits, and Council in the
Thinking skill lets agents debate a question.
Under Claude Code, twelve of them run on Claude models. The five provider researchers are OpenCode only: under one subscription they would have no model of their own, and DeepResearcher covers the role.
Multi-research
The researcher agents exist for one reason: to look at the same question through
different model families. Five researchers on one model is one opinion five
times. The Research skill splits a question into angles and sends them out in
parallel. Every URL the researchers return is fetched before it is used, since
research agents invent links. The answer is a synthesis: where the sources agree,
where they differ, and where each claim comes from.
| Mode | Say | Researchers |
|---|---|---|
| Quick | "research X" | one |
| Standard | "standard research", "research X from multiple angles" | three in parallel |
| Extensive | "extensive research" | four to five model families, several angles each; asks before it starts |
Without extra keys, every researcher runs on the main profile's model. Connect
the providers with /connect, or put ANTHROPIC_API_KEY, XAI_API_KEY and
FIREWORKS_API_KEY in ~/.opencode/.env, and switch with --multi-research:
bun .opencode/tools/switch-provider.ts fireworks --multi-research
bun .opencode/tools/switch-provider.ts --researchers # which model each researcher runsA researcher whose provider has no key falls back to the main profile, and
--researchers shows it. The names are older than the routing: GeminiResearcher
and PerplexityResearcher run Qwen Max and DeepSeek Flash on Fireworks, which gives
real model diversity with one key. With a Google or Perplexity key, one line in
.opencode/profiles/researchers.yaml moves each back to its own provider.
The Algorithm
The Algorithm is how PAI works through a task that needs more than a quick
answer. It is Daniel Miessler's, and has its own repository:
danielmiessler/TheAlgorithm.
The version PAI runs is in .opencode/PAI/Algorithm/.
It moves a task from the current state to an ideal state that can be checked, in seven phases: OBSERVE (what is asked, and the criteria that will show it is done), THINK, PLAN, BUILD, EXECUTE, VERIFY (evidence for each criterion) and LEARN (a reflection on the run, saved to memory). Five effort levels set the time budget and how many criteria a task needs, from Standard (under two minutes) to Comprehensive (up to two hours). The criteria and their status are kept in a PRD.
The PRD
PRD stands for Product Requirements Document, a term borrowed from software
teams. In PAI it is one Markdown file per task, PRD.md in the task's folder
under .opencode/MEMORY/WORK/, and it holds the task's state, so the state does
not live only in the conversation. The assistant writes it; the hooks only read
it. It has up to four sections:
- Context: what was asked for and what was not, the constraints and the risks.
- Criteria: the Ideal State Criteria, as checkboxes. Each one is a single end
state that is either true or false: "
bun testpasses with no failures", not "run the tests". Anti-criteria (ISC-A-) say what must not happen. Larger tasks need more of them: at least 8 at the standard effort level, 64 at the highest. - Decisions: the choices that were not obvious, with the reason.
- Verification: the evidence for each criterion, written in the VERIFY phase.
The frontmatter keeps the status, the current phase and the progress. Because it is a file, a task outlives the conversation: when the context is compacted, the criteria and their status go into the summary, and a follow-up on the same task continues the same PRD. An excerpt:
--- id: PRD-20261006-readme-diagrams title: "Add diagrams to the public README" status: ACTIVE effort_level: Standard last_phase: EXECUTE verification_summary: "2/8" --- ## Criteria - [x] ISC-1: README shows the architecture diagram above the install section - [x] ISC-2: The banner image path resolves in the public copy - [ ] ISC-3: Diagrams stay readable in GitHub's light and dark themes - [ ] ISC-A-1: No upstream image describes features this repository lacks
Memory
PAI's memory is plain files in .opencode/MEMORY/, so you can read, change or
delete anything in it. Everything except STATE/ is in git with the rest of the
repository, so you can also see how it changed.
| Folder | What is there |
|---|---|
WORK/ |
one folder per task, with its PRD |
LEARNING/ |
ratings, the reflection from each Algorithm run, and notes on what went wrong |
WISDOM/ |
rules learned per domain (development, architecture, security …), read in OBSERVE and extended in LEARN |
RESEARCH/ |
what the researcher agents found |
STATE/ |
runtime state and the security log; stays on the machine |
It learns from you in two ways. Send a number from 1 to 10 (8, 3/10, or
4 - missed the point) and it is saved as a rating of the last answer; without
one, the tone of your messages is read as an implicit rating. A low rating also
writes a note on what went wrong. bun .opencode/PAI/Tools/MineReflections.ts --dry-run looks for patterns across the reflections.
Security
Every tool call passes through the security guard in pai-core before it runs,
under both engines:
- Prompt injection. The text in each tool call is checked against patterns in seven categories (instruction override, role hijacking, system prompt extraction, safety bypass, context separators, MCP tool injection and credential leaks). A match is blocked.
- Shell commands. The guard matches what the shell will actually run, not
text it only writes to a file. Destructive commands such as
rm -rf /are blocked; risky ones such asgit push --forceandgit reset --hardask you first. The message names the pattern that matched. - Credentials. Writes to
.envfiles,~/.ssh/, GnuPG, cloud and cluster credentials, private keys and/etc/are blocked outright: those are changed by hand. Your keys live in~/.opencode/.env: the assistant is told never to read it, the guard blockscaton it, andpai keysshows which keys are set, never their values. - Secrets in output. Whatever a tool prints is checked before the assistant
sees it, under both engines. A key from your
~/.opencode/.env, a password in a URL, anAuthorizationheader, a private key or a known token format is replaced with a mark like[MASKED:GITEA_TOKEN]: the model knows something was there, but never sees it. PAI's own logs and memory files are masked the same way. A key you paste into a prompt yourself still reaches the model, so rotate it then. - The log. Every decision is written to a security log in
STATE/(see Memory), with secrets redacted, including each masking.
The Security skills go the other way: threat models, web assessments with
OSINT, recon and prompt-injection testing of your own systems.
Self-test
Neither engine says so when a hook PAI depends on stops firing, and an engine update can change what PAI assumes about it. So PAI checks itself:
- At every start. Both engines are pinned to an exact version in
.opencode/package.json, and each has a smoke test that runs the real engine with PAI and writes a receipt when every hook has fired and the security guard has blocked what it should:bun Tools/V2Smoke.tsandbun Tools/ClaudeSmoke.ts.paiandpai --claudecheck that the installed engine is the pinned one, and that the smoke test passed against it and against the adapter code as it is now. If not, you get one line with the command that fixes it; otherwise nothing. - On demand.
pai doctorruns the same checks and the slower ones: the model registry, the MCP servers, handlers that ran without effect, and the database. It changes nothing. - Before an update. The
HarnessUpdateskill checks a new engine version before it comes in: see Keeping it current.
Keeping it current
The engines are pinned on purpose. PAI hooks into each engine's events, tool
names and data, and a new version can change one of them without any error:
nothing crashes, PAI just stops guarding or remembering something. That has
happened: one Claude Code version cut the context PAI gives every session to its
first 2 KB, and nothing said so. So a new engine version comes in only after it
has been checked, and claude update, opencode upgrade and the engines' own
update prompts are never used: they move past the pin without the check.
pai version shows the pinned and the installed version of each.
Two skills keep PAI current. Run them yourself now and then; neither runs on a timer, and neither changes anything without you:
| Say | Skill | What it does |
|---|---|---|
| "check the engines for updates" | HarnessUpdate |
reads what changed in a new engine version against what PAI depends on, and sorts each change into "may break", "fixes something" or "harmless". It never upgrades; if you go ahead, the smoke test decides. guide/engine-updates.md |
| "update models" | ModelUpdate |
checks every model ID in the OpenCode profiles against OpenCode's model registry, so a retired model is replaced before an agent fails, and weighs new models per role. The change is a branch you merge. guide/model-updates.md |
For developers
Parts of the code comments and the skills are in Norwegian: they come from the repository PAI is copied from.
.opencode/pai-core/ |
The engine-independent core. Never imports an engine SDK |
.opencode/pai-adapters/opencode-v2/ |
The OpenCode side, with OpenCode pinned exactly in .opencode/package.json |
claude-plugin/ |
The Claude Code side: hooks, MCP server, a generated mirror of the skills and agents |
.opencode/skills/ |
The 52 skills, including Infrastructure/ |
.opencode/skills/Utilities/HarnessUpdate/AssumptionRegister.md |
What PAI assumes about each engine, and what the smoke tests guard |
bun install (cd .opencode && bun install) # the OpenCode binary and types, about 780 MB bun test # green out of the box bun run lint
Both installs are needed: the one in the root does not cover .opencode/, and
without it the type check in bun test has neither the binary nor the types.
The self-test and the smoke tests are described under Self-test. A bump of either engine is not done until its smoke test is green.
Three things worth knowing before you change anything:
- The core never imports an engine SDK. The two-engine design rests on
pai-core/taking primitives and returning data. A grep forharness ===should hit onlyruntime.tsand the adapters, and a test guards it. claude-plugin/skills/andclaude-plugin/agents/are generated. Edit the source under.opencode/skills/and runpai claude sync. The mirror is tracked in git and built from what git tracks, not from what is on disk, and a test fails if it drifts from the source.- The hook binary is a build artefact.
bin/pai-hookfalls back to the source when it is missing, so everything works without it, only slower.pai claude syncbuilds it.
Where this comes from
PAI is Daniel Miessler's Personal AI Infrastructure. This repository started as a fork of Steffen's port to OpenCode, pai-opencode, and has since been rebuilt around two engines. OpenCode is by Anomaly.
This project grew out of my own need for a framework that fits my own way of working, above all documenting and auditing a homelab and work IT environment: what runs where, what changed, and why. It has reached a level of maturity where I'm confident in sharing it with the open-source community.
MIT License, see LICENSE.
来源:GitHub · github.com