# pi-morphllm-plugin

<p align="center">
  <img src="./images/morph-plugin-icon.svg" alt="Pi Morph plugin icon" width="160" height="160" />
</p>

Pi runtime extension package that integrates Morph into Pi as real tools, commands, hooks, and compaction behavior, with file-based configuration for consistent setup.

This package is not just a prompt snippet or a thin skill wrapper. It installs as a normal Pi package and runs an active extension that registers Morph tools, lifecycle hooks, status commands, and Morph-first routing guidance.

## What This Plugin Does

`pi-morphllm-plugin` makes Morph a practical part of everyday Pi coding sessions.

Use it when you want Pi to:

- apply larger or multi-location edits through `morph_fastapply`
- search the current workspace with natural language through `warpgrep_codebase_search`
- inspect public GitHub repositories through `warpgrep_github_search`
- test direct URLs through `morph_browser_test_url`
- test GitHub pull request previews through `morph_preview_test_pr`
- compact long conversations with Morph before falling back to Pi
- keep Morph configuration in one JSON file instead of scattered runtime environment variables

In short, this plugin turns Morph into a real Pi runtime integration instead of a manual workflow.

## When To Use It

This plugin is a good fit if you use Pi regularly and want Morph to be part of your default workflow, especially for:

- refactoring or editing larger files safely
- searching unfamiliar codebases with natural language
- inspecting public GitHub repositories without cloning them locally
- validating direct URLs with Morph-managed browser automation
- validating PR preview deployments with CI-style browser checks
- reducing long session context with Morph compaction
- standardizing Morph setup across projects with file-based config

If you only need a one-off note about Morph or a manual prompt pattern, this package is probably more than you need.

## Features

- `morph_fastapply` for Fast Apply style file merging with dry-run preview
- `morph_context_research` for WarpGrep collection plus Fast Model research briefs
- `warpgrep_codebase_search` for local exploratory code search
- `warpgrep_github_search` for public GitHub source lookup
- `morph_browser_test_url` for generic URL testing with Morph browser automation
- `morph_preview_test_pr` for PR-native preview testing with GitHub context and browser automation
- Morph compaction integration for large conversations
- File-based `baseUrl` / `baseApi` endpoint configuration
- Single-key and multiple-key Morph API configuration
- Prompt-level Morph-first routing guidance without blocking native Pi tools
- Bundled skills for power users, reviewers, and PR testing workflows

## Agent-Facing Tool Guide

This section is written for Pi, Codex-style coding agents, and humans who want predictable tool selection.

### What the plugin adds

This plugin adds seven practical Morph capabilities to Pi:

- existing-file editing through `morph_fastapply`
- distilled context research through `morph_context_research`
- local codebase exploration through `warpgrep_codebase_search`
- public GitHub source exploration through `warpgrep_github_search`
- browser automation for direct URLs through `morph_browser_test_url`
- PR-aware preview validation through `morph_preview_test_pr`
- session compaction through Morph compact integration

### Choose the right tool

- Existing file, non-trivial edit -> `morph_fastapply`
- Existing file, tiny exact one-block replacement -> native `edit`
- Brand new file -> native `write`
- Broad audit/explanation/planning before reading many files -> `morph_context_research`
- Local codebase question -> `warpgrep_codebase_search`
- Quick local codebase location question -> `warpgrep_codebase_search`
- Public GitHub source question -> `warpgrep_github_search`
- Direct URL or visible UI flow -> `morph_browser_test_url`
- PR preview with GitHub context -> `morph_preview_test_pr`
- Long session context reduction -> Morph compaction

### FastApply guidance

Use `morph_fastapply` when all of the following are true:

- the file already exists
- the edit is non-trivial: file over about 80 lines, block-shape change, multi-location edit, refactor, or brittle exact replacement
- you have already read the file
- you can provide exact marker-wrapped anchor snippets

Do not use `morph_fastapply` when:

- the file is brand new
- the change is a tiny exact one-block replacement that native `edit` handles cleanly
- you have not read the file yet
- you only have feature intent and not actual `code_edit` anchor snippets

When using `morph_fastapply`:

- read the target file first
- provide `code_edit` with `// ... existing code ...` markers
- use `dry_run: true` first for risky merges or large files
- tighten anchors once if anchor quality is weak, then fall back to native `edit` only for tiny exact replacements or `write` for new files

### Context research guidance

Use `morph_context_research` for broad questions where the agent would otherwise read many files, such as audits, architecture explanations, debugging investigations, and planning. It collects contexts with WarpGrep, distills them with Fast Model when enabled, and falls back to local `rg` evidence when Morph keys are blocked, invalid, or rate-limited.

