@laurentenhoor/devclaw 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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/dist/index.d.ts +88 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +107 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/audit.d.ts +2 -0
  8. package/dist/lib/audit.d.ts.map +1 -0
  9. package/dist/lib/audit.js +42 -0
  10. package/dist/lib/audit.js.map +1 -0
  11. package/dist/lib/binding-manager.d.ts +35 -0
  12. package/dist/lib/binding-manager.d.ts.map +1 -0
  13. package/dist/lib/binding-manager.js +88 -0
  14. package/dist/lib/binding-manager.js.map +1 -0
  15. package/dist/lib/cli.d.ts +12 -0
  16. package/dist/lib/cli.d.ts.map +1 -0
  17. package/dist/lib/cli.js +69 -0
  18. package/dist/lib/cli.js.map +1 -0
  19. package/dist/lib/dispatch.d.ts +58 -0
  20. package/dist/lib/dispatch.d.ts.map +1 -0
  21. package/dist/lib/dispatch.js +163 -0
  22. package/dist/lib/dispatch.js.map +1 -0
  23. package/dist/lib/model-selector.d.ts +21 -0
  24. package/dist/lib/model-selector.d.ts.map +1 -0
  25. package/dist/lib/model-selector.js +74 -0
  26. package/dist/lib/model-selector.js.map +1 -0
  27. package/dist/lib/notify.d.ts +54 -0
  28. package/dist/lib/notify.d.ts.map +1 -0
  29. package/dist/lib/notify.js +143 -0
  30. package/dist/lib/notify.js.map +1 -0
  31. package/dist/lib/onboarding.d.ts +5 -0
  32. package/dist/lib/onboarding.d.ts.map +1 -0
  33. package/dist/lib/onboarding.js +124 -0
  34. package/dist/lib/onboarding.js.map +1 -0
  35. package/dist/lib/projects.d.ts +64 -0
  36. package/dist/lib/projects.d.ts.map +1 -0
  37. package/dist/lib/projects.js +127 -0
  38. package/dist/lib/projects.js.map +1 -0
  39. package/dist/lib/providers/github.d.ts +23 -0
  40. package/dist/lib/providers/github.d.ts.map +1 -0
  41. package/dist/lib/providers/github.js +130 -0
  42. package/dist/lib/providers/github.js.map +1 -0
  43. package/dist/lib/providers/gitlab.d.ts +23 -0
  44. package/dist/lib/providers/gitlab.d.ts.map +1 -0
  45. package/dist/lib/providers/gitlab.js +133 -0
  46. package/dist/lib/providers/gitlab.js.map +1 -0
  47. package/dist/lib/providers/index.d.ts +12 -0
  48. package/dist/lib/providers/index.d.ts.map +1 -0
  49. package/dist/lib/providers/index.js +25 -0
  50. package/dist/lib/providers/index.js.map +1 -0
  51. package/dist/lib/providers/provider.d.ts +35 -0
  52. package/dist/lib/providers/provider.d.ts.map +1 -0
  53. package/dist/lib/providers/provider.js +13 -0
  54. package/dist/lib/providers/provider.js.map +1 -0
  55. package/dist/lib/services/health.d.ts +38 -0
  56. package/dist/lib/services/health.d.ts.map +1 -0
  57. package/dist/lib/services/health.js +100 -0
  58. package/dist/lib/services/health.js.map +1 -0
  59. package/dist/lib/services/heartbeat.d.ts +38 -0
  60. package/dist/lib/services/heartbeat.d.ts.map +1 -0
  61. package/dist/lib/services/heartbeat.js +199 -0
  62. package/dist/lib/services/heartbeat.js.map +1 -0
  63. package/dist/lib/services/pipeline.d.ts +36 -0
  64. package/dist/lib/services/pipeline.d.ts.map +1 -0
  65. package/dist/lib/services/pipeline.js +90 -0
  66. package/dist/lib/services/pipeline.js.map +1 -0
  67. package/dist/lib/services/queue.d.ts +14 -0
  68. package/dist/lib/services/queue.d.ts.map +1 -0
  69. package/dist/lib/services/queue.js +31 -0
  70. package/dist/lib/services/queue.js.map +1 -0
  71. package/dist/lib/services/tick.d.ts +62 -0
  72. package/dist/lib/services/tick.d.ts.map +1 -0
  73. package/dist/lib/services/tick.js +160 -0
  74. package/dist/lib/services/tick.js.map +1 -0
  75. package/dist/lib/setup/agent.d.ts +14 -0
  76. package/dist/lib/setup/agent.d.ts.map +1 -0
  77. package/dist/lib/setup/agent.js +72 -0
  78. package/dist/lib/setup/agent.js.map +1 -0
  79. package/dist/lib/setup/config.d.ts +22 -0
  80. package/dist/lib/setup/config.d.ts.map +1 -0
  81. package/dist/lib/setup/config.js +67 -0
  82. package/dist/lib/setup/config.js.map +1 -0
  83. package/dist/lib/setup/index.d.ts +53 -0
  84. package/dist/lib/setup/index.d.ts.map +1 -0
  85. package/dist/lib/setup/index.js +68 -0
  86. package/dist/lib/setup/index.js.map +1 -0
  87. package/dist/lib/setup/workspace.d.ts +6 -0
  88. package/dist/lib/setup/workspace.d.ts.map +1 -0
  89. package/dist/lib/setup/workspace.js +69 -0
  90. package/dist/lib/setup/workspace.js.map +1 -0
  91. package/dist/lib/templates.d.ts +9 -0
  92. package/dist/lib/templates.d.ts.map +1 -0
  93. package/dist/lib/templates.js +163 -0
  94. package/dist/lib/templates.js.map +1 -0
  95. package/dist/lib/tiers.d.ts +55 -0
  96. package/dist/lib/tiers.d.ts.map +1 -0
  97. package/dist/lib/tiers.js +74 -0
  98. package/dist/lib/tiers.js.map +1 -0
  99. package/dist/lib/tool-helpers.d.ts +44 -0
  100. package/dist/lib/tool-helpers.d.ts.map +1 -0
  101. package/dist/lib/tool-helpers.js +65 -0
  102. package/dist/lib/tool-helpers.js.map +1 -0
  103. package/dist/lib/tools/health.d.ts +28 -0
  104. package/dist/lib/tools/health.d.ts.map +1 -0
  105. package/dist/lib/tools/health.js +61 -0
  106. package/dist/lib/tools/health.js.map +1 -0
  107. package/dist/lib/tools/onboard.d.ts +24 -0
  108. package/dist/lib/tools/onboard.d.ts.map +1 -0
  109. package/dist/lib/tools/onboard.js +27 -0
  110. package/dist/lib/tools/onboard.js.map +1 -0
  111. package/dist/lib/tools/project-register.d.ts +51 -0
  112. package/dist/lib/tools/project-register.d.ts.map +1 -0
  113. package/dist/lib/tools/project-register.js +172 -0
  114. package/dist/lib/tools/project-register.js.map +1 -0
  115. package/dist/lib/tools/queue-status.test.d.ts +2 -0
  116. package/dist/lib/tools/queue-status.test.d.ts.map +1 -0
  117. package/dist/lib/tools/queue-status.test.js +48 -0
  118. package/dist/lib/tools/queue-status.test.js.map +1 -0
  119. package/dist/lib/tools/setup.d.ts +76 -0
  120. package/dist/lib/tools/setup.d.ts.map +1 -0
  121. package/dist/lib/tools/setup.js +102 -0
  122. package/dist/lib/tools/setup.js.map +1 -0
  123. package/dist/lib/tools/status.d.ts +24 -0
  124. package/dist/lib/tools/status.d.ts.map +1 -0
  125. package/dist/lib/tools/status.js +53 -0
  126. package/dist/lib/tools/status.js.map +1 -0
  127. package/dist/lib/tools/task-comment.d.ts +40 -0
  128. package/dist/lib/tools/task-comment.d.ts.map +1 -0
  129. package/dist/lib/tools/task-comment.js +84 -0
  130. package/dist/lib/tools/task-comment.js.map +1 -0
  131. package/dist/lib/tools/task-create.d.ts +54 -0
  132. package/dist/lib/tools/task-create.d.ts.map +1 -0
  133. package/dist/lib/tools/task-create.js +77 -0
  134. package/dist/lib/tools/task-create.js.map +1 -0
  135. package/dist/lib/tools/task-update.d.ts +40 -0
  136. package/dist/lib/tools/task-update.d.ts.map +1 -0
  137. package/dist/lib/tools/task-update.js +79 -0
  138. package/dist/lib/tools/task-update.js.map +1 -0
  139. package/dist/lib/tools/task-update.test.d.ts +7 -0
  140. package/dist/lib/tools/task-update.test.d.ts.map +1 -0
  141. package/dist/lib/tools/task-update.test.js +55 -0
  142. package/dist/lib/tools/task-update.test.js.map +1 -0
  143. package/dist/lib/tools/work-finish.d.ts +43 -0
  144. package/dist/lib/tools/work-finish.d.ts.map +1 -0
  145. package/dist/lib/tools/work-finish.js +77 -0
  146. package/dist/lib/tools/work-finish.js.map +1 -0
  147. package/dist/lib/tools/work-start.d.ts +39 -0
  148. package/dist/lib/tools/work-start.d.ts.map +1 -0
  149. package/dist/lib/tools/work-start.js +129 -0
  150. package/dist/lib/tools/work-start.js.map +1 -0
  151. package/dist/lib/types.d.ts +17 -0
  152. package/dist/lib/types.d.ts.map +1 -0
  153. package/dist/lib/types.js +8 -0
  154. package/dist/lib/types.js.map +1 -0
  155. package/docs/ARCHITECTURE.md +662 -0
  156. package/docs/CONFIGURATION.md +336 -0
  157. package/docs/MANAGEMENT.md +120 -0
  158. package/docs/ONBOARDING.md +251 -0
  159. package/docs/QA_WORKFLOW.md +120 -0
  160. package/docs/ROADMAP.md +96 -0
  161. package/docs/TESTING.md +339 -0
  162. package/docs/TOOLS.md +361 -0
  163. package/package.json +55 -0
