adding-skill-program
Create a new skill-based program for the PostHog wizard. Use when adding a program type (like revenue analytics, audit, error tracking) that installs a…
npx skills add https://github.com/posthog/wizard --skill adding-skill-programAdding a Skill-Based Program
Read wizard-development for the shared design policy. Use Pi for new agent work and prefer an orchestrator flow. Linear runs are for very simple work and existing flows. The Anthropic SDK remains a supported legacy fallback, deprecated as the default for new work; the shared guide owns the fallback criteria and gateway model/effort/system-prompt contract.
These are contribution defaults. A program whose config sets no binding runs
on DEFAULT_BINDING, Pi and
linear.
Choose the contribution surface
- Content-only capability: use the existing skill command machinery when it
can express the workflow.
Context-mill owns skill content and
cliEntries. A new skill-backed child of an existing family ships through context-mill; inspect family dispatch. Unpromoted skills run through the skill command. - Native program: use a ProgramConfig when the wizard needs its own flow, screens, detection, composition, or other native behavior. Keep product instructions in context-mill.
Command names, program id, context-mill agentFlow, and skillId have
different roles. Use the full product name for public commands. The config field
is id.
Build a native orchestrator program
Use metrics as the current Pi/orchestrator example and read the runner architecture when changing execution behavior.
- Add the program folder
src/programs/<id>/. Itsindex.tsis the only entry: it exports theProgramConfigasconfig, and any detectors or keys the TUI and CLI need. No other file in the folder is namedindex.ts. Copy a sibling'stsconfig.jsoninto the folder, unchanged, and add{ "path": "<id>" }toreferencesinsrc/programs/tsconfig.json. The folder is one project, its tests included, andpnpm typecheck(tsc -b) builds it. Without the reference, the registry's import fails with TS6307. Keep tests in<id>/__tests__; they reach other layers through their entries only. Shared program code goes insrc/programs/shared/,detection/,warehouse-sources/,frameworks/oroauth/, never in another program's folder, and has noindex.ts. A program imports its own files and the shared program code by relative path, such as../shared/skill-program. SetagentFlowwhen its context-mill flow differs fromid; setting it explicitly also documents the content dependency. Keep arundefinition sorunProgramexecutes agent work. - Write the flow's task prompts in the program's
agent/folder, one<type>.mdper task with itsdependsOnand applicable skills, import them inagent-prompts.ts, and set the config'sagentPrompts; see agent prompts. Withwizard-agent-dagon, the orchestrator reads them, queues a folder with no seed prompt from its frontmatter, and checks task-skill variants before running. With the flag off it loadsagentFlow ?? idfrom context-mill, which must supply the same prompts and a seed prompt.run.skillIdalone does not define this flow. - Register the config: import it as
import { config as <name> } from '@programs/<id>'in program-registry.ts and add it toPROGRAM_REGISTRY. Set the config'sbindingto Pi and the orchestrator.ProgramIdis astring. - For a standalone native command, create a command module in
src/cli/commands with
nativeCommandFactory
and register it in
runCliin src/cli/index.ts. A native family child uses the handlers in family dispatch. Program registration derives screen sequences and store lookup, not the top-level CLI.use()chain. - Check the program's OAuth scopes against the tools it needs. Add
oauthScopeAdditionsto the config only when the base set is insufficient. Keep a program's own set in its folder, as replay-vision does; sets several programs share live inprogram-scopes.ts.
Model and effort selections in flow frontmatter must be supported by the wizard and gateway. Follow the cross-repo procedure in wizard-development before introducing a model or changing gateway-required prompt material.
Simple linear programs and existing flows
For a very simple linear flow, use
createSkillProgram to
configure installation of one skill. Register the native program as above. The
factory sets no binding, so the program runs on DEFAULT_BINDING unless you
add one to the config it returns. Read SkillProgramOptions for required
fields; audit demonstrates factory customization
and a dynamic run(session) that seeds a ledger.
Revenue analytics builds its
config directly and adds prerequisite detection.
ProgramRun.customPrompt, abortCases, postRun, and buildOutroData are
consumed by the
linear sequence. postRun
runs after success; buildOutroData receives session and credentials, with host
information inside credentials. The orchestrator currently uses its own task
prompts, failure handling, and outro, and does not invoke those hooks. Check
this limitation before migrating a linear flow; setting an orchestrator binding
does not preserve these behaviors automatically.
Screens, prerequisites, and composition
The program's UI lives in src/tui/programs/<id>/: flow.ts, deck/,
screens/, screen-ids.ts, and an index.ts(x) entry whose TUI_PROGRAMS
maps each program id it serves to a
TuiProgram (flow, deck, tips and screens).
Outside the folder, the
TUI program registry imports and spreads
the entry. A program with no entry runs
AGENT_SKILL_STEPS: intro,
health check, auth, run, and outro, with the skill deck. runProgram
removes the skills a run installed when it ends. Map
its steps to swap in your own intro screen, as
audit's flow does. Auth also applies
the shared AI opt-in gate,
withAiOptInGate(config, flow), for agent programs. Set healthCheck: false on
the config when the run skips the PostHog readiness check, and leave the
health-check step out of its flow. Flow steps are
FlowSteps. Override screenId when adapting one. A
new screen needs an id in the folder's screen-ids.ts, a component, and an
entry in its entry's screens; the
screen registry mounts every program's
screens. State only the TUI reads, such as a screen's answer, lives in
TuiState and is written through a store
setter, such as store.setIntegrate. The folder is its own tsconfig project:
it may import the TUI core, through @tui/* and never by relative path,
@programs, @programs/types and its own @programs/<id>, never another
program's folder or the registry. Besides its line in src/tui/programs/index.ts, a new folder
needs a tsconfig.json copied from a sibling, unchanged, and
{ "path": "programs/<id>" } in references in
src/tui/tsconfig.json. Then run
pnpm typecheck. Without both, the registry's @tui/programs/<id> import fails
with TS6307.
Follow ink-tui for rendering and store usage. If the
program is team-owned, add both folders to CODEOWNERS.
Use the config's onReady(ctx) for session-dependent detection, then render
structured frameworkContext.detectError data in the intro. A step's onInit
runs when the TUI starts rendering with its initial session. onReady runs
after the real session is assigned: runTui calls runReadyHooks in
store.ts before the intro, and
detectProgram runs it as the
detection of a run whose store has none, as in headless. Set ciPreRun only
when a CI session needs a different prerequisite strategy: detectProgram runs
it in place of onReady for a CI session. Programs read a ProgramSession and
never use the UI: run gets a RunnerContext, ciPreRun gets a
CiRunnerContext and a run step's onRunPrep gets the runner's log
(runner-context.ts).
requires currently records metadata; it does not execute or enforce prior
programs. Compose real work through runSteps, keyed by the flow step id, with
runProgramId, onRunPrep and targetDir when needed.
Self-driving runs
posthog-integration composed at its integrate-run step. Composed sub-runs
are structurally linear; orchestrators cannot nest. A run step without
runProgramId can set targetDir and onRunPrep to scope the program's own
agent to a picked project and keep its sequence, as
error-tracking does for its
run step.
Validate the affected path
Reuse the relevant registry, binding, detection, or routing checks. Add a focused behavioral test only for a meaningful gap; avoid tests that repeat configuration fields. Check that the selected CLI route resolves the intended program and that its context-mill flow is available. Use the exploration guide for a warranted end-to-end run against a disposable app. Follow wizard-development for proportionate checks; documentation-only changes need source/link verification.