dsh-doctor
asdf17128
Find what your DeepSeek Harness (dsh) patches silently broke — dead patches, config fields dropped by whole-config repla…
PROJECT TOPICS
PROJECT README
A minimal terminal coding agent harness in Go — a sibling to Pi.
mcp_list / mcp_inspect / mcp_call) to discover and call on demand. Same Gate / Ask / Hooks path as built-in tools. See MCP

Install the latest release (macOS / Linux):
curl -fsSL https://raw.githubusercontent.com/pulseaiclub/phi/main/scripts/install.sh | bash
Windows (PowerShell 5.1+):
irm https://raw.githubusercontent.com/pulseaiclub/phi/main/scripts/install.ps1 | iex
First launch needs a model. Open the config editor (creates ~/.phi layout
and writes ~/.phi/config.yaml):
phi config
Or set env vars for a one-off run:
export PHI_MODEL=gpt-4o
export PHI_API_KEY=sk-...
Then start the TUI:
phi
Or build from source (Go 1.26.3+, see go.mod):
make build # produces ./phi
make install # build and install into $GOBIN
On first start, phi automatically creates ~/.phi/{bin,skills,hooks,session}. Search
tools (fd, rg) download into ~/.phi/bin in the background when missing.
The TUI gives the model four core tools — read, write, edit, and
bash — plus grep, glob, list, and fetch. The model uses these to
fulfill your requests.
phi aims to stay cheap to run and cheap to hack on. Numbers below are for a
stripped release build (CGO_ENABLED=0, -ldflags="-s -w"), measured on
macOS arm64 unless noted.
| Metric | phi |
|---|---|
| Release binary | ~12 MB |
| Idle RSS (1 session) | ~21 MB |
| 10 idle sessions (total RSS) | ~196 MB (~20 MB each) |
| Time to first frame | ~40 ms (27–65 ms) |
Cold go build (empty GOCACHE) |
~5.5 s |
| Warm rebuild | ~0.7 s |
| Go source (excl. tests) | ~22k LOC / 107 files |
| Go packages | 32 |
| Direct module deps | 6 (15 modules total) |
| Linked runtimes | system libs only (no Node / Electron / Python) |
phi reads ~/.phi/config.yaml (standard YAML). Environment variables
override it for one-off runs. phi config opens an HTML editor for the same
file in your browser.

