@jfrk/pony
v0.6.2
Published
Git-backed agent-native project workspace.
Readme
Pony: Git-Backed Agent-Native Project Workspace
Status: early release. The CLI, repository state, server-backed web view, and
static snapshots are available in @jfrk/pony.
Short Version
Pony is a repository-native project workspace for AI-assisted projects.
It stores project state as files in the git repository, exposes a simple browser UI for customers and project managers, and lets AI agents mutate state through a CLI first. MCP is planned as a thin adapter over the same core logic, not as a separate source of behavior.
The v1 product is deliberately narrow:
- a read-only project web view,
- a visible global inbox for all human input,
- a kanban board rendered from repo files,
- a minimal customer-facing documentation area,
- a CLI for agents and maintainers,
- a packaged server app for viewing project state and receiving inbox messages,
- a static read-only snapshot for ordinary web servers.
The key idea is that customers and project managers should not need to know where feedback belongs. They write into a shared inbox. AI agents then triage that input according to project rules, link it to tasks/docs, create new tasks when appropriate, and update the board.
Get Started
Pony requires Node.js 22 or newer. Activate the Node.js version you use, install the launcher, verify it, then add Pony to the ChatGPT desktop app's supported personal plugin marketplace:
nvm use 22
npm install --global @jfrk/pony
pony --version
pony setup chatgptWith nvm, global npm packages belong to the active Node.js installation. After
installing or switching to another Node.js version, install the Pony launcher
for that version as well. Use node --version, npm prefix --global, and
command -v pony to verify which installation a terminal resolves. The first
line from pony --version should start with Pony launcher.
pony setup chatgpt is safe to rerun. It creates or updates Pony's entry in
~/.agents/plugins/marketplace.json without replacing other personal plugins.
Restart ChatGPT, open Plugins > Personal, install Pony, and start a new task
so ChatGPT discovers the skill.
The separate Codex CLI remains an optional advanced surface. Users who have it
installed can instead run pony setup codex to add the public JFRK plugin
catalog and install Pony from the terminal.
Claude Code 2.0.12 or newer is supported through its official plugin CLI:
pony setup claudeThis adds Pony's public npm-backed marketplace and installs pony@jfrk-pony at
Claude Code's user scope, making the skill available across projects without
changing project settings. Rerunning the command refreshes the marketplace,
keeps a matching installation unchanged, and upgrades an older plugin to the
running Pony version. A newer installed plugin is left untouched. Run
/reload-plugins in Claude Code or start a new session after setup. This
command targets Claude Code only, not Claude Desktop.
Initialize each repository that should use Pony:
cd /path/to/project
pony init --name "Project name" --language sv
pony doctor
pony startInitialization creates version-controlled project state in .pony/ and pins a
compatible local Pony runtime as a development dependency. In repositories
without package.json, Pony creates a minimal private manifest and adds
node_modules/ to .gitignore. PHP, Python, and other non-JavaScript projects
therefore get reproducible Pony tooling without becoming JavaScript
applications.
To update the launcher and a project's runtime later:
pony upgradepony upgrade resolves npm's stable latest version once, upgrades the global
launcher first, and then uses the repository's detected npm, pnpm, Yarn, or Bun
installation to upgrade the local runtime to that same version. Use
--launcher-only or --project-only when only one installation should change;
the flags are mutually exclusive. A project-only upgrade is rejected when it
would become incompatible with the current launcher.
If the launcher succeeds but the project update fails, Pony keeps the launcher upgrade, exits non-zero, and prints the exact local package-manager command to retry. With nvm, global packages belong to the active Node.js version; reactivate the intended version and retry the printed npm command if the global step fails.
Interactive Pony commands perform a silent best-effort npm update check at most
once every 24 hours after success, or once per hour after a failed or timed-out
lookup. The request starts before command delegation and has a short timeout, so
normal command execution does not wait for the registry. Checks are disabled in
CI, for non-TTY and --json output, while pony upgrade runs, and when
PONY_DISABLE_UPDATE_CHECK=1 is set. Cache state lives in the current user's
platform cache directory, never in the repository.
Plugin integrations have separate lifecycles. Package upgrades do not modify
ChatGPT, Codex, or Claude Code plugin state; rerun the corresponding
pony setup command when an installed integration should be synchronized.
The global pony owns setup and project discovery. Project commands such as
doctor, task, and start are always provided by the repository's local
package. npm exec -- pony remains available when a user wants to invoke the
local installation directly.
Releasing
Releases use npm Trusted Publishing from the private jfrk/pony repository. The
GitHub Actions workflow uses short-lived OIDC credentials to publish tagged
releases directly; it does not store an npm publishing token or make the private
repository readable. npm provenance is unavailable because the source repository
is private.
Configure the package once under npm > @jfrk/pony > Settings > Trusted Publisher:
- provider: GitHub Actions,
- organization:
jfrk, - repository:
pony, - workflow filename:
publish.yml, - environment: leave empty,
- allowed actions: npm publish only.
After the first successful OIDC release, set npm publishing access to Require two-factor authentication and disallow tokens and revoke obsolete automation tokens.
For each release, update package.json, package-lock.json,
.codex-plugin/plugin.json, .claude-plugin/plugin.json,
.claude-plugin/marketplace.json, and CHANGELOG.md, commit the release, then
push a tag matching the package version, such as v0.2.0. The workflow
validates the tag, formatting, tests, types, build, and isolated package smoke
test before running npm publish. Contributors can run the same artifact check
after npm run build with npm run test:package. The matching Git tag is the
release approval; there is no separate npm review step.
Why Pony Instead of GitHub Issues and Projects
GitHub Issues and Projects coordinate people around GitHub objects. Pony coordinates people and AI agents around a repository. Its core difference is where project state lives and how responsibility is divided between humans and agents.
- The project plan travels with the code. Tasks, feedback, and documentation
live in
.pony/, so they share the repository's history, review model, portability, and backup story. A project handover can include both the implementation and the context needed to understand it. - Pony is agent-native rather than tied to one agent platform. Agents work through a local CLI and a documented file contract. They can inspect context, validate records, triage feedback, and update project state without depending on GitHub-specific APIs or a particular AI provider.
- Humans describe needs instead of administering tickets. Customers and project managers write to one inbox instead of choosing the right repository, issue type, label, project, status, or template. Agents preserve the original message and turn it into structured, actionable work.
- Non-technical stakeholders get a focused project surface. The browser UI shows the board, inbox, and shared documentation without exposing branches, pull requests, labels, or other developer-oriented controls.
- Feedback, work, and decisions stay connected. Inbox messages, tasks, and documents can reference each other, leaving a trace from the original request to the resulting work and the decisions behind it.
- The workflow remains plain Git. Pony can be used with different Git hosts, self-hosted infrastructure, or local repositories. Project rules and workflow configuration can be versioned and reviewed alongside the project.
Pony is a strong fit for small AI-assisted development teams, customer projects, consultancies, self-hosted environments, and projects that need a complete and portable handover. It deliberately does not compete with GitHub Projects on feature breadth. GitHub remains the stronger choice when a team needs mature real-time collaboration, advanced permissions, notifications, organization-wide reporting, roadmaps, or a large integration ecosystem.
Pony is an agent-native project workspace that lives in the Git repository. Customers get a simple project view and inbox, while agents handle triage, structure, and project updates. The result is less administration, stronger traceability, and project context that can move with the code.
Dogfooding This Repository
This repository now uses Pony to track Pony's own project work.
The boundary is intentional:
src/,tests/, package files, and Astro config are the implementation of Pony as a product..pony/is dogfooded project state for this repository. It is not a fixture, template, cache, or generated build output..pony/should be committed like normal project data when its contents are meaningful.dist/,.astro/,.pony-web/, andnode_modules/are runtime/build artifacts and should stay untracked.
Set Up The Dogfood Launcher
The package checkout is its own project runtime. Node package self-reference
lets the launcher discover this root without a circular dependency or a second
link inside node_modules/. Prepare a clean checkout with the active nvm
installation:
nvm use
npm install
npm run build:cli
npm link
pony --version
pony doctor
pony contextpony --version should report compatible launcher and project-runtime versions.
The global link belongs to the active Node.js version, so rerun npm link after
switching nvm versions. Rebuild with npm run build:cli after changing CLI or
core source; the global link continues to point at this checkout, but it runs
the compiled files in dist/.
If Pony finds the checkout but reports a missing CLI runtime, run
npm run build:cli. If command -v pony or pony --version still resolves
another installation, check npm prefix --global after nvm use, then rerun
npm link. Do not run npm link @jfrk/pony in this repository: package
self-reference already provides the project-runtime link. Consumer repositories
continue to use the local dependency installed by pony init.
When changing project-management state for Pony itself, prefer the Pony CLI:
pony inbox add "Message body" --author "Name" --email "[email protected]"
pony inbox add "Message with files" --email "[email protected]" --attachment ./brief.pdf
pony task create "Task title" "Task body" --status triage
pony task update TASK-001 "Rewritten body" --title "Revised title"
pony task move TASK-001 in_progress
pony task prioritize TASK-001 soon
pony task rename-status todo backlog
pony task migrate-priorities --inspect
pony task renumber TASK-001 TASK-010
pony task shift TASK-010 --by 2
pony docs create "Doc title" "Doc body" --type note
pony docs update DOC-001 "Rewritten body"
pony inbox link MSG-... --task TASK-001 --doc DOC-001
pony task link TASK-001 --inbox MSG-... --doc DOC-001Manual edits inside .pony/ are acceptable for repairs or deliberate bulk
changes, but normal workflow changes should go through Pony commands so the
dogfooding path exercises the same file contract as external projects.
Do not add special product behavior that only works because this repository
contains .pony/. Pony's implementation must continue to treat .pony/ as
ordinary project data supplied by whatever repository is being viewed or
mutated.
Background
The original need was a way to keep a kanban board inside a git repo as files, instead of using GitHub Projects or another SaaS project-management surface. The board should be manageable by AI agents, auditable through git, and usable without forcing less technical users into terminals or GitHub workflows.
During discovery, a few existing tools shaped the direction:
tikiis attractive because it is broader than a kanban board: it treats tasks, docs, prompts, and notes as a Markdown workspace in a git repo. Its drawback for this use case is that it is terminal-first.VibeOps Trackeris close to the desired v1: web board, Markdown issue files, MCP tools, and agent workflow. It is primarily local-first and localhost-only.Knutpunktis also close: Markdown task files, web UI, backend API, and MCP server. It is narrower and more kanban-shaped.LiveBoardshows a useful Markdown-native kanban UI/CLI/API direction, though the AI agent layer appears to be roadmap rather than core.kanban-mdis useful as an agent-first reference: per-task Markdown files, claims, CLI/TUI, compact output, and skills. It is still terminal/TUI oriented rather than a customer-friendly browser surface.Task Master AIis relevant for agent task workflow patterns, but it is not primarily a browser kanban/document workspace.
The conclusion was not to clone any one of these. Pony should borrow the repo-file contract and agent workflow ideas, but provide a browser-first read-only surface for non-technical stakeholders.
Product Principles
Files Are the Contract
The canonical project state lives in .pony/ inside the repository. The app,
CLI, server, and future MCP adapter all read and write the same files through a
shared core library.
Generated indexes and caches may exist later, but they are not the source of truth.
Pure Git, Not GitHub Product Features
It is acceptable to commit and push files to a git remote. It is not acceptable for the core product model to depend on GitHub Issues, Discussions, Projects, the GitHub CLI, or similar product-specific features.
GitHub may host a repository, but Pony's workflow should remain plain git.
Humans Write to an Inbox
Customers and project managers often do not know which card, document, or workflow state a comment belongs to. Asking them to place feedback correctly creates friction and often produces bad structure.
In v1, all human input goes into a global inbox. It is visible to everyone in the project view. Agents later triage, link, summarize, create tasks, or close items based on project guidelines.
Agent Mutation, Human Visibility
The web UI is mostly read-only in v1. Humans can submit inbox messages in server-backed mode, but board/task/doc mutation is expected to happen through the CLI and AI agents.
Keep v1 Small
The product should avoid building a full collaborative project-management app in v1. It should prove the core loop:
- Project state lives in git-backed files.
- Humans can see the project in a browser.
- Humans can submit visible input.
- Agents can triage and mutate the state safely through CLI commands.
Target Users
Primary users:
- developers and AI agents working inside a repo,
- project leads who want git-backed project state,
- customers and project managers who need a browser view but should not need to understand git, Markdown, or agent workflows.
V1 Scope
V1 includes:
- package-installed CLI,
.pony/repository state folder,- Markdown files with YAML frontmatter,
- mostly read-only web UI with inbox submission in server mode,
- kanban board,
- shared visible inbox,
- minimal documentation list,
- server-backed inbox submit flow,
- configurable git write policy,
- context export for agents.
V1 does not include:
- user accounts inside Pony,
- full task editing in the browser,
- comments attached directly to cards,
- advanced permissions,
- GitHub Issues/Projects/Discussions integration,
- MCP-over-HTTP,
- full custom branding,
- real-time collaboration.
Authentication for a deployed read-only site can be handled outside Pony, for example with basic auth at the web server or hosting layer. Pony collects a required name and email in a browser-local profile; this is attribution, not verified identity.
V2 and Later Ideas
These ideas came up during discovery but are intentionally outside the tight v1 scope. They should guide architecture choices so v1 does not close obvious future paths.
Reference-Aware Inbox UI
Keep the global inbox model, but make it easier to reference existing objects from the UI.
Possible interactions:
- a "comment" or "send message about this" button on each task/doc/card,
- clicking the button opens the global message box,
- the message is prefilled with a reference such as
TASK-001orDOC-001, - the inbox message still lands in the shared inbox rather than as a direct child comment on the object,
- the card/doc can later show linked inbox messages as references.
This preserves the product principle that humans do not need to choose the perfect location for feedback, while still making contextual feedback easier.
Linked Feedback on Cards and Docs
V2 can display references in both directions:
- an inbox message can reference tasks/docs,
- a task/doc can show related inbox messages,
- agents can add or adjust links during triage,
- humans can see which feedback influenced a task or decision.
This should not require moving messages out of the inbox. The inbox remains the global intake stream.
Authenticated or Identified Comments
V1 may rely on external access control and a browser-local name/email profile. V2 can add stronger identity:
- authenticated users,
- verified author identity on inbox messages,
- "edit my own open messages",
- clearer audit display for who wrote what,
- optional role/permission model.
Even with auth, normal users do not need direct task-edit permissions. Their primary write capability can remain "send inbox input".
Interactive Web UI Without Direct File Editing
The web UI may become more interactive without becoming the canonical mutation surface for all project state.
Possible v2 interactions:
- richer inbox submission and editing,
- filtering/searching/sorting in the browser,
- acknowledging or following inbox items,
- commenting with prefilled references,
- viewing task/doc history or related feedback,
- lightweight approval/sign-off actions if they map cleanly to core commands.
If browser interactions mutate state, they should still call the same core operations as CLI/MCP/server actions. Do not create a separate web-only behavior model.
MCP Adapter
MCP can ship after the CLI proves the workflow.
V2 MCP direction:
pony mcpas stdio MCP server,- tools are thin wrappers around core operations,
- no duplicate validation or file logic,
- possibly MCP-over-HTTP later if there is a real hosted-agent use case,
- auth for MCP-over-HTTP should follow the MCP authorization model instead of a custom one.
The first MCP tools should probably mirror the small CLI surface: list/show inbox, claim inbox, close inbox, list/create/move tasks, list/show docs, and get context.
Attachments and Offline Drafts
The browser stores the local author profile in localStorage and the pending
message, including file blobs, in IndexedDB. This keeps the profile small while
allowing the complete draft to survive a reload.
Inbox messages support up to 10 common image/document attachments, limited to 10
MiB per file and 25 MiB in total. Attachment metadata is stored in the message
frontmatter and binary files live in a message-scoped sibling directory.
Supported extensions are .png, .jpg, .jpeg, .gif, .webp, .avif,
.pdf, .txt, .md, .csv, .json, .doc, .docx, .xls, .xlsx,
.ppt, .pptx, .odt, .ods, and .odp.
Richer Documentation Space
The v1 docs area should stay small. Later, the document model can grow if real projects need it.
Possible additions:
- internal vs shared visibility,
- richer decision lifecycle,
- linked decisions and supersession graphs,
- open-question workflow,
- generated status/handoff summaries,
- project glossary,
- reference library for external links/assets.
Avoid turning docs into a generic wiki until there is concrete pressure.
Configurable Branding
V1 should use a neutral, minimal, shadcn-like design. Later, projects may need light branding for customer-facing deployments.
Possible additions:
- logo,
- accent color,
- light/dark preference,
- customer/project display name,
- limited theme tokens.
Keep this shallow; Pony should not become a theme system.
Browser-Based Task Mutation
V1 should not include full task editing in the browser. Later, some interactive task operations may be useful, especially for project leads.
Possible additions:
- create task from inbox message,
- approve agent-created task,
- mark a task as accepted/done,
- reorder cards,
- add/remove references,
- edit task metadata.
Any such mutation must go through core and respect the same schemas, write policy, locks, and git behavior as CLI/MCP.
Better Git Sync and Conflict Handling
V1 write policy can be simple. Real deployments may need stronger sync behavior.
Possible additions:
- pull-before-write,
- configurable target branch,
- retry policy for push failures,
- clearer conflict UI,
- background sync status,
- protected branch workflow,
- optional commit signing,
- server-side queue for writes.
Do not overbuild this before the first deployed projects show which failure modes matter.
Installation and Packaging Model
The same @jfrk/pony package has two runtime roles. A global installation is a
launcher that owns init, setup, upgrade, top-level help, version
reporting, and update checks. A repository-local installation owns all project
commands and the web runtime. This keeps onboarding to one package while making
every repository's behavior reproducible from its package manifest and lockfile.
pony init creates .pony/, detects npm, pnpm, Yarn, or Bun from the
packageManager field or lockfile, and installs
@jfrk/pony@^<launcher-version> as a development dependency. An existing
dependency declaration keeps its current dependency section and range. If
initialization succeeds but package installation fails, .pony/ remains intact
and Pony prints the exact command to retry.
The launcher delegates unknown commands instead of keeping a fixed list of
project commands. Delegation is allowed only when the local version has the same
major version as the launcher and is not newer than it. pony --version reports
both versions and compatibility; pony --help combines launcher commands with
the command manifest exposed by the local package. These rules prevent a stale
launcher from invoking a runtime with assumptions it does not understand.
The package also contains the Pony plugin. pony setup chatgpt safely merges an
npm-backed Pony entry into ChatGPT's supported personal marketplace file; the
user still chooses Install in the ChatGPT Plugins UI. Pony does not use npm
lifecycle scripts to modify ChatGPT configuration.
pony setup codex remains available for users of the optional Codex CLI. It
delegates marketplace, installation, and cache management to that CLI.
pony setup claude similarly delegates all discovery, user-scope settings, and
plugin-cache mutations to Claude Code. The marketplace metadata is served from
the public npm package because Pony's source repository is private, and its
versioned plugin entry exposes skills/pony without writing Claude settings or
cache files directly.
The package contains a prebuilt server, the web source used internally by static
snapshot builds, and skills/pony. Consumer projects do not copy or configure
that source.
The published SSR server bundles its complete non-Node dependency closure. Its
generated imports therefore cannot drift when a consumer resolves a newer Astro
or transitive dependency from the ranges used by Pony's source and static build
tooling. Node.js built-ins remain external because the package declares the
required Node.js version. The release workflow builds the package, creates the
actual npm tarball, installs it in an isolated consumer with the known Astro
7.1.3 and cookie 2.0.1 regression versions, and requires
pony start --no-open to reach readiness before publishing.
pony start binds to 127.0.0.1, uses web.port, accepts request bodies up to
web.max_request_body_bytes, and opens the browser after the packaged server is
ready. The default request limit is 32 MiB so the validated 25 MiB attachment
allowance also has room for multipart framing. --port, --host, and
--no-open support temporary overrides and headless deployments. Exposing a
non-loopback host requires an authenticated reverse proxy because the web app
can write project state.
Pressing Ctrl+C stops the launcher and its web process before control returns to
the calling terminal. Pony also restores raw input and disables inherited mouse
reporting during shutdown. If an older Pony version already left a terminal
printing escape sequences after clicks, run that terminal's reset command or
open a fresh terminal session once, then upgrade Pony before restarting it.
pony build captures the current .pony/ state in .pony-web/ as plain HTML,
CSS, JavaScript, and attachment files. --base makes links work below a chosen
deployment path, while --out-dir overrides the generated directory. The
snapshot includes shared instructions, inbox messages, and sender addresses, but
excludes the profile, message composer, Actions, and every other write path.
Upload and rebuild automation remain the responsibility of the surrounding
deployment workflow.
Pony contributors run npm run dev in this repository to use Astro's source
development mode on the reserved port 45670. Consumer projects never invoke
Astro directly; pony build uses the web source shipped in the Pony package.
Production environments that omit development dependencies must either include
dev dependencies for Pony operations or move @jfrk/pony to dependencies.
Linked installations remain supported during package development. The packaged
web server has an explicit Node.js runtime requirement.
If a linked source checkout reports that dist/web/server/entry.mjs is missing,
rebuild and refresh the link in the active nvm installation:
nvm use
npm run build
npm linkThat recovery applies only to linked development checkouts. If a
registry-installed package contains the server build but exits on an unresolved
or incompatible import, the published artifact is broken; rebuilding inside the
consumer does not repair it. Record pony --version, then install a fixed Pony
release or run pony upgrade when one is available.
The repo contains only state/config:
.pony/
config.yml
instructions.md
inbox/
tasks/
docs/The installed package is treated as read-only. Runtime caches, generated
indexes, static snapshots, and local lock files must remain outside both
.pony/ and the package.
Future Launcher Architectures
The dual-role package is the smallest useful step now. Keep these alternatives available if distribution or compatibility needs change:
- Global monolith: let one global package execute every project command. This is simple but projects cannot reproduce their CLI behavior independently of the user's machine.
- Separate thin launcher package: publish a small global launcher distinct
from
@jfrk/pony. This reduces global install size and clarifies ownership, but introduces another package and release relationship. - Local-only CLI: remove the global installation and standardize on
npm exec -- pony. This is maximally explicit but gives non-JavaScript users more setup friction. - Launcher-managed runtime cache: let the launcher download versions into a
user cache and pin the selected version in
.pony/. This avoids modifying a host project's package manifest but duplicates package-manager behavior and adds a new cache lifecycle.
A later launcher/runtime protocol version could replace strict product-version comparison. That would allow independently versioned launchers and runtimes once the handshake has enough capabilities to negotiate safely.
Technical Stack
Language and Runtime
Use TypeScript.
The package should be runtime-portable across Node and Bun where practical:
- ESM package,
- Node-compatible filesystem/process code,
- avoid Bun-only APIs in core,
- use small runtime adapters only where needed.
Web Framework
Use Astro.
Reasons:
- Astro supports a prebuilt standalone Node server with on-demand rendering.
- Runtime rendering lets one packaged app use each repository's
.pony/data and project name without rebuilding. - Static generation produces a point-in-time read-only snapshot for hosts that cannot or should not run Node.js.
- Astro content collections/loaders can help with file-backed content.
- Astro Actions are a good fit for type-safe submit flows in server mode.
Use Astro features where they fit before adding custom code. Add packages where they prevent unnecessary implementation work.
UI
Use a minimal, neutral, shadcn-like visual style.
No theme builder, branding system, or custom accent colors in v1. At most, the project name comes from config.
Interactivity should start with Astro plus small vanilla client-side scripts. React/Svelte islands can be added later for components that genuinely need them.
Schema and Parsing
Use Zod for schema validation and inferred TypeScript types.
Use established packages for Markdown/frontmatter parsing and rendering. Do not hand-roll frontmatter parsing or Markdown rendering.
Markdown with YAML frontmatter is the source format. The implemented context export is Markdown; compact formats such as TOON remain a possible future optimization and must not become canonical storage.
Git
Use the system git CLI for git operations.
Reasons:
- respects the user's existing git config,
- works with SSH/auth the way developers expect,
- avoids reimplementing remote/auth edge cases in JavaScript.
Do not depend on gh or GitHub-specific APIs for core behavior.
High-Level Architecture
@pony/core
read/write .pony files
validate schemas
perform queries
handle claim/lock state
apply git write policy
produce context exports
@pony/cli
global launcher plus repository-local command runtime
uses @pony/core
primary v1 agent interface
@pony/web
shared Astro pages and components
prebuilt standalone server with inbox submit
static snapshot without actions or profile state
uses @pony/core
@pony/mcp
future thin adapter over @pony/core
no separate file or workflow logicThe implementation can be one package internally at first. The important boundary is conceptual: all mutation and validation logic belongs in core, not in individual adapters.
Repository Data Layout
Default layout:
.pony/
config.yml
instructions.md
inbox/
MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8A.md
tasks/
TASK-001.md
TASK-002.md
docs/
DOC-001.mdUse stable filenames that do not include titles. Titles change; IDs should not.
Shared Instructions
.pony/instructions.md is Pony's canonical versioned source for shared project
instructions. It is separate from repository-level agent files such as
AGENTS.md and from customer-visible Pony docs.
Treat the file as public, non-secret project guidance: everyone who can inspect the Pony workspace or static snapshot can read it. Host/system policy and repository instructions have higher authority. Pony instructions provide shared defaults that a specific task or inbox message may refine only when the more specific requirement does not violate that higher-authority policy. Pony does not merge or rewrite conflicting instruction sources automatically.
---
type: instructions
---
Keep changes focused and document non-obvious decisions near the code.The frontmatter has one fixed field, type: instructions, and unknown fields
are invalid. The body contains only the shared instructions and may be empty.
New projects receive a minimal empty record. Older projects may omit the file;
Pony treats absence as effective empty instructions until the first supported
update creates it. pony doctor validates the file when present.
Use pony instructions show to read it and pony instructions update [body] to
replace it. Both commands support --json, and updates also accept stdin. JSON
output distinguishes a missing legacy file from a present empty record. Updates
use the configured write.mode, including isolated commit/push delivery,
rollback, and no-op handling without empty commits.
The live Instructions page renders the same sanitized Markdown and saves through the shared core update and write policy. The static snapshot shows that content as visibly read-only. Pony adds no authentication layer for this editor; the hosting environment must restrict access to the live server when required.
IDs
Use a hybrid ID model:
- Inbox messages:
MSG-<ULID>. - Tasks: human-readable sequence IDs such as
TASK-001. - Docs: human-readable sequence IDs such as
DOC-001.
Inbox uses ULID rather than UUID so files sort roughly chronologically in a
directory listing. created_at remains the source of truth for timestamps.
Inbox ULIDs are generated by the client before submit. This allows local pending state and idempotent retries.
Task IDs are display identifiers, not globally unique identities. Before task
creation, Pony makes a best-effort fetch of the current branch's configured
upstream and allocates after the highest task number visible locally or in that
ref. A missing repository/upstream or failed fetch warns and falls back to the
local files; another clone can still allocate the same number after the fetch.
The confirmed longer-term direction is an immutable ULID-based task identity for
structured relationships while TASK-NNN remains a human-readable, renumberable
display ID. That schema migration is not implemented yet.
File Formats
Inbox Message
Inbox messages are visible to everyone and are the only user-created content in v1.
Path:
.pony/inbox/MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8A.mdExample:
---
id: MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8A
type: inbox_message
status: open
author_name: "Anna Andersson"
author_email: "[email protected]"
created_at: "2026-07-05T12:00:00Z"
updated_at: "2026-07-05T12:00:00Z"
claimed_by: null
claimed_at: null
references:
tasks: []
docs: []
attachments:
- name: "launch-copy.pdf"
path: "MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8A.attachments/001-launch-copy.pdf"
media_type: "application/pdf"
size: 48321
sha256: "15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225"
---
I am not sure whether the launch copy on the start page matches what we agreed
in the last meeting.Inbox records created before the titleless format are intentionally not read by
this version. Migrate them manually by removing title from frontmatter and,
when the title contains information that is not already in the message, prepend
that text to the Markdown body. Add attachments: [] and a non-null
author_email to every migrated record.
Suggested statuses:
open: user can still edit the message.claimed: an agent has started working on it; user edits are no longer allowed.closed: triage is complete.
An agent claim should set status: claimed, claimed_by, and claimed_at
immediately. This avoids having to solve branch/version conflicts in v1.
User edits before claim can update the same file. Git history is the audit trail for original content and edits. No separate append-only revisions are needed in v1.
Task
Path:
.pony/tasks/TASK-001.mdExample:
---
id: TASK-001
type: task
title: "Implement customer inbox submit flow"
status: backlog
priority: soon
assignee: null
created_at: "2026-07-05T12:00:00Z"
updated_at: "2026-07-05T12:00:00Z"
references:
inbox:
- MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8A
docs: []
---
## Context
Customers need a simple way to send project feedback without choosing the right
card.
## Requirements
- Add a global inbox submit form in the packaged server app.
- Store the complete pending message in IndexedDB until the server confirms it.
- Write one inbox file per submitted message.
## Acceptance Criteria
- [ ] A submitted message creates a `.pony/inbox/MSG-*.md` file.
- [ ] The server returns the path and git commit SHA.
- [ ] The client clears the pending IndexedDB state only after confirmation.Default task statuses are:
triagebacklogin_progressreviewdone
Use triage for preliminary cards that still lack required intent, scope,
constraints, decisions, or success criteria. Move a card to backlog only when
it has a localized user story or goal, acceptance criteria, scope, and the
necessary confirmed decisions. Keep selected work in backlog until
implementation starts, then move it to in_progress.
The exact status list is configurable in .pony/config.yml. Existing projects
that configure todo remain unchanged and continue to support it. Projects with
the exact pre-triage default list gain triage during normalization while
keeping todo for compatibility; custom status lists are preserved unchanged.
Migrate explicitly with pony task rename-status todo backlog. The command
updates matching task files and removes todo from the configured statuses,
merging into the existing backlog entry when present.
Task priority describes when work should flow through the configured statuses:
asap: precedes every other eligible task regardless of status.soon: precedes normal and later work within the same status.null: the persisted default, shown as In turn, follows normal Kanban ordering. CLI inputsin_turnandnullboth store this value.later: follows every other eligible task regardless of status.not_planned: is excluded from work selection and ordinary Board/Done views.
These values and semantics are fixed Pony behavior rather than project
configuration. Use pony task prioritize TASK-001 <priority> to change only
the scheduling priority. Within each status column, Pony sorts cards by
asap, soon, In turn, and later, then by oldest created_at and task ID.
Only explicit ASAP, Soon, and Later labels appear on ordinary cards; task detail
pages always show the complete priority.
Not-planned tasks retain their workflow status and appear in the Archive view, which uses the same configured status columns as the primary board. Assigning a new priority restores the task to its retained column. Archive is therefore not a workflow step after Done; remove a task file when the record itself should no longer be retained.
Projects created before semantic priorities remain readable in legacy mode. Inspect the required explicit decisions before migrating:
pony task migrate-priorities --inspect
pony task migrate-priorities \
--map high=soon \
--restore TASK-042=backlogRepeat --map for every legacy priority and --restore for every task whose
legacy status is archived. Pony suggests common mappings but never applies
them without confirmation. Migration validates all target priorities and
statuses before writing, updates the files atomically through the configured
write policy, removes the legacy archived status, and records
priority_model: scheduling_v1. pony doctor reports readable legacy values
as migration diagnostics until this is complete.
Document
Docs are customer-visible project context, not an attempt to build a complete knowledge base in v1.
Path:
.pony/docs/DOC-001.mdExample:
---
id: DOC-001
type: decision
title: "Use a global inbox instead of card comments in v1"
status: active
visibility: shared
created_at: "2026-07-05T12:00:00Z"
updated_at: "2026-07-05T12:00:00Z"
superseded_by: null
---
Customers often do not know where feedback belongs. All human input should
therefore go to a global inbox first. Agents can later link inbox items to
tasks, docs, or decisions.Suggested document types:
briefdecisionquestionreferencenote
Suggested document statuses:
activeresolvedsuperseded
Decision documents should support superseded_by so outdated decisions are not
silently treated as current.
Config
Path:
.pony/config.ymlExample:
schema_version: 1
project:
name: "Project name"
language: en
web:
port: 4567
max_request_body_bytes: 33554432
write:
mode: working_tree
branch: current
commit_author:
name: "Pony Inbox"
email: "[email protected]"
tasks:
statuses:
- triage
- backlog
- in_progress
- review
- done
priority_model: scheduling_v1Write modes:
working_tree: write files but do not commit. Best for local development.commit: write files and create one local commit per changing mutation, but never push. Pony requires an existing initial commit and a checked-out named branch.branch: currentaccepts that branch; any other value must exactly match it.commit_and_push: write files, commit, and push. Best for a deployed customer-facing Pony server. The checked-out branch must already have an upstream and exactly match its local tracking ref before each mutation.
Commit delivery checks every mutation target against HEAD before writing.
Existing targets must be tracked and clean in both the working tree and index,
while new targets must not already exist. Unrelated modified, staged, and
untracked files are preserved. Pony builds the commit with a temporary index,
includes only the declared mutation files, applies commit_author without
changing Git config, and returns the full commit SHA. It never creates or
switches branches, signs a commit, or runs repository hooks. Task creation may
already have performed the worktree-preserving upstream fetch described under
IDs.
Filesystem writes and commit delivery form one transaction. If Git cannot create the commit, Pony restores existing files byte-for-byte and removes files or attachment directories created by the mutation. An error explicitly reports when that rollback also fails. Idempotent no-change results return a null SHA without running Git preflight or creating an empty commit.
Commit-and-push mode uses the same isolated commit transaction, then pushes the
new SHA explicitly to the checked-out branch's configured upstream remote ref.
It does not infer origin, use push.default, configure an upstream, or push
another ref. Pony makes one push attempt and runs no local Git hooks.
If push fails, the local commit and mutation files remain intact. The error includes the retained SHA, destination, and an exact retry command. Pony never rolls the commit back, force pushes, pulls, merges, or rebases as part of delivery. A branch that is already ahead, behind, or diverged from its upstream is rejected before the first file write; ahead errors include the pending push command, while behind and diverged errors require manual fetch and reconciliation. Successful no-change operations still return a null SHA without running Git or contacting the remote.
.pony/config.yml provides project defaults. CLI flags and environment
variables should be able to override runtime/deployment-sensitive settings,
especially write mode. This decision should be revisited after the MVP has been
used in real projects.
project.language is a canonical BCP 47 tag for new human-readable task and doc
content. pony init --language sv-se stores sv-SE; omitted values default to
en, including legacy configs without the field. It does not localize Pony's
UI, CLI messages, structured frontmatter, or existing records. Inbox text is
always preserved in the language submitted by its author.
web.max_request_body_bytes defaults to 33554432 (32 MiB). Values must be
positive whole bytes and cannot exceed 268435456 (256 MiB), Pony's framework
transport ceiling.
Inbox Submit Flow
Server-backed submit flow:
- Browser creates a ULID and message ID:
MSG-<ULID>. - Browser stores the draft and file blobs in IndexedDB.
- Browser submits the payload as multipart form data.
- Server validates identity, content, attachments, and message ID format.
- Server checks whether the target file already exists.
- If the file exists with the same payload, return success with the existing path/commit where possible.
- If the file exists with different content, return a conflict.
- If the file does not exist, write the inbox file and attachment directory.
- Apply the configured write policy.
- Return
path,commitShawhen a commit was created, and structured Git delivery state describing whether commit or push actions remain. - Browser clears pending IndexedDB state only after confirmation.
There should be one git commit per incoming inbox message in commit and
commit_and_push modes.
Suggested commit message:
feat(inbox): add message MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8AFor edits before claim:
fix(inbox): update message MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8AFor claims:
chore(inbox): claim message MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8ABrowser Behavior
The browser UI stores the required name/email profile in localStorage and the
complete pending submission in IndexedDB.
Reasoning:
- Web Storage is sufficient for the small profile record.
- IndexedDB can persist structured draft data and file blobs.
- The pending message ID remains stable across reloads and retries.
The UI should make pending state explicit:
- "Saving..."
- "Saved in commit
<sha>" - "Still pending, retry"
- "This message has been claimed and can no longer be edited"
Web UI V1
Pages/views:
- Project overview
- Kanban board, read-only
- Inbox, visible to everyone
- Documents, read-only list/detail
- Shared instructions, editable in the packaged server
- Global "send message" box
Packaged server behavior:
- render the same UI,
- enable inbox submission,
- apply write policy,
- read project state and branding from
.pony/at request time.
Static snapshot behavior:
- render board, tasks, docs, shared instructions, inbox messages, sender addresses, and attachments,
- mark shared instructions visibly read-only,
- omit profile, message submission, Actions, and all write paths,
- support deployment at the web root or below a configured base path,
- write a self-contained directory for an external deployment workflow.
V2 can add UI shortcuts from cards/docs into the global message box, for example
pre-filling references such as TASK-001. V1 does not need comments directly
attached to cards.
CLI
Use one binary/command with subcommands.
The globally installed binary acts as a launcher. It can initialize a project,
configure integrations, report global/local versions, and discover local
commands. Once .pony/config.yml and a compatible local package are present, it
delegates every project command to that package.
Current command surface:
pony setup chatgpt
pony setup claude
pony setup codex
pony upgrade
pony upgrade --launcher-only
pony upgrade --project-only
pony init --name "Project name" --language en
pony --version
pony --help
pony start
pony build --base /projects/example/
pony doctor
pony doctor --json
pony instructions show
pony instructions show --json
pony instructions update "Shared instruction body"
printf '%s\n' "# Shared rules" "" "Keep changes focused." | pony instructions update
pony inbox list
pony inbox show MSG-...
pony inbox add "Message" --email [email protected] --attachment ./brief.pdf
pony inbox claim MSG-... --by "Agent name"
pony inbox close MSG-...
pony inbox link MSG-... --task TASK-001
pony task list
pony task show TASK-001
pony task create "Task title" "Task body" --status triage
pony task move TASK-001 in_progress
pony task prioritize TASK-001 asap
pony task rename-status todo backlog
pony task migrate-priorities --inspect
pony task update TASK-001 "New body" --title "New title"
pony task link TASK-001 --inbox MSG-... --doc DOC-001
pony task renumber TASK-001 TASK-010
pony task shift TASK-010 --by 2
pony task conflict TASK-016 --json
pony task resolve-conflict TASK-016 --move-stage 3 --to TASK-025
pony docs list
pony docs show DOC-001
pony docs create "Doc title" "Doc body" --type note
pony docs update DOC-001 "New body" --title "New title"
pony context --format markdownAfter pony inbox add, Pony reports whether the configured write mode leaves
the message waiting for a commit or push. In the default working_tree mode,
the browser and human-readable CLI output include a ready-to-use message such
as:
Git reminder: commit and push this message. Suggested commit: feat(inbox): add message MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8Apony inbox add --json exposes the same state without mixing prose into the
JSON document:
{
"gitDelivery": {
"writeMode": "working_tree",
"commitRequired": true,
"pushRequired": true,
"suggestedCommitMessage": "feat(inbox): add message MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8A",
"suggestedCommitBody": "Add MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8A at .pony/inbox/MSG-01JZ7M8E6R4D9W0M4X3N2P1Q8A.md."
}
}The object is part of the full inbox result; unrelated record fields are omitted
from this example. commit requires only a push, while commit_and_push
requires neither action and therefore adds no human-readable Git reminder.
pony doctor validates .pony/config.yml, the optional canonical instructions,
and every inbox, task, and doc frontmatter record without stopping at the first
malformed file. It prints a human-readable report and exits with status 1 when
violations exist. pony doctor --json returns a versioned report with relative
file paths, record types, field paths, Zod issue codes, structured issue
details, and messages for agents and scripts. Run it again after repairs until
valid is true.
CLI output supports:
- human-readable table output,
- JSON for scripts,
- Markdown context output for agents.
The CLI is the primary v1 interface for AI agents.
Concurrent Task ID Recovery
Automatic allocation is best-effort and does not reserve an ID. Before pushing
concurrent work, fetch and inspect the combined history. During an interactive
rebase, use pony task renumber for one exact move or pony task shift to make
space for a range, then review the reported textual occurrences and continue the
rebase yourself. Pony updates filenames, task frontmatter, and structured
Pony-owned task references; it never rewrites Markdown titles/bodies, Git
history, commit messages, or external links.
For an active add/add conflict, inspect the index with
pony task conflict TASK-NNN. Stage 2 is Git's ours variant and stage 3 is
theirs; titles and creation times help identify each task. Resolve by moving
one variant to a free ID:
pony task resolve-conflict TASK-016 --move-stage 3 --to TASK-025
git merge --continueDuring an active merge or rebase, Pony recovery commands stage only their
declared .pony files, create no commit, perform no push, and leave the
relevant git merge --continue or git rebase --continue to the user. Existing
references to the shared add/add ID are ambiguous and remain untouched for
manual review.
The skill in skills/pony/SKILL.md ships with the npm/plugin release. Editing
that source file does not mutate an already installed Codex, ChatGPT, or Claude
plugin cache. Install or upgrade the matching release and reload the integration
when the host requires it before expecting agents to use newly documented
commands.
MCP
MCP is not required to prove the first MVP. It should be designed in, but can ship later as a thin adapter.
If included early, keep it minimal:
- stdio transport only,
- no MCP-over-HTTP,
- no separate auth model,
- no separate file logic,
- tools just call the same core operations as the CLI.
Possible first tools:
list_inboxget_inbox_messageclaim_inbox_messageclose_inbox_messagelist_taskscreate_taskmove_taskget_context
Agent Workflow
The intended agent loop:
- Read project rules and current context.
- List open inbox messages.
- Claim one inbox message before doing triage.
- Interpret the message according to project guidelines.
- Create incomplete cards in the configured triage status. Ask one question at a time and confirm intent, scope, constraints, and success criteria before promoting them to backlog.
- Link it to existing tasks/docs, create a new task, create/update a doc, or close it as handled.
- Leave a clear trace in files and git commits.
Agents should not silently rewrite user-submitted messages after claim. If a message needs interpretation, put that interpretation in task/doc metadata, links, or agent-created notes/docs, not by changing the user's words.
Documentation Surface
The document area is intentionally small in v1. It exists so customers and PMs can understand the board and inbox without reading the source repo.
Useful doc categories:
- project brief/scope,
- decisions,
- open questions,
- references,
- notes.
Internal agent knowledge should stay outside the customer-facing docs, for example in repository instructions or implementation files. Pony may later support internal visibility more formally, but it is not required for v1.
Deployment Modes
Local Development
Typical write mode: working_tree.
The app writes files but does not commit or push unless explicitly configured.
Local Agent Workflow
Possible write mode: working_tree or commit.
This lets agents mutate .pony/ through CLI while the human controls when
changes are committed/pushed.
Customer-Facing Server
Typical write mode: commit_and_push.
The server receives inbox submissions, writes one file per message, commits, and pushes so the published/read-only view can update.
Authentication can be handled by the web server/hosting layer in v1. Pony itself should avoid building a full auth system until it has a clearer need.
Important Technical Decisions So Far
- Use shared Astro views for the packaged server and static snapshot.
- Install as a package; do not copy app source into each project repo.
- Let
pony buildrender project-specific static output from packaged source. - Treat one Pony server process as bound to one
.pony/workspace. - Render
project.nameand project state at request time in server mode and at build time in static mode. - Treat package contents as read-only and keep session state in memory.
- Bind the web server to loopback unless external exposure is explicit.
- Version the
.pony/config.ymlschema independently of the package version. - Store project state in
.pony/. - Use Markdown with YAML frontmatter as canonical storage.
- Keep generated context exports separate from canonical Markdown storage.
- Use Zod for schemas.
- Use established packages for parsing/rendering.
- Use system
gitCLI. - Use TypeScript, compatible with both Node and Bun where practical.
- Use a single CLI command with subcommands.
- Use the same npm package as a global launcher and repository-local runtime.
- Let the launcher own initialization, setup, help, and version reporting.
- Require the local runtime to share the launcher's major version and not be newer than the launcher.
- Make CLI the primary v1 agent interface.
- Treat MCP as a future/thin adapter over core.
- Use a global inbox, not card-specific comments, in v1.
- Make all inbox input visible to everyone.
- Let users edit inbox messages only until an agent claims them.
- Use git history as audit trail for edits.
- Use ULID for inbox message IDs.
- Use stable task/doc filenames without titles.
- Commit one inbox message per commit when commit mode is enabled.
- Make write policy configurable and overrideable.
Future Decisions
These remain deliberately undecided until real project usage provides stronger requirements:
- Whether docs should support
visibility: internal. - Whether
.pony/locks/is needed at all, or whether persistent claim state in frontmatter plus process-level file locking is enough. - Whether a future delivery queue should add scheduled push retries beyond the current explicit, idempotent retry command.
- Whether
pony initshould offer to add instructions to rootAGENTS.md, or only create.pony/agent-guide.md. - When the launcher/runtime handshake should move from product-version checks to an independently versioned capability protocol.