The output is a compact brief with direct answer, mechanism, relevant files, evidence, risks, recommended next reads, and explicit fallback notes. `context-research` means WarpGrep collection plus Fast Model distillation succeeded; `local-fallback` means Morph collection or distillation was unavailable and the tool returned lower-confidence local evidence. For edits, still read target files before modifying them.

Use `warpgrep_codebase_search` for quicker exploratory local questions such as:

- where a feature lives
- how routing works
- which files own a behavior
- where an API call is made
- which components, handlers, or imports are involved
- how a workflow is wired across the repo

Use `warpgrep_github_search` for the same style of question, but only for public GitHub source questions outside the local workspace.

### Browser testing guidance

Use `morph_browser_test_url` when you already have a concrete URL and want Morph to:

- navigate pages
- click through flows
- smoke test a page
- verify visible UI behavior
- capture recording artifacts

Use `morph_preview_test_pr` when the task is specifically about a GitHub pull request and you want:

- PR context lookup
- preview deployment discovery
- browser validation against the preview
- check-run updates
- optional PR comments with artifacts

### Fallback rules

If Morph is unavailable or a Morph call is not a good fit, use native Pi tools instead.

- `morph_fastapply` -> tighten anchors once, then fall back to native `edit` only for tiny exact replacements or native `write` for new files
- `warpgrep_codebase_search` -> fall back to `bash` with `rg`, then read matching files
- `warpgrep_github_search` -> fall back to GitHub search/file tools or web search
- browser tools -> fall back to manual browser tools when Morph credentials or preview integration are missing

## Install

### From npm

```bash
pi install npm:pi-morphllm-plugin
```

This is the main installation path and does not require cloning the repository.

### For the current run only

```bash
pi -e npm:pi-morphllm-plugin
```

This loads the package temporarily for the current Pi run without adding it to your installed package list.

### For local development

```bash
pi -e .
```

This loads the package manifest from `package.json`, so Pi discovers the Morph extension, bundled prompt resource, and skills.

### Compiled extension development shortcut

```bash
npm run build
pi -e ./dist/extensions/morph/index.js
```

Use the compiled extension entrypoint directly only when you intentionally want to test the extension file by itself. That path does not load the package prompt resource from `prompts/morph-tools.md` or bundled skills.

## Editing Policy

Pi Morph uses guidance, not runtime blocking.

- Read the target file before choosing between native `edit` and `morph_fastapply`.
- Tiny exact one-block replacements should use native `edit`; non-trivial existing-file edits should use `morph_fastapply` after reading the file.
- Brand new files should use native `write`.
- Files over about 80 lines, multi-location edits, block-shape changes, refactors, and brittle exact replacements should use `morph_fastapply` after preparing marker-wrapped anchor snippets.
- If the model chooses a workable native tool anyway, the extension allows it and continues instead of blocking.

## Config

Pi Morph uses JSON config files only.

Config lookup order:

- `~/.pi/morph/morph.json` as the default global Pi config
- `.pi/morph.json` in the current project
- `morph.config.json` in the current project

The recommended setup is global config in `~/.pi/morph/morph.json`.
Pi Morph auto-creates that file on first runtime load when no existing Morph config is found. Installing the package alone does not create it; the extension must actually be loaded by Pi.

A repo example is available at `.pi/morph.json.example`.
You can also create the global config manually:

```bash
mkdir -p ~/.pi/morph
cp .pi/morph.json.example ~/.pi/morph/morph.json
```

### Single API key

Example config:

```json
{
  "apiKey": "sk-...",
  "editEnabled": true,
  "warpgrepEnabled": true,
  "warpgrepGithubEnabled": true,
  "browserUrlEnabled": true,
  "autoCompactEnabled": true,
  "previewPrEnabled": true,
  "routing": {
    "editMode": "force",
    "codebaseSearchMode": "force",
    "githubSearchMode": "force",
    "fallbackToNativeTools": true
  },
  "statsDashboard": {
    "enabled": false,
    "host": "127.0.0.1",
    "port": 47631,
    "token": "",
    "autoOpen": true,
    "storagePath": "~/.pi/morph/morph-stats.jsonl",
    "retentionDays": 30
  },
  "fastModel": {
    "enabled": true,
    "model": "morph-qwen36-27b",
    "fallbackModels": ["morph-qwen35-397b"],
    "timeoutMs": 30000,
    "errorClassifierEnabled": true,
    "errorClassifierConfidenceThreshold": 0.8,
    "contextResearchDistillationEnabled": true,
    "repairSuggestionsEnabled": true
  },
  "compactContextThreshold": 0.85,
  "compactPreserveRecent": 1,
  "compactRatio": 0.3,
  "compactEmergencyThreshold": 1.0,
  "compactTargetThreshold": 0.5,
  "compactEmergencyRatio": 0.05,
  "compactMaxEmergencyPasses": 3,
  "compactMinimumTriggerRatio": 0.8,
  "compactQuery": "Keep the current user task, accepted constraints, recent decisions, file/code context, tool outputs, and next actionable steps. Remove greetings, repetition, and irrelevant detours.",
  "compactIncludeMarkers": true,
  "compactIncludeLineRanges": true,
  "timeoutMs": 30000,
  "warpGrepTimeoutMs": 60000,
  "compactTimeoutMs": 60000,
  "browserTimeoutMs": 300000,
  "browserRecordingTimeoutMs": 300000,
  "previewBrowserTimeoutMs": 300000
}
```