# ~/.phi/config.yaml
models:
- name: gpt-4o # model name; "claude-*" routes to the Anthropic API
api_key: sk-... # or set PHI_API_KEY
base_url: https://api.openai.com/v1 # default; PHI_BASE_URL overrides
context_window: 128000 # optional
default: true # the model used at startup; first entry wins if absent
- name: claude-sonnet-4-20250514 # extra models; switchable at runtime
api_key: sk-ant-...
base_url: https://api.anthropic.com
context_window: 200000
skill_path: ~/.phi/skills # where SKILL.md files are loaded from
agents:
enabled: true # default; set false to disable agent_* sub-agent tools
permissions:
mode: interactive # interactive | readonly | autopilot | headless-strict
bash:
default: ask # ask | allow | deny
allow:
- "go test ./..."
deny:
- "rm -rf *"
fetch:
default: allow
allowed_hosts:
- "github.com"
Environment overrides:
| Variable | Overrides |
|---|---|
PHI_API_KEY |
models[].api_key (default model) |
PHI_MODEL |
models[].name (default model) |
PHI_BASE_URL |
models[].base_url (default model) |
PHI_SKILL_PATH |
skill_path |
Provider routing: a base URL containing anthropic or a model name starting
with claude uses the Anthropic Messages API; everything else uses the
OpenAI-compatible /chat/completions path.
~/.phi/
├── config.yaml # global configuration
├── bin/ # downloaded search tools (fd, ripgrep)
├── skills/ # SKILL.md skill directories
├── hooks/ # tool-loop hook scripts (hook.json + run)
├── jobs/ # sub-agent job artifacts (meta, logs, result.md)
└── session/ # persisted sessions, one dir per working directory
└── <encoded-cwd>/
phi (or phi tui) starts the TUI: a chat transcript on top, an editor at
the bottom, and a footer with the current activity. When a newer release is
available, the footer shows a hint like 0.2.0 available · phi update.
Assistant output is rendered as Markdown (CommonMark/GFM): headings, emphasis,
strikethrough, links, blockquotes, lists, task checkboxes, and tables are
styled with the active theme; fenced code blocks get a frame and per-language
syntax highlighting. Structural markers (#, `, *) are stripped.
The editor supports:
@ — fuzzy file mention picker (type @ and start typing a path)/ — slash command picker (/sessions, /resume, /clear)!command — run a shell command locally and stream its output into the
transcript (see Commands)Ctrl+K — command palette: settings → model / theme / permissions / agents, skills, hooks| Key | Action |
|---|---|
Ctrl+C |
Quit phi |
Esc |
Cancel the running agent / close pickers |
Ctrl+K |
Toggle the command palette |
Ctrl+Shift+C |
Copy the selected transcript text |
Themes: Dark, Darcula, Pink, and Terminal (default), switchable from
the palette under settings → theme.
| Command | Description |
|---|---|
phi / phi tui |
Start the interactive TUI |
phi run -p "…" |
Run one agent loop headlessly (see below) |
phi update |
Download and install the latest GitHub release |
phi update --check |
Query the latest release without installing |
phi sessions list |
List persisted sessions for this directory |
/sessions |
List sessions for this directory (TUI) |
/resume <id> |
Resume a session by id or unique prefix (TUI) |
/clear |
Start a fresh empty session (TUI) |
!command |
Run a shell command locally, stream output into the transcript; Esc cancels it |
In the TUI, !command runs locally via bash -c — outside the agent loop. It
doesn't count toward agent busy state, and the running command can be cancelled
with Esc without touching an in-flight agent turn.
Sessions persist automatically per working directory under
~/.phi/session/<encoded-cwd>/ as JSONL trajectories.
phi sessions list — list session id, mtime, and preview for the current
directory/sessions in the TUI — same, in-app/resume <id> — continue a session (id or unique prefix)/clear — start a fresh session (new id, empty transcript)phi run --session <id> / phi run --continue-last — resume headlesslyphi run -p "fix the failing test in internal/tools"
Runs one agent loop without a TUI. Human logs go to stderr; with --jsonl,
machine-readable events go to stdout, one JSON object per line.
Flags:
| Flag | Description |
|---|---|
-p, --prompt STRING |
Prompt to run (required) |
--jsonl |
Emit JSONL events to stdout |
--max-rounds N |
Cap tool rounds (default 64) |
--timeout DURATION |
Limit the agent run wall-clock time (e.g. 10m; disabled by default) |
--session ID |
Resume a persisted session by id or unique prefix |
--continue-last |
Resume the newest persisted session for this directory |
--session-dir DIR |
Override the session storage directory |
Exit codes: 0 success · 1 runtime/LLM error · 2 max rounds reached ·
3 config/usage error.
In the interactive TUI, exhausting the tool-round budget prompts Continue /
Stop. Headless phi run has no confirmation UI, so it exits with code 2.
In headless mode, permission ask decisions are denied (there is no approval
UI), so readonly-style safety applies without extra flags.
Skills are directories containing a SKILL.md file with YAML frontmatter and
a Markdown body. They are loaded from ~/.phi/skills/ (or skill_path /
PHI_SKILL_PATH) and injected into the agent's context, letting you give the
model reusable procedures:
---
name: My Skill
description: What this skill does
license: MIT
compatibility: claude, openai
---
Instructions the agent should follow when this skill is relevant.
In the TUI, add skills from the palette (skills → list), then submit the message with the selected skills applied.
Tool execution is gated by a permission policy, so the agent can run read-only
by default and ask before anything destructive. Configure it under
permissions: in ~/.phi/config.yaml.
Modes:
| Mode | Behavior |
|---|---|
interactive |
Default. ask decisions prompt in the TUI. |
readonly |
Deny writes / bash; read tools still work. |
autopilot |
Fold ask → allow, run unattended. |
headless-strict |
Fold ask → deny (used by phi run). |
Per-tool rules: bash.default / bash.allow / bash.deny (exact command
prefix matching) and fetch.default / fetch.allowed_hosts. Global keys:
workspace_only_writes (default true), ask_timeout_sec, and
dangerously_allow_all (default false).
In the TUI, an approval dialog replaces the editor with options to approve, deny with feedback, or allow all for the session / for every session. The palette's settings → permissions entry toggles session-wide bypass.
Hooks run custom logic around each tool call — before the permission gate and
after execution. Use them for organization policy, audit trails, or rewriting
tool input, without changing phi's binary or config.yaml.
Each hook is a directory containing a hook.json manifest and an executable:
{
"name": "guard-bash",
"event": "pre_tool",
"match": "bash",
"run": "./run.sh",
"fail_closed": true
}
Hooks load from ~/.phi/hooks/ and <cwd>/.phi/hooks/; a project hook with
the same name replaces the user hook. In the TUI, list or reload them via
Ctrl+K → hooks. In readonly permission mode, only fail_closed hooks run
so slow audit hooks don't stall exploration. Full guide:
doc/hooks.md.
Configure 100 MCP servers. Pay ~0 schema tokens until you call one.
Most MCP hosts dump every tools/list schema into the model context before
you ask a question — browser stacks alone can burn 50k+ tokens. phi does not.
Instead the agent gets three meta-tools, and the system prompt lists configured server names (no schemas):
| Tool | Role |
|---|---|
mcp_list |
List servers, or tool names on one server (compact text) |
mcp_inspect |
Fetch a slim parameter summary for one tool |
mcp_call |
Run server + tool + args |
Flow: pick a server from the prompt → mcp_list(server=…) → mcp_inspect → mcp_call. Subprocesses start lazily on first use.
Calls still go through PreHooks → Gate / Ask → Run → PostHooks.
phi mcp add browsermcp -- npx @browsermcp/mcp@latest
phi mcp doctor
# In the TUI, the model can use configured servers without guessing MCP exists
Config: ~/.phi/mcp.json (project <cwd>/.phi/mcp.json overrides by name).
Disable with PHI_MCP=off. Stdio and HTTP in v1.
Full guide: doc/mcp.md.
Sub-agent tools (agent_spawn, agent_task, …) are on by default. To
keep a session lean, disable them in ~/.phi/config.yaml:
agents:
enabled: false
Or toggle for the current session via the palette: settings → agents. When disabled, those tools are not registered and the model cannot spawn jobs.
Sub-agents themselves use a role (explore default | review | worker):
| Role | Tools | Use for |
|---|---|---|
explore |
read-only (+ allowlisted bash) | Search / map structure |
review |
read-only (+ allowlisted bash) | Diffs / checks; no edits |
worker |
full tools except nesting | Planned, independent edits |
Default stays explore (read-only). Prefer worker only after the parent has a concrete plan.
Built-in tools the model can call (see internal/tools/):
| Tool | Purpose |
|---|---|
bash |
Run a shell command in the working directory |
read |
Read a file |
write |
Write a file (gated by permissions) |
edit |
Targeted edit of a file |
grep |
Regex search across files |
glob |
File patterns |
list |
Directory listing |
fetch |
HTTP fetch (host-gated by permissions) |
agent_spawn |
Start an isolated sub-agent job (async) |
agent_task |
Spawn + wait for one sub-agent summary |
agent_wait |
Wait for a job; returns short summary only |
agent_list |
List jobs |
agent_log |
Tail a job's event log |
agent_cancel |
Cancel a running job |
Sub-agent transcripts live under ~/.phi/jobs/<id>/ and are not injected
into the parent context — only the wait/task summary is.
Fast search tools (fd, ripgrep) are downloaded on first startup into
~/.phi/bin when missing.
See Project layout for the source tree map.
See CONTRIBUTING.md for development setup, code style, and commit conventions.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。