@phi-code-admin/phi-code 0.85.0 → 0.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/README.md +92 -123
- package/config/routing.example.json +129 -0
- package/config/routing.schema.json +58 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +6 -6
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +1 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +2 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +9 -8
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/pi-user-agent.d.ts.map +1 -1
- package/dist/utils/pi-user-agent.js +4 -1
- package/dist/utils/pi-user-agent.js.map +1 -1
- package/docs/compaction.md +11 -11
- package/docs/custom-provider.md +4 -4
- package/docs/development.md +2 -2
- package/docs/extensions.md +47 -47
- package/docs/fork-policy.md +63 -0
- package/docs/index.md +7 -7
- package/docs/json.md +3 -3
- package/docs/keybindings.md +5 -5
- package/docs/models.md +6 -6
- package/docs/packages.md +37 -37
- package/docs/prompt-templates.md +4 -4
- package/docs/providers.md +9 -9
- package/docs/quickstart.md +25 -25
- package/docs/rpc.md +3 -3
- package/docs/sdk.md +34 -34
- package/docs/session-format.md +4 -4
- package/docs/sessions.md +11 -11
- package/docs/settings.md +9 -9
- package/docs/shell-aliases.md +2 -2
- package/docs/skills.md +9 -9
- package/docs/terminal-setup.md +7 -7
- package/docs/termux.md +6 -6
- package/docs/themes.md +9 -9
- package/docs/tmux.md +3 -3
- package/docs/tui.md +8 -8
- package/docs/usage.md +39 -39
- package/docs/windows.md +2 -2
- package/extensions/phi/agents.ts +5 -113
- package/extensions/phi/orchestrator.ts +6 -38
- package/extensions/phi/providers/agent-def.ts +128 -0
- package/extensions/phi/skill-loader.ts +5 -5
- package/package.json +2 -1
package/docs/tui.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
>
|
|
1
|
+
> phi can create TUI components. Ask it to build one for your use case.
|
|
2
2
|
|
|
3
3
|
# TUI Components
|
|
4
4
|
|
|
@@ -425,7 +425,7 @@ renderResult(result, options, theme, context) {
|
|
|
425
425
|
**For Markdown**, use `getMarkdownTheme()`:
|
|
426
426
|
|
|
427
427
|
```typescript
|
|
428
|
-
import { getMarkdownTheme } from "@
|
|
428
|
+
import { getMarkdownTheme } from "@phi-code-admin/phi-code";
|
|
429
429
|
import { Markdown } from "@earendil-works/pi-tui";
|
|
430
430
|
|
|
431
431
|
renderResult(result, options, theme, context) {
|
|
@@ -590,8 +590,8 @@ These patterns cover the most common UI needs in extensions. **Copy these patter
|
|
|
590
590
|
For letting users pick from a list of options. Use `SelectList` from `@earendil-works/pi-tui` with `DynamicBorder` for framing.
|
|
591
591
|
|
|
592
592
|
```typescript
|
|
593
|
-
import type { ExtensionAPI } from "@
|
|
594
|
-
import { DynamicBorder } from "@
|
|
593
|
+
import type { ExtensionAPI } from "@phi-code-admin/phi-code";
|
|
594
|
+
import { DynamicBorder } from "@phi-code-admin/phi-code";
|
|
595
595
|
import { Container, type SelectItem, SelectList, Text } from "@earendil-works/pi-tui";
|
|
596
596
|
|
|
597
597
|
pi.registerCommand("pick", {
|
|
@@ -650,7 +650,7 @@ pi.registerCommand("pick", {
|
|
|
650
650
|
For operations that take time and should be cancellable. `BorderedLoader` shows a spinner and handles escape to cancel.
|
|
651
651
|
|
|
652
652
|
```typescript
|
|
653
|
-
import { BorderedLoader } from "@
|
|
653
|
+
import { BorderedLoader } from "@phi-code-admin/phi-code";
|
|
654
654
|
|
|
655
655
|
pi.registerCommand("fetch", {
|
|
656
656
|
handler: async (_args, ctx) => {
|
|
@@ -682,7 +682,7 @@ pi.registerCommand("fetch", {
|
|
|
682
682
|
For toggling multiple settings. Use `SettingsList` from `@earendil-works/pi-tui` with `getSettingsListTheme()`.
|
|
683
683
|
|
|
684
684
|
```typescript
|
|
685
|
-
import { getSettingsListTheme } from "@
|
|
685
|
+
import { getSettingsListTheme } from "@phi-code-admin/phi-code";
|
|
686
686
|
import { Container, type SettingItem, SettingsList, Text } from "@earendil-works/pi-tui";
|
|
687
687
|
|
|
688
688
|
pi.registerCommand("settings", {
|
|
@@ -737,7 +737,7 @@ ctx.ui.setStatus("my-ext", undefined);
|
|
|
737
737
|
|
|
738
738
|
### Pattern 4b: Working Indicator Customization
|
|
739
739
|
|
|
740
|
-
Customize the inline working indicator shown while
|
|
740
|
+
Customize the inline working indicator shown while phi is streaming a response.
|
|
741
741
|
|
|
742
742
|
```typescript
|
|
743
743
|
// Static indicator
|
|
@@ -822,7 +822,7 @@ Token stats available via `ctx.sessionManager.getBranch()` and `ctx.model`.
|
|
|
822
822
|
Replace the main input editor with a custom implementation. Useful for modal editing (vim), different keybindings (emacs), or specialized input handling.
|
|
823
823
|
|
|
824
824
|
```typescript
|
|
825
|
-
import { CustomEditor, type ExtensionAPI } from "@
|
|
825
|
+
import { CustomEditor, type ExtensionAPI } from "@phi-code-admin/phi-code";
|
|
826
826
|
import { matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
|
|
827
827
|
|
|
828
828
|
type Mode = "normal" | "insert";
|
package/docs/usage.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Using
|
|
1
|
+
# Using phi
|
|
2
2
|
|
|
3
3
|
This page collects day-to-day usage details that do not fit on the quickstart page.
|
|
4
4
|
|
|
@@ -53,7 +53,7 @@ Type `/` in the editor to open command completion. Extensions can register custo
|
|
|
53
53
|
| `/reload` | Reload keybindings, extensions, skills, prompts, and context files |
|
|
54
54
|
| `/hotkeys` | Show all keyboard shortcuts |
|
|
55
55
|
| `/changelog` | Display version history |
|
|
56
|
-
| `/quit` | Quit
|
|
56
|
+
| `/quit` | Quit phi |
|
|
57
57
|
|
|
58
58
|
## Message Queue
|
|
59
59
|
|
|
@@ -64,20 +64,20 @@ You can submit messages while the agent is still working:
|
|
|
64
64
|
- **Escape** aborts and restores queued messages to the editor.
|
|
65
65
|
- **Alt+Up** retrieves queued messages back to the editor.
|
|
66
66
|
|
|
67
|
-
On Windows Terminal, Alt+Enter is fullscreen by default. Remap it as described in [Terminal setup](terminal-setup.md) if you want
|
|
67
|
+
On Windows Terminal, Alt+Enter is fullscreen by default. Remap it as described in [Terminal setup](terminal-setup.md) if you want phi to receive the shortcut.
|
|
68
68
|
|
|
69
69
|
Configure delivery in [Settings](settings.md) with `steeringMode` and `followUpMode`.
|
|
70
70
|
|
|
71
71
|
## Sessions
|
|
72
72
|
|
|
73
|
-
Sessions are saved automatically to `~/.
|
|
73
|
+
Sessions are saved automatically to `~/.phi/agent/sessions/`, organized by working directory.
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
phi -c # Continue most recent session
|
|
77
|
+
phi -r # Browse and select a session
|
|
78
|
+
phi --no-session # Ephemeral mode; do not save
|
|
79
|
+
phi --session <path|id> # Use a specific session file or session ID
|
|
80
|
+
phi --fork <path|id> # Fork a session into a new session file
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
Useful session commands:
|
|
@@ -92,9 +92,9 @@ See [Sessions](sessions.md) and [Compaction](compaction.md) for details.
|
|
|
92
92
|
|
|
93
93
|
## Context Files
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
phi loads `AGENTS.md` or `CLAUDE.md` at startup from:
|
|
96
96
|
|
|
97
|
-
- `~/.
|
|
97
|
+
- `~/.phi/agent/AGENTS.md` for global instructions
|
|
98
98
|
- parent directories, walking up from the current working directory
|
|
99
99
|
- the current directory
|
|
100
100
|
|
|
@@ -104,8 +104,8 @@ Use context files for project conventions, commands, safety rules, and preferenc
|
|
|
104
104
|
|
|
105
105
|
Replace the default system prompt with:
|
|
106
106
|
|
|
107
|
-
- `.
|
|
108
|
-
- `~/.
|
|
107
|
+
- `.phi/SYSTEM.md` for a project
|
|
108
|
+
- `~/.phi/agent/SYSTEM.md` globally
|
|
109
109
|
|
|
110
110
|
Append to the default prompt without replacing it with `APPEND_SYSTEM.md` in either location.
|
|
111
111
|
|
|
@@ -120,24 +120,24 @@ If you use pi for open source work and want to publish sessions for model, promp
|
|
|
120
120
|
## CLI Reference
|
|
121
121
|
|
|
122
122
|
```bash
|
|
123
|
-
|
|
123
|
+
phi [options] [@files...] [messages...]
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
### Package Commands
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
129
|
+
phi install <source> [-l] # Install package, -l for project-local
|
|
130
|
+
phi remove <source> [-l] # Remove package
|
|
131
|
+
phi uninstall <source> [-l] # Alias for remove
|
|
132
|
+
phi update [source|self] # Update phi and packages; skips pinned packages
|
|
133
|
+
phi update --extensions # Update packages only
|
|
134
|
+
phi update --self # Update phi only
|
|
135
|
+
phi update --extension <src> # Update one package
|
|
136
|
+
phi list # List installed packages
|
|
137
|
+
phi config # Enable/disable package resources
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
-
See [
|
|
140
|
+
See [Phi Packages](packages.md) for package sources and security notes.
|
|
141
141
|
|
|
142
142
|
### Modes
|
|
143
143
|
|
|
@@ -149,10 +149,10 @@ See [Pi Packages](packages.md) for package sources and security notes.
|
|
|
149
149
|
| `--mode rpc` | RPC mode over stdin/stdout; see [RPC mode](rpc.md) |
|
|
150
150
|
| `--export <in> [out]` | Export a session to HTML |
|
|
151
151
|
|
|
152
|
-
In print mode,
|
|
152
|
+
In print mode, phi also reads piped stdin and merges it into the initial prompt:
|
|
153
153
|
|
|
154
154
|
```bash
|
|
155
|
-
cat README.md |
|
|
155
|
+
cat README.md | phi -p "Summarize this text"
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
### Model Options
|
|
@@ -204,7 +204,7 @@ Built-in tools: `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`.
|
|
|
204
204
|
Combine `--no-*` with explicit flags to load exactly what you need, ignoring settings. Example:
|
|
205
205
|
|
|
206
206
|
```bash
|
|
207
|
-
|
|
207
|
+
phi --no-extensions -e ./my-extension.ts
|
|
208
208
|
```
|
|
209
209
|
|
|
210
210
|
### Other Options
|
|
@@ -223,7 +223,7 @@ Prefix files with `@` to include them in the message:
|
|
|
223
223
|
|
|
224
224
|
```bash
|
|
225
225
|
pi @prompt.md "Answer this"
|
|
226
|
-
|
|
226
|
+
phi -p @screenshot.png "What's in this image?"
|
|
227
227
|
pi @code.ts @test.ts "Review these files"
|
|
228
228
|
```
|
|
229
229
|
|
|
@@ -231,36 +231,36 @@ pi @code.ts @test.ts "Review these files"
|
|
|
231
231
|
|
|
232
232
|
```bash
|
|
233
233
|
# Interactive with initial prompt
|
|
234
|
-
|
|
234
|
+
phi "List all .ts files in src/"
|
|
235
235
|
|
|
236
236
|
# Non-interactive
|
|
237
|
-
|
|
237
|
+
phi -p "Summarize this codebase"
|
|
238
238
|
|
|
239
239
|
# Non-interactive with piped stdin
|
|
240
|
-
cat README.md |
|
|
240
|
+
cat README.md | phi -p "Summarize this text"
|
|
241
241
|
|
|
242
242
|
# Different model
|
|
243
|
-
|
|
243
|
+
phi --provider openai --model gpt-4o "Help me refactor"
|
|
244
244
|
|
|
245
245
|
# Model with provider prefix
|
|
246
|
-
|
|
246
|
+
phi --model openai/gpt-4o "Help me refactor"
|
|
247
247
|
|
|
248
248
|
# Model with thinking level shorthand
|
|
249
|
-
|
|
249
|
+
phi --model sonnet:high "Solve this complex problem"
|
|
250
250
|
|
|
251
251
|
# Limit model cycling
|
|
252
|
-
|
|
252
|
+
phi --models "claude-*,gpt-4o"
|
|
253
253
|
|
|
254
254
|
# Read-only mode
|
|
255
|
-
|
|
255
|
+
phi --tools read,grep,find,ls -p "Review the code"
|
|
256
256
|
```
|
|
257
257
|
|
|
258
258
|
### Environment Variables
|
|
259
259
|
|
|
260
260
|
| Variable | Description |
|
|
261
261
|
|----------|-------------|
|
|
262
|
-
| `
|
|
263
|
-
| `
|
|
262
|
+
| `PHI_CODING_AGENT_DIR` | Override config directory; default is `~/.phi/agent` |
|
|
263
|
+
| `PHI_CODING_AGENT_SESSION_DIR` | Override session storage directory; overridden by `--session-dir` |
|
|
264
264
|
| `PI_PACKAGE_DIR` | Override package directory, useful for Nix/Guix store paths |
|
|
265
265
|
| `PI_OFFLINE` | Disable startup network operations, including update checks, package update checks, and install/update telemetry |
|
|
266
266
|
| `PI_SKIP_VERSION_CHECK` | Skip the phi-code version update check at startup. This prevents the npm registry latest-version request |
|
|
@@ -270,7 +270,7 @@ pi --tools read,grep,find,ls -p "Review the code"
|
|
|
270
270
|
|
|
271
271
|
## Design Principles
|
|
272
272
|
|
|
273
|
-
|
|
273
|
+
phi keeps the core small and pushes workflow-specific behavior into extensions, skills, prompt templates, and packages.
|
|
274
274
|
|
|
275
275
|
It bundles MCP support (see the MCP extension) and sub-agents. It does not include permission popups, to-dos, or background bash by default: you can build or install those workflows as extensions or packages, or use external tools such as containers and tmux.
|
|
276
276
|
|
package/docs/windows.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Windows Setup
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
phi requires a bash shell on Windows. Checked locations (in order):
|
|
4
4
|
|
|
5
|
-
1. Custom path from `~/.
|
|
5
|
+
1. Custom path from `~/.phi/agent/settings.json`
|
|
6
6
|
2. Git Bash (`C:\Program Files\Git\bin\bash.exe`)
|
|
7
7
|
3. `bash.exe` on PATH (Cygwin, MSYS2, WSL)
|
|
8
8
|
|
package/extensions/phi/agents.ts
CHANGED
|
@@ -4,123 +4,15 @@
|
|
|
4
4
|
* Provides:
|
|
5
5
|
* - /agents command to list all configured sub-agents
|
|
6
6
|
* - /agents <name> to show detailed agent info
|
|
7
|
-
* - Agent definitions loaded from ~/.phi/agent/agents/ and
|
|
8
|
-
*
|
|
7
|
+
* - Agent definitions loaded from .phi/agents/, ~/.phi/agent/agents/ and the
|
|
8
|
+
* bundled agents/ directory via the shared providers/agent-def module
|
|
9
|
+
* (same parser and search order as the /plan orchestrator).
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import type { ExtensionAPI } from "phi-code";
|
|
12
|
-
import {
|
|
13
|
-
import { join, basename } from "node:path";
|
|
14
|
-
import { homedir } from "node:os";
|
|
15
|
-
|
|
16
|
-
interface AgentDefinition {
|
|
17
|
-
name: string;
|
|
18
|
-
description: string;
|
|
19
|
-
tools: string[];
|
|
20
|
-
model: string;
|
|
21
|
-
source: string; // "global", "project", "bundled"
|
|
22
|
-
filePath: string;
|
|
23
|
-
systemPrompt: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Parse YAML frontmatter from agent .md file
|
|
28
|
-
*/
|
|
29
|
-
function parseAgentFile(filePath: string): AgentDefinition | null {
|
|
30
|
-
try {
|
|
31
|
-
const content = readFileSync(filePath, "utf-8");
|
|
32
|
-
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
33
|
-
|
|
34
|
-
if (!fmMatch) return null;
|
|
35
|
-
|
|
36
|
-
const frontmatter = fmMatch[1];
|
|
37
|
-
const body = fmMatch[2].trim();
|
|
38
|
-
|
|
39
|
-
// Simple YAML parser for our frontmatter
|
|
40
|
-
const fields: Record<string, string> = {};
|
|
41
|
-
for (const line of frontmatter.split("\n")) {
|
|
42
|
-
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
43
|
-
if (match) {
|
|
44
|
-
fields[match[1]] = match[2].trim();
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!fields.name) return null;
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
name: fields.name,
|
|
52
|
-
description: fields.description || "No description",
|
|
53
|
-
tools: (fields.tools || "").split(",").map(t => t.trim()).filter(Boolean),
|
|
54
|
-
model: fields.model || "default",
|
|
55
|
-
source: "unknown",
|
|
56
|
-
filePath,
|
|
57
|
-
systemPrompt: body,
|
|
58
|
-
};
|
|
59
|
-
} catch {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Scan a directory for agent .md files
|
|
66
|
-
*/
|
|
67
|
-
function scanAgentDir(dir: string, source: string): AgentDefinition[] {
|
|
68
|
-
const agents: AgentDefinition[] = [];
|
|
69
|
-
|
|
70
|
-
if (!existsSync(dir)) return agents;
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const entries = readdirSync(dir);
|
|
74
|
-
for (const entry of entries) {
|
|
75
|
-
if (!entry.endsWith(".md")) continue;
|
|
76
|
-
const fullPath = join(dir, entry);
|
|
77
|
-
if (!statSync(fullPath).isFile()) continue;
|
|
78
|
-
|
|
79
|
-
const agent = parseAgentFile(fullPath);
|
|
80
|
-
if (agent) {
|
|
81
|
-
agent.source = source;
|
|
82
|
-
agents.push(agent);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
} catch {
|
|
86
|
-
// Directory not readable
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return agents;
|
|
90
|
-
}
|
|
13
|
+
import { type AgentDef, discoverAgents } from "./providers/agent-def.js";
|
|
91
14
|
|
|
92
15
|
export default function agentsExtension(pi: ExtensionAPI) {
|
|
93
|
-
/**
|
|
94
|
-
* Discover all agent definitions from all sources
|
|
95
|
-
*/
|
|
96
|
-
function discoverAgents(): AgentDefinition[] {
|
|
97
|
-
const seen = new Set<string>();
|
|
98
|
-
const allAgents: AgentDefinition[] = [];
|
|
99
|
-
|
|
100
|
-
const addAgents = (agents: AgentDefinition[]) => {
|
|
101
|
-
for (const agent of agents) {
|
|
102
|
-
if (!seen.has(agent.name)) {
|
|
103
|
-
seen.add(agent.name);
|
|
104
|
-
allAgents.push(agent);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
// 1. Project-local agents
|
|
110
|
-
addAgents(scanAgentDir(join(process.cwd(), ".phi", "agents"), "project"));
|
|
111
|
-
|
|
112
|
-
// 2. Global agents
|
|
113
|
-
addAgents(scanAgentDir(join(homedir(), ".phi", "agent", "agents"), "global"));
|
|
114
|
-
|
|
115
|
-
// 3. Bundled agents (shipped with package)
|
|
116
|
-
const bundledDir = join(__dirname, "..", "..", "..", "agents");
|
|
117
|
-
if (existsSync(bundledDir)) {
|
|
118
|
-
addAgents(scanAgentDir(bundledDir, "bundled"));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return allAgents;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
16
|
/**
|
|
125
17
|
* /agents command
|
|
126
18
|
*/
|
|
@@ -163,7 +55,7 @@ ${agent.systemPrompt.substring(0, 800)}${agent.systemPrompt.length > 800 ? "\n..
|
|
|
163
55
|
let output = `**🤖 Sub-Agents (${agents.length})**\n\n`;
|
|
164
56
|
|
|
165
57
|
// Group by source
|
|
166
|
-
const bySource: Record<string,
|
|
58
|
+
const bySource: Record<string, AgentDef[]> = {};
|
|
167
59
|
for (const agent of agents) {
|
|
168
60
|
const key = agent.source;
|
|
169
61
|
if (!bySource[key]) bySource[key] = [];
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
parsePhaseVerdict,
|
|
30
30
|
} from "./providers/orchestrator-helpers.js";
|
|
31
31
|
import { defaultExplorerSpecs, READONLY_EXPLORER_TOOLS, runExploreFanout } from "./providers/explore-fanout.js";
|
|
32
|
+
import { type AgentDef, loadAgentDef } from "./providers/agent-def.js";
|
|
32
33
|
|
|
33
34
|
// ─── Types ───────────────────────────────────────────────────────────────
|
|
34
35
|
|
|
@@ -212,12 +213,6 @@ export default function orchestratorExtension(pi: ExtensionAPI) {
|
|
|
212
213
|
|
|
213
214
|
// ─── Orchestration State ─────────────────────────────────────────
|
|
214
215
|
|
|
215
|
-
interface AgentDef {
|
|
216
|
-
name: string;
|
|
217
|
-
tools: string[];
|
|
218
|
-
systemPrompt: string;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
216
|
interface OrchestratorPhase {
|
|
222
217
|
key: string;
|
|
223
218
|
label: string;
|
|
@@ -330,45 +325,18 @@ export default function orchestratorExtension(pi: ExtensionAPI) {
|
|
|
330
325
|
\`\`\`
|
|
331
326
|
- **Internal orchestration data:** notes injected as "Previous phase summary" or budget/handoff reminders are for YOUR use only. Do not repeat them back to the user.`;
|
|
332
327
|
|
|
333
|
-
/**
|
|
334
|
-
* Parse agent .md file with YAML frontmatter
|
|
335
|
-
*/
|
|
336
|
-
function loadAgentDef(name: string): AgentDef | null {
|
|
337
|
-
const dirs = [
|
|
338
|
-
join(process.cwd(), ".phi", "agents"),
|
|
339
|
-
join(homedir(), ".phi", "agent", "agents"),
|
|
340
|
-
];
|
|
341
|
-
for (const dir of dirs) {
|
|
342
|
-
const filePath = join(dir, `${name}.md`);
|
|
343
|
-
try {
|
|
344
|
-
const content = readFileSync(filePath, "utf-8");
|
|
345
|
-
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
346
|
-
if (!fmMatch) continue;
|
|
347
|
-
const fields: Record<string, string> = {};
|
|
348
|
-
for (const line of fmMatch[1].split("\n")) {
|
|
349
|
-
const m = line.match(/^(\w+):\s*(.*)$/);
|
|
350
|
-
if (m) fields[m[1]] = m[2].trim();
|
|
351
|
-
}
|
|
352
|
-
return {
|
|
353
|
-
name: fields.name || name,
|
|
354
|
-
tools: (fields.tools || "").split(",").map(t => t.trim()).filter(Boolean),
|
|
355
|
-
systemPrompt: fmMatch[2].trim(),
|
|
356
|
-
};
|
|
357
|
-
} catch { continue; }
|
|
358
|
-
}
|
|
359
|
-
return null;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
328
|
/**
|
|
363
329
|
* Load a skill body (SKILL.md) so a phase instruction can embed it verbatim.
|
|
364
|
-
* Search order mirrors
|
|
365
|
-
*
|
|
366
|
-
*
|
|
330
|
+
* Search order mirrors agent-def.ts: project .phi/skills, then the global
|
|
331
|
+
* ~/.phi/agent/skills (postinstall copies bundled skills there), then the
|
|
332
|
+
* bundled <package>/skills for the repo layout. YAML frontmatter is
|
|
333
|
+
* stripped. Returns null when the skill is not installed.
|
|
367
334
|
*/
|
|
368
335
|
function loadSkillContent(name: string): string | null {
|
|
369
336
|
const dirs = [
|
|
370
337
|
join(process.cwd(), ".phi", "skills"),
|
|
371
338
|
join(homedir(), ".phi", "agent", "skills"),
|
|
339
|
+
join(__dirname, "..", "..", "skills"),
|
|
372
340
|
];
|
|
373
341
|
for (const dir of dirs) {
|
|
374
342
|
try {
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent definitions (.md with YAML frontmatter) — single source of truth for
|
|
3
|
+
* parsing and discovery, shared by the /plan orchestrator (loadAgentDef) and
|
|
4
|
+
* the /agents command (discoverAgents). Before this module existed the two
|
|
5
|
+
* call sites each had their own parser and they had already drifted.
|
|
6
|
+
*
|
|
7
|
+
* Search order (first match wins):
|
|
8
|
+
* 1. <cwd>/.phi/agents/ (project)
|
|
9
|
+
* 2. ~/.phi/agent/agents/ (global — postinstall copies bundled here)
|
|
10
|
+
* 3. <package>/agents/ (bundled, repo layout)
|
|
11
|
+
*
|
|
12
|
+
* Layout note: this file lives in extensions/phi/providers/. Three hops up is
|
|
13
|
+
* the package root in the repo layout (packages/coding-agent) AND the agent
|
|
14
|
+
* dir in the installed layout (~/.phi/agent), so the "bundled" candidate
|
|
15
|
+
* resolves to a real agents/ directory in both.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
import { basename, join } from "node:path";
|
|
21
|
+
|
|
22
|
+
export type AgentSource = "project" | "global" | "bundled";
|
|
23
|
+
|
|
24
|
+
export interface AgentDef {
|
|
25
|
+
name: string;
|
|
26
|
+
description: string;
|
|
27
|
+
tools: string[];
|
|
28
|
+
model: string;
|
|
29
|
+
systemPrompt: string;
|
|
30
|
+
filePath: string;
|
|
31
|
+
source: AgentSource;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parse an agent .md file: YAML frontmatter (name, description, tools, model)
|
|
36
|
+
* followed by the system prompt body. `tools` is a comma-separated list.
|
|
37
|
+
* Returns null when the file has no frontmatter block.
|
|
38
|
+
*/
|
|
39
|
+
export function parseAgentMarkdown(content: string, filePath: string, source: AgentSource): AgentDef | null {
|
|
40
|
+
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
41
|
+
if (!fmMatch) return null;
|
|
42
|
+
|
|
43
|
+
const fields: Record<string, string> = {};
|
|
44
|
+
for (const line of fmMatch[1].split("\n")) {
|
|
45
|
+
const m = line.match(/^(\w+):\s*(.*)$/);
|
|
46
|
+
if (m) fields[m[1]] = m[2].trim();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const name = fields.name || basename(filePath).replace(/\.md$/, "");
|
|
50
|
+
if (!name) return null;
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
name,
|
|
54
|
+
description: fields.description || "No description",
|
|
55
|
+
tools: (fields.tools || "")
|
|
56
|
+
.split(",")
|
|
57
|
+
.map((t) => t.trim())
|
|
58
|
+
.filter(Boolean),
|
|
59
|
+
model: fields.model || "default",
|
|
60
|
+
systemPrompt: fmMatch[2].trim(),
|
|
61
|
+
filePath,
|
|
62
|
+
source,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readAgentFile(filePath: string, source: AgentSource): AgentDef | null {
|
|
67
|
+
try {
|
|
68
|
+
return parseAgentMarkdown(readFileSync(filePath, "utf-8"), filePath, source);
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Agent directories in precedence order (project > global > bundled). */
|
|
75
|
+
export function agentSearchDirs(cwd: string = process.cwd()): Array<{ dir: string; source: AgentSource }> {
|
|
76
|
+
return [
|
|
77
|
+
{ dir: join(cwd, ".phi", "agents"), source: "project" },
|
|
78
|
+
{ dir: join(homedir(), ".phi", "agent", "agents"), source: "global" },
|
|
79
|
+
{ dir: join(__dirname, "..", "..", "..", "agents"), source: "bundled" },
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Load a single agent definition by name. Used by the /plan orchestrator to
|
|
85
|
+
* activate a phase persona.
|
|
86
|
+
*/
|
|
87
|
+
export function loadAgentDef(name: string, cwd: string = process.cwd()): AgentDef | null {
|
|
88
|
+
for (const { dir, source } of agentSearchDirs(cwd)) {
|
|
89
|
+
const def = readAgentFile(join(dir, `${name}.md`), source);
|
|
90
|
+
if (def) return def;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Discover every agent definition across all sources, first match per name
|
|
97
|
+
* wins. Used by the /agents command.
|
|
98
|
+
*/
|
|
99
|
+
export function discoverAgents(cwd: string = process.cwd()): AgentDef[] {
|
|
100
|
+
const seen = new Set<string>();
|
|
101
|
+
const agents: AgentDef[] = [];
|
|
102
|
+
|
|
103
|
+
for (const { dir, source } of agentSearchDirs(cwd)) {
|
|
104
|
+
if (!existsSync(dir)) continue;
|
|
105
|
+
let entries: string[];
|
|
106
|
+
try {
|
|
107
|
+
entries = readdirSync(dir);
|
|
108
|
+
} catch {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
for (const entry of entries) {
|
|
112
|
+
if (!entry.endsWith(".md")) continue;
|
|
113
|
+
const filePath = join(dir, entry);
|
|
114
|
+
try {
|
|
115
|
+
if (!statSync(filePath).isFile()) continue;
|
|
116
|
+
} catch {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const def = readAgentFile(filePath, source);
|
|
120
|
+
if (def && !seen.has(def.name)) {
|
|
121
|
+
seen.add(def.name);
|
|
122
|
+
agents.push(def);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return agents;
|
|
128
|
+
}
|
|
@@ -28,12 +28,12 @@ import { join } from "node:path";
|
|
|
28
28
|
import { homedir } from "node:os";
|
|
29
29
|
|
|
30
30
|
export default function skillLoaderExtension(pi: ExtensionAPI) {
|
|
31
|
-
// Bundled skills live
|
|
32
|
-
// copies them to ~/.phi/agent/skills
|
|
33
|
-
//
|
|
34
|
-
//
|
|
31
|
+
// Bundled skills live at <package>/skills in the repo layout (two hops up
|
|
32
|
+
// from extensions/phi/); postinstall copies them to ~/.phi/agent/skills
|
|
33
|
+
// (== globalDir) in the installed layout. Probe both (the scanner dedupes
|
|
34
|
+
// by skill name).
|
|
35
35
|
const bundledCandidates = [
|
|
36
|
-
join(__dirname, "..", "..", "
|
|
36
|
+
join(__dirname, "..", "..", "skills"),
|
|
37
37
|
join(homedir(), ".phi", "agent", "skills"),
|
|
38
38
|
];
|
|
39
39
|
const config: SkillsConfig = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phi-code-admin/phi-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.86.0",
|
|
4
4
|
"description": "Coding agent CLI with persistent memory, sub-agents, intelligent routing, and orchestration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"skills",
|
|
30
30
|
"agents",
|
|
31
31
|
"extensions",
|
|
32
|
+
"config",
|
|
32
33
|
"README.md",
|
|
33
34
|
"LICENSE",
|
|
34
35
|
"CHANGELOG.md",
|