devctl-plugin-authoring
Write, update, and troubleshoot devctl plugins that speak the NDJSON stdio protocol v2 (handshake + request/response/event frames). Use when creating a new devctl plugin, converting repo scripts into devctl pipeline ops, adding dynamic commands, wiring .devctl.yaml, or debugging protocol contamination/timeouts and other plugin failures.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
Devctl Plugin Authoring
Overview
Build repo-specific dev environment logic as a devctl plugin while devctl handles orchestration, supervision, and logs.
Workflow
1. Collect repo context
- Identify services to run and their commands, env vars, ports, and health checks.
- Identify prerequisites (node, docker, db migrations) and build/prepare steps.
- Decide which devctl ops you will implement.
2. Choose ops and config keys
- Start small:
config.mutate,validate.run, andlaunch.planare usually enough. - Add
build.runandprepare.runwhen you need ordered steps with artifacts. - Add
command.runonly for dynamic helper commands (db reset, seed, etc.). - Keep config keys stable and descriptive:
env.*,services.<name>.port,services.<name>.url,artifacts.*.
3. Implement the protocol skeleton
- Emit a handshake as the very first stdout line.
- After the handshake, stdout must be only NDJSON frames (one JSON object per line).
- Send all logs to stderr; flush after every stdout write.
- Return
E_UNSUPPORTEDfor unhandled ops to keep behavior explicit.
See references/protocol-quickref.md for minimal Python and bash skeletons.
4. Implement ops
-
config.mutate: return a config patch with dotted keys; avoid side effects. -
build.run/prepare.run: run named steps and return step results; respectctx.deadline_ms. -
validate.run: return actionable errors/warnings; do not hide failures. -
launch.plan: return services for devctl to supervise; do not start processes yourself. -
command.run: execute a named command fromcapabilities.commands; returnexit_code.
Always honor:
-
ctx.repo_rootfor relative paths. -
ctx.dry_runby avoiding side effects. -
ctx.deadline_msby enforcing timeouts in subprocesses.
5. Wire the repo config
Add .devctl.yaml at repo root, e.g.:
-
id: stable plugin identifier. -
path+args: how to run the plugin. -
env: optional plugin env vars. -
priority: merge precedence.
6. Test the loop
Use the tight feedback loop:
-
devctl plugins list -
devctl plan -
devctl up -
devctl status -
devctl logs --service <name> --follow -
devctl down
If protocol issues appear, retry with devctl --log-level debug plugins list.
7. Troubleshoot common failures
- stdout contamination: non-JSON output on stdout; move logs to stderr.
- missing handshake: first stdout frame not a handshake; emit immediately.
-
timeouts: enforce per-command timeouts using
ctx.deadline_ms. -
health failures: check ports/URLs and health config in
launch.plan.
References
- Use
devctl help devctl-user-guidefor the user workflow. - Use
devctl help devctl-scripting-guidefor practical plugin patterns. - Use
devctl help devctl-plugin-authoringfor full protocol schemas.
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-02-05
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!