@self-deprecated/pi-agent-tick
Agent Tick integration package for pi-coding-agent, based on the pi-ask-user interactive UI package
Package details
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

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) orinline(rendered directly in the flow) - Runtime overlay toggle: press the configured overlay-toggle key (
alt+oby 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
promptSnippetandpromptGuidelines - Optional timeout for auto-dismiss in both overlay and fallback input modes
- Structured
detailson all results for session state reconstruction - Graceful fallback when interactive UI is unavailable
- Bundled
agent-tick-decision-gateskill for mandatory decision-gating in high-stakes or ambiguous tasks - Optional Agent Tick mirroring when
agent-tick loginconfig is present - Opt-in
bashapproval 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:
- Gather evidence and summarize context
- Ask one focused question via
agent_tick_ask_user - Wait for explicit user choice
- 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:
AGENT_TICK_CONFIG, or~/.config/agent-tick/config.jsonAGENT_TICK_SERVERand/orAGENT_TICK_TOKENoverriding 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-submitPI_AGENT_TICK_OPTION_PLACEMENT=sticky-bottom|inline-after-contentPI_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:
- built-in defaults
- global config:
~/.config/pi-agent-tick/config.json - project-local config:
.pi/agent-tick.json - 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:
- Per-call
displayModeparameter (if provided) PI_ASK_USER_DISPLAY_MODE(if set to"overlay"or"inline")- Fallback default:
"overlay"
Unrecognised values are silently ignored and fall back to "overlay".
Shortcuts
Effective order for both overlayToggleKey and commentToggleKey:
- Per-call parameter (if provided)
- Matching env var (
PI_ASK_USER_OVERLAY_TOGGLE_KEY/PI_ASK_USER_COMMENT_TOGGLE_KEY) - Built-in defaults:
alt+oandctrl+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.