Pi Web Plugin and Skill Management APIs: Complete Reference for Extension Management

Pi Web's plugin and skill management APIs expose HTTP endpoints under /api/plugins and /api/skills that enable listing, installing, updating, enabling, disabling, and toggling extensions and individual skills with security controls and trust validation.

The pi-coding-agent SDK provides the underlying engine for these operations, with routes defined in app/api/ and core logic delegated to DefaultPackageManager, DefaultResourceLoader, and SettingsManager. This guide covers the complete API surface for managing both plugin packages and individual skills in Pi Web.

Plugin API (/api/plugins)

The plugin management endpoint in app/api/plugins/route.ts handles two primary methods: GET for listing and POST for state changes.

GET /api/plugins

Returns a complete snapshot of every configured plugin package for the specified working directory.

// Query parameters
curl "http://localhost:3000/api/plugins?cwd=/path/to/project"

The readPlugins() function validates the cwd parameter against allowed file roots, loads settings from ~/.pi/agent/settings.json and project-specific files, and returns a PluginsResponse containing per-package metadata, resource totals, and diagnostics.

POST /api/plugins

Executes package lifecycle actions with a JSON body specifying action, optional source and scope, and mandatory cwd.

Action Description
install Adds a new package from npm, git, or local source
remove Uninstalls an existing package
update Refreshes package to latest compatible version
disable Marks package as disabled in settings without removal
enable Reactivates a previously disabled package
// Install an npm package globally
const installBody = {
  action: 'install',
  cwd: '/home/user/project',
  source: 'npm:@pi-ai/core-tools@^2.0.0',
  scope: 'global'
};

const response = await fetch('/api/plugins', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(installBody)
});

Security and Trust Verification

Every plugin operation enforces three security layers:

  • Path validation – getAllowedFileRoots() and isExistingFilePathAllowed() ensure cwd resides within permitted directories
  • Project trust – getProjectTrustStatus() from lib/project-trust.ts must return true for project-scoped modifications
  • Settings persistence – setPackageDisabled() updates the in-memory settings and flush() writes to disk

Resource Aggregation

DefaultPackageManager.collectResources() builds resource maps tracking four resource types per package:

  • extensions – Code contributions to the IDE
  • skills – Markdown-based assistant capabilities
  • prompts – Reusable prompt templates
  • themes – Visual customization definitions

The readPackageMetadata() helper reads package.json for installed packages, while getConfiguredVersion() parses version hints from source strings like npm:foo@1.2.3.

Skill API (/api/skills)

Skill management operates on individual .md files rather than packages. The endpoint in app/api/skills/route.ts delegates loading to lib/skills-service.ts.

GET /api/skills

Returns all discoverable skills for a working directory with install metadata.

// Load skills for current project
const skills = await fetch(`/api/skills?cwd=${encodeURIComponent(cwd)}`)
  .then(r => r.json()); // => SkillsResponse

The loadSkillsWithInstallInfo() function:

  1. Instantiates DefaultResourceLoader with cwd and global agentDir
  2. Calls loader.getSkills() with projectTrustReloadOptions based on trust status
  3. Applies annotateSkillsWithInstallInfo() from lib/skill-lock.ts to attach package source, scope, and version hash

PATCH /api/skills

Toggles the disable-model-invocation front-matter flag without modifying skill content.

// Disable LLM invocation for a specific skill
const patchBody = {
  filePath: '/home/user/.agents/skills/database/SKILL.md',
  disableModelInvocation: true
};

await fetch('/api/skills', {
  method: 'PATCH',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(patchBody)
});

The handler:

  • Validates filePath against allowed roots (including ~/.agents/skills via explicit whitelist addition)
  • Parses YAML front-matter with parseFrontmatter
  • Inserts or removes disable-model-invocation while preserving formatting
  • Writes back with writeFileSync

Common Infrastructure

Both APIs share foundational security and type systems defined in lib/api-types.ts.

File Access Security

lib/file-access.ts centralizes path validation:

  • getAllowedFileRoots() – Returns permitted directory list
  • isExistingFilePathAllowed() – Validates specific paths

Project Trust System

lib/project-trust.ts governs whether project-scoped resources load automatically:

  • Trusted projects: Full resource loading with projectTrustReloadOptions
  • Untrusted projects: Restricted loading or explicit user confirmation required

