dev-loops 0.5.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/agents/dev-loop.md +2 -1
  3. package/.claude/agents/review.md +2 -1
  4. package/.claude/commands/loop-auto.md +7 -0
  5. package/.claude/commands/loop-continue.md +15 -0
  6. package/.claude/commands/loop-enqueue.md +22 -0
  7. package/.claude/commands/loop-grill.md +17 -0
  8. package/.claude/commands/loop-info.md +7 -0
  9. package/.claude/commands/loop-queue-status.md +24 -0
  10. package/.claude/commands/loop-start-spike.md +16 -0
  11. package/.claude/commands/loop-start.md +7 -0
  12. package/.claude/commands/loop-status.md +6 -0
  13. package/.claude/hooks/_bash-command-classify.mjs +333 -29
  14. package/.claude/hooks/_hook-decisions.mjs +138 -15
  15. package/.claude/hooks/_run-context.mjs +11 -4
  16. package/.claude/hooks/pre-tool-use-bash-gate.mjs +36 -15
  17. package/.claude/skills/copilot-pr-followup/SKILL.md +24 -12
  18. package/.claude/skills/dev-loop/SKILL.md +19 -10
  19. package/.claude/skills/docs/acceptance-criteria-verification.md +6 -2
  20. package/.claude/skills/docs/anti-patterns.md +2 -1
  21. package/.claude/skills/docs/artifact-authority-contract.md +22 -4
  22. package/.claude/skills/docs/copilot-loop-operations.md +1 -1
  23. package/.claude/skills/docs/cross-harness-regression-contract.md +60 -0
  24. package/.claude/skills/docs/issue-intake-procedure.md +1 -0
  25. package/.claude/skills/docs/local-planning-flow.md +1 -1
  26. package/.claude/skills/docs/local-planning-worked-example.md +1 -1
  27. package/.claude/skills/docs/merge-preconditions.md +17 -1
  28. package/.claude/skills/docs/plan-file-contract.md +1 -1
  29. package/.claude/skills/docs/public-dev-loop-contract.md +1 -1
  30. package/.claude/skills/docs/release-runbook.md +45 -0
  31. package/.claude/skills/docs/retrospective-checkpoint-contract.md +90 -76
  32. package/.claude/skills/docs/ui-e2e-scoping-step.md +134 -0
  33. package/.claude/skills/docs/workflow-handoff-contract.md +39 -0
  34. package/.claude/skills/local-implementation/SKILL.md +26 -15
  35. package/.claude/skills/loop-grill/SKILL.md +163 -0
  36. package/AGENTS.md +1 -0
  37. package/CHANGELOG.md +125 -0
  38. package/README.md +29 -10
  39. package/agents/dev-loop.agent.md +11 -3
  40. package/agents/developer.agent.md +1 -1
  41. package/agents/docs.agent.md +1 -1
  42. package/agents/fixer.agent.md +1 -1
  43. package/agents/quality.agent.md +1 -1
  44. package/agents/refiner.agent.md +1 -1
  45. package/agents/review.agent.md +3 -2
  46. package/cli/index.mjs +35 -40
  47. package/extension/README.md +4 -4
  48. package/extension/checks.ts +1 -5
  49. package/extension/harness-types.ts +1 -0
  50. package/extension/index.ts +16 -1
  51. package/extension/pi-extension-adapter.ts +2 -0
  52. package/extension/presentation.ts +16 -13
  53. package/lib/dev-loops-core.mjs +141 -0
  54. package/package.json +10 -8
  55. package/scripts/claude/generate-claude-assets.mjs +15 -1
  56. package/scripts/github/capture-review-threads.mjs +2 -9
  57. package/scripts/github/comment-issue.mjs +174 -0
  58. package/scripts/github/create-label.mjs +133 -0
  59. package/scripts/github/detect-checkpoint-evidence.mjs +170 -14
  60. package/scripts/github/detect-linked-issue-pr.mjs +22 -6
  61. package/scripts/github/edit-pr.mjs +259 -0
  62. package/scripts/github/fetch-ci-logs.mjs +208 -0
  63. package/scripts/github/list-issues.mjs +184 -0
  64. package/scripts/github/manage-sub-issues.mjs +46 -10
  65. package/scripts/github/offer-human-handoff.mjs +8 -5
  66. package/scripts/github/post-gate-findings.mjs +14 -2
  67. package/scripts/github/probe-ci-status.mjs +8 -2
  68. package/scripts/github/probe-copilot-review.mjs +21 -14
  69. package/scripts/github/ready-for-review.mjs +26 -8
  70. package/scripts/github/reconcile-draft-gate.mjs +7 -3
  71. package/scripts/github/reply-resolve-review-thread.mjs +16 -5
  72. package/scripts/github/reply-resolve-review-threads.mjs +69 -7
  73. package/scripts/github/request-copilot-review.mjs +8 -2
  74. package/scripts/github/resolve-handoff-candidates.mjs +7 -3
  75. package/scripts/github/resolve-tracker-local-spec.mjs +9 -3
  76. package/scripts/github/stage-reviewer-draft.mjs +10 -2
  77. package/scripts/github/tick-verified-checkboxes.mjs +202 -0
  78. package/scripts/github/upsert-checkpoint-verdict.mjs +31 -10
  79. package/scripts/github/verify-fresh-review-context.mjs +150 -31
  80. package/scripts/github/view-pr.mjs +150 -0
  81. package/scripts/github/write-gate-context.mjs +248 -61
  82. package/scripts/github/write-gate-findings-log.mjs +75 -1
  83. package/scripts/lib/jq-output.mjs +18 -0
  84. package/scripts/loop/_handoff-contract.mjs +1 -0
  85. package/scripts/loop/_post-convergence-change.mjs +211 -0
  86. package/scripts/loop/_pr-runner-coordination.mjs +70 -0
  87. package/scripts/loop/_repo-root-resolver.mjs +47 -0
  88. package/scripts/loop/build-handoff-envelope.mjs +14 -4
  89. package/scripts/loop/check-retro-tooling.mjs +14 -3
  90. package/scripts/loop/checkpoint-contract.mjs +7 -4
  91. package/scripts/loop/cleanup-worktree.mjs +12 -3
  92. package/scripts/loop/conductor-monitor.mjs +12 -18
  93. package/scripts/loop/copilot-pr-handoff.mjs +135 -14
  94. package/scripts/loop/debt-remediate.mjs +24 -12
  95. package/scripts/loop/detect-change-scope.mjs +36 -7
  96. package/scripts/loop/detect-copilot-loop-state.mjs +11 -5
  97. package/scripts/loop/detect-copilot-session-activity.mjs +7 -3
  98. package/scripts/loop/detect-initial-copilot-pr-state.mjs +7 -3
  99. package/scripts/loop/detect-internal-only-pr.mjs +8 -2
  100. package/scripts/loop/detect-issue-refinement-artifact.mjs +6 -3
  101. package/scripts/loop/detect-pr-gate-coordination-state.mjs +179 -66
  102. package/scripts/loop/detect-reviewer-loop-state.mjs +12 -2
  103. package/scripts/loop/detect-tracker-first-loop-state.mjs +9 -1
  104. package/scripts/loop/detect-tracker-pr-state.mjs +10 -3
  105. package/scripts/loop/ensure-worktree.mjs +8 -3
  106. package/scripts/loop/info.mjs +12 -4
  107. package/scripts/loop/inspect-run-viewer/constants.mjs +4 -18
  108. package/scripts/loop/inspect-run-viewer/vendor/mermaid.min.js +3405 -0
  109. package/scripts/loop/inspect-run-viewer-ci-changes.mjs +18 -6
  110. package/scripts/loop/inspect-run-viewer.mjs +67 -4
  111. package/scripts/loop/inspect-run.mjs +8 -2
  112. package/scripts/loop/outer-loop.mjs +8 -4
  113. package/scripts/loop/pr-runner-coordination.mjs +2 -9
  114. package/scripts/loop/pre-commit-branch-guard.mjs +16 -10
  115. package/scripts/loop/pre-flight-gate.mjs +9 -9
  116. package/scripts/loop/pre-pr-ready-gate.mjs +8 -4
  117. package/scripts/loop/pre-write-remote-freshness-guard.mjs +13 -4
  118. package/scripts/loop/provision-worktree.mjs +74 -3
  119. package/scripts/loop/resolve-dev-loop-startup.mjs +76 -7
  120. package/scripts/loop/resolve-gate-dispatch.mjs +134 -0
  121. package/scripts/loop/resolve-pr-conflicts.mjs +14 -7
  122. package/scripts/loop/run-conductor-cycle.mjs +13 -2
  123. package/scripts/loop/run-queue.mjs +18 -9
  124. package/scripts/loop/run-refinement-audit.mjs +8 -9
  125. package/scripts/loop/run-watch-cycle.mjs +2 -9
  126. package/scripts/loop/sanctioned-commands.mjs +104 -0
  127. package/scripts/loop/steer-loop.mjs +29 -16
  128. package/scripts/loop/validate-pr-body-spec.mjs +207 -0
  129. package/scripts/loop/watch-initial-copilot-pr.mjs +8 -3
  130. package/scripts/pages/build-site.mjs +59 -8
  131. package/scripts/pages/build-state-atlas.mjs +441 -0
  132. package/scripts/projects/_resolve-project.mjs +148 -0
  133. package/scripts/projects/add-queue-item.mjs +60 -54
  134. package/scripts/projects/archive-done-items.mjs +49 -84
  135. package/scripts/projects/ensure-queue-board.mjs +10 -36
  136. package/scripts/projects/list-queue-items.mjs +116 -65
  137. package/scripts/projects/move-queue-item.mjs +28 -49
  138. package/scripts/projects/reconcile-queue.mjs +253 -0
  139. package/scripts/projects/reorder-queue-item.mjs +41 -47
  140. package/scripts/projects/resolve-active-board-item.mjs +255 -0
  141. package/scripts/projects/sync-item-status.mjs +15 -10
  142. package/scripts/refine/_refine-helpers.mjs +21 -5
  143. package/scripts/refine/exit-spike.mjs +18 -8
  144. package/scripts/refine/promote-plan.mjs +22 -16
  145. package/scripts/refine/prose-linkage-detector.mjs +1 -1
  146. package/scripts/refine/refine-plan-file.mjs +13 -4
  147. package/scripts/refine/refinement-completeness-checker.mjs +1 -1
  148. package/scripts/refine/scaffold-spike-file.mjs +179 -0
  149. package/scripts/refine/scope-boundary-cross-checker.mjs +1 -1
  150. package/scripts/refine/tree-integrity-validator.mjs +1 -1
  151. package/scripts/refine/validate-plan-file.mjs +1 -1
  152. package/scripts/refine/validate-spike-file.mjs +1 -1
  153. package/scripts/refine/verify.mjs +11 -6
  154. package/scripts/release/assert-core-dependency-version.mjs +123 -0
  155. package/scripts/release/extract-changelog-section.mjs +125 -0
  156. package/skills/copilot-pr-followup/SKILL.md +24 -12
  157. package/skills/dev-loop/SKILL.md +22 -6
  158. package/skills/docs/acceptance-criteria-verification.md +6 -2
  159. package/skills/docs/anti-patterns.md +2 -1
  160. package/skills/docs/artifact-authority-contract.md +22 -4
  161. package/skills/docs/copilot-loop-operations.md +1 -1
  162. package/skills/docs/cross-harness-regression-contract.md +60 -0
  163. package/skills/docs/issue-intake-procedure.md +1 -0
  164. package/skills/docs/local-planning-flow.md +1 -1
  165. package/skills/docs/local-planning-worked-example.md +1 -1
  166. package/skills/docs/merge-preconditions.md +17 -1
  167. package/skills/docs/plan-file-contract.md +1 -1
  168. package/skills/docs/public-dev-loop-contract.md +1 -1
  169. package/skills/docs/release-runbook.md +45 -0
  170. package/skills/docs/retrospective-checkpoint-contract.md +90 -76
  171. package/skills/docs/ui-e2e-scoping-step.md +134 -0
  172. package/skills/docs/workflow-handoff-contract.md +39 -0
  173. package/skills/local-implementation/SKILL.md +26 -15
  174. package/skills/loop-grill/SKILL.md +165 -0
  175. package/scripts/loop/conductor.mjs +0 -233
  176. package/scripts/loop/detect-stale-runner.mjs +0 -265
  177. package/scripts/loop/pre-push-main-guard.mjs +0 -117
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "dev-loop"
3
3
  description: "Use as the single public workflow entrypoint. Route from canonical current state to the deterministic internal strategy, preferring GitHub-first paths and only using local phase implementation when explicitly requested. Keywords: dev-loop, public entrypoint, route workflow, continue dev loop."
