@backburner/cli 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 (202) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +329 -0
  3. package/dist/src/agents/antigravity-launch.js +162 -0
  4. package/dist/src/agents/antigravity.js +251 -0
  5. package/dist/src/agents/claude-launch.js +117 -0
  6. package/dist/src/agents/claude.js +271 -0
  7. package/dist/src/agents/codex-launch.js +82 -0
  8. package/dist/src/agents/codex.js +261 -0
  9. package/dist/src/agents/composite-runner.js +13 -0
  10. package/dist/src/agents/extraction.js +442 -0
  11. package/dist/src/agents/failure-feedback.js +132 -0
  12. package/dist/src/agents/gemini-launch.js +96 -0
  13. package/dist/src/agents/gemini.js +392 -0
  14. package/dist/src/agents/job-agent-assignments.js +180 -0
  15. package/dist/src/agents/lifecycle-feedback.js +147 -0
  16. package/dist/src/agents/limit-detector.js +83 -0
  17. package/dist/src/agents/opencode-launch.js +138 -0
  18. package/dist/src/agents/opencode.js +156 -0
  19. package/dist/src/agents/plan-breakdown-feedback.js +97 -0
  20. package/dist/src/agents/pool.js +168 -0
  21. package/dist/src/agents/prompt.js +462 -0
  22. package/dist/src/agents/provider-sessions.js +105 -0
  23. package/dist/src/agents/review-feedback.js +255 -0
  24. package/dist/src/agents/selection.js +113 -0
  25. package/dist/src/agents/service.js +1087 -0
  26. package/dist/src/agents/types.js +1 -0
  27. package/dist/src/agents/wave-assessment-feedback.js +69 -0
  28. package/dist/src/attention/derive.js +337 -0
  29. package/dist/src/capabilities/index.js +2 -0
  30. package/dist/src/capabilities/projection.js +183 -0
  31. package/dist/src/capabilities/types.js +1 -0
  32. package/dist/src/capabilities/validator.js +87 -0
  33. package/dist/src/cli/commands/broker-smoke.js +96 -0
  34. package/dist/src/cli/commands/broker.js +56 -0
  35. package/dist/src/cli/commands/init.js +4 -0
  36. package/dist/src/cli/commands/journal.js +43 -0
  37. package/dist/src/cli/commands/run-loop.js +56 -0
  38. package/dist/src/cli/commands/run.js +1241 -0
  39. package/dist/src/cli/commands/tui.js +187 -0
  40. package/dist/src/cli/dispatcher.js +51 -0
  41. package/dist/src/cli/errors.js +2 -0
  42. package/dist/src/cli/init.js +92 -0
  43. package/dist/src/cli/options.js +86 -0
  44. package/dist/src/cli/output.js +231 -0
  45. package/dist/src/cli/run-journal.js +342 -0
  46. package/dist/src/cli/run-types.js +1 -0
  47. package/dist/src/cli/run.js +32 -0
  48. package/dist/src/cli/runtime/dispatch-lifecycle.js +772 -0
  49. package/dist/src/cli/runtime/provider-runtime-types.js +1 -0
  50. package/dist/src/cli/runtime/provider-runtime.js +152 -0
  51. package/dist/src/cli/runtime/provider-tools.js +73 -0
  52. package/dist/src/cli/runtime/providers/antigravity.js +26 -0
  53. package/dist/src/cli/runtime/providers/claude.js +20 -0
  54. package/dist/src/cli/runtime/providers/codex.js +20 -0
  55. package/dist/src/cli/runtime/providers/gemini.js +20 -0
  56. package/dist/src/cli/runtime/providers/opencode.js +20 -0
  57. package/dist/src/cli/runtime/run-context.js +131 -0
  58. package/dist/src/cli/tui/index.js +3 -0
  59. package/dist/src/cli/tui/screen.js +388 -0
  60. package/dist/src/cli/tui/tracker.js +558 -0
  61. package/dist/src/cli/tui/types.js +1 -0
  62. package/dist/src/cli/tui/views.js +1006 -0
  63. package/dist/src/config/loader.js +136 -0
  64. package/dist/src/config/schemas.js +403 -0
  65. package/dist/src/config/types.js +1 -0
  66. package/dist/src/context/types.js +331 -0
  67. package/dist/src/domain/entities.js +1 -0
  68. package/dist/src/git/backburner-git-tool-service.js +680 -0
  69. package/dist/src/git/gitops-lifecycle.js +56 -0
  70. package/dist/src/git/merge-prep-workspace.js +357 -0
  71. package/dist/src/git/types.js +484 -0
  72. package/dist/src/github/broker-model-content.js +215 -0
  73. package/dist/src/github/broker-server.js +237 -0
  74. package/dist/src/github/broker.js +638 -0
  75. package/dist/src/github/composed.js +53 -0
  76. package/dist/src/github/gateway.js +478 -0
  77. package/dist/src/github/normalize.js +222 -0
  78. package/dist/src/github/pr-classification.js +73 -0
  79. package/dist/src/github/scoped-broker.js +35 -0
  80. package/dist/src/github/security.js +126 -0
  81. package/dist/src/github/service.js +332 -0
  82. package/dist/src/github/types.js +1 -0
  83. package/dist/src/github/utils.js +16 -0
  84. package/dist/src/journal/index.js +4 -0
  85. package/dist/src/journal/reader.js +62 -0
  86. package/dist/src/journal/renderer.js +50 -0
  87. package/dist/src/journal/service.js +72 -0
  88. package/dist/src/journal/types.js +1 -0
  89. package/dist/src/journal/writer.js +19 -0
  90. package/dist/src/mcps/bridge.js +166 -0
  91. package/dist/src/mcps/index.js +5 -0
  92. package/dist/src/mcps/reconcile.js +113 -0
  93. package/dist/src/mcps/smithery-client.js +186 -0
  94. package/dist/src/mcps/smithery.js +164 -0
  95. package/dist/src/mcps/types.js +1 -0
  96. package/dist/src/memory/json-store.js +71 -0
  97. package/dist/src/memory/prompt.js +16 -0
  98. package/dist/src/memory/scope.js +31 -0
  99. package/dist/src/memory/store.js +1 -0
  100. package/dist/src/memory/tool-service.js +142 -0
  101. package/dist/src/memory/types.js +12 -0
  102. package/dist/src/onboarding/config-writer.js +96 -0
  103. package/dist/src/onboarding/engine.js +86 -0
  104. package/dist/src/onboarding/prompt.js +102 -0
  105. package/dist/src/onboarding/services/provider-discovery.js +58 -0
  106. package/dist/src/onboarding/services/repo-discovery.js +246 -0
  107. package/dist/src/onboarding/steps/check-github-auth.js +32 -0
  108. package/dist/src/onboarding/steps/check-requirements.js +34 -0
  109. package/dist/src/onboarding/steps/configure-models.js +105 -0
  110. package/dist/src/onboarding/steps/configure-paths.js +33 -0
  111. package/dist/src/onboarding/steps/discover-providers.js +38 -0
  112. package/dist/src/onboarding/steps/discover-repos.js +39 -0
  113. package/dist/src/onboarding/steps/generate-config.js +106 -0
  114. package/dist/src/onboarding/steps/readiness-validation.js +158 -0
  115. package/dist/src/onboarding/steps/select-repos.js +76 -0
  116. package/dist/src/onboarding/steps/show-summary.js +97 -0
  117. package/dist/src/onboarding/steps/welcome.js +27 -0
  118. package/dist/src/onboarding/types.js +1 -0
  119. package/dist/src/onboarding/validators/git-access.js +63 -0
  120. package/dist/src/onboarding/validators/github-auth.js +85 -0
  121. package/dist/src/onboarding/validators/tool-checker.js +153 -0
  122. package/dist/src/onboarding/validators/workspace.js +83 -0
  123. package/dist/src/prompts/resolver.js +106 -0
  124. package/dist/src/retrospectives/candidates.js +224 -0
  125. package/dist/src/retrospectives/derive.js +169 -0
  126. package/dist/src/retrospectives/ingest.js +321 -0
  127. package/dist/src/retrospectives/management-prs.js +380 -0
  128. package/dist/src/retrospectives/proposals.js +199 -0
  129. package/dist/src/retrospectives/scopes.js +272 -0
  130. package/dist/src/retrospectives/scoring.js +171 -0
  131. package/dist/src/retrospectives/types.js +1 -0
  132. package/dist/src/state/loader.js +473 -0
  133. package/dist/src/state/types.js +15 -0
  134. package/dist/src/tasks/derivation/builders.js +70 -0
  135. package/dist/src/tasks/derivation/handlers/comment-response.js +169 -0
  136. package/dist/src/tasks/derivation/handlers/implement-plan.js +103 -0
  137. package/dist/src/tasks/derivation/handlers/index.js +54 -0
  138. package/dist/src/tasks/derivation/handlers/packet-pr-control.js +92 -0
  139. package/dist/src/tasks/derivation/handlers/packet-worktree.js +29 -0
  140. package/dist/src/tasks/derivation/handlers/plan-breakdown.js +45 -0
  141. package/dist/src/tasks/derivation/handlers/prepare-for-merge.js +91 -0
  142. package/dist/src/tasks/derivation/handlers/product-discovery.js +36 -0
  143. package/dist/src/tasks/derivation/handlers/retry-failed-task.js +125 -0
  144. package/dist/src/tasks/derivation/handlers/review-pr.js +28 -0
  145. package/dist/src/tasks/derivation/handlers/shared.js +24 -0
  146. package/dist/src/tasks/derivation/handlers/sync-parent-branch.js +100 -0
  147. package/dist/src/tasks/derivation/handlers/wave-assessment.js +223 -0
  148. package/dist/src/tasks/derivation/handlers/write-plan.js +101 -0
  149. package/dist/src/tasks/derivation/handlers/write-product-spec.js +49 -0
  150. package/dist/src/tasks/derivation/runner.js +18 -0
  151. package/dist/src/tasks/derivation/types.js +1 -0
  152. package/dist/src/tasks/derive.js +810 -0
  153. package/dist/src/tasks/mcp-skip.js +10 -0
  154. package/dist/src/tasks/retry.js +10 -0
  155. package/dist/src/tasks/task.js +184 -0
  156. package/dist/src/tasks/types.js +1 -0
  157. package/dist/src/utils/command-runner.js +192 -0
  158. package/dist/src/utils/json.js +38 -0
  159. package/dist/src/utils/logger.js +43 -0
  160. package/dist/src/utils/paths.js +38 -0
  161. package/dist/src/utils/slug.js +10 -0
  162. package/dist/src/workflows/registry.js +174 -0
  163. package/dist/src/workflows/triage.js +90 -0
  164. package/dist/src/workflows/workstream-machine/derive-events.js +121 -0
  165. package/dist/src/workflows/workstream-machine/evaluate.js +24 -0
  166. package/dist/src/workflows/workstream-machine/events.js +1 -0
  167. package/dist/src/workflows/workstream-machine/machine.js +355 -0
  168. package/dist/src/workflows/workstream-machine/selectors.js +19 -0
  169. package/dist/src/workflows/workstream-machine/types.js +1 -0
  170. package/dist/src/workstreams/branch-sync.js +87 -0
  171. package/dist/src/workstreams/derive.js +629 -0
  172. package/dist/src/workstreams/mcp-blockers.js +420 -0
  173. package/dist/src/workstreams/packet-candidates.js +42 -0
  174. package/dist/src/workstreams/packet-lifecycle-feedback.js +60 -0
  175. package/dist/src/workstreams/packet-lifecycle-report.js +199 -0
  176. package/dist/src/workstreams/packet-plan.js +597 -0
  177. package/dist/src/workstreams/packet-projections.js +190 -0
  178. package/dist/src/workstreams/parent-branch-sync-action.js +312 -0
  179. package/dist/src/workstreams/parent-branch-sync.js +148 -0
  180. package/dist/src/workstreams/phases.js +4 -0
  181. package/dist/src/workstreams/plan-breakdown-generator/export.js +19 -0
  182. package/dist/src/workstreams/plan-breakdown-generator/index.js +6 -0
  183. package/dist/src/workstreams/plan-breakdown-generator/inspect.js +50 -0
  184. package/dist/src/workstreams/plan-breakdown-generator/patch.js +139 -0
  185. package/dist/src/workstreams/plan-breakdown-generator/session.js +19 -0
  186. package/dist/src/workstreams/plan-breakdown-generator/store.js +27 -0
  187. package/dist/src/workstreams/plan-breakdown-generator/tools.js +407 -0
  188. package/dist/src/workstreams/plan-breakdown-generator/types.js +6 -0
  189. package/dist/src/workstreams/plan-breakdown-generator/validate.js +212 -0
  190. package/dist/src/workstreams/plan-breakdown-schema.js +175 -0
  191. package/dist/src/workstreams/utils.js +15 -0
  192. package/dist/src/workstreams/wave-assessment/checkpoint.js +172 -0
  193. package/dist/src/workstreams/wave-assessment/index.js +3 -0
  194. package/dist/src/workstreams/wave-assessment/session.js +84 -0
  195. package/dist/src/workstreams/wave-assessment/store.js +51 -0
  196. package/dist/src/workstreams/wave-assessment/tools.js +419 -0
  197. package/dist/src/workstreams/wave-assessment/types.js +1 -0
  198. package/dist/src/workstreams/wave-assessment/validate.js +78 -0
  199. package/dist/src/worktrees/service.js +839 -0
  200. package/dist/src/worktrees/workspace-ownership.js +34 -0
  201. package/dist/src/worktrees/workspace-recovery.js +177 -0
  202. package/package.json +61 -0
