dev-loops 0.1.0

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 (156) hide show
  1. package/.pi/dev-loop/defaults.yaml +477 -0
  2. package/AGENTS.md +25 -0
  3. package/CHANGELOG.md +18 -0
  4. package/LICENSE +21 -0
  5. package/README.md +178 -0
  6. package/agents/dev-loop.agent.md +82 -0
  7. package/agents/developer.agent.md +37 -0
  8. package/agents/docs.agent.md +33 -0
  9. package/agents/fixer.agent.md +53 -0
  10. package/agents/quality.agent.md +28 -0
  11. package/agents/refiner.agent.md +87 -0
  12. package/agents/review.agent.md +64 -0
  13. package/cli/index.mjs +424 -0
  14. package/extension/README.md +233 -0
  15. package/extension/checks.ts +94 -0
  16. package/extension/index.ts +131 -0
  17. package/extension/post-merge-update.ts +512 -0
  18. package/extension/presentation.ts +107 -0
  19. package/lib/dev-loops-core.mjs +284 -0
  20. package/package.json +103 -0
  21. package/scripts/README.md +1007 -0
  22. package/scripts/_cli-primitives.mjs +10 -0
  23. package/scripts/_core-helpers.mjs +30 -0
  24. package/scripts/docs/validate-links.mjs +567 -0
  25. package/scripts/docs/validate-no-duplicate-rules.mjs +250 -0
  26. package/scripts/github/_review-thread-mutations.mjs +214 -0
  27. package/scripts/github/capture-review-threads.mjs +180 -0
  28. package/scripts/github/create-draft-pr.mjs +108 -0
  29. package/scripts/github/detect-checkpoint-evidence.mjs +393 -0
  30. package/scripts/github/detect-linked-issue-pr.mjs +331 -0
  31. package/scripts/github/manage-sub-issues.mjs +394 -0
  32. package/scripts/github/probe-copilot-review.mjs +323 -0
  33. package/scripts/github/ready-for-review.mjs +93 -0
  34. package/scripts/github/reconcile-draft-gate.mjs +328 -0
  35. package/scripts/github/reply-resolve-review-thread.mjs +42 -0
  36. package/scripts/github/reply-resolve-review-threads.mjs +329 -0
  37. package/scripts/github/request-copilot-review.mjs +551 -0
  38. package/scripts/github/resolve-tracker-local-spec.mjs +205 -0
  39. package/scripts/github/stage-reviewer-draft.mjs +191 -0
  40. package/scripts/github/upsert-checkpoint-verdict.mjs +694 -0
  41. package/scripts/github/verify-fresh-review-context.mjs +125 -0
  42. package/scripts/github/write-gate-findings-log.mjs +212 -0
  43. package/scripts/loop/_checkpoint-io.mjs +55 -0
  44. package/scripts/loop/_checkpoint-paths.mjs +28 -0
  45. package/scripts/loop/_handoff-contract.mjs +230 -0
  46. package/scripts/loop/_inspect-run-viewer-adapter.mjs +345 -0
  47. package/scripts/loop/_loop-evidence.mjs +32 -0
  48. package/scripts/loop/_pr-runner-coordination.mjs +611 -0
  49. package/scripts/loop/_stale-runner-detection.mjs +145 -0
  50. package/scripts/loop/_steering-state-file.mjs +134 -0
  51. package/scripts/loop/build-handoff-envelope.mjs +181 -0
  52. package/scripts/loop/checkpoint-contract.mjs +49 -0
  53. package/scripts/loop/conductor-monitor.mjs +1850 -0
  54. package/scripts/loop/conductor.mjs +214 -0
  55. package/scripts/loop/copilot-pr-handoff.mjs +493 -0
  56. package/scripts/loop/debt-remediate.mjs +304 -0
  57. package/scripts/loop/detect-change-scope.mjs +102 -0
  58. package/scripts/loop/detect-copilot-loop-state.mjs +454 -0
  59. package/scripts/loop/detect-copilot-session-activity.mjs +186 -0
  60. package/scripts/loop/detect-initial-copilot-pr-state.mjs +318 -0
  61. package/scripts/loop/detect-internal-only-pr.mjs +270 -0
  62. package/scripts/loop/detect-issue-refinement-artifact.mjs +163 -0
  63. package/scripts/loop/detect-pr-gate-coordination-state.mjs +509 -0
  64. package/scripts/loop/detect-reviewer-loop-state.mjs +231 -0
  65. package/scripts/loop/detect-stale-runner.mjs +250 -0
  66. package/scripts/loop/detect-tracker-first-loop-state.mjs +76 -0
  67. package/scripts/loop/detect-tracker-pr-state.mjs +102 -0
  68. package/scripts/loop/info.mjs +267 -0
  69. package/scripts/loop/inspect-run-viewer/cli.mjs +117 -0
  70. package/scripts/loop/inspect-run-viewer/constants.mjs +80 -0
  71. package/scripts/loop/inspect-run-viewer/graph.mjs +757 -0
  72. package/scripts/loop/inspect-run-viewer/handoff-envelope-renderer.mjs +398 -0
  73. package/scripts/loop/inspect-run-viewer/inbox.mjs +308 -0
  74. package/scripts/loop/inspect-run-viewer/managed-instance.mjs +750 -0
  75. package/scripts/loop/inspect-run-viewer/rendering.mjs +411 -0
  76. package/scripts/loop/inspect-run-viewer/server.mjs +638 -0
  77. package/scripts/loop/inspect-run-viewer/shared.mjs +103 -0
  78. package/scripts/loop/inspect-run-viewer/status.mjs +715 -0
  79. package/scripts/loop/inspect-run-viewer-ci-changes.mjs +77 -0
  80. package/scripts/loop/inspect-run-viewer.mjs +82 -0
  81. package/scripts/loop/inspect-run.mjs +382 -0
  82. package/scripts/loop/outer-loop.mjs +419 -0
  83. package/scripts/loop/pr-runner-coordination.mjs +143 -0
  84. package/scripts/loop/pre-commit-branch-guard.mjs +68 -0
  85. package/scripts/loop/pre-flight-gate.mjs +236 -0
  86. package/scripts/loop/pre-pr-ready-gate.mjs +183 -0
  87. package/scripts/loop/pre-push-main-guard.mjs +103 -0
  88. package/scripts/loop/pre-write-remote-freshness-guard.mjs +32 -0
  89. package/scripts/loop/print-gates.mjs +42 -0
  90. package/scripts/loop/resolve-dev-loop-startup.mjs +533 -0
  91. package/scripts/loop/run-conductor-cycle.mjs +322 -0
  92. package/scripts/loop/run-queue.mjs +124 -0
  93. package/scripts/loop/run-refinement-audit.mjs +513 -0
  94. package/scripts/loop/run-watch-cycle.mjs +358 -0
  95. package/scripts/loop/steer-loop.mjs +841 -0
  96. package/scripts/loop/ui-designer-review-contract.mjs +76 -0
  97. package/scripts/loop/watch-initial-copilot-pr.mjs +253 -0
  98. package/scripts/projects/add-queue-item.mjs +528 -0
  99. package/scripts/projects/ensure-queue-board.mjs +837 -0
  100. package/scripts/projects/list-queue-items.mjs +489 -0
  101. package/scripts/projects/move-queue-item.mjs +549 -0
  102. package/scripts/projects/reorder-queue-item.mjs +518 -0
  103. package/scripts/refine/_refine-helpers.mjs +258 -0
  104. package/scripts/refine/prose-linkage-detector.mjs +92 -0
  105. package/scripts/refine/refinement-completeness-checker.mjs +88 -0
  106. package/scripts/refine/scope-boundary-cross-checker.mjs +163 -0
  107. package/scripts/refine/tree-integrity-validator.mjs +211 -0
  108. package/scripts/refine/verify.mjs +178 -0
  109. package/scripts/repo-wiki-local.mjs +156 -0
  110. package/scripts/repo-wiki.mjs +119 -0
  111. package/skills/copilot-pr-followup/SKILL.md +380 -0
  112. package/skills/dev-loop/SKILL.md +141 -0
  113. package/skills/dev-loop/scripts/dev-mode-context.mjs +152 -0
  114. package/skills/dev-loop/scripts/dev-mode-context.test.mjs +80 -0
  115. package/skills/dev-loop/scripts/init-phase.mjs +71 -0
  116. package/skills/dev-loop/scripts/log-bash-exit-1.mjs +25 -0
  117. package/skills/dev-loop/scripts/phase-files.mjs +29 -0
  118. package/skills/dev-loop/scripts/post-gate-verdict-fallback.mjs +480 -0
  119. package/skills/dev-loop/scripts/post-gate-verdict-fallback.test.mjs +732 -0
  120. package/skills/dev-loop/scripts/render-template.mjs +82 -0
  121. package/skills/dev-loop/scripts/render-template.test.mjs +63 -0
  122. package/skills/dev-loop/templates/bootstrap-agents.md +26 -0
  123. package/skills/dev-loop/templates/bootstrap-implementation-state.md +31 -0
  124. package/skills/dev-loop/templates/bootstrap-implementation-workflow.md +17 -0
  125. package/skills/dev-loop/templates/dev-mode-retrospective.md +15 -0
  126. package/skills/dev-loop/templates/dev-mode-review.md +17 -0
  127. package/skills/dev-loop/templates/dev-mode-skill-changes.md +11 -0
  128. package/skills/dev-loop/templates/merged-phase-plan.md +19 -0
  129. package/skills/dev-loop/templates/phase-doc.md +27 -0
  130. package/skills/dev-loop/templates/phase-summary.md +13 -0
  131. package/skills/dev-loop/templates/phase-variant.md +15 -0
  132. package/skills/dev-loop/templates/retrospective.md +11 -0
  133. package/skills/dev-loop/templates/review.md +32 -0
  134. package/skills/dev-loop/templates/ui-vision-review.md +55 -0
  135. package/skills/docs/acceptance-criteria-verification.md +21 -0
  136. package/skills/docs/anti-patterns.md +21 -0
  137. package/skills/docs/artifact-authority-contract.md +119 -0
  138. package/skills/docs/confirmation-rules.md +28 -0
  139. package/skills/docs/copilot-ci-status-contract.md +52 -0
  140. package/skills/docs/copilot-loop-operations.md +233 -0
  141. package/skills/docs/debt-remediation-contract.md +107 -0
  142. package/skills/docs/entrypoint-strategies.md +115 -0
  143. package/skills/docs/epic-tree-refinement-procedure.md +234 -0
  144. package/skills/docs/issue-intake-procedure.md +235 -0
  145. package/skills/docs/main-agent-contract.md +72 -0
  146. package/skills/docs/merge-preconditions.md +29 -0
  147. package/skills/docs/pr-lifecycle-contract.md +209 -0
  148. package/skills/docs/public-dev-loop-contract.md +497 -0
  149. package/skills/docs/retrospective-checkpoint-contract.md +159 -0
  150. package/skills/docs/stop-conditions.md +29 -0
  151. package/skills/docs/structural-quality.md +42 -0
  152. package/skills/docs/tracker-first-loop-state.md +281 -0
  153. package/skills/docs/validation-policy.md +27 -0
  154. package/skills/docs/workflow-handoff-contract.md +135 -0
  155. package/skills/final-approval/SKILL.md +19 -0
  156. package/skills/local-implementation/SKILL.md +640 -0
