npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@drive9/drive9-pi

v0.1.0

Published

Turnkey Drive9 filesystem and durable evidence integration for Pi

Readme

drive9-pi

Drive9 filesystem and durable tool-result evidence integration for Pi.

The normative contract is docs/design-lock.md.

Install

Install the public npm package through Pi's standard package manager:

# Personal install: recorded in ~/.pi/agent/settings.json
pi install npm:@drive9/drive9-pi

# Team/project install: recorded in .pi/settings.json
pi install -l npm:@drive9/drive9-pi
git add .pi/settings.json

To test an unreleased repository revision instead, use Pi's Git package source:

pi install git:github.com/drive9-ai/drive9-pi@tag-or-commit
pi install -l git:github.com/drive9-ai/drive9-pi@tag-or-commit

After a teammate trusts the project, Pi installs a missing project package on startup. Pi packages execute code with the permissions of the Pi process, so review packages before trusting them. For Git sources, Pi's normal @tag-or-commit suffix pins the exact revision.

Authenticate Drive9

The extension uses the normal Drive9 SDK credentials. Configure them once in ~/.drive9/config, or provide the same environment variables consumed by Client.defaultClient():

export DRIVE9_SERVER="https://api.drive9.ai"
export DRIVE9_API_KEY="d9_..."

/drive9 setup does not ask for, write, or copy Drive9 credentials. In particular, .pi/drive9.json contains only non-secret project settings and can be shared independently of each teammate's Drive9 credentials.

Configure a Project

Start Pi from the local project that should use Drive9:

cd ./my-project
pi

Inside Pi, run:

/drive9 setup

Choose /workspaces/my-project when prompted. You can also supply the root directly as /drive9 setup /workspaces/my-project. Everything after setup is treated as the root, so /drive9 setup /workspaces/team project also works. Setup performs a Drive9 preflight before changing the project: credentials must work, the root must be a directory, and it cannot be the tenant root /. If the root is missing, setup can create it and any missing parent directories after one confirmation. The Drive9 CLI is not required for setup.

After a successful preflight, setup ensures .pi/settings.json exists as Pi's standard project-trust marker, atomically writes .pi/drive9.json, and reloads Pi. It never overwrites an existing .pi/settings.json. A failed preflight leaves the previous configuration untouched.

The generated file has this strict, versioned shape:

{
  "version": 1,
  "enabled": true,
  "root": "/workspaces/my-project"
}

See schema/drive9.schema.json for the JSON Schema. A project configuration is valid only when .pi/settings.json is also present and Pi trusts the project; a standalone .pi/drive9.json is not loaded. Commit both files when the whole team should use the same Drive9 root.

Commands and State

All management commands are available in interactive Pi sessions:

| Command | Behavior | | --- | --- | | /drive9 setup [root] | Prompt for a root when omitted, preflight it, atomically save it, and reload Pi. It never collects authentication. | | /drive9 status | Report the resolved state, configuration source, root, trust status, and last successful check without exposing secrets. An unavailable state includes its initialization error. | | /drive9 disable | For project or programmatic activation, save enabled: false while retaining the root, then reload Pi. CLI and environment overrides must instead be removed or suppressed with --no-drive9. | | /drive9 verify | Perform a read-only stat/list verification. | | /drive9 verify write | Write, read back, and explicitly delete a randomly named temporary file. Success means that delete completed; a detected cleanup failure is reported as a verification failure. |

The footer reflects the resolved runtime state:

| State | Footer | Meaning | | --- | --- | --- | | Inactive | none | No Drive9 root was requested; Pi uses its ordinary local tools. | | Disabled | Drive9: off | The project config is disabled, or --no-drive9 was used. | | Checking | Drive9: checking… | Drive9 preflight is in progress. | | Active | Drive9: /workspaces/my-project | Filesystem operations are routed to the selected root. | | Unavailable | Drive9: unavailable | Drive9 was requested but preflight failed; the extension does not fall back to local files. |

One-Shot and Headless Usage

Flags override the saved project choice for one process and never rewrite .pi/drive9.json:

# Use a different root for this session
pi --drive9-root /workspaces/one-off

# Keep Drive9 disabled for this session
pi --no-drive9

DRIVE9_PI_ROOT=/workspaces/one-off pi remains available for environment-based automation. The explicit CLI flags are clearer for manual one-shot use.