4
- tools: [read, search, execute, bash, agent, todo, subagent]
4
+ tools: read, search, execute, bash, agent, todo, subagent
5
5
  argument-hint: "A dev-loop intent such as issue number/URL, PR number/URL, or a request to continue/inspect current state."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -23,10 +23,18 @@ The envelope is the primary handoff artifact — it is derived from resolver out
23
23
  - `nextAction` — the bounded task to execute
24
24
  - `stopRules` — stop boundaries that must not be crossed without authorization
25
25
  - `acceptance` — self-validation criteria for declaring completion
26
+ - `sanctionedCommands` — the operation → wrapper command map (reads/edits/lifecycle), plus the forbidden and orchestrator-owned lists. Carried by DEFAULT on every build so you never re-derive which wrapper performs a GitHub/loop operation. Do NOT restate the map here — the single source of truth is `scripts/loop/sanctioned-commands.mjs`, surfaced verbatim in the envelope.
26
27
 
27
28
  **Construction sequence:**
28
29
  <!-- pi-only -->
29
- **CLI invocation (`<dev-loops-package-root>`):** dev-loop CLI commands 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>` from this agent's own installed path: this agent is installed at `<package-root>/.pi/agents/dev-loop.agent.md`, so the package root is `../..` from this agent's directory (`agents` `.pi` package root). (The `dev-loop` skill resolves it analogously from its own installed path.)
30
+ **CLI invocation (`<dev-loops-package-root>`):** dev-loop CLI commands 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 (this agent may be installed user-level at `~/.agents/`, where the old `../..` package-relative guess resolves to `~`, not the package):
31
+
32
+ 1. **Node module resolution** (best-effort first try): `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.
33
+ 2. **Pi user-agent npm root** (reliable for user-level installs): `~/.pi/agent/npm/node_modules/dev-loops`.
34
+ 3. **Package-relative (legacy):** `../..` from this agent's own directory (the original package-local install layout).
35
+ 4. **Global npm root:** `$(npm root -g)/dev-loops`.
36
+
37
+ 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` skill resolves it analogously.)
30
38
  <!-- /pi-only -->
31
39
 
32
40
  1. Run the deterministic startup resolver to produce the authoritative state bundle: `node <dev-loops-package-root>/cli/index.mjs loop startup --issue <n>` for issues, or `node <dev-loops-package-root>/cli/index.mjs loop startup --pr <n>` for PRs.
@@ -64,7 +72,7 @@ If local facts, GitHub facts, and helper/state-machine output do not agree well
64
72
  ## Subagent delegation
65
73
 
66
74
  <!-- pi-only -->
67
- This agent has `tools: [subagent]` and `maxSubagentDepth: 3` to allow orchestrating parallel review, chains, and staged fix passes.
75
+ This agent's frontmatter `tools:` comma-token scalar includes `subagent` (single-line comma form, no brackets — see #1111) and it sets `maxSubagentDepth: 3` to allow orchestrating parallel review, chains, and staged fix passes.
68
76
  <!-- /pi-only -->
69
77
 
70
78
  All delegation must originate from the handoff envelope: the envelope's `nextAction`, `requiredReads`, `stopRules`, and `acceptance` define the bounded task. The envelope is passed to child subagents as their primary handoff artifact.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "developer"
3
3
  description: "Use for direct product implementation in this repository: focused code changes, refactors, tests, bug fixes, and feature work within an already-scoped task. Keywords: implement feature, write code, refactor module, add tests, fix bug, update source."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "Focused implementation task, relevant files, success criteria, and required verification."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "docs"
3
3
  description: "Use for README updates, plan docs, architecture notes, agent docs, migration notes, narrow documentation changes that must stay aligned with implementation work, and documentation-correctness review for the current change. Keywords: docs, README, plans, documentation, agent docs, rollout notes, changelog-style summary, docs review."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "Documentation task or documentation-correctness review, affected files, source changes to reflect, and required level of detail."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "fixer"
3
3
  description: "Use for addressing active pull request review comments and threads: inspect unresolved feedback, make the narrow fix, verify it, push the fixing commit, reply with the resolving commit, and resolve the thread. Keywords: fixer, PR comments, address review feedback, resolve review threads, push fix commit."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "Review-fix task, PR number or branch, target reviewer/thread/file, and required verification."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "quality"
3
3
  description: "Use for build systems, test runners, type-checking, linting, package scripts, GitHub Actions workflows, caches, release verification, and quality gates. Keywords: CI, workflow, GitHub Actions, build, test, cache, typecheck, package scripts, branch protection."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "Quality or CI task, relevant workflows/config files, required checks, and verification expectations."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "refiner"
3
3
  description: "Use for refining one approved implementation phase at a time into a complete, testable plan with acceptance criteria, definition of done, risks, non-goals, unresolved questions, and RFC escalation notes. Keywords: refiner, phase refinement, acceptance criteria, definition of done, RFC escalation, merged plan."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "Active phase doc or rough plan, phase boundary, known constraints, and any prior planning artifacts to refine."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "review"
3
3
  description: "Use for pull request review from a product and engineering perspective: check the implementation against the PR description, relevant plan, acceptance criteria, definition of done, non-goals, coding best practices, security expectations, and merge readiness. Keywords: review, PR review, acceptance criteria review, DoD review, security review, plan compliance."
4
- tools: [read, search, execute, bash, edit, write]
4
+ tools: read, search, execute, bash, edit, write
5
5
  argument-hint: "PR number or branch, relevant plan files, and any specific review focus areas or constraints."
6
6
  systemPromptMode: append
7
7
  inheritProjectContext: true
@@ -33,7 +33,8 @@ This agent has two modes. The default mode is the full-PR review described in th
33
33
 
34
34
  You are in scoped angle-review mode when the invocation supplies a single review `<angle>` plus a gate-context artifact path (`tmp/gate-context/<repo-slug>/pr-<N>/<gate>-<headSha>.json`, written by `scripts/github/write-gate-context.mjs`). Review like an external code reviewer hunting real bugs, not a process auditor: your job is to find concrete defects in the changed code and its call paths, not to remark that "there is no test" or that a doc section is thin. In that mode:
35
35
 
36
- - **Fresh-context guard (mandatory):** run `node scripts/github/verify-fresh-review-context.mjs --scope <angle>` at startup. If it reports `fresh: false` (exit 1) or any error, refuse to proceed and report contamination do not review on inherited context. "Fresh" here means your context is the **neutral builder artifact + your angle**, and explicitly NOT the main agent's conversation/state or a prior reviewer session's state. The injected neutral bundle is the INTENDED seed (allowed); main-agent or cross-session state bleed still fails closed.
36
+ - **No worktree isolation (mandatory):** you review in the PR's actual worktree/head the same checkout the gate-context preamble ran in never a fresh isolated worktree. You are read-only and never mutate files, so isolation buys nothing; a fresh worktree would also be checked out from `main` (not the PR head) and would lack the gitignored, worktree-local gate-context bundle below, so you'd silently review the wrong stale tree (#1135).
37
+ - **Fresh-context guard (mandatory):** run `node scripts/github/verify-fresh-review-context.mjs --scope <angle> --context-path <gate-context-artifact-path>` at startup. If it reports `fresh: false` (exit 1) or any error — including a missing gate-context artifact (`gateContextPresent: false`), which is the fail-closed signal that you're not in the right worktree/head — refuse to proceed and report the failure; do not review on inherited context or without seeded context. "Fresh" here means your context is the **neutral builder artifact + your angle**, and explicitly NOT the main agent's conversation/state or a prior reviewer session's state. The injected neutral bundle is the INTENDED seed (allowed); main-agent or cross-session state bleed still fails closed.
37
38
  - **Use the PROVIDED neutral bundle as your base; do NOT re-derive it from scratch:** the gate-context artifact carries the diff (`scope.diffPath`) and a deterministic, neutral adjacent-code bundle in `adjacentCode` (each changed file plus its 1-hop import in/out-edges, with a `stripped`/`truncated`/`missing` manifest). Start from this bundle — it is the build-once, work-deduped seed shared verbatim across all reviewers. Only widen (load more files) per-angle when the bundle genuinely lacks something your angle needs; do not re-build the whole diff/adjacent-code graph yourself.
38
39
  - **Single-angle scope:** review ONLY the supplied angle's concern. Read the gate-context artifact for the resolved angle set, change scope (branch, head SHA, touched files), acceptance-criteria pointer, and validation posture. Do not review other angles; the fan-in pass consolidates across angles.
39
40
  - **Read the FULL diff, not just hunks:** the gate-context artifact records `scope.diffPath` (a repo-relative path to the complete captured diff) and `scope.changedFiles` (full repo-relative paths). Read the entire diff from `scope.diffPath`; if it is `null` or missing, reconstruct it yourself with `git diff` against the change's base (e.g. `git diff origin/main...HEAD` or the branch/head SHA in `scope`). Do not review off a partial or summarized hunk list — you must see every changed line for your angle.
package/cli/index.mjs CHANGED
@@ -17,6 +17,34 @@ import { createPiAdapter } from "@dev-loops/core/harness";
17
17
 
18
18
  const REPO_ROOT = fileURLToPath(new URL("..", import.meta.url));
19
19
 
20
+ // `project` is an alias for `queue` minus the run driver — derived, not duplicated.
21
+ const QUEUE_ROUTES = {
22
+ run: "scripts/loop/run-queue.mjs",
23
+ list: "scripts/projects/list-queue-items.mjs",
24
+ add: "scripts/projects/add-queue-item.mjs",
25
+ move: "scripts/projects/move-queue-item.mjs",
26
+ reorder: "scripts/projects/reorder-queue-item.mjs",
27
+ "archive-done": "scripts/projects/archive-done-items.mjs",
28
+ "sync-status": "scripts/projects/sync-item-status.mjs",
29
+ ensure: "scripts/projects/ensure-queue-board.mjs",
30
+ "resolve-active": "scripts/projects/resolve-active-board-item.mjs",
31
+ reconcile: "scripts/projects/reconcile-queue.mjs",
32
+ };
33
+ const { run: _queueRunRoute, ...PROJECT_ROUTES } = QUEUE_ROUTES;
34
+
35
+ const QUEUE_DESCRIPTIONS = {
36
+ run: "Run queue driver",
37
+ list: "List queue board items",
38
+ add: "Add issue/PR to queue board",
39
+ move: "Move queue item between Status columns",
40
+ reorder: "Reorder items (move-to-top/move-after/order, --dry-run)",
41
+ "archive-done": "Archive closed Done items older than a duration",
42
+ "sync-status": "Sync a queued issue/PR's board Status column (best-effort)",
43
+ ensure: "Create/repair queue board bootstrap surface",
44
+ reconcile: "Reconcile board Status columns from live GitHub state (idempotent)",
45
+ };
46
+ const { run: _queueRunDescription, ...PROJECT_DESCRIPTIONS } = QUEUE_DESCRIPTIONS;
47
+
20
48
  const SUBCOMMAND_ROUTES = {
21
49
  gate: {
22
50
  "upsert-verdict": "scripts/github/upsert-checkpoint-verdict.mjs",
@@ -50,25 +78,8 @@ const SUBCOMMAND_ROUTES = {
50
78
  "ready-for-review": "scripts/github/ready-for-review.mjs",
51
79
  "reconcile-draft": "scripts/github/reconcile-draft-gate.mjs",
52
80
  },
53
- project: {
54
- list: "scripts/projects/list-queue-items.mjs",
55
- add: "scripts/projects/add-queue-item.mjs",
56
- move: "scripts/projects/move-queue-item.mjs",
57
- reorder: "scripts/projects/reorder-queue-item.mjs",
58
- "archive-done": "scripts/projects/archive-done-items.mjs",
59
- "sync-status": "scripts/projects/sync-item-status.mjs",
60
- ensure: "scripts/projects/ensure-queue-board.mjs",
61
- },
62
- queue: {
63
- run: "scripts/loop/run-queue.mjs",
64
- list: "scripts/projects/list-queue-items.mjs",
65
- add: "scripts/projects/add-queue-item.mjs",
66
- move: "scripts/projects/move-queue-item.mjs",
67
- reorder: "scripts/projects/reorder-queue-item.mjs",
68
- "archive-done": "scripts/projects/archive-done-items.mjs",
69
- "sync-status": "scripts/projects/sync-item-status.mjs",
70
- ensure: "scripts/projects/ensure-queue-board.mjs",
71
- },
81
+ queue: QUEUE_ROUTES,
82
+ project: PROJECT_ROUTES,
72
83
  inspect: {
73
84
  run: "scripts/loop/inspect-run.mjs",
74
85
  viewer: "scripts/loop/inspect-run-viewer.mjs",
@@ -141,25 +152,8 @@ const SUBCOMMAND_DESCRIPTIONS = {
141
152
  "ready-for-review": "Mark PR ready for review",
142
153
  "reconcile-draft": "Reconcile non-draft PR",
143
154
  },
144
- project: {
145
- list: "List queue board items",
146
- add: "Add issue/PR to queue board",
147
- move: "Move queue item between Status columns",
148
- reorder: "Reorder items (move-to-top/move-after/order, --dry-run)",
149
- "archive-done": "Archive closed Done items older than a duration",
150
- "sync-status": "Sync a queued issue/PR's board Status column (best-effort)",
151
- ensure: "Create/repair queue board bootstrap surface",
152
- },
153
- queue: {
154
- run: "Run queue driver",
155
- list: "List queue board items",
156
- add: "Add issue/PR to queue board",
157
- move: "Move queue item between Status columns",
158
- reorder: "Reorder items (move-to-top/move-after/order, --dry-run)",
159
- "archive-done": "Archive closed Done items older than a duration",
160
- "sync-status": "Sync a queued issue/PR's board Status column (best-effort)",
161
- ensure: "Create/repair queue board bootstrap surface",
162
- },
155
+ queue: QUEUE_DESCRIPTIONS,
156
+ project: PROJECT_DESCRIPTIONS,
163
157
  inspect: {
164
158
  run: "Inspect run state",
165
159
  viewer: "Start inspection viewer",
@@ -250,7 +244,8 @@ function buildCliHelpLines() {
250
244
  "Use `dev-loops <category> <subcommand> --help` for per-subcommand usage.",
251
245
  "",
252
246
  "`/dev-loops hide` remains an extension-only Pi command.",
253
- "Run via `npx dev-loops` (or `npm install -g dev-loops` for the shell command); see the",
247
+ "Run via `npx dev-loops@<version>` pinned to your plugin/extension version (a global",
248
+ "`npm install -g dev-loops` can drift and is not the supported invocation path); see the",
254
249
  "README for Pi-extension and Claude Code plugin setup.",
255
250
  ];
256
251
  }
@@ -273,7 +268,7 @@ function orderedCliSetupSteps(checks) {
273
268
  return [
274
269
  "1. Use `/dev-loop` (Claude Code) or `/skill:dev-loop` (Pi) to start or continue a dev loop — the single public entry.",
275
270
  "2. Run `dev-loops status` whenever you want a concise readiness snapshot.",
276
- "3. Run via `npx dev-loops` (or `npm install -g dev-loops` for the shell command); see the README for Pi-extension and Claude Code plugin setup.",
271
+ "3. Run via `npx dev-loops@<version>` pinned to your plugin/extension version (a global `npm install -g dev-loops` can drift and is not the supported invocation path); see the README for Pi-extension and Claude Code plugin setup.",
277
272
  ];
278
273
  }
279
274
 
@@ -9,6 +9,8 @@ Installing the package exposes two thin wrappers over one shared deterministic c
9
9
 
10
10
  Installing the package with `pi install git:github.com/mfittko/dev-loops` exposes the packaged skills through `package.json` `pi.skills`, and the extension syncs packaged agent files (`agents/*.agent.md`) into `~/.agents/` on `session_start`.
11
11
 
12
+ **Version pinning (no global-install skew).** The extension resolves both the CLI and `@dev-loops/core` from the installed, pinned package (module imports, not a global `dev-loops` binary), and updates itself via the pinned `pi update git:github.com/mfittko/dev-loops` seam above. It never depends on a separately-updated global install, so it stays in lockstep with the installed version by construction (#1036).
13
+
12
14
  ## Command surface
13
15
 
14
16
  - `/dev-loops`
@@ -144,7 +146,6 @@ autonomy:
144
146
 
145
147
  workflow:
146
148
  requireRetrospective: true
147
- requireRetrospectiveGate: true
148
149
  requireDraftFirst: true
149
150
  devModeDefault: true
150
151
  ```
@@ -178,13 +179,12 @@ The optional `workflow` family carries repo-level workflow posture without hardc
178
179
  ```yaml
179
180
  workflow:
180
181
  requireRetrospective: false
181
- requireRetrospectiveGate: false
182
182
  requireDraftFirst: false
183
183
  devModeDefault: false
184
184
  ```
185
185
 
186
186
  - `requireRetrospective` — when enabled by repo settings, the next qualifying GitHub-first async start/resume must honor the retrospective checkpoint gate
187
- - `requireRetrospectiveGate` — when enabled by repo settings, merge readiness after `pre_approval_gate` requires a completed retrospective checkpoint that explicitly approves merge. The retrospective must include: `state: "complete"`, `mergeApproved: true`, `followedWorkingAgreement` (boolean), `gateQuality` (string), `unexpectedFindings` (string), and `mergeRecommendation` (string). A `SKIPPED` retrospective does not satisfy the merge gate when this flag is enabled.
187
+ - `requireRetrospectiveGate` — **removed (issue #1077)**. The retrospective is now advisory: it always runs and returns findings to the conductor via the envelope's `retrospectiveFindings` field and an advisory PR comment, never blocking merge or any transition. There is no longer a merge-gate config key for the retrospective.
188
188
  - `requireDraftFirst` — marks draft-first PR creation as required workflow policy for repos that opt in
189
189
  - `devModeDefault` — declares that local implementation should default to formal dev mode; this is config-only for now and establishes source-of-truth config plus docs for future runtime consumers
190
190
 
@@ -222,7 +222,7 @@ Current Phase 3+ contract:
222
222
 
223
223
  Root verification and test commands are intentionally explicit:
224
224
  - `npm run verify` is the canonical root verification path (`npm test` + `npm run test:dev-loop`)
225
- - `npm test` runs the current root test suite (`test:assets`, `test:extension`, `test:scripts`, and `test:core`)
225
+ - `npm test` runs the current root test suite (`test:assets`, `test:extension`, `test:scripts`, `test:core`, and `test:docs`)
226
226
  - `npm run test:extension`
227
227
  - `npm run test:extension` currently expands to one `node --import tsx --test ...` invocation in `package.json`; prefer the script entrypoint over copying the file list into downstream docs or runbooks
228
228
  - `npm run test:scripts`
@@ -1,4 +1,4 @@
1
- import { collectDevLoopChecks as collectSharedDevLoopChecks, DEV_LOOP_CHECK_IDS } from '../lib/dev-loops-core.mjs';
1
+ import { DEV_LOOP_CHECK_IDS } from '../lib/dev-loops-core.mjs';
2
2
  import { createInspectRunViewerLifecycleManager } from '../scripts/loop/inspect-run-viewer/managed-instance.mjs';
3
3
  import type { ExtensionHarnessAdapter } from './harness-types.ts';
4
4
 
@@ -77,7 +77,3 @@ export function createExtensionCoreRuntime(
77
77
  },
78
78
  };
79
79
  }
80
-
81
- export async function collectDevLoopChecks(adapter: ExtensionHarnessAdapter): Promise<DevLoopCheck[]> {
82
- return collectSharedDevLoopChecks(createExtensionCoreRuntime(adapter));
83
- }
@@ -29,6 +29,7 @@ export type HarnessContext = {
29
29
  cwd: string;
30
30
  hasUI: boolean;
31
31
  ui: HarnessUi;
32
+ sendUserMessage?: (message: string, options?: Record<string, unknown>) => unknown;
32
33
  };
33
34
 
34
35
  export type HarnessLifecycleEvent = 'session_start' | 'tool_result' | 'user_bash' | 'agent_end';
@@ -8,6 +8,7 @@ import { createExtensionCoreRuntime } from './checks.ts';
8
8
  import { createPostMergeUpdateHook } from './post-merge-update.ts';
9
9
  import { createPiExtensionAdapter, type ExtensionAPI } from './pi-extension-adapter.ts';
10
10
  import {
11
+ buildEntrypointLines,
11
12
  buildHelpLines,
12
13
  buildInspectLines,
13
14
  buildInspectNotification,
@@ -25,6 +26,10 @@ const STATUS_KEY = 'dev-loops';
25
26
  const WIDGET_KEY = 'dev-loops.setup';
26
27
  const PACKAGED_AGENTS_ROOT = new URL('../agents/', import.meta.url);
27
28
 
29
+ async function dispatchDevLoopIntent(ctx: { sendUserMessage?: (message: string) => unknown }, intent: string) {
30
+ await ctx.sendUserMessage?.(`/skill:dev-loop ${intent}`);
31
+ }
32
+
28
33
  export function syncPackagedAgents({
29
34
  sourceRoot = fileURLToPath(PACKAGED_AGENTS_ROOT),
30
35
  targetRoot = path.join(os.homedir(), '.agents'),
@@ -72,7 +77,7 @@ export default function (pi: ExtensionAPI, runtimeOverrides: ExtensionRuntimeOve
72
77
  });
73
78
 
74
79
  adapter.registerCommand('dev-loops', {
75
- description: 'Manage dev-loops readiness and inspect-run local UI lifecycle: /dev-loops [help|status|doctor|gates|hide|inspect ...]',
80
+ description: 'Run a dev-loop entrypoint or manage readiness: /dev-loops [start <issue>|auto <issue>|continue [issue|pr]|start-spike <question>|info <issue|pr>|status|doctor|gates|hide|inspect ...]',
76
81
  handler: async (args, ctx) => {
77
82
  const result = await executeDevLoopsCommand({
78
83
  input: args,
@@ -89,6 +94,16 @@ export default function (pi: ExtensionAPI, runtimeOverrides: ExtensionRuntimeOve
89
94
  ctx.ui.setWidget(WIDGET_KEY, buildHelpLines(), { placement: 'belowEditor' });
90
95
  ctx.ui.notify('dev-loops help', 'info');
91
96
  return;
97
+ case 'entrypoint':
98
+ ctx.ui.setWidget(WIDGET_KEY, buildEntrypointLines(result.action, result.intent), { placement: 'belowEditor' });
99
+ await dispatchDevLoopIntent(ctx, result.intent);
100
+ ctx.ui.notify(`dev-loops ${result.action}: ${result.intent}`, 'info');
101
+ return;
102
+ case 'start_spike':
103
+ ctx.ui.setWidget(WIDGET_KEY, buildEntrypointLines('start-spike', result.intent), { placement: 'belowEditor' });
104
+ await dispatchDevLoopIntent(ctx, result.intent);
105
+ ctx.ui.notify(`dev-loops start-spike: ${result.intent}`, 'info');
106
+ return;
92
107
  case 'checks':
93
108
  ctx.ui.setWidget(WIDGET_KEY, buildWidgetLines(result.action as Extract<DevLoopsAction, 'doctor' | 'status'>, result.checks), {
94
109
  placement: 'belowEditor',
@@ -26,6 +26,7 @@ import type {
26
26
 
27
27
  export function toHarnessContext(ctx: Partial<ExtensionContext> | undefined): HarnessContext {
28
28
  const ui = ctx?.ui;
29
+ const sender = (ctx as { sendUserMessage?: (message: string, options?: Record<string, unknown>) => unknown } | undefined)?.sendUserMessage;
29
30
  return {
30
31
  cwd: (ctx?.cwd as string) ?? process.cwd(),
31
32
  hasUI: Boolean(ctx?.hasUI),
@@ -34,6 +35,7 @@ export function toHarnessContext(ctx: Partial<ExtensionContext> | undefined): Ha
34
35
  setWidget: (key, lines, options) => ui?.setWidget?.(key, lines as never, options as never),
35
36
  setStatus: (key, text) => ui?.setStatus?.(key, text as never),
36
37
  },
38
+ sendUserMessage: sender ? (message, options) => sender.call(ctx, message, options) : undefined,
37
39
  };
38
40
  }
39
41
 
@@ -19,7 +19,7 @@ function checkMap(checks: DevLoopCheck[]): Map<DevLoopCheckId, DevLoopCheck> {
19
19
  return new Map(checks.map((check) => [check.id, check]));
20
20
  }
21
21
 
22
- export function orderedSetupSteps(checks: DevLoopCheck[]): string[] {
22
+ function orderedSetupSteps(checks: DevLoopCheck[]): string[] {
23
23
  const byId = checkMap(checks);
24
24
  const uniqueSteps = [...new Set(DEV_LOOP_CHECK_IDS.filter((id) => byId.get(id)?.ok === false).map((id) => SETUP_GUIDANCE[id]))];
25
25
  const steps = uniqueSteps.map((step, index) => `${index + 1}. ${step}`);
@@ -35,21 +35,24 @@ export function orderedSetupSteps(checks: DevLoopCheck[]): string[] {
35
35
  ];
36
36
  }
37
37
 
38
+ export function buildEntrypointLines(action: string, intent: string): string[] {
39
+ return [
40
+ `dev-loops ${action}`,
41
+ `Dispatch this public intent through the dev-loop skill:`,
42
+ `/skill:dev-loop ${intent}`,
43
+ 'The dev-loop skill resolves authoritative state and routes deterministically — no strategy choice needed.',
44
+ ];
45
+ }
46
+
38
47
  export function buildHelpLines(): string[] {
39
48
  return [
40
49
  'dev-loops help',
41
- 'Workflow entry:',
42
- '- /skill:dev-loop single public entrypoint; routing handles the rest',
43
- 'Commands:',
44
- '- /dev-loops status',
45
- '- /dev-loops doctor',
46
- '- /dev-loops hide',
47
- '- /dev-loops inspect open [--repo <owner/name>]',
48
- '- /dev-loops inspect resume [--repo <owner/name>]',
49
- '- /dev-loops inspect status [--repo <owner/name>]',
50
- '- /dev-loops inspect stop [--repo <owner/name>]',
51
- '- /dev-loops inspect restart [--repo <owner/name>]',
52
- 'Use `pi install git:github.com/mfittko/dev-loops` to install skills and agents; packaged agents sync into `~/.agents/` on session start.',
50
+ '/skill:dev-loop <intent> starts the public loop',
51
+ '/dev-loops start <issue> | /dev-loops auto <issue>',
52
+ '/dev-loops continue [issue|pr] | /dev-loops info <issue|pr>',
53
+ '/dev-loops start-spike <question>',
54
+ '/dev-loops inspect open|resume|status|stop|restart',
55
+ '/dev-loops status | /dev-loops doctor | /dev-loops hide',
53
56
  ];
54
57
  }
55
58
 
@@ -9,6 +9,19 @@ const LOCAL_READINESS_IDS = ['subagent-command', 'git-repo'];
9
9
  const REMOTE_READINESS_IDS = ['gh-installed', 'gh-auth', 'subagent-command', 'git-repo'];
10
10
  const INSPECT_ACTIONS = new Set(['open', 'resume', 'status', 'stop', 'restart']);
11
11
 
12
+ // Direct dev-loop entrypoints (#972): thin named wrappers over the public dev-loop contract.
13
+ // Each maps `<verb> <issue|pr>` to the canonical public-intent shorthand the `dev-loop` skill
14
+ // already accepts — no new routing/strategy logic lives here. `start`/`auto` target an issue,
15
+ // `continue` (#988) targets an issue OR a PR (the resolver picks the canonical artifact) and
16
+ // also accepts a bare form (no number) that continues the current in-progress board item;
17
+ // `info` is the read-only state shortcut for an issue or PR.
18
+ const ENTRYPOINT_VERBS = {
19
+ start: { target: 'issue', phrase: (n) => `start dev loop on issue ${n}` },
20
+ auto: { target: 'issue', phrase: (n) => `auto dev loop on issue ${n}` },
21
+ continue: { target: 'either', allowBare: true, phrase: (n) => (n ? `continue dev loop on ${n}` : 'continue the current dev loop') },
22
+ info: { target: 'either', phrase: (n) => `inspect dev loop state on ${n}` },
23
+ };
24
+
12
25
  const UNICODE_SPACE_RE = /[\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/g;
13
26
 
14
27
  function normalizeInput(input) {
@@ -88,6 +101,100 @@ function parseInspectCommand(tokens, { surface }) {
88
101
  };
89
102
  }
90
103
 
104
+ // Normalize a single entrypoint target token to a bare issue/PR number.
105
+ // Accepts `123`, `#123`, or a GitHub issue/PR URL (.../issues/123 or .../pull/123).
106
+ // Returns the numeric string, or null when it is not a recognized target.
107
+ function normalizeTargetNumber(raw) {
108
+ const token = String(raw).trim();
109
+ if (/^#?\d+$/.test(token)) {
110
+ return token.replace(/^#/, '');
111
+ }
112
+ const urlMatch = token.match(/^https?:\/\/[^\s]*\/(?:issues|pull)\/(\d+)(?:[/?#].*)?$/);
113
+ if (urlMatch) {
114
+ return urlMatch[1];
115
+ }
116
+ return null;
117
+ }
118
+
119
+ // `start-spike` is a SIBLING of the numeric verbs (start/auto/continue/info): it
120
+ // takes FREE TEXT (a question) or `--file <path>`, NOT a numeric target, so it is
121
+ // parsed on its own path to keep the numeric-validation invariant of the other
122
+ // verbs intact (#988 P2). It is a thin wrapper over the shipped `--spike` intake:
123
+ // the inline-question form scaffolds a startable findings artifact, then both
124
+ // forms hand the resolved spike path to `loop startup --spike <path>`. No new
125
+ // spike behavior lives here — see skills/docs/spike-mode-contract.md.
126
+ function parseStartSpikeCommand(rest, tokens) {
127
+ const positional = rest.filter((t) => t !== undefined);
128
+ // `--file <path>`: start from a pre-authored spike artifact (no scaffolding).
129
+ if (positional[0] === '--file') {
130
+ const file = positional[1];
131
+ // Reject any leading `-`: the path is forwarded to `resolve-dev-loop-startup
132
+ // --spike <path>`, so a value like `-x` could be read as an option (option
133
+ // injection). Fail closed. The free-text question path below is unaffected.
134
+ if (typeof file !== 'string' || file.length === 0 || file.startsWith('-') || positional.length !== 2) {
135
+ return invalidCommand('`start-spike --file` requires exactly one `<path>`.', 'start-spike', tokens);
136
+ }
137
+ return {
138
+ kind: 'start_spike',
139
+ mode: 'file',
140
+ file,
141
+ question: null,
142
+ intent: `start a dev-loop spike from ${file}`,
143
+ tokens,
144
+ };
145
+ }
146
+ // Inline free-text question. Joined verbatim so multi-word questions survive.
147
+ const question = positional.join(' ').trim();
148
+ if (question.length === 0) {
149
+ return invalidCommand('`start-spike` requires a question (or `--file <path>`).', 'start-spike', tokens);
150
+ }
151
+ return {
152
+ kind: 'start_spike',
153
+ mode: 'question',
154
+ file: null,
155
+ question,
156
+ intent: `start a dev-loop spike on the question: ${question}`,
157
+ tokens,
158
+ };
159
+ }
160
+
161
+ function parseEntrypointCommand(action, args, tokens) {
162
+ const spec = ENTRYPOINT_VERBS[action];
163
+ const positional = args.filter((a) => a !== undefined);
164
+ const targetHint = spec.target === 'pr' ? '<pr>' : spec.target === 'either' ? '<issue|pr>' : '<issue>';
165
+ const targetNoun = spec.target === 'pr' ? 'PR' : spec.target === 'either' ? 'issue/PR' : 'issue';
166
+
167
+ // Bare form (no target): only verbs that opt in (e.g. `continue` resumes the
168
+ // current in-progress board item). The command/skill does the board resolve.
169
+ if (positional.length === 0 && spec.allowBare) {
170
+ return {
171
+ kind: 'entrypoint',
172
+ action,
173
+ target: spec.target,
174
+ number: null,
175
+ intent: spec.phrase(null),
176
+ tokens,
177
+ };
178
+ }
179
+
180
+ if (positional.length !== 1) {
181
+ const arity = spec.allowBare ? `at most one ${targetHint}` : `exactly one ${targetHint}`;
182
+ return invalidCommand(`\`${action}\` requires ${arity} argument.`, action, tokens);
183
+ }
184
+ const number = normalizeTargetNumber(positional[0]);
185
+ if (number === null) {
186
+ return invalidCommand(`\`${action}\` expects a numeric ${targetNoun}, got: ${positional[0]}.`, action, tokens);
187
+ }
188
+ return {
189
+ kind: 'entrypoint',
190
+ action,
191
+ target: spec.target,
192
+ number,
193
+ intent: spec.phrase(number),
194
+ tokens,
195
+ };
196
+ }
197
+
91
198
  export function parseDevLoopsCommand(input, { surface = 'extension' } = {}) {
92
199
  const tokens = normalizeInput(input);
93
200
  const [rawAction, rawScope, ...rest] = tokens;
@@ -98,6 +205,15 @@ export function parseDevLoopsCommand(input, { surface = 'extension' } = {}) {
98
205
  return parseInspectCommand(tokens, { surface });
99
206
  }
100
207
 
208
+ // `start-spike` is free-text/path, not a numeric verb — parsed on its own path.
209
+ if (action === 'start-spike') {
210
+ return parseStartSpikeCommand([rawScope, ...rest], tokens);
211
+ }
212
+
213
+ if (action && Object.prototype.hasOwnProperty.call(ENTRYPOINT_VERBS, action)) {
214
+ return parseEntrypointCommand(action, [rawScope, ...rest], tokens);
215
+ }
216
+
101
217
  switch (action) {
102
218
  case undefined:
103
219
  case '':
@@ -255,6 +371,31 @@ export async function executeDevLoopsCommand({ input, surface = 'extension', run
255
371
  }
256
372
  }
257
373
 
374
+ if (parsed.kind === 'start_spike') {
375
+ // Thin wrapper: surface the spike intent so the operator dispatches it through
376
+ // the dev-loop skill, which scaffolds (inline question) or uses the given file,
377
+ // then runs `loop startup --spike <path>`. No spike behavior is decided here.
378
+ return {
379
+ kind: 'start_spike',
380
+ mode: parsed.mode,
381
+ file: parsed.file,
382
+ question: parsed.question,
383
+ intent: parsed.intent,
384
+ };
385
+ }
386
+
387
+ if (parsed.kind === 'entrypoint') {
388
+ // Thin wrapper: surface the canonical public intent so the user dispatches it through the
389
+ // `dev-loop` skill (the single public router). No routing/strategy decision is made here.
390
+ return {
391
+ kind: 'entrypoint',
392
+ action: parsed.action,
393
+ target: parsed.target,
394
+ number: parsed.number,
395
+ intent: parsed.intent,
396
+ };
397
+ }
398
+
258
399
  if (parsed.kind !== 'action') {
259
400
  return parsed;
260
401
  }
package/package.json CHANGED
@@ -24,13 +24,15 @@
24
24
  "verify": "npm test && npm run test:dev-loop",
25
25
  "test": "npm run test:assets && npm run test:extension && npm run test:scripts && npm run test:core && npm run test:docs",
26
26
  "test:assets": "node --test --test-reporter ./test/failure-summary-reporter.mjs test/dev-loop-init-phase-smoke.test.mjs test/contracts/*.test.mjs test/workflow-handoff-contract.test.mjs",
27
- "test:extension": "node --import tsx --test --test-reporter ./test/failure-summary-reporter.mjs test/extension-checks.test.mjs test/extension-post-merge-update.test.mjs test/extension-command-contract.test.mjs test/extension-package-contract.test.mjs test/extension-pi-adapter.test.mjs test/extension-claude-adapter.test.mjs test/dev-loops-core.test.mjs test/dev-loops-cli.test.mjs",
28
- "test:scripts": "node --test --test-reporter ./test/failure-summary-reporter.mjs test/github/*.test.mjs test/loop/*.test.mjs test/docs/*.test.mjs test/projects/*.test.mjs test/pages/*.test.mjs",
27
+ "test:extension": "node --import tsx --test --test-reporter ./test/failure-summary-reporter.mjs test/extension-checks.test.mjs test/extension-post-merge-update.test.mjs test/extension-command-contract.test.mjs test/extension-package-contract.test.mjs test/extension-pi-adapter.test.mjs test/dev-loops-core.test.mjs test/dev-loops-cli.test.mjs",
28
+ "test:scripts": "GIT_CONFIG_COUNT=2 GIT_CONFIG_KEY_0=core.fsync GIT_CONFIG_VALUE_0=none GIT_CONFIG_KEY_1=core.fsyncObjectFiles GIT_CONFIG_VALUE_1=false node --test --test-reporter ./test/failure-summary-reporter.mjs test/github/*.test.mjs test/loop/*.test.mjs test/docs/*.test.mjs test/projects/*.test.mjs test/pages/*.test.mjs",
29
29
  "test:core": "node --test --test-reporter ./test/failure-summary-reporter.mjs packages/core/test/*.test.mjs",
30
30
  "test:dev-loop": "node --test --test-reporter ./test/failure-summary-reporter.mjs skills/dev-loop/scripts/dev-mode-context.test.mjs skills/dev-loop/scripts/render-template.test.mjs skills/dev-loop/scripts/post-gate-verdict-fallback.test.mjs",
31
- "test:playwright:viewer": "playwright test -c playwright.inspect-run-viewer.config.mjs",
32
- "test:playwright:deep-dive": "playwright test -c playwright.dev-loops-deep-dive.config.mjs",
33
- "test:playwright:intro-deck": "playwright test -c playwright.intro-deck.config.mjs",
31
+ "test:playwright:viewer": "PW_UI_SLICE=inspect-run-viewer playwright test --project=inspect-run-viewer",
32
+ "test:playwright:deep-dive": "PW_UI_SLICE=deep-dive-deck playwright test --project=deep-dive-deck",
33
+ "test:playwright:intro-deck": "PW_UI_SLICE=intro-deck playwright test --project=intro-deck",
34
+ "test:playwright:intro-article": "PW_UI_SLICE=intro-article playwright test --project=intro-article",
35
+ "test:playwright:deep-dive-article": "PW_UI_SLICE=deep-dive-article playwright test --project=deep-dive-article",
34
36
  "smoke:headless": "node scripts/claude/headless-info-smoke.mjs",
35
37
  "test:docs": "node scripts/docs/validate-links.mjs && node scripts/docs/validate-no-duplicate-rules.mjs",
36
38
  "repo-wiki": "node scripts/repo-wiki.mjs",
@@ -69,8 +71,7 @@
69
71
  ]
70
72
  },
71
73
  "dependencies": {
72
- "mermaid": "11.15.0",
73
- "@dev-loops/core": "^0.5.0"
74
+ "@dev-loops/core": "^0.7.1"
74
75
  },
75
76
  "repository": {
76
77
  "type": "git",
@@ -80,7 +81,7 @@
80
81
  "url": "https://github.com/mfittko/dev-loops/issues"
81
82
  },
82
83
  "homepage": "https://github.com/mfittko/dev-loops#readme",
83
- "version": "0.5.0",
84
+ "version": "0.7.1",
84
85
  "files": [
85
86
  "cli/",
86
87
  "lib/",
@@ -91,6 +92,7 @@
91
92
  ".claude-plugin/",
92
93
  ".claude/.claude-plugin/",
93
94
  ".claude/agents/",
95
+ ".claude/commands/",
94
96
  ".claude/skills/",
95
97
  ".claude/hooks/",
96
98
  "README.md",