### Multiple API keys

Pi Morph also supports rotation across multiple Morph API keys.
Set `apiKey` to `"multiple"`, put keys in the fixed `~/.pi/morph/morph.env` file, and choose an `apiKeyStrategy`.
Only add `baseUrl` when you want to use a custom Morph-compatible endpoint.

HTTP proxy support is separate from API-key rotation. Set `proxyEnabled` to `true` and put one HTTP(S) proxy per line in the fixed `~/.pi/morph/morp_proxy.txt` file. When enabled, Morph API-key calls use the selected proxy. Multiple proxies support `round-robin` or `random` via `proxyStrategy`. Blank lines and `#` comments are ignored. Failed proxies are written to `~/.pi/morph/morph-proxies.json`, skipped during their cooldown, and the same operation retries with the next available proxy when possible.

Example `morp_proxy.txt`:

```txt
http://user:pass@38.154.203.95:5863/
http://user:pass@proxy-2.example:5863/
```

```json
{
  "apiKey": "multiple",
  "apiKeyStrategy": "round-robin"
}
```

Example `morph.env`:

```txt
sk-key-1
owner@example.com|sk-key-2
team|label|sk-key-3
```

Supported key-file line formats:

- `api-key` - plain key
- `email|api-key` - owner metadata plus key
- `name|label|api-key` - two metadata columns plus key

Supported strategies:

- `round-robin` - rotate sequentially between configured keys
- `random` - choose a random configured key for each client call

Invalid-key failover and quarantine:

- Auth failures such as `401`, `403`, `unauthorized`, `forbidden`, or `invalid api key` remove the failing key from fixed `~/.pi/morph/morph.env` and append the original line to `morph-invalid.env`.
- Invalid keys are not written to `morph-api-keys.json`; that state file is only for temporary rate-limit cooldowns.
- Same-call failover is built in: when one key fails authentication, Pi Morph retries the same operation with the next usable key in the configured list.
- Preferred or sticky keys still run first when applicable, then fall through to the remaining keys on auth failure.

The key file parser also ignores blank lines and `#` comments.
If a line contains pipe-delimited metadata like `email|sk-key`, Pi Morph uses the second field as the API key; with `name|label|sk-key`, it uses the third field.

All runtime settings come from JSON config files only.

Runtime config lives inside `morph.json`:

| Key | Default | Description |
| --- | --- | --- |
| `apiKey` | none | Morph API key, or `multiple` to load keys from fixed `~/.pi/morph/morph.env`. |
| `apiKeyStrategy` | `round-robin` | API key selection strategy for multiple keys: `round-robin` or `random`. |
| `proxyEnabled` | `false` | Enables HTTP proxy routing for Morph API calls. |
| `proxyStrategy` | `round-robin` | Proxy selection strategy when `~/.pi/morph/morp_proxy.txt` contains multiple proxies: `round-robin` or `random`. |
| `proxyStatePath` | `~/.pi/morph/morph-proxies.json` | Local state file used for temporary failed-proxy cooldowns. |
| `proxyFailureCooldownMs` | `600000` | Cooldown window in milliseconds before a failed proxy is retried; default is 10 minutes. |
| `apiKeyStatePath` | `~/.pi/morph/morph-api-keys.json` | Local state file used for temporary rate-limit cooldowns. Invalid auth keys are removed from `~/.pi/morph/morph.env` and archived instead of being tracked here. |
| `invalidApiKeyCooldownMs` | `Infinity` | Legacy compatibility field; confirmed auth-failed keys are now treated as permanently invalid and removed from the active pool instead of re-entering after a cooldown. |
| `editEnabled` | `true` | Registers `morph_fastapply`. |
| `warpgrepEnabled` | `true` | Registers `warpgrep_codebase_search`. |
| `warpgrepGithubEnabled` | `true` | Registers `warpgrep_github_search`. |
| `contextResearchEnabled` | `true` | Registers `morph_context_research`. |
| `contextResearchMaxContexts` | `12` | Default maximum contexts collected for a research brief. |
| `contextResearchMaxInputChars` | `80000` | Default evidence budget sent to Fast Model or local summary. |
| `contextResearchMaxOutputChars` | `6000` | Default Fast Model distillation output budget. |
| `browserUrlEnabled` | `true` | Registers `morph_browser_test_url` for generic URL browser testing. |
| `previewPrEnabled` | `true` | Registers `morph_preview_test_pr` for PR-native preview testing. |
| `autoCompactEnabled` | `true` | Enables Morph-first automatic compaction. |
| `allowReadonlyAgents` | `false` | Allows Morph mutating tools in readonly agents when `true`. |
| `profileRegistryPath` | `~/.pi/morph/morph-profiles.json` | Local sticky browser profile registry path. |
| `routing.editMode` | `force` | Prompt-level Morph edit guidance mode: `prefer`, `strong`, or `force`. |
| `routing.codebaseSearchMode` | `force` | Prompt-level local WarpGrep guidance mode: `prefer`, `strong`, or `force`. |
| `routing.githubSearchMode` | `force` | Prompt-level public GitHub WarpGrep guidance mode: `prefer`, `strong`, or `force`. |
| `routing.fallbackToNativeTools` | `true` | Makes Morph tool guidance explicitly fall back to native Pi tools on failure. |
| `compactContextThreshold` | `0.85` | Fraction of the model context window that triggers proactive Morph compaction. |
| `compactPreserveRecent` | `1` | Number of recent messages to keep out of Morph compaction input. |
| `compactRatio` | `0.3` | Normal Morph compression ratio, clamped to the API minimum `0.05`. |
| `compactEmergencyThreshold` | `1.0` | Multiplier of `compactContextThreshold` that switches compaction into emergency mode. |
| `compactTargetThreshold` | `0.5` | Emergency target as a fraction of the model context window. |
| `compactEmergencyRatio` | `0.05` | Emergency Morph compression ratio, clamped to the API minimum `0.05`. |
| `compactMaxEmergencyPasses` | `3` | Maximum number of emergency re-compaction passes. |
| `compactMinimumTriggerRatio` | `0.8` | Safety guard: proactive auto compact will not trigger below this fraction of the selected model window, even if the char heuristic says the threshold was crossed. |
| `compactQuery` | built-in query | Relevance query sent to Morph Compact to preserve current task state and remove stale history. |
| `compactIncludeMarkers` | `true` | Includes filtered-content markers in compacted output. |
| `compactIncludeLineRanges` | `true` | Includes removed line-range metadata. |
| `compactTokenLimit` | unset | Optional token budget override for each Morph Compact request chunk. |
| `timeoutMs` | `30000` | Default Morph SDK request timeout. |
| `warpGrepTimeoutMs` | `60000` | WarpGrep request timeout. |
| `compactTimeoutMs` | `60000` | Morph Compact request timeout. |
| `browserTimeoutMs` | `300000` | Direct URL browser task completion timeout. |
| `browserRecordingTimeoutMs` | `300000` | Direct URL recording processing timeout after task completion. |
| `previewBrowserTimeoutMs` | `300000` | PR preview browser automation and recording timeout. |
| `fastModel.enabled` | `true` | Master switch for OpenAI-compatible Morph Fast Model helpers. |
| `fastModel.model` | `morph-qwen36-27b` | Fast Model name used by the AI helper calls; can be changed in `morph.json`. |
| `fastModel.fallbackModels` | `["morph-qwen35-397b"]` | Backup Fast Models tried after transient model failures such as timeouts or 5xx responses. |
| `fastModel.baseUrl` | `https://api.morphllm.com/v1` | Optional OpenAI-compatible Fast Model endpoint override; omit it to use Morph's default. |
| `fastModel.timeoutMs` | `30000` | Timeout for Fast Model helper calls. |
| `fastModel.errorClassifierEnabled` | `true` | Lets Fast Model classify unfamiliar assistant errors as context overflow before triggering auto-compact. |
| `fastModel.errorClassifierConfidenceThreshold` | `0.8` | Minimum confidence required before AI-classified context overflow triggers auto-compact. |
| `fastModel.contextResearchDistillationEnabled` | `true` | Lets Fast Model distill collected local code research into a structured answer with evidence and recommended reads. |
| `fastModel.repairSuggestionsEnabled` | `true` | Adds Fast Model repair hints to selected tool failures such as `morph_fastapply` anchor errors. |

Invalid routing mode strings automatically fall back to safe defaults:

- `routing.editMode` -> `force`
- `routing.codebaseSearchMode` -> `force`
- `routing.githubSearchMode` -> `force`