Non-interactive modes never open the setup UI or create project configuration. When an enabled project config, DRIVE9_PI_ROOT, or --drive9-root explicitly requests Drive9 and preflight fails, the extension reports a DRIVE9_INIT_FAILED extension error and keeps the Drive9-controlled tool surface fail-closed instead of silently using local filesystem tools. Pi hosts decide how extension errors affect provider turns and process exit status, so automation must treat that extension error as fatal rather than relying only on a non-zero exit code. With no Drive9 request, or with Drive9 explicitly disabled, normal local Pi behavior remains available.

Project-local packages and .pi/drive9.json require project trust. Headless Pi does not show a trust prompt, so use a saved trust decision or pass --approve only after reviewing the project:

pi --approve --drive9-root /workspaces/my-project -p \
  'Write "hello from headless Pi" to headless.txt'

End-to-End Hello Example

This example proves that Pi wrote to Drive9 rather than to a same-named host path. It does not require the Drive9 CLI.

# Start Pi in the local project
cd ./my-project
pi

Set up the root and then ask Pi:

/drive9 setup /workspaces/my-project

Use the write tool to write exactly "hello from Pi\n" to hello.txt,
then use the read tool to confirm it.

Verify through the extension:

/drive9 verify write

If the Drive9 CLI is already installed, drive9 fs cat :/workspaces/my-project/hello.txt provides an optional independent check.

Manage or Remove the Package

Use Pi's standard package commands rather than editing settings by hand:

pi list                              # show installed package sources
pi config                            # enable/disable personal resources
pi config -l                         # configure trusted project resources
pi remove npm:@drive9/drive9-pi      # personal npm install
pi remove -l npm:@drive9/drive9-pi   # project npm install

For a Git install, pass its original git:github.com/drive9-ai/drive9-pi source to pi remove instead. /drive9 disable keeps the package installed and only turns off Drive9 for the project. pi config controls whether Pi loads the package resource, while pi remove removes the package registration.

Storage-Only Boundary

When Drive9 is active, the extension replaces Pi's filesystem tools with the official coding-agent tool factories and their standard schemas and result shapes:

  • read, write, and edit use Drive9 SDK operations;
  • ls is Pi's canonical directory-listing tool backed by Drive9;
  • relative paths resolve from the selected Drive9 root;
  • the system prompt identifies the Drive9 workspace.

Drive9 is storage, not compute. In Drive9 mode, model bash, grep, and find calls fail closed instead of operating on a different host filesystem. The extension also registers a refusal handler for interactive ! commands; the Pi interceptor-order limitation is described below. Applications that need a shared filesystem and process world must provide a separate sandbox or mount bridge; this package never pretends a local process can open an SDK-only Drive9 path. A requested but unavailable Drive9 root also fails closed and never resumes local model-tool access.

Extension Composition and Load Order

Drive9 uses Pi's standard same-name tool override mechanism for read, write, edit, and ls. Pi owns tool-conflict diagnostics and precedence: built-in override warnings are expected, and when two extensions register the same tool name, extension load order determines the winner. Treat a conflict involving those four tools as unsafe; disable the competing extension or arrange for Drive9 to be the winning owner before using Drive9 mode. Drive9 also removes model process tools from the active set and blocks them at the tool-call boundary while its remote filesystem is active.

Interactive ! commands use Pi's user_bash interceptor chain rather than the tool registry. The first interceptor that returns operations wins, so an earlier-loaded shell/SSH/sandbox extension can prevent Drive9's storage-only refusal from running. Pi does not expose effective user_bash ownership to an extension, so Drive9 cannot verify or enforce that ordering. Load Drive9 before other user_bash interceptors, or do not use interactive ! commands in that composition. Treat ! as an explicit user-controlled host escape hatch, not part of the Drive9 model-tool isolation boundary. Pi's normal tool-name conflict diagnostics do not detect this event-handler ordering limitation.

Programmatic Coding-Agent Integration

Pi extensions that need explicit configuration can reuse the same extension factory:

import type { ExtensionFactory } from "@earendil-works/pi-coding-agent";
import { Client } from "drive9";
import { createDrive9PiExtension } from "@drive9/drive9-pi";

const extension: ExtensionFactory = createDrive9PiExtension({
  defaultRoot: "/workspaces/my-project",
  createClient: () => Client.defaultClient(),
});

export default extension;

For lower-level composition, createDrive9CodingAgentTools returns Pi's official read, write, edit, ls, and bash definitions. The bash definition is intentionally storage-only and returns an error; it never starts a host process. The remote edit tool explicitly overrides Pi's local-filesystem preview with a neutral Drive9-safe renderer so the TUI cannot fall back to a built-in renderer that reads a same-named host file. Custom hosts that register only these definitions must also block any separately enabled local grep and find execution. They must decide explicitly how to handle interactive !; createDrive9PiExtension registers a refusal handler, subject to Pi's first-interceptor-wins composition behavior.