Type Definitions

Core response shapes in api-types.ts:

Type Purpose
PluginsResponse Complete plugin listing with PluginPackageInfo[], totals, diagnostics
SkillsResponse Skill collection with metadata annotations
PluginPackageInfo Single package record with resources, version, install status
SkillInfo Individual skill with front-matter and provenance

Practical Integration Examples

Complete Plugin Lifecycle

import { useCallback, useEffect, useState } from 'react';

function usePluginManager(cwd) {
  const [plugins, setPlugins] = useState(null);

  const refresh = useCallback(async () => {
    const res = await fetch(`/api/plugins?cwd=${encodeURIComponent(cwd)}`);
    const data = await res.json();
    setPlugins(data);
    return data;
  }, [cwd]);

  const install = useCallback(async (source, scope = 'global') => {
    await fetch('/api/plugins', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ action: 'install', cwd, source, scope })
    });
    return refresh();
  }, [cwd, refresh]);

  const toggle = useCallback(async (packageName, enable) => {
    await fetch('/api/plugins', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ 
        action: enable ? 'enable' : 'disable', 
        cwd, 
        source: packageName 
      })
    });
    return refresh();
  }, [cwd, refresh]);

  useEffect(() => { refresh(); }, [refresh]);

  return { plugins, install, toggle, refresh };
}

Skill Invocation Control

// React component for skill management
function SkillToggle({ skill, onChange }) {
  const handleToggle = async () => {
    const newValue = !skill.disableModelInvocation;
    await fetch('/api/skills', {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        filePath: skill.filePath,
        disableModelInvocation: newValue
      })
    });
    onChange();
  };

  return (
    <button onClick={handleToggle}>
      {skill.disableModelInvocation ? 'Enable' : 'Disable'} LLM Use
    </button>
  );
}

Key Implementation Files

File Responsibility
app/api/plugins/route.ts Plugin GET/POST handlers, readPlugins() implementation
app/api/skills/route.ts Skill GET/PATCH handlers, front-matter manipulation
lib/skills-service.ts loadSkillsWithInstallInfo(), resource loader orchestration
lib/api-types.ts TypeScript interfaces for all API responses
lib/file-access.ts Whitelist validation and security boundaries
lib/project-trust.ts Trust determination for project-scoped operations
lib/skill-lock.ts Install metadata annotation for skills

Summary

  • Two API endpoints provide complete plugin and skill management: /api/plugins for packages and /api/skills for individual capabilities
  • Five plugin actions (install, remove, update, disable, enable) run through POST with scope control for global versus project-specific extension
  • Skill PATCH enables runtime toggling of LLM invocation without content modification via YAML front-matter manipulation
  • Security layers include path whitelisting in lib/file-access.ts, project trust verification in lib/project-trust.ts, and explicit global skills directory handling
  • Type safety derives from centralized definitions in lib/api-types.ts with response shapes matching PluginsResponse and SkillsResponse

Frequently Asked Questions

How does Pi Web validate that plugin operations are allowed?

Pi Web validates operations through getAllowedFileRoots() and isExistingFilePathAllowed() in lib/file-access.ts, which ensure the cwd parameter resides within permitted directories. For project-scoped modifications, getProjectTrustStatus() additionally verifies the project is trusted before allowing state changes.

What is the difference between disabling and removing a plugin?

Disabling a plugin via action: 'disable' marks it as inactive in settings without uninstalling files—the package remains on disk but contributes zero resources. Removing via action: 'remove' completely uninstalls the package and deletes its files. Disabled packages can be re-enabled instantly; removed packages must be re-installed from source.

Can skills be managed independently of their parent plugins?

Yes. The /api/skills endpoint provides granular control over individual skills regardless of package origin. The PATCH method modifies only the disable-model-invocation front-matter flag in the specific .md file, leaving the skill's package and other skills untouched. This permits fine-grained capability control without affecting extension-wide settings.

Where does Pi Web store plugin and skill configuration?

Global configuration resides in ~/.pi/agent/settings.json managed by SettingsManager, with project-specific overrides in each project's settings file. Disabled states are persisted as empty resource arrays. The global skills directory at ~/.agents/skills is explicitly whitelisted for access even though it lives outside the agent directory proper.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →