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

avtc-pi-settings-ui

v1.0.5

Published

A schema-driven settings UI for pi extensions — define your settings, get a tabbed modal, a /<name>:settings command, and layered persistence.

Readme

avtc-pi-settings-ui

A schema-driven settings UI for pi extensions — define your settings, get a tabbed modal, a /:settings command, and layered persistence.

Features

  • Schema-driven UI — define settings once in a typed schema; the library renders the full modal automatically
  • Tabbed layout — group settings into tabs; each tab renders as a separate panel
  • Typed setting items — 7 built-in types (string, number, duration, compact-threshold, boolean, model, thinking-level) with presets, optional min/max bounds, and custom type support
  • Filter-first UI — type to filter every list by substring (over label + value); Tab/Shift+Tab switch tabs, Enter opens the value picker
  • Layered persistence — choose any combination of three storage levels via storageLevels (session, project, global); see Persistence & session lifecycle
  • Atomic writes — settings are saved atomically to prevent corruption
  • Value normalization — every value is validated against the schema before it's saved
  • Environment variable propagation — settings can be propagated to child processes via env vars (e.g. for subagents)

The rendered modal — tabbed panels, filter-first lists, and per-type value pickers:

settings modal

settings dropdown

Installation

Add it as a dependency of your extension:

npm install avtc-pi-settings-ui

Dialog coordination (optional)

The settings modal queues behind other open dialogs (instead of stealing keyboard focus) via avtc-pi-ui-components's dialog coordinator. If you want that coordination, also install ui-components and load it as a companion extension:

// your package.json
{
  "dependencies": {
    "avtc-pi-settings-ui": "^1.0.0",
    "avtc-pi-ui-components": "^1.0.0"
  },
  "pi": {
    "extensions": [
      "./index.ts",
      "node_modules/avtc-pi-ui-components/index.ts"
    ],
    "allowedCodeDeps": ["avtc-pi-settings-ui"]  // ui-components is loaded, not imported
  }
}

Without ui-components the settings modal still works — it just opens immediately rather than queuing.

Public API

| Export | Purpose | |---|---| | registerSettingsCommand(pi, schema, opts) | Register the /<name>:settings command + modal; returns the settings handle you read from at runtime | | settingsFilePaths(name) | Global + project file paths for <name>'s settings — ~/.pi/agent/<name>-settings.json and <cwd>/.pi/<name>-settings.json (spread into the schema) | | SettingSchema, SettingsSchema | Define your settings and tabs | | StorageLevel, SettingsHandle, RegisterSettingsOptions | Types: storage levels (session/project/global), the returned handle, the options bag | | PresetsSource, PresetPair, PresetElement, PresetValue | Author presets: full pairs or bare values (string/number/boolean/null) | | registerTypeDefinition, TypeDefinition, TypeContext | Register custom types (7 built-ins ship pre-registered) | | formatHumanDuration | Human form of a duration |

Usage

import { registerSettingsCommand, SettingsSchema, settingsFilePaths } from "avtc-pi-settings-ui";

const schema: SettingsSchema = {
  settings: [
    {
      id: "mySetting",
      label: "My Setting",
      description: "Controls something important",
      type: "string",
      defaultValue: "default",
      presets: [
        ["Default", "default"],
        ["Alternative", "alternative"],
      ],
    },
  ],
  tabs: [{ label: "General", settingIds: ["mySetting"] }],
  ...settingsFilePaths("my-extension"),
};

const settings = registerSettingsCommand(pi, schema, {
  commandName: "my-extension:settings",
  title: "My Extension Settings",
  titleRight: "avtc-pi-my-extension",
  storageLevels: ["session", "project", "global"],
  envVar: "PI_SETTINGS_MY_EXT",
});

settings.getSettings();

Setting types

Every setting has a type resolved through a first-class TypeDefinition registry. Seven built-ins ship out of the box:

| Type | Stored value | Notes | |------|-------------|-------| | string | string | Text; closed enums via presets (["a", "b"]) | | number | number | null | Optional min/max bounds; null (unbounded/off) via a null preset | | duration | number | null | Human-readable (5m, 1h, 30s, 14d); stored as milliseconds. null = unbounded/off, via a null preset | | compact-threshold | string | Compact-context thresholds (none, compact, compact>75K, …) | | boolean | boolean | true/false; presets [true, false] | | model | string | null | Model id (provider/id); presets generated from the available models. Nullable via a ["Default", null] preset | | thinking-level | string | Thinking level (label equals value) |

A presets array can hold full [label, value] pairs or bare values ("a", 5, true, null); a bare value pairs with itself, except a duration string which parses to milliseconds.

Custom types: register your own via registerTypeDefinition(def) and reference it as type: "<def.id>". A TypeDefinition declares how values parse/format and optional presets; null support comes from including a null-valued preset (e.g. ["Off", null]).

Persistence & session lifecycle

Choose your storage levels via storageLevels on registerSettingsCommand (default: all three):

  • session — in-memory only; survives /reload (same process) but not a full restart or new terminal. When session is included, settings are also propagated to subagents via the env var.
  • project<cwd>/.pi/<name>-settings.json. Survives across processes in that project.
  • global~/.pi/agent/<name>-settings.json. Survives everywhere.

How edits save: with a single file level (global or project), each edit writes straight to that file, so every pi instance reads the same on-disk truth. With multiple levels (or session), edits are held as a draft until you press a save key — Ctrl+S saves project, Ctrl+D saves global (each only when its level is enabled).

Full suite

Check out the full suite of related extensions, avtc-pi — deterministic feature development, subagent delegation, working-memory, behavioral learning, parallel-work guardrails, durable decisions, notifications, and more.

Developed with Z.ai — get 10% off your subscription via this referral link.

License

MIT