Low-Level Agent SDK and Evidence

createDrive9PiIntegration remains available for applications built directly on @earendil-works/pi-agent-core. It binds the harness read, write, and edit tools plus a Drive9-specific direct-child list tool, adds bounded result_read and result_search, and composes durable large-result capture with an existing afterToolCall hook.

The six required integration fields are explicit. Workspace and evidence credentials should be different scoped Drive9 credentials:

import { Agent } from "@earendil-works/pi-agent-core";
import { Client } from "drive9";
import { createDrive9PiIntegration } from "@drive9/drive9-pi";

function required(name: string): string {
  const value = process.env[name]?.trim();
  if (!value) throw new Error(`${name} is required`);
  return value;
}

const workspaceClient = Client.defaultClient();
const evidenceClient = new Client(
  required("DRIVE9_EVIDENCE_SERVER"),
  required("DRIVE9_EVIDENCE_API_KEY"),
);

const drive9 = createDrive9PiIntegration({
  workspaceClient,
  workspaceRoot: required("DRIVE9_WORKSPACE_ROOT"),
  evidenceClient,
  evidenceRoot: required("DRIVE9_EVIDENCE_ROOT"),
  sessionId: required("PI_SESSION_ID"),
  runId: required("PI_RUN_ID"),
});

const agent = new Agent(
  drive9.withAgentOptions({
    streamFn,
    initialState: { model, tools: applicationTools },
    afterToolCall: applicationAfterToolCall,
  }),
);

The application does not construct an execution environment or attach the evidence fallback itself. Existing tools and hooks are preserved; duplicate tool names and session mismatches fail during setup. No shell is installed by default. A caller may explicitly supply its own sandbox Shell and selected Pi harness tools; without one, executionEnv.exec returns shell_unavailable and never reaches the host.

The low-level harness tool is named list because pi-agent-core does not provide the coding-agent ls definition. This is an intentional compatibility surface, not a claim that list is a standard Pi coding-agent tool. Public Pi CLI usage should use the package extension above and its canonical ls tool.

Filesystem API

Drive9FileSystem implements Pi's FileSystem and maps operations directly to Drive9 SDK read, write, append, list, stat, rename, mkdir, and delete APIs. It does not require a mount or LayerFS layer.

import { getOrThrow } from "@earendil-works/pi-agent-core";
import { Client } from "drive9";
import { Drive9FileSystem } from "@drive9/drive9-pi";

const fileSystem = new Drive9FileSystem({
  client: Client.defaultClient(),
  root: "/workspaces/my-project",
});

getOrThrow(await fileSystem.writeFile("src/auth.ts", "export const enabled = true;\n"));
const source = getOrThrow(await fileSystem.readTextFile("src/auth.ts"));

The adapter normalizes every path inside root, maps backend failures to Pi FileError results, creates parent directories for writes and appends, and serializes mutations issued through one adapter instance. Recursive remove and atomic rename use the corresponding Drive9 SDK operations. Bounded readTextLines reads from readStream and cancels once enough lines arrive. Temporary objects use exclusive create operations and failed cleanup remains retryable.

Drive9 paths are NFC-normalized before containment checks. Until a Drive9 SDK release that safely encodes every URL path segment is available, %, ?, #, backslashes, ASCII controls, and malformed Unicode are rejected before any SDK call. Spaces and well-formed Unicode filenames remain supported.

This adapter mutates the live Drive9 filesystem. It does not create a layer and does not promise branch, checkpoint, or rollback semantics.

Evidence API

PersistentToolResultStore stores immutable output chunks and publishes a stable reference only after a CAS-protected terminal manifest is durable.

import { createDrive9ResultStore } from "@drive9/drive9-pi";

const results = createDrive9ResultStore({
  client: evidenceClient,
  evidenceRoot: "/evidence/session-42",
});

The package provides:

  • createAfterToolCallFallback for oversized all-text results;
  • createResultSearchTool for bounded literal search;
  • createResultReadTool for bounded line reads.

It intentionally does not provide a command-execution tool. A caller tool may stream stdout or stderr into ToolResultStore, but the caller runtime remains the executor. result_read and result_search outputs are never recursively offloaded by the fallback.

Evidence Isolation

verifyEvidenceIsolation checks that workspace and evidence roots are disjoint, verifies create/read/replace/delete with the evidence credential, and requires explicit authorization denial for workspace-credential access to the evidence root.

Validation

npm test
npm run check
npm run check:e2e
npm run build
npm pack --dry-run