## Commands

### Core commands

- `/morph_status` shows the loaded config path, API key status, live API probe result, SDK status, base URL, feature flags, routing mode state, and whether Morph-first guidance is active.
- `/morph_settings` opens a simple interactive menu for updating routing settings in `morph.json`.
- `/morph_selftest` runs end-to-end checks for the API probe, FastApply, local WarpGrep, GitHub WarpGrep, compact, and reports Preview PR readiness or missing setup.
- `/morph_test_keys` tests every configured API key, clears keys that now pass, and keeps auth-failed keys quarantined for the configured cooldown window.
- `/morph_autotest` starts interactive URL-first autotest intake by default, or accepts a JSON context payload and returns an executable subflow-based autotest plan plus markdown report.
- `/morph_compact` is explicit and Morph-only: the manual command requires Morph compaction and reports a clear warning when compaction cannot run.

### Profile command

Use one consolidated `/morph_profile <action> [json]` command instead of many profile-specific slash commands.

Actions:

- `/morph_profile list` shows local sticky-profile registry entries.
- `/morph_profile list-remote` shows remote Morph repos and profiles using a deterministic API key selection policy and prints the real active API key fingerprint.
- `/morph_profile register {...}` registers or updates local profile metadata so `profileAlias` and sticky-key selection can be used safely.
- `/morph_profile remove {...}` removes only local sticky metadata without touching the remote Morph profile.
- `/morph_profile sync {...}` syncs remote profiles for a repo into the local sticky registry using deterministic API key selection and prints the real active API key fingerprint.
- `/morph_profile create {...}` creates a remote Morph profile and auto-binds it into the local sticky registry.
- `/morph_profile update {...}` opens a remote Morph profile update flow, but does not treat local sticky metadata as confirmed until you register or sync after the remote update is actually finished.
- `/morph_profile delete {...}` deletes a remote Morph profile and removes the matching local sticky metadata.

Interactive-friendly behavior:

- If you run `/morph_profile create`, `/morph_profile update`, `/morph_profile delete`, `/morph_profile sync`, `/morph_profile register`, or `/morph_profile remove` without JSON args, the command starts a short intake flow first.
- The intake asks only for the missing fields, then guides you toward the exact command payload instead of forcing you to remember the JSON shape up front.

### Automatic compaction behavior

- Automatic compaction is Morph-first when `autoCompactEnabled` is enabled, then falls back to Pi if Morph cannot produce a result.
- Proactive auto compaction runs internally inside the extension; it is not exposed as a user slash command.
- `compactQuery` sends an explicit relevance query to Morph Compact so pruning stays focused on the active task instead of relying on auto-detection.
- `compactIncludeMarkers` controls whether Morph inserts `(filtered N lines)` markers in compacted output.
- `compactIncludeLineRanges` controls whether Morph returns removed line-range metadata alongside compacted messages.
- By default, automatic Morph compaction starts when the session reaches about `85%` of the selected model context window via `compactContextThreshold: 0.85`, but the `compactMinimumTriggerRatio: 0.8` guard prevents proactive compaction from firing while the raw model-window estimate is still below about `80%`.
- When `autoCompactEnabled` is `false`, automatic compaction skips Morph entirely and leaves compaction to Pi.

Example `/morph_status` fields:

- `Morph plugin version: 0.2.0`
- `Morph config: ~/.pi/morph/morph.json`
- `Morph API key: configured`
- `Morph API key source: single key`, `3 keys (round-robin)`, or `key file: ~/.pi/morph/morph.env` depending on your configuration
- `Morph API live test: ok`, `failed (authentication error: ...)`, `failed (request timeout: ...)`, `failed (network/base URL error: ...)`, or `skipped (...)`
- `Morph FastApply enabled: true`
- `Preview PR enabled: true`
- `Auto compaction policy: Morph first, Pi fallback`
- `Manual /morph_compact policy: Morph required`
- `Routing edit mode: force`
- `Routing codebase search mode: force`
- `Routing GitHub search mode: force`
- `Morph FastApply-first guidance active: true`
- `Morph-first local search guidance active: true`
- `Morph-first GitHub search guidance active: true`
- `Fallback to native tools: true`
- `Manual /morph_compact` will report a warning if there are no older messages available to compact.

`/morph_status` is the main summary view. Use `/morph_settings` when you want to change config interactively instead of editing JSON by hand. Reload the extension or session after credential or key-file changes so Morph clients are rebuilt with the new config. The footer also shows `MorphLLM` plus the loaded API key count, including the active strategy when multiple keys are configured.

Use `/morph_test_keys` after rotating credentials, recovering a previously invalid key, or when all keys appear blocked and you want to re-probe the pool immediately instead of waiting for the cooldown window to expire.

