@self-deprecated/pi-agent-tick

Agent Tick integration package for pi-coding-agent, based on the pi-ask-user interactive UI package

Packages

Package details

extensionskill

Install @self-deprecated/pi-agent-tick from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@self-deprecated/pi-agent-tick
Package
@self-deprecated/pi-agent-tick
Version
0.1.0
Published
May 26, 2026
Downloads
not available
Author
jeprecated
License
MIT
Types
extension, skill
Size
187.5 KB
Dependencies
2 dependencies · 3 peers
Pi manifest JSON
{
  "extensions": [
    "./index.ts"
  ],
  "skills": [
    "./skills"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-agent-tick

Agent Tick integration package for Pi.

This repository is based on Enzo Lucchesi's edlsh/pi-ask-user, an MIT-licensed Pi package for interactive user prompts. The original copyright and MIT license are preserved in LICENSE.

The imported ask_user implementation remains the foundation for rich local Pi prompts, now with Agent Tick mirroring for steering plus configurable approval/status hooks.

Demo

ask_user demo

High-quality video: ask-user-demo.mp4

The bundled media shows the local Pi prompt UI. For the launch-safe Agent Tick mirrored Steering and risky bash approval script, see Pi Native Extension launch demo verification. That script is the source for a future side-by-side Pi + phone/web recording and keeps the demo limited to sanitized sample data.

Features

  • Searchable single-select option lists with wrapped titles and descriptions
  • Responsive split-pane details preview on wide terminals with single-column fallback on narrow terminals
  • Multi-select option lists
  • Optional freeform responses
  • User-toggleable extra context on structured selections
  • Context display support
  • Configurable display mode: overlay (modal, default) or inline (rendered directly in the flow)
  • Runtime overlay toggle: press the configured overlay-toggle key (alt+o by default, configurable per call or via env var) while the prompt is open to temporarily hide/show the popup so you can read prior agent output, then press it again to bring it back
  • Pi-TUI-aligned keybinding and editor behavior
  • Custom TUI rendering for tool calls and results
  • System prompt integration via promptSnippet and promptGuidelines
  • Optional timeout for auto-dismiss in both overlay and fallback input modes
  • Structured details on all results for session state reconstruction
  • Graceful fallback when interactive UI is unavailable
  • Bundled agent-tick-decision-gate skill for mandatory decision-gating in high-stakes or ambiguous tasks
  • Optional Agent Tick mirroring when agent-tick login config is present
  • Opt-in bash approval gates through configurable Pi/Agent Tick hooks
  • Configurable best-effort lifecycle status updates to Agent Tick

Bundled skill: agent-tick-decision-gate

This package ships a skill at skills/agent-tick-decision-gate/SKILL.md that nudges/mandates the agent to use the Agent Tick-backed agent_tick_ask_user when:

  • architectural trade-offs are high impact
  • requirements are ambiguous or conflicting
  • assumptions would materially change implementation

The skill follows a "decision handshake" flow:

  1. Gather evidence and summarize context
  2. Ask one focused question via agent_tick_ask_user
  3. Wait for explicit user choice
  4. Confirm the decision, then proceed

See: skills/agent-tick-decision-gate/references/agent-tick-decision-gate-spec.md.

Install

pi install npm:@self-deprecated/pi-agent-tick

For local development or unreleased changes, install from GitHub instead:

pi install git:github.com/self-deprecated/pi-agent-tick

Agent Tick configuration

Run agent-tick login before installing/using the package to enable phone/web mirroring. Config resolution matches the Agent Tick CLI:

  1. AGENT_TICK_CONFIG, or ~/.config/agent-tick/config.json
  2. AGENT_TICK_SERVER and/or AGENT_TICK_TOKEN overriding file values

If no config is found, local Pi prompts still work. Set PI_AGENT_TICK_DISABLED=1 to opt out even when config exists. PI_AGENT_TICK_TIMEOUT_MS controls the remote wait timeout for steering and approvals; the default is unlimited (0). Set it to a positive millisecond value for a finite remote wait, or to 0, none, never, infinite, or unlimited for no timeout.

Remote Agent Tick choice presentation can be configured with:

  • PI_AGENT_TICK_CHOICE_INTERACTION_MODE=click-to-submit|select-then-submit
  • PI_AGENT_TICK_OPTION_PLACEMENT=sticky-bottom|inline-after-content
  • PI_AGENT_TICK_CONFIRM_BEFORE_SUBMIT=always|never (also accepts boolean-ish values)

Defaults are click-to-submit, inline-after-content, and always confirm-before-submit. For a verified demo path, use a finite PI_AGENT_TICK_TIMEOUT_MS such as 120000 so remote waits cannot hang a recording indefinitely.

Extension behavior configuration

Behavior config is separate from Agent Tick auth config. It resolves in this order:

  1. built-in defaults
  2. global config: ~/.config/pi-agent-tick/config.json
  3. project-local config: .pi/agent-tick.json
  4. optional explicit config path: PI_AGENT_TICK_EXTENSION_CONFIG=/path/to/config.json

Project-local config overrides global config. Rule maps are keyed by ID, so a local config can disable or change a single global rule without copying all rules.

Sanctions are opt-in. By default there are no allow/deny rules and no command approval gate is applied.

Status hook example

{
  "status": {
    "enabled": true,
    "hooks": {
      "before_agent_start": { "send": true, "state": "working", "message": "task-summary" },
      "turn_end": { "send": false, "state": "working", "message": "none" },
      "agent_end": { "send": true, "state": "waiting", "message": "none" },
      "session_shutdown": { "send": false, "state": "done", "message": "none" }
    }
  }
}

message: "task-summary" derives a short status message from the current user prompt. message: "none" uses a minimal state label because the Agent Tick status API requires a message field. session_shutdown defaults off because final delivery during process exit depends on Pi/Node being able to await shutdown handlers.

Sanction policy examples

allow-by-default lets everything run unless a deny rule matches:

{
  "sanctions": {
    "enabled": true,
    "policy": "allow-by-default",
    "tools": {
      "bash": {
        "deny": {
          "rm-rf": {
            "command": "rm",
            "argsRegex": "(^|\\s)-[A-Za-z]*r[A-Za-z]*f",
            "riskClass": "destructive-filesystem",
            "reason": "recursive forced remove"
          },
          "push": {
            "commandRegex": "^(git|jj)$",
            "argsRegex": "\\bpush\\b",
            "riskClass": "vcs-push",
            "reason": "VCS push"
          }
        }
      }
    }
  }
}

approve-by-default asks for approval unless an allow rule matches:

{
  "sanctions": {
    "enabled": true,
    "policy": "approve-by-default",
    "tools": {
      "bash": {
        "allow": {
          "jj-status": { "command": "jj", "argsRegex": "^st(atus)?$" },
          "list-files": { "command": "ls" },
          "safe-tests": { "commandRegex": "^(bun|npm)$", "argsRegex": "\\btest\\b" }
        }
      }
    }
  }
}

If allow and deny rules both match, deny wins. Bash commands are split into command units across &&, ||, ;, pipelines, subshells, command substitutions, and common shell wrappers such as bash -lc. This is not a full shell interpreter; aliases/functions known only to an interactive shell cannot be expanded. Invalid regex rules are ignored with a warning rather than crashing extension load.

Approval disclosure is controlled by sanctions.approval.discloseCommand: never, safe-only (default), or always (redacted).

Agent Tick behavior

Steering

agent_tick_ask_user preserves the local Pi popup/dialog UX and mirrors prompts to Agent Tick when config is available. The first valid local or remote answer wins. If local Pi wins, the pending Agent Tick request is abandoned; if Agent Tick wins, the mapped response is returned to Pi. The old ask_user name is also registered as a compatibility alias.

Current Agent Tick mapping uses the SDK-backed /v1/requests API. It sends stable choice IDs for single-select steering requests, plus a single Cancel choice, when choiceInteractionMode is click-to-submit. When choiceInteractionMode is select-then-submit, single-select prompts are sent as structured questionnaire requests so Agent Tick can render radio buttons plus a final send action; multi-select prompts use structured questionnaire requests with checkboxes. Option objects may include Agent Tick flags such as favorite, safest, fastest, thorough, or reversible; use favorite for the recommended answer so the mobile app can highlight it.

Option descriptions are sent as structured choice/question-option descriptions only; they are no longer duplicated into the request body.

Sanctions

When Pi exposes the tool_call hook, configured sanction policies can gate tool calls with local/Agent Tick approval. No sanction rules are enabled by default. Configure allow-by-default deny rules for risky-only approval, or approve-by-default allow rules for locked-down sessions.

Sanction request bodies are redacted before remote disclosure. The command field follows the configured disclosure policy. For public demos, prefer a harmless dry-run command such as jj git push --dry-run and show the deny path first; Agent Tick returns the approval decision but never executes the command.

Status

When Agent Tick config exists and status config is enabled, lifecycle hooks send best-effort status updates according to the configured hooks. Supported states are working, waiting, blocked, done, and failed. Defaults send a task-summary working update at before_agent_start and a minimal waiting update at agent_end; turn heartbeats and shutdown updates are disabled by default. Updates include host, working directory, and Agent Tick client name.

Tool name

The canonical registered tool name is:

  • agent_tick_ask_user

For compatibility with existing Pi agents, skills, and prompts, the package also registers:

  • ask_user

This package is intended to replace pi-ask-user in a Pi installation. Installing both packages is not recommended because both provide the same compatibility ask_user tool name.

Parameters

Parameter Type Default Description
question string required The question to ask the user
context string? Relevant context summary shown before the question
options (string | {title, description?, flags?})[]? [] Multiple-choice options. flags are Agent Tick mobile-visible cues such as favorite, safest, fastest, thorough, or reversible.
allowMultiple boolean? false Enable multi-select mode
allowFreeform boolean? true Add a "Type something" freeform option
allowComment boolean? false Expose a user-toggleable extra-context option in the custom UI (ctrl+g or the toggle row) and collect an optional comment in fallback dialogs
displayMode "overlay" | "inline"? env var or "overlay" Controls custom UI rendering: overlay shows the centered modal (current behavior), inline renders without overlay framing
overlayToggleKey string? env var or "alt+o" Shortcut for hiding/showing the overlay popup (overlay mode only). Pi-TUI key spec, e.g. "alt+o", "ctrl+shift+h". Pass "off" to disable.
commentToggleKey string? env var or "ctrl+g" Shortcut for toggling the optional comment/extra-context row when allowComment: true. Pass "off" to disable.
timeout number? Auto-dismiss local UI after N ms and use the same remote wait timeout. If omitted, remote Agent Tick waits default to unlimited (PI_AGENT_TICK_TIMEOUT_MS=0).
choiceInteractionMode "click-to-submit" | "select-then-submit"? env var or "click-to-submit" Remote Agent Tick choice behavior. select-then-submit asks Agent Tick to render radio/checkbox rows with a final send action.
optionPlacement "sticky-bottom" | "inline-after-content"? env var or "inline-after-content" Remote Agent Tick option placement. inline-after-content lets the user scroll through the body before reaching options.
confirmBeforeSubmit boolean? env var or true Ask Agent Tick to confirm before submitting direct clickable options.

Example usage shape

{
  "question": "Which option should we use?",
  "context": "We are choosing a deploy target.",
  "options": [
    "staging",
    { "title": "production", "description": "Customer-facing", "flags": ["favorite", "production"] }
  ],
  "allowMultiple": false,
  "allowFreeform": true,
  "allowComment": true,
  "displayMode": "inline",
  "optionPlacement": "inline-after-content",
  "choiceInteractionMode": "select-then-submit",
  "confirmBeforeSubmit": true
}

displayMode: "inline" uses the same interaction logic but skips overlay mode when calling ctx.ui.custom(...). RPC/headless fallback behavior is unchanged.

Personal preferences via environment variables

Configure your defaults globally by setting these in your shell profile (~/.zshrc, ~/.bash_profile, etc.):

export PI_ASK_USER_DISPLAY_MODE=inline
export PI_ASK_USER_OVERLAY_TOGGLE_KEY=alt+h
export PI_ASK_USER_COMMENT_TOGGLE_KEY=alt+c

Display mode

Effective order:

  1. Per-call displayMode parameter (if provided)
  2. PI_ASK_USER_DISPLAY_MODE (if set to "overlay" or "inline")
  3. Fallback default: "overlay"

Unrecognised values are silently ignored and fall back to "overlay".

Shortcuts

Effective order for both overlayToggleKey and commentToggleKey:

  1. Per-call parameter (if provided)
  2. Matching env var (PI_ASK_USER_OVERLAY_TOGGLE_KEY / PI_ASK_USER_COMMENT_TOGGLE_KEY)
  3. Built-in defaults: alt+o and ctrl+g

Pass "off", "none", or "disabled" (at any level) to disable the shortcut entirely. Invalid specs are silently dropped and the next source is used. Specs follow the Pi-TUI KeyId format: [mod+]...key where modifiers are ctrl, shift, alt, super, in any order, joined by + (e.g. ctrl+g, alt+shift+x, escape, tab).

Controls

While an ask_user prompt is open:

Key Action
alt+o (configurable via overlayToggleKey) Hide/show the overlay popup so you can read the agent's prior output. Available in overlay mode only. The first time you hide it, a notification reminds you which key brings it back.
ctrl+g (configurable via commentToggleKey) Toggle the optional comment/extra-context row (when allowComment: true).
enter Confirm the focused option, submit a freeform response, or submit/skip an optional comment.
esc Clear the search filter, exit freeform/comment mode, or cancel the prompt.
/ , ctrl+k / ctrl+j Navigate options. ctrl+k / ctrl+j (vim-style) work while typing in searchable prompts without disturbing the filter.

If you prefer never to see the overlay, set displayMode: "inline" per call or PI_ASK_USER_DISPLAY_MODE=inline globally.

Result details

All tool results include a structured details object for rendering and session state reconstruction:

type AskResponse =
  | { kind: "selection"; selections: string[]; comment?: string }
  | { kind: "freeform"; text: string };

interface AskToolDetails {
  question: string;
  context?: string;
  options: QuestionOption[];
  response: AskResponse | null;
  cancelled: boolean;
}

Changelog

See CHANGELOG.md.