원클릭으로
dev-loop
Single public dev-loop entrypoint. Resolve canonical current state first, then load only route-specific internal skills.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Single public dev-loop entrypoint. Resolve canonical current state first, then load only route-specific internal skills.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Single public dev-loop entrypoint. Resolve canonical current state first, then load only route-specific internal skills.
Internal routed strategy behind `dev-loop` for local phase-bounded implementation. Use it when authoritative startup/resume routing selects the `local_implementation` strategy so the child agent can own phase planning, artifact discipline, test-first implementation, and local validation in fresh context.
Standalone pre-loop Socratic Q&A grill for issues (tracker-first), PR bodies, or local plan files (local-planning). Detects spec gaps, asks clarifying questions (interactive) or self-answers them from codebase context (--auto), then synthesizes Acceptance criteria / Definition of done / Non-goals into the body while keeping the raw Q&A only in an ephemeral tmp artifact.
Internal routed strategy behind `dev-loop` for local phase-bounded implementation. Use it when authoritative startup/resume routing selects the `local_implementation` strategy so the child agent can own phase planning, artifact discipline, test-first implementation, and local validation in fresh context.
Standalone pre-loop Socratic Q&A grill for issues (tracker-first), PR bodies, or local plan files (local-planning). Detects spec gaps, asks clarifying questions (interactive) or self-answers them from codebase context (--auto), then synthesizes Acceptance criteria / Definition of done / Non-goals into the body while keeping the raw Q&A only in an ephemeral tmp artifact.
Internal routed strategy behind `dev-loop` for GitHub-first Copilot-owned PR follow-up: inspect the canonical PR state, request or re-request Copilot when appropriate, wait deterministically for new review activity, run narrow Pi fix/reply/resolve passes, verify gate evidence, and stop for explicit human approval before merge.
| name | dev-loop |
| description | Single public dev-loop entrypoint. Resolve canonical current state first, then load only route-specific internal skills. |
| user-invocable | true |
| compatibility | Pi skill for git+GitHub repositories. Requires gh auth; async follow-up works best in Pi/TelePi sessions. |
| allowed-tools | read bash edit write subagent review_loop |
No-implicit-start rule: Never start implementation without explicit instruction.
Work-origin rule: All work must originate from a tracked artifact: a GitHub issue (tracker-first), a persisted markdown plan file (local-planning), or — on the sanctioned lightweight path — the PR description itself as the spec-of-record (--lightweight, canonicalSpecSource: pr_body; no committed plan artifact; issue-less PR-first is size-gated by the light-mode threshold unless localImplementation.issueless sanctions it at any change scope). See Artifact Authority Contract for canonical mode definitions and settings. No work may originate from a PR (other than the sanctioned lightweight PR-body-as-spec path) or a direct local change unless explicitly requested.
This is the public dev-loop façade — a summary of the authoritative routing contract. The authoritative contract is Public Dev Loop Contract. Runtime evaluator: @dev-loops/core/loop/public-dev-loop-routing. For status/progress/readiness/merge-state/next-step queries, resolve authoritative artifact identity first; for issue targets, identity resolution is handled by the startup resolver. Fail closed to reconcile/unknown when unresolved. When an open linked PR exists, treat it as the single canonical artifact for the issue and reuse it instead of opening another PR.
Required installed runtime contract docs are shared bundled copies under ../docs/ from this skill directory. Read those bundled ../docs/ files from the installed skill layout — do not assume a source checkout. If a required bundled contract doc is missing, treat it as a packaging/installer bug.
The main agent must always dispatch the dev-loop async subagent for any dev-loop work.
Do not run dev-loops loop startup or any startup resolver in the main agent.
For async-required routes (config workflow.asyncStartMode, default required) the resolver needs an async run-id marker: the Pi runtime injects PI_SUBAGENT_RUN_ID into each async subagent's child env, or the main agent mints and propagates the neutral DEVLOOPS_RUN_ID before dispatch; under the Claude Code harness the requirement is relaxed automatically (no marker needed). The startup resolver also runs without a marker for non-async routes. Regardless, only the dev-loop subagent runs it — never the main agent.
Under the Claude Code harness the dev-loop runs as a single agent: run these steps directly — no read-only boundary and no separate async-subagent dispatch. See Main Agent Contract.
CLI invocation (<dev-loops-package-root>): dev-loop CLI commands below are invoked as node <dev-loops-package-root>/cli/index.mjs <verb...> using the package-local CLI rather than npx, so they resolve unambiguously from the installed package without a global install. Resolve <dev-loops-package-root> via the first of these bounded candidates whose cli/index.mjs exists — never assume a single fixed layout (under a Pi user-level install the package lives at ~/.pi/agent/npm/node_modules/dev-loops/, so the old ../../.. package-relative guess from skills/dev-loop/SKILL.md overshoots the package root):
node -e "try{const p=require('node:path');console.log(p.resolve(p.dirname(require.resolve('dev-loops/cli/index.mjs')),'..'))}catch{process.exit(1)}" — resolves the package root when dev-loops is reachable from Node's module search path (notably under ~/.pi/agent/npm); this is cwd-dependent and commonly misses from a target-repo cwd, so the probe is wrapped in try/catch (no stack trace, exits non-zero on miss) — treat a non-zero exit as "probe missed, try the next candidate", not a hard failure.~/.pi/agent/npm/node_modules/dev-loops.../../.. from this skill's own directory (the original package-local install layout).$(npm root -g)/dev-loops.NEVER fall back to find / or any unbounded filesystem walk to locate the CLI — it stalls and trips the needs-attention timeout. If every bounded candidate fails, stop and ask the orchestrator/operator for the dev-loops package root rather than searching. (The dev-loop agent resolves it analogously.)
Resolve authoritative state via the startup resolver (node <dev-loops-package-root>/cli/index.mjs loop startup --issue <n> for issues, node <dev-loops-package-root>/cli/index.mjs loop startup --pr <n> for PRs), then immediately build the handoff envelope via node <dev-loops-package-root>/cli/index.mjs loop build-envelope --input <resolver-output.json>. The envelope determines requiredReads, nextAction, stopRules, and acceptance — load only those files, execute only that bounded task. It is the first handoff artifact consumed before loading any route pack. See Workflow Handoff Contract for the derivation contract.
Retrospective checkpoint gate: the resolver reads .pi/dev-loop-retrospective-checkpoint.json and injects the state. When the checkpoint is missing and the repo config workflow.requireRetrospective (set via .devloops at repo root) is true, the resolver returns needs_reconcile. Complete or explicitly skip the retrospective before starting.
Pre-flight PR gate (mandatory): Before working an existing PR, the dev-loop must run node <dev-loops-package-root>/cli/index.mjs loop handoff --repo <owner/name> --pr <number> and abort if action: "stop". When terminal: true, proceed inline. When terminal: false, resolve the blocking condition first.
Worktree cwd (mandatory): Always use a worktree checkout for git operations, file reads/writes, and validation commands — never use the main checkout.
Worktree fetch (mandatory): Always run git fetch origin before creating or reusing any worktree.
When the startup resolver returns a fresh-start routing but an existing outer-loop checkpoint
(tmp/copilot-loop/<owner>/<repo>/pr-<n>/outer-loop-state.json) is present on disk, the
dev-loop must check the checkpoint before treating the start as a fresh intake or follow-up:
outer-loop.mjs).outerAction is continue_wait, reenter_copilot_loop, or reenter_reviewer_loop:
pr field). For reenter_copilot_loop, enter the copilot-pr-followup
path. For reenter_reviewer_loop, enter the reviewer-loop path.copilotState and reviewerState as last-known context for
re-attachment, then re-baseline with fresh detectors (copilot-pr-handoff.mjs
or detect-copilot-loop-state.mjs) before acting on the state.outerAction is stop:
reason field and the authoritative state from the checkpoint.stop means the loop is blocked or needs a human decision; ask for direction
rather than guessing or starting fresh.outerAction is done:
This eliminates the manual "report-and-resume" or "exit and resume later" pattern when the deterministic state already knows the next action.
The outer-loop checkpoint is the canonical re-attachment artifact. Do not rely on chat context, local notes, or prose recollection of "where we left off."
Load only the route-specific internal skill required by selectedStrategy:
| Strategy | Route pack to load |
|---|---|
local_implementation | Local Implementation Skill |
issue_intake | Copilot PR Follow-up Skill + Copilot Loop Operations + Issue Intake Procedure |
copilot_pr_followup | Copilot PR Follow-up Skill + Copilot Loop Operations |
external_pr_followup | same as copilot_pr_followup |
reviewer_fixer | same as copilot_pr_followup |
wait_watch | same as copilot_pr_followup |
final_approval | same as copilot_pr_followup + Final Approval Skill |
Do not preload route packs before the resolver selects the strategy.
Async dispatch rule (enforced): the resolver fails closed for GitHub-first strategies when canonicalStateSummary.requiresAsyncDispatch is true (default required mode) — inline invocation without an async run-id marker (DEVLOOPS_RUN_ID or PI_SUBAGENT_RUN_ID) is rejected for those routes. Under the Claude Code harness this requirement is relaxed automatically. See Startup procedure.
When the @dev-loops/core package is not installed in the consumer repo, the full scripts/github/upsert-checkpoint-verdict.mjs helper (referenced from the copilot-pr-followup skill procedure) is unavailable. To keep the PR audit trail intact in that mode, the dev-loop skill ships a small gh-only fallback poster at scripts/post-gate-verdict-fallback.mjs (relative to the dev-loop skill root) that renders the same visible comment format and fails closed if posting cannot succeed.
Use the fallback poster only when the full helper cannot be reached:
node scripts/github/upsert-checkpoint-verdict.mjs --help from the consumer repo. If the script is absent or imports fail, switch to the fallback path.node <resolved-skill-scripts>/post-gate-verdict-fallback.mjs --repo <owner/name> --pr <number> --head-sha <sha> --verdict <clean|findings_present|blocked> (--findings-summary <text> | --findings-file <path>) --next-action <text> [--gate <draft_gate|pre_approval_gate>].When @dev-loops/core is available again, switch back to the full helper. The fallback poster is a degraded path, not a permanent replacement.
Info/handoff requests can be served directly via node <dev-loops-package-root>/cli/index.mjs loop info (read-only; no full dev-loop run required):
node <dev-loops-package-root>/cli/index.mjs loop info --issue <n> — human-readable issue state summary (strategy, route, linked PR, next action)node <dev-loops-package-root>/cli/index.mjs loop info --pr <n> — human-readable PR state summary (branch, CI, threads, rounds, action)node <dev-loops-package-root>/cli/index.mjs loop info --issue <n> --json — machine-readable JSON outputWhen you need a fact from a dev-loops JSON-emitting script, climb this ladder and stop at the first rung that answers the question — never read more output than you need:
Prefer the dev-loops subcommand / concise mode. Use loop info or a script's --concise/--summary mode (e.g. run-watch-cycle.mjs --concise, probe-copilot-review.mjs --concise) for a human-readable digest. The concise modes surface loop state, Copilot round count, unresolved/actionable thread counts, round-cap-clean eligibility, CI status, next action, and the current round's new Copilot comment bodies.
--silent / -s for a yes/no check. Reads ZERO output: … --jq '<predicate>' --silent; echo $? exits 0 for true / 1 for false. Without --jq, --silent maps the script's success (ok:true) to exit 0, failure to 1 — unless the tool documents a stricter --silent contract in its own usage text (e.g. request-copilot-review.mjs exits 0 only for status: "requested"). Example: probe-copilot-review.mjs --repo o/r --pr N --jq '.status=="idle"' -s.
--jq <filter> to extract a single field. --jq/--silent are a BASE-CLI GUARANTEE across every operator-facing JSON-result dev-loops command — each accepts a gh-style --jq filter (jq subset: field access, .[]/.[N], |, select(...), ==/!=/</<=/>/>=, length, keys), not just a named subset. (A few scripts are out of scope — e.g. build/smoke tooling, dashboard servers, dormant/unwired adapters, and scripts that write JSON to a file rather than stdout — each carried with its reason in the contract test's exclusion list.) It prints only the filtered value. An invalid filter fails closed (stderr + exit 2), distinct from a clean predicate-false (silent exit 1). This is enforced by a contract test (test/contracts/jq-output-base-guarantee-contract.test.mjs) that fails the build if a new JSON-emitting command ships without wiring the shared scripts/lib/jq-output.mjs emit path.
Use a dev-loops wrapper for gh reads — never an agent-level raw gh. A raw gh call is a recorded advisory retro violation (issue #1077: reported, never blocking). If no script covers the read you need, treat it as a tooling gap: file/build a thin wrapper (reuse scripts/lib/jq-output.mjs, like the ones below), don't shell out. The reads/edits that already have wrappers:
scripts/github/fetch-ci-logs.mjs --repo <o/r> --pr <n> [--failed-only] [--tail <n>], never raw gh run view --log/--log-failed. (probe-ci-status.mjs names the failed checks; this returns the LOG.)scripts/github/list-issues.mjs --repo <o/r> [--state <open|closed|all>] [--label <l>] [--limit <n>], never raw gh issue list. (The queue tool lists the project board; this is for arbitrary issue queries.)scripts/github/comment-issue.mjs --repo <o/r> --issue <n> (--body <text> | --body-file <path>), never raw gh issue comment. Returns { ok, commentUrl }.scripts/github/edit-comment.mjs --repo <o/r> --comment-id <n> (--body <text> | --body-file <path>), never raw gh api -X PATCH .../issues/comments/{id}. Covers both issue comments and PR issue-comments (same REST endpoint). Returns { ok, commentUrl }.scripts/github/edit-issue.mjs --repo <o/r> --issue <n> [--title <t>] [--body <b> | --body-file <path>] [--add-assignee <u>] [--remove-assignee <u>] [--milestone <m>] [--state <open|closed>] [--reason <completed|not_planned>], never raw gh issue edit / gh issue close / gh issue reopen. --state runs the close/reopen call after any other edits. Returns { ok, repo, issue, edited }.scripts/github/view-issue.mjs --repo <o/r> --issue <n> [--json <fields>], never raw gh issue view. Returns the issue body that list-issues.mjs does not; the sanctioned reader for issue bodies (parallel to view-pr).scripts/github/view-pr.mjs --repo <o/r> --pr <n> [--json <fields>], never raw gh pr view. (For composite loop-routing/CI facts prefer loop info --pr; this is the thin field-read counterpart.)scripts/github/edit-pr.mjs --repo <o/r> --pr <n> [--title <t>] [--body <b> | --body-file <path>] [--add-assignee <u>] [--remove-assignee <u>] [--milestone <m>], never raw gh pr edit. Returns { ok, repo, pr, edited }.scripts/github/probe-ci-status.mjs --repo <o/r> --pr <n> --timeout-ms 0 for a single live combined-CI check, never raw gh pr checks. (Provider-agnostic; drop the --timeout-ms 0 to block-wait.)These accept the same --jq/--silent output flags as every other JSON-emitting script (base-CLI guarantee) — including probe-ci-status.mjs, watch-shaped as it is.
NEVER | python3 or node -e to parse tool JSON. If a field you need is missing from a script's output, add it to the script (or its concise mode), not an inline parser.
Handoff envelope precedence: The dev-loop builds the envelope immediately after authoritative-state resolution and treats it as the first handoff artifact. Read it first, load only requiredReads, execute nextAction. See Resolve authoritative state. Derivation contract: Workflow Handoff Contract.
Handoff contract rule: When no envelope is present, use the workflow-handoff-contract.md contract. Never delegate with abbreviated task summaries. Include deterministic routing inputs, explicit cwd, bounded task scope, exit conditions.
Inline-first rule: Prefer inline commands over nested async delegation when managing a single PR. Use nested delegation only for parallel fan-out or when the parent needs to continue other work.
Bounded async task contract: Break work into discrete tasks with clear inputs, explicit outputs, bounded scope. No shell polling — use run-watch-cycle.mjs or gh run watch. Fan-out reviewer waits (gate sub-loops or any Agent-tool fan-out) follow ANTIPATTERN-FANIN-WAIT in Anti-patterns: await completion via the harness notification or the reviewer's findings artifact at its deterministic path and join via consolidateFanin — never transcript-tail, node -e/python3-parse tool JSON, or sleep-poll.
Round-cap budget check (enforced): After every watch cycle, fix pass, or reply-resolve, check whether completed Copilot review rounds have reached the resolved round cap (refinement.maxCopilotRounds, default 5; light-dispatched PRs resolve the lower resolveEffectiveCopilotRoundCap, default 1 — owned by COPILOT-FOLLOWUP-ROUND-CAP). Stop re-requesting Copilot review when the limit is reached within that review cycle. Exception: the post-convergence new-cycle re-request carve-out (a converged loop that later takes significant post-convergence changes on a newer head opens a new cycle even if the previous one hit the cap) is owned by COPILOT-FOLLOWUP-ROUND-CAP. Read these gate-cadence facts via the token-economical convention above (run-watch-cycle.mjs --concise, or --jq/--silent for a single field/predicate) — never | python3 or node -e.
auto dev loop on issue <n> → public dev-loop intent auto_continue_current after authoritative current-state resolutionHeadless/--auto only. The enqueue refinement gate never lets an un-refined issue reach the pickup column: in --auto mode add-queue-item.mjs diverts it to the non-pickup park column with a recorded reason (refined:false, diverted:true, parkedColumn, reason), and it deliberately does NOT grill — synthesizing the missing artifact is this orchestrator's job, never the coordinator script's (keeps OPS-NO-INLINE-INTERPRETER clean). So a headless auto session that finds the pickup source empty may still have parked issues awaiting refinement. Before idling, run this bounded sub-loop (skip it entirely for interactive runs and for a specific --issue/--pr target):
node <dev-loops-package-root>/cli/index.mjs queue parked-unrefined --repo <owner/name> (project auto-resolved from .devloops; add --jq/--silent per the token-economical convention). It reads the park column and runs the same refinement-completeness check as the enqueue gate; each item carries { issueNumber, reason, missing }. Empty list → nothing to refine; proceed to the normal fail-closed idle. Iterate the returned items in ascending issueNumber order, and attempt each at most once per session (steps 2–4). This is what bounds the sub-loop: one grill attempt per discovered item, then idle./loop-grill <issueNumber> --auto (the loop-grill skill synthesizes AC/DoD/Non-goals into the issue body — do not re-implement grilling, and never move it into a coordinator script).grill-clean verdict the issue is refined — confirmed by the same completeness check the enqueue gate runs (detectIssueRefinementArtifact), so this move admits exactly what the gate would. The item is already on the board in the park column, so move it into the pickup column with node <dev-loops-package-root>/cli/index.mjs queue move --repo <owner/name> --item <issueNumber> --to-column "<pickup column>" (the configured Next Up column). Do NOT use add-queue-item here: it is an idempotent no-op for an already-present item and cannot promote it. Then continue to the next discovered item.N unresolved items, or the body still lacks AC/DoD/linked-doc), do NOT move the issue into the pickup column. It is already in the park column where discovery found it, so leave it there — no move, no re-enqueue — and surface the reason from the step-1 discovery output for a human. Advance to the next discovered item — do NOT re-grill an item already attempted this session, so a permanently-unrefinable issue can neither spin the loop nor starve the refinable items behind it. Never hand-move an item into the pickup column that grilling could not refine.The sub-loop terminates once every discovered item has had its single attempt (all refinable ones promoted, the rest left parked with a reason), then proceeds to the normal fail-closed idle. A still-parked item is re-attempted only on a later session, not within this one. This wires the auto-refine convenience at the orchestrator (LLM-agent) layer only. The deterministic scripts keep just the park/allow decision and the parked-item discovery.
All PRs must pass the full gate pipeline before merge. No scope is exempt: docs-only, tooling, meta, configuration, internal-process — all require draft_gate, current-head pre_approval_gate evidence, and Copilot review (except internal-only PRs detected by path pattern, which skip the Copilot convergence requirement).