@@ -0,0 +1,94 @@
1
+ import type { ExtensionAPI } from '@mariozechner/pi-coding-agent';
2
+
3
+ import { collectDevLoopChecks as collectSharedDevLoopChecks, DEV_LOOP_CHECK_IDS } from '../lib/dev-loops-core.mjs';
4
+ import { createInspectRunViewerLifecycleManager } from '../scripts/loop/inspect-run-viewer/managed-instance.mjs';
5
+
6
+ export type DevLoopCheckId = (typeof DEV_LOOP_CHECK_IDS)[number];
7
+
8
+ export type DevLoopCheck = {
9
+ id: DevLoopCheckId;
10
+ label: string;
11
+ ok: boolean;
12
+ detail: string;
13
+ };
14
+
15
+ async function commandExists(pi: ExtensionAPI, command: string): Promise<boolean> {
16
+ try {
17
+ const result = await pi.exec('bash', ['-lc', `command -v ${command} >/dev/null 2>&1`], {
18
+ timeout: 5_000,
19
+ });
20
+ return result.code === 0;
21
+ } catch {
22
+ return false;
23
+ }
24
+ }
25
+
26
+ async function ghAuthOk(pi: ExtensionAPI): Promise<boolean> {
27
+ try {
28
+ const result = await pi.exec('bash', ['-lc', 'gh auth status >/dev/null 2>&1'], {
29
+ timeout: 10_000,
30
+ });
31
+ return result.code === 0;
32
+ } catch {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ async function insideGitRepo(pi: ExtensionAPI): Promise<boolean> {
38
+ try {
39
+ const result = await pi.exec(
40
+ 'bash',
41
+ ['-lc', 'git rev-parse --is-inside-work-tree >/dev/null 2>&1'],
42
+ { timeout: 5_000 },
43
+ );
44
+ return result.code === 0;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+
50
+ async function getRepoRoot(pi: ExtensionAPI): Promise<string> {
51
+ const result = await pi.exec('bash', ['-lc', 'git rev-parse --show-toplevel'], {
52
+ timeout: 10_000,
53
+ });
54
+ if (result.code !== 0) {
55
+ throw new Error('Open Pi inside a git repository before using `/dev-loops inspect`.');
56
+ }
57
+ const repoRoot = `${result.stdout ?? ''}`.trim();
58
+ if (!repoRoot) {
59
+ throw new Error('Could not determine the repository root for `/dev-loops inspect`.');
60
+ }
61
+ return repoRoot;
62
+ }
63
+
64
+ export function createExtensionCoreRuntime(
65
+ pi: ExtensionAPI,
66
+ {
67
+ uiLifecycle = createInspectRunViewerLifecycleManager(),
68
+ getRepoRoot: getRepoRootOverride,
69
+ }: {
70
+ uiLifecycle?: ReturnType<typeof createInspectRunViewerLifecycleManager>;
71
+ getRepoRoot?: () => Promise<string>;
72
+ } = {},
73
+ ) {
74
+ return {
75
+ surface: 'extension' as const,
76
+ commandExists: (command: string) => commandExists(pi, command),
77
+ ghAuthOk: () => ghAuthOk(pi),
78
+ insideGitRepo: () => insideGitRepo(pi),
79
+ getRepoRoot: () => (getRepoRootOverride ? getRepoRootOverride() : getRepoRoot(pi)),
80
+ uiLifecycle,
81
+ async getSubagentAvailability() {
82
+ const ok = await commandExists(pi, 'subagent');
83
+ return {
84
+ ok,
85
+ availableDetail: '`subagent` command is available.',
86
+ unavailableDetail: 'Install or enable subagent support so `subagent` is available.',
87
+ };
88
+ },
89
+ };
90
+ }
91
+
92
+ export async function collectDevLoopChecks(pi: ExtensionAPI): Promise<DevLoopCheck[]> {
93
+ return collectSharedDevLoopChecks(createExtensionCoreRuntime(pi));
94
+ }
@@ -0,0 +1,131 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import type { ExtensionAPI } from '@mariozechner/pi-coding-agent';
6
+
7
+ import { executeDevLoopsCommand } from '../lib/dev-loops-core.mjs';
8
+ import { createExtensionCoreRuntime } from './checks.ts';
9
+ import { createPostMergeUpdateHook } from './post-merge-update.ts';
10
+ import {
11
+ buildHelpLines,
12
+ buildInspectLines,
13
+ buildInspectNotification,
14
+ buildNotificationMessage,
15
+ buildWidgetLines,
16
+ type DevLoopsAction,
17
+ type InspectAction,
18
+ } from './presentation.ts';
19
+
20
+ type ExtensionRuntimeOverrides = NonNullable<Parameters<typeof createExtensionCoreRuntime>[1]> & {
21
+ postMergeUpdateHook?: ReturnType<typeof createPostMergeUpdateHook>;
22
+ };
23
+
24
+ const STATUS_KEY = 'dev-loops';
25
+ const WIDGET_KEY = 'dev-loops.setup';
26
+ const PACKAGED_AGENTS_ROOT = new URL('../agents/', import.meta.url);
27
+
28
+ export function syncPackagedAgents({
29
+ sourceRoot = fileURLToPath(PACKAGED_AGENTS_ROOT),
30
+ targetRoot = path.join(os.homedir(), '.agents'),
31
+ } = {}) {
32
+ if (!fs.existsSync(sourceRoot)) {
33
+ return;
34
+ }
35
+
36
+ fs.mkdirSync(targetRoot, { recursive: true });
37
+
38
+ for (const entry of fs.readdirSync(sourceRoot, { withFileTypes: true })) {
39
+ if (!entry.isFile() || !entry.name.endsWith('.agent.md')) {
40
+ continue;
41
+ }
42
+
43
+ fs.copyFileSync(path.join(sourceRoot, entry.name), path.join(targetRoot, entry.name));
44
+ }
45
+ }
46
+
47
+ export default function (pi: ExtensionAPI, runtimeOverrides: ExtensionRuntimeOverrides = {}) {
48
+ const postMergeUpdateHook = runtimeOverrides.postMergeUpdateHook ?? createPostMergeUpdateHook(pi);
49
+
50
+ pi.on('session_start', async (_event, ctx) => {
51
+ postMergeUpdateHook.onSessionStart();
52
+ try {
53
+ syncPackagedAgents();
54
+ } catch {
55
+ // Best-effort agent sync — do not break session start
56
+ }
57
+ ctx.ui.setStatus(STATUS_KEY, undefined);
58
+ });
59
+
60
+ pi.on('tool_result', async (event, ctx) => {
61
+ await postMergeUpdateHook.onToolResult(event, ctx);
62
+ });
63
+
64
+ pi.on('user_bash', async (event, ctx) => {
65
+ return postMergeUpdateHook.onUserBash(event, ctx);
66
+ });
67
+
68
+ pi.on('agent_end', async (event, ctx) => {
69
+ await postMergeUpdateHook.onAgentEnd(event, ctx);
70
+ });
71
+
72
+ pi.registerCommand('dev-loops', {
73
+ description: 'Manage dev-loops readiness and inspect-run local UI lifecycle: /dev-loops [help|status|doctor|gates|hide|inspect ...]',
74
+ handler: async (args, ctx) => {
75
+ const result = await executeDevLoopsCommand({
76
+ input: args,
77
+ surface: 'extension',
78
+ runtime: createExtensionCoreRuntime(pi, runtimeOverrides),
79
+ });
80
+
81
+ switch (result.kind) {
82
+ case 'hide':
83
+ ctx.ui.setWidget(WIDGET_KEY, undefined);
84
+ ctx.ui.notify('dev-loops widget hidden', 'info');
85
+ return;
86
+ case 'help':
87
+ ctx.ui.setWidget(WIDGET_KEY, buildHelpLines(), { placement: 'belowEditor' });
88
+ ctx.ui.notify('dev-loops help', 'info');
89
+ return;
90
+ case 'checks':
91
+ ctx.ui.setWidget(WIDGET_KEY, buildWidgetLines(result.action as Extract<DevLoopsAction, 'doctor' | 'status'>, result.checks), {
92
+ placement: 'belowEditor',
93
+ });
94
+ ctx.ui.notify(buildNotificationMessage(result.action as Extract<DevLoopsAction, 'doctor' | 'status'>, result.checks), 'info');
95
+ return;
96
+ case 'gates':
97
+ ctx.ui.notify('Gate angles printed to console. Run `dev-loops gates` in a terminal to see review prompts.', 'info');
98
+ return;
99
+ case 'inspect_result': {
100
+ const structuredStoppedSuccess = result.state === 'stopped'
101
+ && result.record === null
102
+ && (result.action === 'stop' || result.action === 'status');
103
+ const fallbackStoppedSuccess = result.state === 'stopped'
104
+ && ((result.action === 'stop' && /stopped the managed inspect-run viewer/i.test(result.detail ?? ''))
105
+ || (result.action === 'status' && /no managed inspect-run viewer is recorded/i.test(result.detail ?? '')));
106
+ const informationalStopped = structuredStoppedSuccess || fallbackStoppedSuccess;
107
+ const notificationLevel = informationalStopped || result.state === 'running' ? 'info' : 'error';
108
+ ctx.ui.setWidget(WIDGET_KEY, buildInspectLines(result.action as InspectAction, result), {
109
+ placement: 'belowEditor',
110
+ });
111
+ ctx.ui.notify(buildInspectNotification(result.action as InspectAction, result.state), notificationLevel);
112
+ return;
113
+ }
114
+ case 'malformed':
115
+ ctx.ui.setWidget(WIDGET_KEY, [result.message, ...buildHelpLines()], { placement: 'belowEditor' });
116
+ ctx.ui.notify(`dev-loops ${result.usageAction ?? 'help'}: invalid arguments`, 'error');
117
+ return;
118
+ case 'unsupported': {
119
+ const message = result.message || 'This command is not supported here.';
120
+ ctx.ui.setWidget(WIDGET_KEY, [message, ...buildHelpLines()], { placement: 'belowEditor' });
121
+ ctx.ui.notify(message, 'error');
122
+ return;
123
+ }
124
+ default: {
125
+ const exhaustiveCheck: never = result;
126
+ throw new Error(`Unhandled extension result: ${JSON.stringify(exhaustiveCheck)}`);
127
+ }
128
+ }
129
+ },
130
+ });
131
+ }