@@ -0,0 +1,473 @@
1
+ import * as path from "node:path";
2
+ import { ProviderLimitStateFileSchema } from "../config/schemas.js";
3
+ import { readJsonFileIfExists, writeJsonFileAtomic } from "../utils/json.js";
4
+ import { outputsStateRoot, outputsLogsRoot } from "../utils/paths.js";
5
+ export class StateStore {
6
+ stateDir;
7
+ executionDir;
8
+ constructor(baseDirOrOptions = { stateDir: outputsStateRoot, executionDir: outputsLogsRoot }) {
9
+ if (typeof baseDirOrOptions === "string") {
10
+ this.stateDir = baseDirOrOptions;
11
+ this.executionDir = baseDirOrOptions;
12
+ return;
13
+ }
14
+ const hasExplicitStateDir = baseDirOrOptions.stateDir !== undefined;
15
+ this.stateDir = baseDirOrOptions.stateDir ?? outputsStateRoot;
16
+ this.executionDir =
17
+ baseDirOrOptions.executionDir ??
18
+ (hasExplicitStateDir ? this.stateDir : outputsLogsRoot);
19
+ }
20
+ async load() {
21
+ const issuesState = await this.loadIssueState();
22
+ const prsState = await this.loadPullRequestState();
23
+ const commentsState = await this.loadCommentState();
24
+ const state = {
25
+ issueMetadata: issuesState.metadata,
26
+ prMetadata: prsState.metadata,
27
+ commentMetadata: commentsState.metadata,
28
+ issues: issuesState.issues,
29
+ prs: prsState.prs,
30
+ comments: commentsState.comments,
31
+ tasks: await this.loadArrayFile(this.stateDir, "tasks.json", "tasks"),
32
+ executions: await this.loadArrayFile(this.executionDir, "executions.json", "executions"),
33
+ worktrees: await this.loadArrayFile(this.stateDir, "worktrees.json", "worktrees"),
34
+ workstreams: await this.loadArrayFile(this.stateDir, "workstreams.json", "workstreams"),
35
+ attention: await this.loadArrayFile(this.stateDir, "attention.json", "attention"),
36
+ repos: await this.loadArrayFile(this.stateDir, "repos.json", "repos"),
37
+ providerSessions: await this.loadArrayFile(this.stateDir, "provider-sessions.json", "providerSessions"),
38
+ jobAgentAssignments: await this.loadArrayFile(this.stateDir, "job-agent-assignments.json", "assignments"),
39
+ retrospectiveCandidates: await this.loadArrayFile(this.stateDir, "retrospective-candidates.json", "retrospectiveCandidates"),
40
+ retrospectiveProblems: await this.loadArrayFile(this.stateDir, "retrospective-problems.json", "retrospectiveProblems"),
41
+ retrospectiveOpportunities: await this.loadArrayFile(this.stateDir, "retrospective-opportunities.json", "retrospectiveOpportunities"),
42
+ learningProposals: await this.loadArrayFile(this.stateDir, "learning-proposals.json", "learningProposals"),
43
+ managementPrs: await this.loadArrayFile(this.stateDir, "management-prs.json", "managementPrs"),
44
+ mcpStatuses: await this.loadArrayFile(this.stateDir, "mcp-status.json", "mcpStatuses"),
45
+ workspaceRecoveries: await this.loadArrayFile(this.stateDir, "workspace-recovery.json", "records"),
46
+ providerLimits: await this.loadProviderLimitsState()
47
+ };
48
+ return state;
49
+ }
50
+ async writeRepoSyncState(records) {
51
+ await writeJsonFileAtomic(path.join(this.stateDir, "repos.json"), {
52
+ repos: [...records].sort(compareRepoSyncRecords).map(sortRepoSyncRecord)
53
+ });
54
+ }
55
+ async writeEntityState(snapshot) {
56
+ await Promise.all([
57
+ writeJsonFileAtomic(path.join(this.stateDir, "issues.json"), {
58
+ metadata: sortMetadata(snapshot.issueMetadata),
59
+ issues: [...snapshot.issues].sort(compareIssueLikeEntities)
60
+ }),
61
+ writeJsonFileAtomic(path.join(this.stateDir, "prs.json"), {
62
+ metadata: sortMetadata(snapshot.prMetadata),
63
+ prs: [...snapshot.prs].sort(compareIssueLikeEntities)
64
+ }),
65
+ writeJsonFileAtomic(path.join(this.stateDir, "comments.json"), {
66
+ metadata: sortMetadata(snapshot.commentMetadata),
67
+ comments: [...snapshot.comments].sort(compareComments)
68
+ })
69
+ ]);
70
+ }
71
+ async writeTaskState(tasks) {
72
+ await writeJsonFileAtomic(path.join(this.stateDir, "tasks.json"), {
73
+ tasks: [...tasks].sort(compareTasks)
74
+ });
75
+ }
76
+ async writeExecutionState(executions) {
77
+ await writeJsonFileAtomic(path.join(this.executionDir, "executions.json"), {
78
+ executions: [...executions].sort(compareExecutions)
79
+ });
80
+ }
81
+ async writeWorktreeState(worktrees) {
82
+ await writeJsonFileAtomic(path.join(this.stateDir, "worktrees.json"), {
83
+ worktrees: [...worktrees].sort(compareWorktrees)
84
+ });
85
+ }
86
+ async writeWorkstreamState(workstreams) {
87
+ await writeJsonFileAtomic(path.join(this.stateDir, "workstreams.json"), {
88
+ workstreams: [...workstreams].sort(compareWorkstreams)
89
+ });
90
+ }
91
+ async writeAttentionState(attention) {
92
+ await writeJsonFileAtomic(path.join(this.stateDir, "attention.json"), {
93
+ attention: [...attention].sort(compareAttentionRecords).map(sortAttentionRecord)
94
+ });
95
+ }
96
+ async writeProviderSessionState(records) {
97
+ await writeJsonFileAtomic(path.join(this.stateDir, "provider-sessions.json"), {
98
+ providerSessions: [...records].sort(compareProviderSessions)
99
+ });
100
+ }
101
+ async writeJobAgentAssignmentState(records) {
102
+ await writeJsonFileAtomic(path.join(this.stateDir, "job-agent-assignments.json"), {
103
+ assignments: [...records].sort(compareJobAgentAssignments)
104
+ });
105
+ }
106
+ async writeRetrospectiveCandidateState(records) {
107
+ await writeJsonFileAtomic(path.join(this.stateDir, "retrospective-candidates.json"), {
108
+ retrospectiveCandidates: [...records].sort(compareRetrospectiveCandidates)
109
+ });
110
+ }
111
+ async writeRetrospectiveProblemState(records) {
112
+ await writeJsonFileAtomic(path.join(this.stateDir, "retrospective-problems.json"), {
113
+ retrospectiveProblems: [...records].sort(compareRetrospectiveProblems)
114
+ });
115
+ }
116
+ async writeRetrospectiveOpportunityState(records) {
117
+ await writeJsonFileAtomic(path.join(this.stateDir, "retrospective-opportunities.json"), {
118
+ retrospectiveOpportunities: [...records].sort(compareLearningOpportunities)
119
+ });
120
+ }
121
+ async writeLearningProposalState(records) {
122
+ await writeJsonFileAtomic(path.join(this.stateDir, "learning-proposals.json"), {
123
+ learningProposals: [...records].sort(compareLearningProposals)
124
+ });
125
+ }
126
+ async writeManagementPrState(records) {
127
+ await writeJsonFileAtomic(path.join(this.stateDir, "management-prs.json"), {
128
+ managementPrs: [...records].sort(compareManagementPrs)
129
+ });
130
+ }
131
+ async writeMcpStatusState(records) {
132
+ await writeJsonFileAtomic(path.join(this.stateDir, "mcp-status.json"), {
133
+ mcpStatuses: [...records].sort((a, b) => a.handle.localeCompare(b.handle))
134
+ });
135
+ }
136
+ async writeWorkspaceRecoveryState(records) {
137
+ await writeJsonFileAtomic(path.join(this.stateDir, "workspace-recovery.json"), {
138
+ records: [...records].sort(compareWorkspaceRecoveryRecords)
139
+ });
140
+ }
141
+ async writeProviderLimitState(records) {
142
+ await writeJsonFileAtomic(path.join(this.stateDir, "provider-limits.json"), {
143
+ providerLimits: [...records].sort(compareProviderLimits)
144
+ });
145
+ }
146
+ async loadWorkspaceRecoveryState() {
147
+ return this.loadArrayFile(this.stateDir, "workspace-recovery.json", "records");
148
+ }
149
+ async loadProviderLimitsState() {
150
+ const filePath = path.join(this.stateDir, "provider-limits.json");
151
+ const existing = await readJsonFileIfExists(filePath);
152
+ if (!existing) {
153
+ return [];
154
+ }
155
+ const result = ProviderLimitStateFileSchema.safeParse(existing);
156
+ if (!result.success) {
157
+ console.error(`Failed to parse provider limits state file from ${filePath}.\n` +
158
+ `Rejected payload: ${JSON.stringify(existing, null, 2)}\n` +
159
+ `Reason: ${result.error.message}`);
160
+ throw result.error;
161
+ }
162
+ return result.data.providerLimits;
163
+ }
164
+ async loadArrayFile(baseDir, fileName, key) {
165
+ const filePath = path.join(baseDir, fileName);
166
+ const existing = await readJsonFileIfExists(filePath);
167
+ if (!existing) {
168
+ const defaultValue = { [key]: [] };
169
+ await writeJsonFileAtomic(filePath, defaultValue);
170
+ return defaultValue[key];
171
+ }
172
+ const value = existing[key];
173
+ if (!Array.isArray(value)) {
174
+ throw new Error(`${filePath}: "${key}" must be an array`);
175
+ }
176
+ return value;
177
+ }
178
+ async loadIssueState() {
179
+ const value = await this.loadEntityFile("issues.json", "issues");
180
+ return {
181
+ metadata: value.metadata,
182
+ issues: value.records
183
+ };
184
+ }
185
+ async loadPullRequestState() {
186
+ const value = await this.loadEntityFile("prs.json", "prs");
187
+ return {
188
+ metadata: value.metadata,
189
+ prs: value.records
190
+ };
191
+ }
192
+ async loadCommentState() {
193
+ const value = await this.loadEntityFile("comments.json", "comments");
194
+ return {
195
+ metadata: value.metadata,
196
+ comments: value.records
197
+ };
198
+ }
199
+ async loadEntityFile(fileName, key) {
200
+ const filePath = path.join(this.stateDir, fileName);
201
+ const existing = await readJsonFileIfExists(filePath);
202
+ if (!existing) {
203
+ const defaultValue = {
204
+ metadata: defaultEntityStateMetadata(),
205
+ [key]: []
206
+ };
207
+ await writeJsonFileAtomic(filePath, defaultValue);
208
+ return {
209
+ metadata: defaultValue.metadata,
210
+ records: defaultValue[key]
211
+ };
212
+ }
213
+ const entities = existing[key];
214
+ if (!Array.isArray(entities)) {
215
+ throw new Error(`${filePath}: "${key}" must be an array`);
216
+ }
217
+ const metadata = validateMetadata(existing.metadata, filePath);
218
+ return {
219
+ metadata,
220
+ records: entities
221
+ };
222
+ }
223
+ }
224
+ export async function loadState(baseDirOrOptions = { stateDir: outputsStateRoot, executionDir: outputsLogsRoot }) {
225
+ return new StateStore(baseDirOrOptions).load();
226
+ }
227
+ function defaultEntityStateMetadata() {
228
+ return {
229
+ partialFailures: []
230
+ };
231
+ }
232
+ function validateMetadata(value, filePath) {
233
+ if (value === undefined) {
234
+ return defaultEntityStateMetadata();
235
+ }
236
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
237
+ throw new Error(`${filePath}: "metadata" must be an object when provided`);
238
+ }
239
+ const metadata = value;
240
+ const partialFailures = metadata.partialFailures;
241
+ if (partialFailures !== undefined && !Array.isArray(partialFailures)) {
242
+ throw new Error(`${filePath}: "metadata.partialFailures" must be an array when provided`);
243
+ }
244
+ return {
245
+ ...(typeof metadata.generatedAt === "string" ? { generatedAt: metadata.generatedAt } : {}),
246
+ ...(typeof metadata.repoCount === "number" ? { repoCount: metadata.repoCount } : {}),
247
+ ...(typeof metadata.entityCount === "number" ? { entityCount: metadata.entityCount } : {}),
248
+ partialFailures: sortFailures(partialFailures ?? [])
249
+ };
250
+ }
251
+ function sortMetadata(metadata) {
252
+ return {
253
+ ...(metadata.generatedAt ? { generatedAt: metadata.generatedAt } : {}),
254
+ ...(metadata.repoCount !== undefined ? { repoCount: metadata.repoCount } : {}),
255
+ ...(metadata.entityCount !== undefined ? { entityCount: metadata.entityCount } : {}),
256
+ partialFailures: sortFailures(metadata.partialFailures)
257
+ };
258
+ }
259
+ function sortFailures(failures) {
260
+ return [...failures].sort((left, right) => {
261
+ const repoComparison = left.repoSlug.localeCompare(right.repoSlug);
262
+ if (repoComparison !== 0) {
263
+ return repoComparison;
264
+ }
265
+ const stepComparison = left.step.localeCompare(right.step);
266
+ if (stepComparison !== 0) {
267
+ return stepComparison;
268
+ }
269
+ return left.scope.localeCompare(right.scope);
270
+ });
271
+ }
272
+ function compareIssueLikeEntities(left, right) {
273
+ const repoComparison = left.repoSlug.localeCompare(right.repoSlug);
274
+ if (repoComparison !== 0) {
275
+ return repoComparison;
276
+ }
277
+ if (left.number !== right.number) {
278
+ return left.number - right.number;
279
+ }
280
+ return left.id.localeCompare(right.id);
281
+ }
282
+ function compareComments(left, right) {
283
+ const repoComparison = left.repoSlug.localeCompare(right.repoSlug);
284
+ if (repoComparison !== 0) {
285
+ return repoComparison;
286
+ }
287
+ if (left.parentNumber !== right.parentNumber) {
288
+ return left.parentNumber - right.parentNumber;
289
+ }
290
+ return left.id.localeCompare(right.id);
291
+ }
292
+ function compareTasks(left, right) {
293
+ const repoComparison = left.repoSlug.localeCompare(right.repoSlug);
294
+ if (repoComparison !== 0) {
295
+ return repoComparison;
296
+ }
297
+ const kindComparison = left.kind.localeCompare(right.kind);
298
+ if (kindComparison !== 0) {
299
+ return kindComparison;
300
+ }
301
+ return left.id.localeCompare(right.id);
302
+ }
303
+ function compareExecutions(left, right) {
304
+ const repoComparison = left.repoSlug.localeCompare(right.repoSlug);
305
+ if (repoComparison !== 0) {
306
+ return repoComparison;
307
+ }
308
+ const taskComparison = left.taskId.localeCompare(right.taskId);
309
+ if (taskComparison !== 0) {
310
+ return taskComparison;
311
+ }
312
+ return left.startedAt.localeCompare(right.startedAt);
313
+ }
314
+ function compareWorktrees(left, right) {
315
+ const repoComparison = left.repoSlug.localeCompare(right.repoSlug);
316
+ if (repoComparison !== 0) {
317
+ return repoComparison;
318
+ }
319
+ return left.id.localeCompare(right.id);
320
+ }
321
+ function compareRepoSyncRecords(left, right) {
322
+ return left.repoSlug.localeCompare(right.repoSlug);
323
+ }
324
+ function compareWorkstreams(left, right) {
325
+ const repoComparison = left.repoKey.localeCompare(right.repoKey);
326
+ if (repoComparison !== 0) {
327
+ return repoComparison;
328
+ }
329
+ return left.id.localeCompare(right.id);
330
+ }
331
+ function compareAttentionRecords(left, right) {
332
+ const repoComparison = left.repoKey.localeCompare(right.repoKey);
333
+ if (repoComparison !== 0) {
334
+ return repoComparison;
335
+ }
336
+ return left.workstreamId.localeCompare(right.workstreamId);
337
+ }
338
+ function compareProviderSessions(left, right) {
339
+ const sL = left.scope;
340
+ const sR = right.scope;
341
+ const repoComparison = sL.repoKey.localeCompare(sR.repoKey);
342
+ if (repoComparison !== 0)
343
+ return repoComparison;
344
+ const workstreamComparison = sL.workstreamId.localeCompare(sR.workstreamId);
345
+ if (workstreamComparison !== 0)
346
+ return workstreamComparison;
347
+ const jobComparison = sL.job.localeCompare(sR.job);
348
+ if (jobComparison !== 0)
349
+ return jobComparison;
350
+ const modelComparison = sL.modelClass.localeCompare(sR.modelClass);
351
+ if (modelComparison !== 0)
352
+ return modelComparison;
353
+ const providerComparison = sL.provider.localeCompare(sR.provider);
354
+ if (providerComparison !== 0)
355
+ return providerComparison;
356
+ const agentComparison = sL.configuredAgentId.localeCompare(sR.configuredAgentId);
357
+ if (agentComparison !== 0)
358
+ return agentComparison;
359
+ const pathComparison = sL.localRepoPath.localeCompare(sR.localRepoPath);
360
+ if (pathComparison !== 0)
361
+ return pathComparison;
362
+ const branchComparison = sL.branchName.localeCompare(sR.branchName);
363
+ if (branchComparison !== 0)
364
+ return branchComparison;
365
+ return left.id.localeCompare(right.id);
366
+ }
367
+ function compareJobAgentAssignments(left, right) {
368
+ const repoComparison = left.repoKey.localeCompare(right.repoKey);
369
+ if (repoComparison !== 0) {
370
+ return repoComparison;
371
+ }
372
+ const workstreamComparison = left.workstreamId.localeCompare(right.workstreamId);
373
+ if (workstreamComparison !== 0) {
374
+ return workstreamComparison;
375
+ }
376
+ const jobKeyComparison = left.jobKey.localeCompare(right.jobKey);
377
+ if (jobKeyComparison !== 0) {
378
+ return jobKeyComparison;
379
+ }
380
+ return left.id.localeCompare(right.id);
381
+ }
382
+ function compareWorkspaceRecoveryRecords(left, right) {
383
+ const taskComparison = left.taskId.localeCompare(right.taskId);
384
+ if (taskComparison !== 0) {
385
+ return taskComparison;
386
+ }
387
+ return left.recoveredAt.localeCompare(right.recoveredAt);
388
+ }
389
+ function compareProviderLimits(left, right) {
390
+ return left.provider.localeCompare(right.provider);
391
+ }
392
+ function compareRetrospectiveCandidates(left, right) {
393
+ const repoComparison = left.repoSlug.localeCompare(right.repoSlug);
394
+ if (repoComparison !== 0)
395
+ return repoComparison;
396
+ const workstreamComparison = left.sourceWorkstreamId.localeCompare(right.sourceWorkstreamId);
397
+ if (workstreamComparison !== 0)
398
+ return workstreamComparison;
399
+ return left.id.localeCompare(right.id);
400
+ }
401
+ function compareRetrospectiveProblems(left, right) {
402
+ const candidateComparison = left.candidateId.localeCompare(right.candidateId);
403
+ if (candidateComparison !== 0)
404
+ return candidateComparison;
405
+ return left.id.localeCompare(right.id);
406
+ }
407
+ function compareLearningOpportunities(left, right) {
408
+ const problemComparison = left.problemId.localeCompare(right.problemId);
409
+ if (problemComparison !== 0)
410
+ return problemComparison;
411
+ return left.id.localeCompare(right.id);
412
+ }
413
+ function compareLearningProposals(left, right) {
414
+ if (!left.selectedScope && !right.selectedScope)
415
+ return left.id.localeCompare(right.id);
416
+ if (!left.selectedScope)
417
+ return 1;
418
+ if (!right.selectedScope)
419
+ return -1;
420
+ const scopeKindComparison = left.selectedScope.kind.localeCompare(right.selectedScope.kind);
421
+ if (scopeKindComparison !== 0)
422
+ return scopeKindComparison;
423
+ return left.id.localeCompare(right.id);
424
+ }
425
+ function compareManagementPrs(left, right) {
426
+ const modeComparison = left.mode.localeCompare(right.mode);
427
+ if (modeComparison !== 0)
428
+ return modeComparison;
429
+ const statusComparison = left.status.localeCompare(right.status);
430
+ if (statusComparison !== 0)
431
+ return statusComparison;
432
+ if (left.prNumber !== undefined && right.prNumber !== undefined) {
433
+ return left.prNumber - right.prNumber;
434
+ }
435
+ if (left.branchName !== undefined && right.branchName !== undefined) {
436
+ return left.branchName.localeCompare(right.branchName);
437
+ }
438
+ return left.id.localeCompare(right.id);
439
+ }
440
+ function sortAttentionRecord(record) {
441
+ return {
442
+ workstreamId: record.workstreamId,
443
+ repoKey: record.repoKey,
444
+ needsAction: record.needsAction,
445
+ reasons: [...record.reasons].sort((left, right) => left.localeCompare(right)),
446
+ ...(record.issueNumber !== undefined ? { issueNumber: record.issueNumber } : {}),
447
+ ...(record.pullRequestNumbers?.length
448
+ ? { pullRequestNumbers: [...record.pullRequestNumbers].sort((left, right) => left - right) }
449
+ : {}),
450
+ ...(record.commentIds?.length
451
+ ? { commentIds: [...record.commentIds].sort((left, right) => left.localeCompare(right)) }
452
+ : {}),
453
+ ...(record.retryTargetCommentId !== undefined ? { retryTargetCommentId: record.retryTargetCommentId } : {}),
454
+ lastObservedAt: record.lastObservedAt,
455
+ ...(record.lastHandledAt ? { lastHandledAt: record.lastHandledAt } : {}),
456
+ ...(record.currentPullRequestHeadSha ? { currentPullRequestHeadSha: record.currentPullRequestHeadSha } : {}),
457
+ ...(record.lastHandledReviewHeadSha ? { lastHandledReviewHeadSha: record.lastHandledReviewHeadSha } : {}),
458
+ ...(record.gaps?.length ? { gaps: [...record.gaps].sort((left, right) => left.localeCompare(right)) } : {})
459
+ };
460
+ }
461
+ function sortRepoSyncRecord(record) {
462
+ return {
463
+ ...record,
464
+ ...(record.githubScan
465
+ ? {
466
+ githubScan: {
467
+ ...record.githubScan,
468
+ failures: sortFailures(record.githubScan.failures)
469
+ }
470
+ }
471
+ : {})
472
+ };
473
+ }
@@ -0,0 +1,15 @@
1
+ export const WORKSTREAM_PHASES = [
2
+ "product_discovery",
3
+ "product_specification",
4
+ "issue_triage",
5
+ "issue_waiting",
6
+ "planning",
7
+ "plan_feedback",
8
+ "implementation_ready",
9
+ "implementing",
10
+ "implementation_done",
11
+ "implementation_feedback",
12
+ "pr_review",
13
+ "resolved",
14
+ "abandoned"
15
+ ];
@@ -0,0 +1,70 @@
1
+ import { workflowRegistry } from "../../workflows/registry.js";
2
+ export function createIssueTask(kind, issue, repoSyncRecord, input, workstreamId) {
3
+ return buildTaskRecord(buildTaskId(kind, issue.id), kind, issue, repoSyncRecord, input, input.contextBuilder.build({
4
+ taskKind: kind,
5
+ repoIdentifier: issue.repoSlug,
6
+ sourceKind: "issue",
7
+ sourceUrl: issue.url,
8
+ labels: issue.labels,
9
+ issueTitle: issue.title,
10
+ issueBody: issue.body,
11
+ ...(repoSyncRecord?.localPath ? { localRepoPath: repoSyncRecord.localPath } : {})
12
+ }), workstreamId);
13
+ }
14
+ export function createPullRequestTask(kind, pr, repoSyncRecord, input, worktree, workstreamId) {
15
+ return buildTaskRecord(buildTaskId(kind, pr.id), kind, pr, repoSyncRecord, input, input.contextBuilder.build({
16
+ taskKind: kind,
17
+ repoIdentifier: pr.repoSlug,
18
+ sourceKind: "pull_request",
19
+ sourceUrl: pr.url,
20
+ labels: pr.labels,
21
+ ...(pr.headSha ? { headSha: pr.headSha } : {}),
22
+ ...(worktree?.path
23
+ ? {
24
+ localRepoPath: worktree.path,
25
+ branchName: worktree.branchName,
26
+ planDocumentPath: worktree.planDocumentPath,
27
+ ...(worktree.productSpecDocumentPath ? { productSpecDocumentPath: worktree.productSpecDocumentPath } : {}),
28
+ logDocumentPath: worktree.logDocumentPath
29
+ }
30
+ : repoSyncRecord?.localPath
31
+ ? { localRepoPath: repoSyncRecord.localPath }
32
+ : {}),
33
+ ...(worktree?.packet?.packetId ? { packetId: worktree.packet.packetId } : {})
34
+ }), workstreamId);
35
+ }
36
+ export function buildTaskRecord(id, kind, source, repoSyncRecord, input, context, workstreamId) {
37
+ const executionPolicy = workflowRegistry.resolveTaskExecutionPolicy(kind);
38
+ return {
39
+ id,
40
+ kind,
41
+ status: "pending",
42
+ executionPermission: executionPolicy.executionPermission,
43
+ handoffPolicy: executionPolicy.handoffPolicy,
44
+ repoId: source.repoId,
45
+ repoName: source.repoName,
46
+ repoSlug: source.repoSlug,
47
+ sourceKind: source.kind,
48
+ sourceId: source.id,
49
+ sourceUrl: source.url,
50
+ relevant: true,
51
+ reason: input.reason,
52
+ summary: input.summary,
53
+ workstreamId,
54
+ context,
55
+ createdAt: input.now,
56
+ updatedAt: input.now
57
+ };
58
+ }
59
+ export function buildTaskId(kind, sourceId) {
60
+ return `task:${kind}:${sourceId}`;
61
+ }
62
+ export function buildCommentReason(comment, previousComment) {
63
+ if (!previousComment) {
64
+ return `Comment is newly seen and needs a response path for ${comment.parentType.replace("_", " ")} #${comment.parentNumber}.`;
65
+ }
66
+ return `Comment changed since the last seen state (updatedAt ${previousComment.updatedAt} -> ${comment.updatedAt}).`;
67
+ }
68
+ export function isAgentAuthoredComment(comment) {
69
+ return comment.body.trimStart().startsWith("🤖");
70
+ }