@@ -0,0 +1,199 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { readProjects } from "../projects.js";
4
+ import { log as auditLog } from "../audit.js";
5
+ import { checkWorkerHealth } from "./health.js";
6
+ import { projectTick } from "./tick.js";
7
+ import { createProvider } from "../providers/index.js";
8
+ // ---------------------------------------------------------------------------
9
+ // Config
10
+ // ---------------------------------------------------------------------------
11
+ export const HEARTBEAT_DEFAULTS = {
12
+ enabled: true,
13
+ intervalSeconds: 60,
14
+ maxPickupsPerTick: 4,
15
+ };
16
+ export function resolveHeartbeatConfig(pluginConfig) {
17
+ const raw = pluginConfig?.work_heartbeat;
18
+ return { ...HEARTBEAT_DEFAULTS, ...raw };
19
+ }
20
+ // ---------------------------------------------------------------------------
21
+ // Service
22
+ // ---------------------------------------------------------------------------
23
+ export function registerHeartbeatService(api) {
24
+ let intervalId = null;
25
+ api.registerService({
26
+ id: "devclaw-heartbeat",
27
+ start: async (ctx) => {
28
+ const pluginConfig = api.pluginConfig;
29
+ const config = resolveHeartbeatConfig(pluginConfig);
30
+ if (!config.enabled) {
31
+ ctx.logger.info("work_heartbeat service disabled");
32
+ return;
33
+ }
34
+ const agents = discoverAgents(ctx.config);
35
+ if (agents.length === 0) {
36
+ ctx.logger.warn("work_heartbeat service: no DevClaw agents registered");
37
+ return;
38
+ }
39
+ ctx.logger.info(`work_heartbeat service started: every ${config.intervalSeconds}s, ${agents.length} agents, max ${config.maxPickupsPerTick} pickups/tick`);
40
+ intervalId = setInterval(() => runHeartbeatTick(agents, config, pluginConfig, ctx.logger), config.intervalSeconds * 1000);
41
+ },
42
+ stop: async (ctx) => {
43
+ if (intervalId) {
44
+ clearInterval(intervalId);
45
+ intervalId = null;
46
+ ctx.logger.info("work_heartbeat service stopped");
47
+ }
48
+ },
49
+ });
50
+ }
51
+ // ---------------------------------------------------------------------------
52
+ // Helpers
53
+ // ---------------------------------------------------------------------------
54
+ /**
55
+ * Discover DevClaw agents by scanning which agent workspaces have projects.
56
+ * Self-discovering: any agent whose workspace contains projects/projects.json is processed.
57
+ */
58
+ function discoverAgents(config) {
59
+ const agentsList = config.agents?.list || [];
60
+ return agentsList
61
+ .filter((a) => {
62
+ if (!a.workspace)
63
+ return false;
64
+ try {
65
+ return fs.existsSync(path.join(a.workspace, "projects", "projects.json"));
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ })
71
+ .map((a) => ({ agentId: a.id, workspace: a.workspace }));
72
+ }
73
+ /**
74
+ * Run one heartbeat tick for all agents.
75
+ */
76
+ async function runHeartbeatTick(agents, config, pluginConfig, logger) {
77
+ try {
78
+ const result = await processAllAgents(agents, config, pluginConfig, logger);
79
+ logTickResult(result, logger);
80
+ }
81
+ catch (err) {
82
+ logger.error(`work_heartbeat tick failed: ${err}`);
83
+ }
84
+ }
85
+ /**
86
+ * Process heartbeat tick for all agents and aggregate results.
87
+ */
88
+ async function processAllAgents(agents, config, pluginConfig, logger) {
89
+ const result = {
90
+ totalPickups: 0,
91
+ totalHealthFixes: 0,
92
+ totalSkipped: 0,
93
+ };
94
+ for (const { agentId, workspace } of agents) {
95
+ const agentResult = await tick({
96
+ workspaceDir: workspace,
97
+ agentId,
98
+ config,
99
+ pluginConfig,
100
+ logger,
101
+ });
102
+ result.totalPickups += agentResult.totalPickups;
103
+ result.totalHealthFixes += agentResult.totalHealthFixes;
104
+ result.totalSkipped += agentResult.totalSkipped;
105
+ }
106
+ return result;
107
+ }
108
+ /**
109
+ * Log tick results if anything happened.
110
+ */
111
+ function logTickResult(result, logger) {
112
+ if (result.totalPickups > 0 || result.totalHealthFixes > 0) {
113
+ logger.info(`work_heartbeat tick: ${result.totalPickups} pickups, ${result.totalHealthFixes} health fixes, ${result.totalSkipped} skipped`);
114
+ }
115
+ }
116
+ // ---------------------------------------------------------------------------
117
+ // Tick (Main Heartbeat Loop)
118
+ // ---------------------------------------------------------------------------
119
+ export async function tick(opts) {
120
+ const { workspaceDir, agentId, config, pluginConfig } = opts;
121
+ const data = await readProjects(workspaceDir);
122
+ const projectIds = Object.keys(data.projects);
123
+ if (projectIds.length === 0) {
124
+ return { totalPickups: 0, totalHealthFixes: 0, totalSkipped: 0 };
125
+ }
126
+ const result = {
127
+ totalPickups: 0,
128
+ totalHealthFixes: 0,
129
+ totalSkipped: 0,
130
+ };
131
+ const projectExecution = pluginConfig?.projectExecution ?? "parallel";
132
+ let activeProjects = 0;
133
+ for (const groupId of projectIds) {
134
+ const project = data.projects[groupId];
135
+ if (!project)
136
+ continue;
137
+ // Health pass: auto-fix zombies and stale workers
138
+ result.totalHealthFixes += await performHealthPass(workspaceDir, groupId, project);
139
+ // Budget check: stop if we've hit the limit
140
+ const remaining = config.maxPickupsPerTick - result.totalPickups;
141
+ if (remaining <= 0)
142
+ break;
143
+ // Sequential project guard: don't start new projects if one is active
144
+ const isProjectActive = await checkProjectActive(workspaceDir, groupId);
145
+ if (projectExecution === "sequential" && !isProjectActive && activeProjects >= 1) {
146
+ result.totalSkipped++;
147
+ continue;
148
+ }
149
+ // Tick pass: fill free worker slots
150
+ const tickResult = await projectTick({
151
+ workspaceDir,
152
+ groupId,
153
+ agentId,
154
+ pluginConfig,
155
+ maxPickups: remaining,
156
+ });
157
+ result.totalPickups += tickResult.pickups.length;
158
+ result.totalSkipped += tickResult.skipped.length;
159
+ if (isProjectActive || tickResult.pickups.length > 0)
160
+ activeProjects++;
161
+ }
162
+ await auditLog(workspaceDir, "heartbeat_tick", {
163
+ projectsScanned: projectIds.length,
164
+ healthFixes: result.totalHealthFixes,
165
+ pickups: result.totalPickups,
166
+ skipped: result.totalSkipped,
167
+ });
168
+ return result;
169
+ }
170
+ /**
171
+ * Run health checks and auto-fix for a project (dev + qa roles).
172
+ */
173
+ async function performHealthPass(workspaceDir, groupId, project) {
174
+ const { provider } = createProvider({ repo: project.repo });
175
+ let fixedCount = 0;
176
+ for (const role of ["dev", "qa"]) {
177
+ const fixes = await checkWorkerHealth({
178
+ workspaceDir,
179
+ groupId,
180
+ project,
181
+ role,
182
+ activeSessions: [],
183
+ autoFix: true,
184
+ provider,
185
+ });
186
+ fixedCount += fixes.filter((f) => f.fixed).length;
187
+ }
188
+ return fixedCount;
189
+ }
190
+ /**
191
+ * Check if a project has active work (dev or qa).
192
+ */
193
+ async function checkProjectActive(workspaceDir, groupId) {
194
+ const fresh = (await readProjects(workspaceDir)).projects[groupId];
195
+ if (!fresh)
196
+ return false;
197
+ return fresh.dev.active || fresh.qa.active;
198
+ }
199
+ //# sourceMappingURL=heartbeat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../../lib/services/heartbeat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { StateLabel, IssueProvider } from "../providers/provider.js";
2
+ export type CompletionRule = {
3
+ from: StateLabel;
4
+ to: StateLabel;
5
+ gitPull?: boolean;
6
+ detectPr?: boolean;
7
+ closeIssue?: boolean;
8
+ reopenIssue?: boolean;
9
+ };
10
+ export declare const COMPLETION_RULES: Record<string, CompletionRule>;
11
+ export declare const NEXT_STATE: Record<string, string>;
12
+ export type CompletionOutput = {
13
+ labelTransition: string;
14
+ announcement: string;
15
+ nextState: string;
16
+ prUrl?: string;
17
+ issueUrl?: string;
18
+ issueClosed?: boolean;
19
+ issueReopened?: boolean;
20
+ };
21
+ export declare function getRule(role: string, result: string): CompletionRule | undefined;
22
+ /**
23
+ * Execute the completion side-effects for a role:result pair.
24
+ */
25
+ export declare function executeCompletion(opts: {
26
+ workspaceDir: string;
27
+ groupId: string;
28
+ role: "dev" | "qa";
29
+ result: string;
30
+ issueId: number;
31
+ summary?: string;
32
+ prUrl?: string;
33
+ provider: IssueProvider;
34
+ repoPath: string;
35
+ }): Promise<CompletionOutput>;
36
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../lib/services/pipeline.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK1E,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,UAAU,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAO3D,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO7C,CAAC;AAWF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEhF;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+C5B"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Pipeline service — declarative completion rules.
3
+ *
4
+ * Replaces 7 if-blocks with a data-driven lookup table.
5
+ */
6
+ import { execFile } from "node:child_process";
7
+ import { promisify } from "node:util";
8
+ import { deactivateWorker } from "../projects.js";
9
+ const execFileAsync = promisify(execFile);
10
+ export const COMPLETION_RULES = {
11
+ "dev:done": { from: "Doing", to: "To Test", gitPull: true, detectPr: true },
12
+ "qa:pass": { from: "Testing", to: "Done", closeIssue: true },
13
+ "qa:fail": { from: "Testing", to: "To Improve", reopenIssue: true },
14
+ "qa:refine": { from: "Testing", to: "Refining" },
15
+ "dev:blocked": { from: "Doing", to: "To Do" },
16
+ "qa:blocked": { from: "Testing", to: "To Test" },
17
+ };
18
+ export const NEXT_STATE = {
19
+ "dev:done": "QA queue",
20
+ "dev:blocked": "returned to queue",
21
+ "qa:pass": "Done!",
22
+ "qa:fail": "back to DEV",
23
+ "qa:refine": "awaiting human decision",
24
+ "qa:blocked": "returned to QA queue",
25
+ };
26
+ const EMOJI = {
27
+ "dev:done": "✅",
28
+ "qa:pass": "🎉",
29
+ "qa:fail": "❌",
30
+ "qa:refine": "🤔",
31
+ "dev:blocked": "🚫",
32
+ "qa:blocked": "🚫",
33
+ };
34
+ export function getRule(role, result) {
35
+ return COMPLETION_RULES[`${role}:${result}`];
36
+ }
37
+ /**
38
+ * Execute the completion side-effects for a role:result pair.
39
+ */
40
+ export async function executeCompletion(opts) {
41
+ const { workspaceDir, groupId, role, result, issueId, summary, provider, repoPath } = opts;
42
+ const key = `${role}:${result}`;
43
+ const rule = COMPLETION_RULES[key];
44
+ if (!rule)
45
+ throw new Error(`No completion rule for ${key}`);
46
+ let prUrl = opts.prUrl;
47
+ // Git pull (dev:done)
48
+ if (rule.gitPull) {
49
+ try {
50
+ await execFileAsync("git", ["pull"], { cwd: repoPath, timeout: 30_000 });
51
+ }
52
+ catch { /* best-effort */ }
53
+ }
54
+ // Auto-detect PR URL (dev:done)
55
+ if (rule.detectPr && !prUrl) {
56
+ try {
57
+ prUrl = await provider.getMergedMRUrl(issueId) ?? undefined;
58
+ }
59
+ catch { /* ignore */ }
60
+ }
61
+ // Deactivate worker + transition label
62
+ await deactivateWorker(workspaceDir, groupId, role);
63
+ await provider.transitionLabel(issueId, rule.from, rule.to);
64
+ // Close/reopen
65
+ if (rule.closeIssue)
66
+ await provider.closeIssue(issueId);
67
+ if (rule.reopenIssue)
68
+ await provider.reopenIssue(issueId);
69
+ // Build announcement
70
+ const issue = await provider.getIssue(issueId);
71
+ const emoji = EMOJI[key] ?? "📋";
72
+ const label = key.replace(":", " ").toUpperCase();
73
+ let announcement = `${emoji} ${label} #${issueId}`;
74
+ if (summary)
75
+ announcement += ` — ${summary}`;
76
+ announcement += `\n📋 Issue: ${issue.web_url}`;
77
+ if (prUrl)
78
+ announcement += `\n🔗 PR: ${prUrl}`;
79
+ announcement += `\n${NEXT_STATE[key]}.`;
80
+ return {
81
+ labelTransition: `${rule.from} → ${rule.to}`,
82
+ announcement,
83
+ nextState: NEXT_STATE[key],
84
+ prUrl,
85
+ issueUrl: issue.web_url,
86
+ issueClosed: rule.closeIssue,
87
+ issueReopened: rule.reopenIssue,
88
+ };
89
+ }
90
+ //# sourceMappingURL=pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../../lib/services/pipeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Queue service — issue queue fetching.
3
+ *
4
+ * Fetches issue queues per project from the issue provider.
5
+ * Pure functions, no tool registration or state mutation.
6
+ */
7
+ import type { Issue } from "../providers/provider.js";
8
+ import type { Project } from "../projects.js";
9
+ export type QueueLabel = "To Improve" | "To Test" | "To Do";
10
+ export declare const QUEUE_PRIORITY: Record<QueueLabel, number>;
11
+ export declare function getTaskPriority(label: QueueLabel, issue: Issue): number;
12
+ export declare function getRoleForLabel(label: QueueLabel): "dev" | "qa";
13
+ export declare function fetchProjectQueues(project: Project): Promise<Record<QueueLabel, Issue[]>>;
14
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../../lib/services/queue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAM9C,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5D,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAIrD,CAAC;AAEF,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAEvE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,GAAG,IAAI,CAE/D;AAMD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAc/F"}
@@ -0,0 +1,31 @@
1
+ import { createProvider } from "../providers/index.js";
2
+ export const QUEUE_PRIORITY = {
3
+ "To Improve": 3,
4
+ "To Test": 2,
5
+ "To Do": 1,
6
+ };
7
+ export function getTaskPriority(label, issue) {
8
+ return QUEUE_PRIORITY[label] * 10000 - issue.iid;
9
+ }
10
+ export function getRoleForLabel(label) {
11
+ return label === "To Test" ? "qa" : "dev";
12
+ }
13
+ // ---------------------------------------------------------------------------
14
+ // Fetching
15
+ // ---------------------------------------------------------------------------
16
+ export async function fetchProjectQueues(project) {
17
+ const { provider } = createProvider({ repo: project.repo });
18
+ const labels = ["To Improve", "To Test", "To Do"];
19
+ const queues = { "To Improve": [], "To Test": [], "To Do": [] };
20
+ for (const label of labels) {
21
+ try {
22
+ const issues = await provider.listIssuesByLabel(label);
23
+ queues[label] = issues.sort((a, b) => getTaskPriority(label, b) - getTaskPriority(label, a));
24
+ }
25
+ catch {
26
+ queues[label] = [];
27
+ }
28
+ }
29
+ return queues;
30
+ }
31
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../../lib/services/queue.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AASvD,MAAM,CAAC,MAAM,cAAc,GAA+B;IACxD,YAAY,EAAE,CAAC;IACf,SAAS,EAAE,CAAC;IACZ,OAAO,EAAE,CAAC;CACX,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,KAAiB,EAAE,KAAY;IAC7D,OAAO,cAAc,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAiB;IAC/C,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AAC5C,CAAC;AAED,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAgB;IACvD,MAAM,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAiB,CAAC,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAChE,MAAM,MAAM,GAAgC,EAAE,YAAY,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAE7F,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACvD,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/F,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * tick.ts — Project-level queue scan + dispatch.
3
+ *
4
+ * Core function: projectTick() scans one project's queue and fills free worker slots.
5
+ * Called by: work_start (fill parallel slot), work_finish (next pipeline step), heartbeat service (sweep).
6
+ */
7
+ import type { Issue, StateLabel } from "../providers/provider.js";
8
+ import type { IssueProvider } from "../providers/provider.js";
9
+ export declare const DEV_LABELS: StateLabel[];
10
+ export declare const QA_LABELS: StateLabel[];
11
+ export declare const PRIORITY_ORDER: StateLabel[];
12
+ export declare function detectLevelFromLabels(labels: string[]): string | null;
13
+ export declare function detectRoleFromLabel(label: StateLabel): "dev" | "qa" | null;
14
+ export declare function findNextIssueForRole(provider: Pick<IssueProvider, "listIssuesByLabel">, role: "dev" | "qa"): Promise<{
15
+ issue: Issue;
16
+ label: StateLabel;
17
+ } | null>;
18
+ /**
19
+ * Find next issue for any role (optional filter). Used by work_start for auto-detection.
20
+ */
21
+ export declare function findNextIssue(provider: Pick<IssueProvider, "listIssuesByLabel">, role?: "dev" | "qa"): Promise<{
22
+ issue: Issue;
23
+ label: StateLabel;
24
+ } | null>;
25
+ export type TickAction = {
26
+ project: string;
27
+ groupId: string;
28
+ issueId: number;
29
+ issueTitle: string;
30
+ issueUrl: string;
31
+ role: "dev" | "qa";
32
+ level: string;
33
+ sessionAction: "spawn" | "send";
34
+ announcement: string;
35
+ };
36
+ export type TickResult = {
37
+ pickups: TickAction[];
38
+ skipped: Array<{
39
+ role?: string;
40
+ reason: string;
41
+ }>;
42
+ };
43
+ /**
44
+ * Scan one project's queue and fill free worker slots.
45
+ *
46
+ * Does NOT run health checks (that's the heartbeat service's job).
47
+ * Non-destructive: only dispatches if slots are free and issues are queued.
48
+ */
49
+ export declare function projectTick(opts: {
50
+ workspaceDir: string;
51
+ groupId: string;
52
+ agentId?: string;
53
+ sessionKey?: string;
54
+ pluginConfig?: Record<string, unknown>;
55
+ dryRun?: boolean;
56
+ maxPickups?: number;
57
+ /** Only attempt this role. Used by work_start to fill the other slot. */
58
+ targetRole?: "dev" | "qa";
59
+ /** Optional provider override (for testing). Uses createProvider if omitted. */
60
+ provider?: Pick<IssueProvider, "listIssuesByLabel" | "transitionLabel">;
61
+ }): Promise<TickResult>;
62
+ //# sourceMappingURL=tick.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tick.d.ts","sourceRoot":"","sources":["../../../lib/services/tick.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAW9D,eAAO,MAAM,UAAU,EAAE,UAAU,EAA4B,CAAC;AAChE,eAAO,MAAM,SAAS,EAAE,UAAU,EAAgB,CAAC;AACnD,eAAO,MAAM,cAAc,EAAE,UAAU,EAAuC,CAAC;AAE/E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAgBrE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,CAI1E;AAED,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,mBAAmB,CAAC,EAClD,IAAI,EAAE,KAAK,GAAG,IAAI,GACjB,OAAO,CAAC;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GAAG,IAAI,CAAC,CAWrD;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,mBAAmB,CAAC,EAClD,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,GAClB,OAAO,CAAC;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GAAG,IAAI,CAAC,CAWrD;AAMD,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,OAAO,GAAG,MAAM,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,UAAU,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,mBAAmB,GAAG,iBAAiB,CAAC,CAAC;CACzE,GAAG,OAAO,CAAC,UAAU,CAAC,CAwEtB"}
@@ -0,0 +1,160 @@
1
+ import { createProvider } from "../providers/index.js";
2
+ import { selectLevel } from "../model-selector.js";
3
+ import { getWorker, getSessionForLevel, readProjects } from "../projects.js";
4
+ import { dispatchTask } from "../dispatch.js";
5
+ import { DEV_LEVELS, QA_LEVELS, isDevLevel } from "../tiers.js";
6
+ // ---------------------------------------------------------------------------
7
+ // Shared constants + helpers (used by tick, work-start, auto-pickup)
8
+ // ---------------------------------------------------------------------------
9
+ export const DEV_LABELS = ["To Do", "To Improve"];
10
+ export const QA_LABELS = ["To Test"];
11
+ export const PRIORITY_ORDER = ["To Improve", "To Test", "To Do"];
12
+ export function detectLevelFromLabels(labels) {
13
+ const lower = labels.map((l) => l.toLowerCase());
14
+ // Match role.level labels (e.g., "dev.senior", "qa.reviewer")
15
+ for (const l of lower) {
16
+ const dot = l.indexOf(".");
17
+ if (dot === -1)
18
+ continue;
19
+ const role = l.slice(0, dot);
20
+ const level = l.slice(dot + 1);
21
+ if (role === "dev" && DEV_LEVELS.includes(level))
22
+ return level;
23
+ if (role === "qa" && QA_LEVELS.includes(level))
24
+ return level;
25
+ }
26
+ // Fallback: plain level name
27
+ const all = [...DEV_LEVELS, ...QA_LEVELS];
28
+ return all.find((l) => lower.includes(l)) ?? null;
29
+ }
30
+ export function detectRoleFromLabel(label) {
31
+ if (DEV_LABELS.includes(label))
32
+ return "dev";
33
+ if (QA_LABELS.includes(label))
34
+ return "qa";
35
+ return null;
36
+ }
37
+ export async function findNextIssueForRole(provider, role) {
38
+ const labels = role === "dev"
39
+ ? PRIORITY_ORDER.filter((l) => DEV_LABELS.includes(l))
40
+ : PRIORITY_ORDER.filter((l) => QA_LABELS.includes(l));
41
+ for (const label of labels) {
42
+ try {
43
+ const issues = await provider.listIssuesByLabel(label);
44
+ if (issues.length > 0)
45
+ return { issue: issues[issues.length - 1], label };
46
+ }
47
+ catch { /* continue */ }
48
+ }
49
+ return null;
50
+ }
51
+ /**
52
+ * Find next issue for any role (optional filter). Used by work_start for auto-detection.
53
+ */
54
+ export async function findNextIssue(provider, role) {
55
+ const labels = role === "dev" ? PRIORITY_ORDER.filter((l) => DEV_LABELS.includes(l))
56
+ : role === "qa" ? PRIORITY_ORDER.filter((l) => QA_LABELS.includes(l))
57
+ : PRIORITY_ORDER;
58
+ for (const label of labels) {
59
+ try {
60
+ const issues = await provider.listIssuesByLabel(label);
61
+ if (issues.length > 0)
62
+ return { issue: issues[issues.length - 1], label };
63
+ }
64
+ catch { /* continue */ }
65
+ }
66
+ return null;
67
+ }
68
+ /**
69
+ * Scan one project's queue and fill free worker slots.
70
+ *
71
+ * Does NOT run health checks (that's the heartbeat service's job).
72
+ * Non-destructive: only dispatches if slots are free and issues are queued.
73
+ */
74
+ export async function projectTick(opts) {
75
+ const { workspaceDir, groupId, agentId, sessionKey, pluginConfig, dryRun, maxPickups, targetRole } = opts;
76
+ const project = (await readProjects(workspaceDir)).projects[groupId];
77
+ if (!project)
78
+ return { pickups: [], skipped: [{ reason: `Project not found: ${groupId}` }] };
79
+ const provider = opts.provider ?? createProvider({ repo: project.repo }).provider;
80
+ const roleExecution = project.roleExecution ?? "parallel";
81
+ const roles = targetRole ? [targetRole] : ["dev", "qa"];
82
+ const pickups = [];
83
+ const skipped = [];
84
+ let pickupCount = 0;
85
+ for (const role of roles) {
86
+ if (maxPickups !== undefined && pickupCount >= maxPickups) {
87
+ skipped.push({ role, reason: "Max pickups reached" });
88
+ continue;
89
+ }
90
+ // Re-read fresh state (previous dispatch may have changed it)
91
+ const fresh = (await readProjects(workspaceDir)).projects[groupId];
92
+ if (!fresh)
93
+ break;
94
+ const worker = getWorker(fresh, role);
95
+ if (worker.active) {
96
+ skipped.push({ role, reason: `Already active (#${worker.issueId})` });
97
+ continue;
98
+ }
99
+ if (roleExecution === "sequential" && getWorker(fresh, role === "dev" ? "qa" : "dev").active) {
100
+ skipped.push({ role, reason: "Sequential: other role active" });
101
+ continue;
102
+ }
103
+ const next = await findNextIssueForRole(provider, role);
104
+ if (!next)
105
+ continue;
106
+ const { issue, label: currentLabel } = next;
107
+ const targetLabel = role === "dev" ? "Doing" : "Testing";
108
+ // Level selection: label → heuristic
109
+ const selectedLevel = resolveLevelForIssue(issue, role);
110
+ if (dryRun) {
111
+ pickups.push({
112
+ project: project.name, groupId, issueId: issue.iid, issueTitle: issue.title, issueUrl: issue.web_url,
113
+ role, level: selectedLevel,
114
+ sessionAction: getSessionForLevel(worker, selectedLevel) ? "send" : "spawn",
115
+ announcement: `[DRY RUN] Would pick up #${issue.iid}`,
116
+ });
117
+ }
118
+ else {
119
+ try {
120
+ const dr = await dispatchTask({
121
+ workspaceDir, agentId, groupId, project: fresh, issueId: issue.iid,
122
+ issueTitle: issue.title, issueDescription: issue.description ?? "", issueUrl: issue.web_url,
123
+ role, level: selectedLevel, fromLabel: currentLabel, toLabel: targetLabel,
124
+ transitionLabel: (id, from, to) => provider.transitionLabel(id, from, to),
125
+ pluginConfig, sessionKey,
126
+ });
127
+ pickups.push({
128
+ project: project.name, groupId, issueId: issue.iid, issueTitle: issue.title, issueUrl: issue.web_url,
129
+ role, level: dr.level, sessionAction: dr.sessionAction, announcement: dr.announcement,
130
+ });
131
+ }
132
+ catch (err) {
133
+ skipped.push({ role, reason: `Dispatch failed: ${err.message}` });
134
+ continue;
135
+ }
136
+ }
137
+ pickupCount++;
138
+ }
139
+ return { pickups, skipped };
140
+ }
141
+ // ---------------------------------------------------------------------------
142
+ // Private helpers
143
+ // ---------------------------------------------------------------------------
144
+ /**
145
+ * Determine the level for an issue based on labels, role overrides, and heuristic fallback.
146
+ */
147
+ function resolveLevelForIssue(issue, role) {
148
+ const labelLevel = detectLevelFromLabels(issue.labels);
149
+ if (labelLevel) {
150
+ // QA role but label specifies a dev level → heuristic picks the right QA level
151
+ if (role === "qa" && isDevLevel(labelLevel))
152
+ return selectLevel(issue.title, issue.description ?? "", role).level;
153
+ // DEV role but label specifies a QA level → heuristic picks the right dev level
154
+ if (role === "dev" && !isDevLevel(labelLevel))
155
+ return selectLevel(issue.title, issue.description ?? "", role).level;
156
+ return labelLevel;
157
+ }
158
+ return selectLevel(issue.title, issue.description ?? "", role).level;
159
+ }
160
+ //# sourceMappingURL=tick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tick.js","sourceRoot":"","sources":["../../../lib/services/tick.ts"],"names":[],"mappings":"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"}