Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/coding-agent/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

## [Unreleased]

### Breaking Changes

- SDK-provided `Skill` objects now carry stable catalog identity, provenance, captured content, digest, and version fields. Use `createSkill()` to construct custom skills.

### Added

- Added a unified skill catalog shared by interactive selection, prompts, commands, SDK and RPC consumers, and frozen evolution inputs.
- Added `pi auth check` provider/model auth preflight with optional credential output ([#7152](https://github.com/earendil-works/pi/issues/7152)).

### Changed
Expand Down
7 changes: 4 additions & 3 deletions packages/coding-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ Standard editing keybindings for delete word, undo, etc. See [docs/keybindings.m

### Commands

Type `/` in the editor to trigger commands. [Extensions](#extensions) can register custom commands, [skills](#skills) are available as `/skill:name`, and [prompt templates](#prompt-templates) expand via `/templatename`.
Type `/` in the editor to trigger commands. [Extensions](#extensions) can register custom commands, [skills](#skills) are referenced through `/skills`, and [prompt templates](#prompt-templates) expand via `/templatename`.

| Command | Description |
|---------|-------------|
Expand All @@ -175,6 +175,7 @@ Type `/` in the editor to trigger commands. [Extensions](#extensions) can regist
| `/model` | Switch models |
| `/scoped-models` | Enable/disable models for Ctrl+P cycling |
| `/settings` | Thinking level, theme, message delivery, transport |
| [`/skills`](docs/skills.md#skill-commands) | Search and reference one or more skills in the editor |
| `/resume` | Pick from previous sessions |
| `/new` | Start a new session |
| `/name <name>` | Set session display name |
Expand Down Expand Up @@ -352,7 +353,7 @@ Place in `~/.diffex/agent/prompts/`, `.diffex/prompts/`, or a [Diffex package](#

### Skills

On-demand capability packages following the [Agent Skills standard](https://agentskills.io). Invoke via `/skill:name` or let the agent load them automatically.
On-demand capability packages following the [Agent Skills standard](https://agentskills.io). Use `/skills` to choose installed workspace, installed global, or evolved skills and insert one or more versioned references into a task, or let the agent load skills automatically.

```markdown
<!-- ~/.diffex/agent/skills/my-skill/SKILL.md -->
Expand Down Expand Up @@ -434,7 +435,7 @@ diffex update npm:@foo/diffex-tools # update one package
diffex config # enable/disable extensions, skills, prompts, themes
```

Packages install to `~/.diffex/agent/git/` (git) or `~/.diffex/agent/npm/` (npm). Use `-l` for project-local installs (`.diffex/git/`, `.diffex/npm/`). Git `@ref` values are pinned tags or commits; pinned packages are skipped by `diffex update --extensions` and `diffex update --all`, so use `diffex install git:host/user/repo@new-ref` to move an existing package to a new ref. Git packages install dependencies with `npm install --omit=dev` by default, so runtime deps must be listed under `dependencies`; when `npmCommand` is configured, git packages use plain `install` for compatibility with wrappers. If you use a Node version manager and want package installs to reuse a stable npm context, set `npmCommand` in `settings.json`, for example `["mise", "exec", "node@20", "--", "npm"]`.
Interactive package installs ask whether to use global storage (`~/.diffex/agent/git/`, `~/.diffex/agent/npm/`) or workspace storage (`.diffex/git/`, `.diffex/npm/`) and show the trust implications. Non-interactive installs default to global; use `-l` to select workspace scope explicitly. Git `@ref` values are pinned tags or commits; pinned packages are skipped by `diffex update --extensions` and `diffex update --all`, so use `diffex install git:host/user/repo@new-ref` to move an existing package to a new ref. Git packages install dependencies with `npm install --omit=dev` by default, so runtime deps must be listed under `dependencies`; when `npmCommand` is configured, git packages use plain `install` for compatibility with wrappers. If you use a Node version manager and want package installs to reuse a stable npm context, set `npmCommand` in `settings.json`, for example `["mise", "exec", "node@20", "--", "npm"]`.

Create a package by adding the compatibility `diffex` key to `package.json`:

Expand Down
19 changes: 7 additions & 12 deletions packages/coding-agent/docs/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ user sends prompt ────────────────────
│ │
├─► (extension commands checked first, bypass if found) │
├─► input (can intercept, transform, or handle) │
├─► (skill/template expansion if not handled) │
├─► (skill-reference/template expansion if not handled) │
├─► before_agent_start (can inject message, modify system prompt)
├─► agent_start │
├─► message_start / message_update / message_end │
Expand Down Expand Up @@ -883,13 +883,13 @@ diffex.on("user_bash", (event, ctx) => {

#### input

Fired when user input is received, after extension commands are checked but before skill and template expansion. The event sees the raw input text, so `/skill:foo` and `/template` are not yet expanded.
Fired when user input is received, after extension commands are checked but before explicit skill references and prompt templates are expanded. The event sees the raw input text, so selected skill references and `/template` have not yet expanded.

**Processing order:**
1. Extension commands (`/cmd`) checked first - if found, handler runs and input event is skipped
2. `input` event fires - can intercept, transform, or handle
3. If not handled: skill commands (`/skill:name`) expanded to skill content
4. If not handled: prompt templates (`/template`) expanded to template content
3. If not handled: prompt templates (`/template`) expand to template content
4. If not handled: explicit references inserted through `/skills` expand to captured skill content
5. Agent processing begins (`before_agent_start`, etc.)

```typescript
Expand All @@ -914,11 +914,6 @@ diffex.on("input", async (event, ctx) => {
// Route by source: skip processing for extension-injected messages
if (event.source === "extension") return { action: "continue" };

// Intercept skill commands before expansion
if (event.text.startsWith("/skill:")) {
// Could transform, block, or let pass through
}

return { action: "continue" }; // Default: pass through to expansion
});
```
Expand Down Expand Up @@ -1528,8 +1523,8 @@ diffex.registerCommand("deploy", {

### diffex.getCommands()

Get the slash commands available for invocation via `prompt` in the current session. Includes extension commands, prompt templates, and skill commands.
The list matches the RPC `get_commands` ordering: extensions first, then templates, then skills.
Get the slash commands available for invocation via `prompt` in the current session. Includes extension commands and prompt templates.
The list matches the RPC `get_commands` ordering: extensions first, then templates.

```typescript
const commands = diffex.getCommands();
Expand All @@ -1543,7 +1538,7 @@ Each entry has this shape:
{
name: string; // Invokable command name without the leading slash. May be suffixed like "review:1"
description?: string;
source: "extension" | "prompt" | "skill";
source: "extension" | "prompt";
sourceInfo: {
path: string;
source: string;
Expand Down
8 changes: 8 additions & 0 deletions packages/coding-agent/docs/keybindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ This routing remains configurable through the ordinary action bindings. For exam
| `app.tree.filter.cycleForward` | `ctrl+o` | Cycle tree filter forward |
| `app.tree.filter.cycleBackward` | `shift+ctrl+o` | Cycle tree filter backward |

### Skills Selector

Used inside the multi-select skill picker opened via `/skills`.

| Keybinding id | Default | Description |
|--------|---------|-------------|
| `app.skills.toggle` | `space` | Toggle the current skill without closing the selector |

### Scoped Models Selector

Used inside the scoped models selector (opened via `/scoped-models`).
Expand Down
4 changes: 3 additions & 1 deletion packages/coding-agent/docs/packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ diffex update --extension npm:@foo/bar

These commands manage Diffex packages and `diffex update` can update the Diffex CLI installation. To uninstall Diffex itself, see [Quickstart](quickstart.md#uninstall).

By default, `install` and `remove` write to user settings (`~/.diffex/agent/settings.json`). Use `-l` to write to project settings (`.diffex/settings.json`) instead. Project settings can be shared with your team, and Diffex installs any missing packages automatically on startup after the project is trusted.
In an interactive terminal, `install` asks whether to install globally or for the current workspace and shows the settings and package-storage destinations plus the security implications. Workspace installation requires project trust; cancellation, an untrusted workspace, or an unwritable destination never falls back to global installation. In non-interactive environments, `install` retains its global default. Pass `-l` to choose workspace scope explicitly without a scope prompt.

`remove` writes to user settings (`~/.diffex/agent/settings.json`) by default; use `-l` for project settings (`.diffex/settings.json`). Project settings can be shared with your team, and Diffex installs any missing packages automatically on startup after the project is trusted.

To try a package without installing it, use `--extension` or `-e`. This installs to a temporary directory for the current run only:

Expand Down
12 changes: 5 additions & 7 deletions packages/coding-agent/docs/rpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ If the agent is streaming and no `streamingBehavior` is specified, the command r

**Extension commands**: If the message is an extension command (e.g., `/mycommand`), it executes immediately even during streaming. Extension commands manage their own LLM interaction via `diffex.sendMessage()`.

**Input expansion**: Skill commands (`/skill:name`) and prompt templates (`/template`) are expanded before sending/queueing.
**Input expansion**: Prompt templates (`/template`) and explicit versioned skill references are expanded before sending or queueing.

Response:
```json
Expand All @@ -83,7 +83,7 @@ The `images` field is optional. Each image uses `ImageContent` format: `{"type":

#### steer

Queue a steering message while the agent is running. It is delivered after the current assistant turn finishes executing its tool calls, before the next LLM call. Skill commands and prompt templates are expanded. Extension commands are not allowed (use `prompt` instead).
Queue a steering message while the agent is running. It is delivered after the current assistant turn finishes executing its tool calls, before the next LLM call. Prompt templates and explicit skill references are expanded. Extension commands are not allowed (use `prompt` instead).

```json
{"type": "steer", "message": "Stop and do this instead"}
Expand All @@ -105,7 +105,7 @@ See [set_steering_mode](#set_steering_mode) for controlling how steering message

#### follow_up

Queue a follow-up message to be processed after the agent finishes. Delivered only when agent has no more tool calls or steering messages. Skill commands and prompt templates are expanded. Extension commands are not allowed (use `prompt` instead).
Queue a follow-up message to be processed after the agent finishes. Delivered only when agent has no more tool calls or steering messages. Prompt templates and explicit skill references are expanded. Extension commands are not allowed (use `prompt` instead).

```json
{"type": "follow_up", "message": "After you're done, also do this"}
Expand Down Expand Up @@ -798,7 +798,7 @@ The current session name is available via `get_state` in the `sessionName` field

#### get_commands

Get available commands (extension commands, prompt templates, and skills). These can be invoked via the `prompt` command by prefixing with `/`.
Get available extension commands and prompt templates. These can be invoked via the `prompt` command by prefixing with `/`.

```json
{"type": "get_commands"}
Expand All @@ -813,8 +813,7 @@ Response:
"data": {
"commands": [
{"name": "session-name", "description": "Set or clear session name", "source": "extension", "path": "/home/user/.diffex/agent/extensions/session.ts"},
{"name": "fix-tests", "description": "Fix failing tests", "source": "prompt", "location": "project", "path": "/home/user/myproject/.diffex/agent/prompts/fix-tests.md"},
{"name": "skill:brave-search", "description": "Web search via Brave API", "source": "skill", "location": "user", "path": "/home/user/.diffex/agent/skills/brave-search/SKILL.md"}
{"name": "fix-tests", "description": "Fix failing tests", "source": "prompt", "location": "project", "path": "/home/user/myproject/.diffex/agent/prompts/fix-tests.md"}
]
}
}
Expand All @@ -826,7 +825,6 @@ Each command has:
- `source`: What kind of command:
- `"extension"`: Registered via `diffex.registerCommand()` in an extension
- `"prompt"`: Loaded from a prompt template `.md` file
- `"skill"`: Loaded from a skill directory (name is prefixed with `skill:`)
- `location`: Where it was loaded from (optional, not present for extensions):
- `"user"`: User-level (`~/.diffex/agent/`)
- `"project"`: Project-level (`./.diffex/agent/`)
Expand Down
19 changes: 18 additions & 1 deletion packages/coding-agent/docs/sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -970,12 +970,29 @@ const loader = new DefaultResourceLoader({
await loader.reload();

const extensions = loader.getExtensions();
const skills = loader.getSkills();
const skillCatalog = loader.getSkills();
const prompts = loader.getPrompts();
const themes = loader.getThemes();
const contextFiles = loader.getAgentsFiles().agentsFiles;
```

`getSkills()` returns the session's unified `SkillCatalog`. Every skill includes its stable `skillId`, `sourceKind`, `scope`, captured `content`, `contentDigest`, and `version` in addition to its model-visible name, description, and location. Catalog diagnostics retain invalid, disabled, and colliding entries that were not admitted.

Use `createSkill()` for SDK-provided skills so the same identity and version rules apply:

```typescript
import { createSkill, createSyntheticSourceInfo } from "@earendil-works/diffex-coding-agent";

const skill = createSkill({
name: "review",
description: "Reviews repository changes.",
filePath: "/virtual/review/SKILL.md",
baseDir: "/virtual/review",
sourceInfo: createSyntheticSourceInfo("/virtual/review/SKILL.md", { source: "sdk" }),
content: "# Review\n\nReview the requested changes.",
});
```

## Return Value

`createAgentSession()` returns:
Expand Down
1 change: 0 additions & 1 deletion packages/coding-agent/docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,6 @@ Paths in `~/.diffex/agent/settings.json` resolve relative to `~/.diffex/agent`.
| `skills` | string[] | `[]` | Local skill file paths or directories |
| `prompts` | string[] | `[]` | Local prompt template paths or directories |
| `themes` | string[] | `[]` | Local theme file paths or directories |
| `enableSkillCommands` | boolean | `true` | Register skills as `/skill:name` commands |

Arrays support glob patterns and exclusions. Use `!pattern` to exclude. Use `+path` to force-include an exact path and `-path` to force-exclude an exact path.

Expand Down
30 changes: 9 additions & 21 deletions packages/coding-agent/docs/skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,31 +63,19 @@ For project-level Claude Code skills, add to `.diffex/settings.json`:

## How Skills Work

1. At startup, Diffex scans skill locations and extracts names and descriptions
2. The system prompt includes available skills in XML format per the [specification](https://agentskills.io/integrate-skills)
3. When a task matches, the agent uses `read` to load the full SKILL.md (models don't always do this; use prompting or `/skill:name` to force it)
4. The agent follows the instructions, using relative paths to reference scripts and assets
1. At startup, Diffex scans skill locations and builds one catalog for the session. Each entry has a stable source identity, source kind, scope, content digest, version, and the full content captured at load time.
2. The system prompt includes available skills in XML format per the [specification](https://agentskills.io/integrate-skills).
3. When a task matches, the agent uses `read` to load the full SKILL.md (models don't always do this; use `/skills` to reference it explicitly).
4. Explicit references use the catalog's captured version, so a file change does not alter a request that has already been admitted. Reload resources to catalog a changed installed skill.
5. The agent follows the instructions, using relative paths to reference scripts and assets.

This is progressive disclosure: only descriptions are always in context, full instructions load on-demand.
This is progressive disclosure: only names, descriptions, and locations are always in context; full instructions load on-demand. The TUI, system prompt, SDK, and evolution snapshots all read from the same catalog.

## Skill Commands

Skills register as `/skill:name` commands:
Use `/skills` in interactive mode to choose installed workspace, installed global, or evolved skills, then search and select one or more skills within that category. Selected skills appear in the editor as purple skill-name tokens while retaining versioned references internally, so you can add the task before submitting. Each reference loads that exact reviewed skill content for one request, including queued steering and follow-up messages. Skills not selected remain progressively disclosed by name, description, and location.

```bash
/skill:brave-search # Load and execute the skill
/skill:pdf-tools extract # Load skill with arguments
```

Arguments after the command are appended to the skill content as `User: <args>`.

Toggle skill commands via `/settings` in interactive mode or in `settings.json`:

```json
{
"enableSkillCommands": true
}
```
To install a package containing skills from an interactive terminal, run `diffex install <source>` and choose `Install globally` or `Install for this workspace`. The prompt shows both destinations and the trust implications. Workspace installation requires project trust and never falls back to global installation. Non-interactive installs retain the global default; pass `-l` to select workspace scope explicitly.

## Skill Structure

Expand Down Expand Up @@ -146,7 +134,7 @@ Per the [Agent Skills specification](https://agentskills.io/specification#frontm
| `compatibility` | No | Max 500 chars. Environment requirements. |
| `metadata` | No | Arbitrary key-value mapping. |
| `allowed-tools` | No | Space-delimited list of pre-approved tools (experimental). |
| `disable-model-invocation` | No | When `true`, skill is hidden from system prompt. Users must use `/skill:name`. |
| `disable-model-invocation` | No | When `true`, skill is hidden from the system prompt. Users must reference it explicitly through `/skills`. |

### Name Rules

Expand Down
3 changes: 2 additions & 1 deletion packages/coding-agent/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ See [Keybindings](keybindings.md) for all shortcuts and customization.

## Slash Commands

Type `/` in the editor to open command completion. Extensions can register custom commands, skills are available as `/skill:name`, and prompt templates expand via `/templatename`.
Type `/` in the editor to open command completion. Extensions can register custom commands, skills are referenced through `/skills`, and prompt templates expand via `/templatename`.

| Command | Description |
|---------|-------------|
Expand All @@ -41,6 +41,7 @@ Type `/` in the editor to open command completion. Extensions can register custo
| `/model` | Switch models |
| `/scoped-models` | Enable/disable models for Ctrl+P cycling |
| `/settings` | Thinking level, theme, message delivery, transport |
| `/skills` | Browse installed workspace, installed global, and evolved skills |
| `/resume` | Pick from previous sessions |
| `/new` | Start a new session |
| `/name <name>` | Set session display name |
Expand Down
Loading