## Stats Dashboard


- Access uses a stable query-string token such as `http://127.0.0.1:47631/?token=...`
- Storage uses JSONL so each Morph activity appends a lightweight event record
- `morph_fastapply` dry runs are intentionally not recorded
- File activity is grouped by project / repo / folder, then listed per edited file
- Multiple-key usage is summarized with safe identifiers based on the last 10 characters of each key
- The dashboard includes client-side search, filters, sorting, and pagination for project cards and recent events
- Filtering is available by project, tool, status, and key suffix, alongside free-text search across the rendered activity data
- The control surface uses a denser command-bar layout with stronger active-state styling so active filters and sorts are visible at a glance

For SSH usage with the default localhost bind:

```bash
ssh -L 47631:127.0.0.1:47631 user@host
```


## Tools

- `morph_fastapply` handles non-trivial edits in existing files using `// ... existing code ...` markers and supports `dry_run` previews.
- Read the file first and provide `code_edit` as marker-wrapped anchor snippets, not feature-level intent alone.
- `warpgrep_codebase_search` answers exploratory questions about the current workspace.
- `warpgrep_github_search` searches public GitHub repositories without cloning them locally.
- `morph_browser_test_url` opens a direct URL in Morph browser automation, follows the provided task, and returns a summary plus recording artifacts when available.
- `morph_preview_test_pr` fetches PR context through the Morph GitHub SDK, finds a successful preview deployment, runs a browser test, and updates a GitHub check run by default.
- Set `postComment: true` when you also want a PR comment with a recording preview.
- Tool preference strength and fallback behavior are controlled by `routing` in `morph.json`.
- In `force` mode, the extension applies the strongest Morph-first guidance for non-trivial existing-file edits after the file has been read and anchor snippets are available; native `edit` remains available for tiny exact replacements and native `write` remains available for new files.
- In `strong` mode, the extension strongly prefers Morph tools for non-trivial existing-file edits after the file has been read and anchor snippets are available.
- In `prefer` mode, the extension gives a softer recommendation without changing tool availability.

Example `morph_fastapply` diff preview:

![Morph FastApply preview](https://raw.githubusercontent.com/rickicode/pi-morphllm-plugin/main/images/fastapply.png)

Example `morph_fastapply` preview call:

```json
{
  "target_filepath": "src/utils/math.ts",
  "instructions": "I am adding input validation to the add function.",
  "code_edit": "function add(a: number, b: number): number {\n  if (typeof a !== 'number' || typeof b !== 'number') {\n    throw new TypeError('Both arguments must be numbers.');\n  }\n  // ... existing code ...\n}",
  "dry_run": true
}
```

Use `dry_run: true` first when you want to inspect the diff preview without writing the file. Then rerun the same tool call with `dry_run: false` or omit it to apply the edit.

Example `morph_browser_test_url` call:

```json
{
  "url": "https://netq.me",
  "task": "Open the premium page and confirm the premium section loads without obvious errors",
  "recordVideo": true,
  "maxSteps": 12
}
```

Example `morph_browser_test_url` call with auth + model:

```json
{
  "url": "https://staging.example.com/dashboard",
  "task": "Open the dashboard, verify the login succeeds, and confirm the summary widgets load without obvious errors.",
  "auth": {
    "username": "test@example.com",
    "password": "secret"
  },
  "model": "morph-computer-use-v1",
  "recordVideo": true,
  "maxSteps": 10
}
```

Example `morph_browser_test_url` call with sticky profile reuse:

```json
{
  "url": "https://staging.example.com/dashboard",
  "task": "Open the dashboard and verify the summary widgets load without obvious errors.",
  "profileAlias": "staging-dashboard-admin",
  "recordVideo": true,
  "maxSteps": 8
}
```

For repeated protected runs, prefer `profileId` or `profileAlias` first when a local Morph profile registry entry already exists and can resolve to an active API key. The local registry stores only metadata such as profile ID, alias, host mapping, and hashed API key fingerprint; it does not store live browser session secrets.

Minimal remote-managed profile workflow:

1. Inspect remote repos and profiles if needed:

```text
/morph_profile list-remote
```

2. Create a remote Morph profile and auto-bind it locally:

```text
/morph_profile create '{"name":"Staging","repoId":"repo_xxx","alias":"staging-dashboard-admin","hostPattern":"staging.example.com","preferredModel":"morph-computer-use-v1"}'
```

3. If you already have a remote `profileId`, register only the local sticky metadata:

```text
/morph_profile register '{"profileId":"prof_xxx","alias":"staging-dashboard-admin","hostPattern":"staging.example.com","createdWithApiKeyFingerprint":"a1b2c3d4e5f67890","preferredModel":"morph-computer-use-v1"}'
```

4. Verify the local registry:

```text
/morph_profile list
```

5. Optionally sync all remote profiles for a repo back into the local registry:

```text
/morph_profile sync '{"repoId":"repo_xxx","repoFullName":"owner/repo"}'
```

6. Reuse the profile via `profileAlias` or `profileId` in `morph_browser_test_url`.

7. Update or delete the remote profile later while preserving sticky-key affinity:

```text
/morph_profile update '{"profileAlias":"staging-dashboard-admin","notes":"refresh login setup"}'
/morph_profile delete '{"profileAlias":"staging-dashboard-admin"}'
```

After `/morph_profile update`, treat the returned setup/edit URL as the source of truth. Once the remote edit is actually finished, run `/morph_profile register` or `/morph_profile sync` if you also want the local sticky metadata to reflect the final remote state.

8. If you only want to detach local sticky metadata and keep the remote Morph profile intact:

```text
/morph_profile remove '{"profileAlias":"staging-dashboard-admin"}'
```

Quick rule of thumb:

- use `/morph_profile create` when AI should create the remote Morph profile for you
- use `/morph_profile register` when the remote `profileId` already exists and you only need local sticky binding
- use `/morph_profile sync` when you want to pull remote profile state back into the local registry
- use `/morph_profile remove` when you want to remove only the local binding
- use `/morph_profile delete` when you want to remove both the remote profile and its local sticky metadata

Remote command behavior notes:

- `/morph_profile list-remote`, `/morph_profile create`, and `/morph_profile sync` now use deterministic API key selection by taking the first active configured key instead of random or round-robin selection.
- Remote command output now includes the real `apiKeyFingerprint` that was used, which makes sticky-key debugging much easier on large multi-key pools.
- Remote listing and sync commands now surface clearer per-call errors instead of only generic failures.
- `/morph_profile update` intentionally does not rewrite local registry metadata up front; after the remote edit is actually finished, use `/morph_profile register` or `/morph_profile sync` to persist the confirmed local view.

Test-mode note:

- Full test mode means the Pi harness dependencies are resolvable, so `test/extension.test.ts` can exercise command/extension behavior normally through its compiled `dist-test/test/extension.test.js` output.
- Degraded test mode means those harness dependencies are unavailable in the current environment. In that case, `test/extension.test.ts` now auto-skips harness-dependent cases instead of hard-failing at import time, while helper/config/profile tests still run normally.

Example `morph_preview_test_pr` call:

```json
{
  "owner": "acme",
  "repo": "app",
  "prNumber": 42,
  "task": "Focus on the login and signup flows changed in this PR",
  "preferredEnvironment": "preview",
  "postComment": true
}
```

This tool assumes the Morph GitHub App is installed for the repository and that the PR has a successful preview deployment. By default it creates and updates a `Morph Preview Test` check run; set `postComment: true` to also publish a PR comment with the result and recording preview.

## Skills

This package includes five bundled skills to enhance Morph workflows. All skills have been comprehensively improved with advanced techniques, troubleshooting guides, and real-world examples.

### `morph-power-user` (13 KB)

Expert-level decision policy for `morph_fastapply`, `warpgrep_codebase_search`, and `warpgrep_github_search`.

**Focus:**
- Strategic tool selection (when to use FastApply vs native edit vs write)
- Dry run strategy and anchor quality guidelines
- WarpGrep usage patterns and fallback strategies
- Execution patterns for safe refactoring and exploratory edits

**New in latest version:**
- ✨ Comprehensive troubleshooting guide with diagnosis and solutions
- ✨ Real-world code examples (function renaming, validation, API updates)
- ✨ Multi-file refactoring coordination strategies (Sequential/Batch/Dependency-Ordered)
- ✨ Anchor quality metrics table
- ✨ Advanced techniques: anchor validation, progressive refinement, fallback chaining

**Use when:**
- Multi-file refactoring or scattered edits
- Exploring unfamiliar codebases
- Working with large files or whitespace-sensitive languages
- Need guidance on Morph tool selection

### `morph-reviewer` (14 KB)

Quality gate and validation layer for Morph tool outputs.

**Focus:**
- Post-execution validation (anchor quality, completeness, safety)
- Search result quality assessment
- Compaction effectiveness evaluation
- Fallback appropriateness validation

**New in latest version:**
- ✨ Severity scoring system (0-10 scale) with priority matrix
- ✨ Automated checks for FastApply and WarpGrep
- ✨ Example review reports (success, warning, critical scenarios)
- ✨ Issue prioritization and escalation path
- ✨ Enhanced safety assessment framework

**Use when:**
- After FastApply completes (especially without dry run)
- After WarpGrep returns results
- After automatic compaction runs
- Before committing Morph-generated changes

### `morph-preview-pr` (12 KB)

Specialized skill for `morph_preview_test_pr` — PR-native browser verification.

**Focus:**
- Task formulation for PR preview testing
- Environment selection strategies
- Check run vs PR comment decision guidance
- Result interpretation and retry strategies

**New in latest version:**
- ✨ Retry strategies for different failure types
- ✨ Flaky test identification and mitigation techniques
- ✨ CI/CD integration guidance with GitHub Actions example
- ✨ Multi-page flow testing examples
- ✨ Severity assessment framework (Critical/High/Medium/Low)

**Use when:**
- Validating PR preview deployments
- Testing UI changes in pull requests
- Running automated browser checks on PRs
- Need guidance on effective PR testing tasks

### `morph-production-tester` (9.3 KB)

Automatic production site testing with code-aware analysis for `morph_browser_test_url`.

**Focus:**
- Automatic detection of routes and components using `warpgrep_codebase_search`
- Comprehensive analysis of buttons, forms, and interactive elements from code
- Single comprehensive browser test execution
- Authentication detection and credential handling
- Code-aware test result analysis and reporting

**New in latest version:**
- ✨ Reduced verbosity by 34% for faster comprehension
- ✨ Dynamic/SPA routing handling
- ✨ Improved end-to-end authentication flow guidance
- ✨ Error recovery strategies
- ✨ Simplified report template
- ✨ Better framework detection patterns (Next.js, React, Vue)

**Use when:**
- Pre-production validation before deploying
- Post-deployment smoke testing
- Testing all functionality on a specific page automatically
- Need code-aware comprehensive testing strategy

### `morph-autotest`

Autonomous multi-session browser testing engine for `morph_browser_test_url`.

**Focus:**
- Preflight guardrails before any run starts
- Executable per-subflow planning instead of one long browser session
- Auth-path selection in this order: `profileAlias/profileId -> cookies -> username/password -> blocked`
- Mandatory code-aware analysis when the codebase is available
- Failure diagnosis that combines browser evidence, code evidence, and PRD alignment
- Markdown report generation with per-subflow status, code-suspect hints, and next actions
- Live code-search-enriched diagnosis for failed or partial subflows when local code search is available
- URL-first browser testing flow without GitHub/PR intake

**Use when:**
- You want TestSprite-style validation without vague automation theater
- You need a slash-command launcher to gather context, then hand off all reasoning to a skill
- You want final output in markdown with recording links and likely code suspects

## Hooks And Compaction

Morph compaction labels require `@earendil-works/pi-coding-agent >=0.75.0`; older Pi cores still run compaction, but they ignore the optional label and show Pi's default compaction text.

This package uses Pi extension hooks rather than command aliases alone.

- `session_start` and `session_shutdown` manage Morph runtime status in the UI.
- `before_agent_start` injects Morph routing hints into the system prompt based on `routing` config.
- `tool_call` normalizes tool input before execution.
- `tool_result` adds Morph metadata such as provider and base URL.
- `model_select` tracks the active model context window.
- `session_before_compact` runs Morph compaction before Pi falls back to its normal compactor, so automatic compaction stays Morph-first while preserving Pi as the safety net whenever `autoCompactEnabled` is on.
- Proactive auto compaction is scheduled internally from `message_end`; it no longer relies on injecting an internal slash command into the conversation.
- When `autoCompactEnabled` is off, `session_before_compact` skips Morph and leaves automatic compaction to Pi.
- `/morph_compact` uses the same hook-based path and always requires Morph compaction instead of silently falling back to Pi.

## Development

Clone the repository only if you want to work on the package itself.

Copy `.pi/morph.json.example` to `.pi/morph.json` or create `~/.pi/morph/morph.json`, fill in `apiKey`, then run:

```bash
pi -e .
```

Useful development modes:

- `pi -e .` loads the full package manifest, extension, bundled prompt resource, and skills.
- `npm run build && pi -e ./dist/extensions/morph/index.js` loads only the compiled extension entrypoint.
- `npm test` runs the package tests.

## Notes

- Default Morph endpoint is `https://api.morphllm.com`.
- Set `baseUrl` or `baseApi` in JSON when you want a custom endpoint.
- The package manifest in `package.json` allows Pi to auto-discover the extension, prompt resource, and skills.
- Automatic config creation happens on first runtime load, not during `pi install`.
- Automatic config creation only targets the global `~/.pi/morph/morph.json` path, never a project-local file by default.

## Testing

Run:

```bash
npm test
```
