@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,34 @@
1
+ import * as path from "node:path";
2
+ /**
3
+ * Validates that a candidate workspace path is owned by the orchestrator.
4
+ *
5
+ * Orchestrator-owned task workspaces are strictly located under:
6
+ * - <codeRoot>/_worktrees/
7
+ * - <codeRoot>/_merge-prep/
8
+ *
9
+ * This function performs a pure string-normalization check without I/O.
10
+ * It rejects the root directories themselves to ensure recovery targets
11
+ * individual task workspaces.
12
+ *
13
+ * @param workspacePath The candidate workspace path to validate.
14
+ * @param codeRoot The orchestrator's code root directory.
15
+ */
16
+ export function validateWorkspaceOwnership(workspacePath, codeRoot) {
17
+ const resolvedCodeRoot = path.resolve(codeRoot);
18
+ const resolvedWorkspacePath = path.resolve(workspacePath);
19
+ const allowedRootSubdirs = ["_worktrees", "_merge-prep"];
20
+ for (const subdir of allowedRootSubdirs) {
21
+ const allowedRoot = path.join(resolvedCodeRoot, subdir);
22
+ const relative = path.relative(allowedRoot, resolvedWorkspacePath);
23
+ const isInside = relative !== "" &&
24
+ !relative.startsWith("..") &&
25
+ !path.isAbsolute(relative);
26
+ if (isInside) {
27
+ return { valid: true };
28
+ }
29
+ }
30
+ return {
31
+ valid: false,
32
+ reason: `Path "${resolvedWorkspacePath}" is not within an orchestrator-owned workspace root (_worktrees or _merge-prep) under "${resolvedCodeRoot}".`,
33
+ };
34
+ }
@@ -0,0 +1,177 @@
1
+ import { validateWorkspaceOwnership } from "./workspace-ownership.js";
2
+ /**
3
+ * Service responsible for recovering agent workspaces after a failure.
4
+ *
5
+ * It ensures that a task-owned workspace is returned to a clean state
6
+ * (synced with its remote branch and free of untracked files) so that
7
+ * subsequent orchestrator turns can safely reuse or refresh it.
8
+ */
9
+ export class WorkspaceRecoveryService {
10
+ now;
11
+ constructor(now = () => new Date().toISOString()) {
12
+ this.now = now;
13
+ }
14
+ /**
15
+ * Resolves the absolute path to the workspace owned by a task, if any.
16
+ *
17
+ * @param task The task record to inspect.
18
+ * @returns The absolute workspace path, or null if the task does not use a persistent local workspace.
19
+ */
20
+ resolveTaskWorkspacePath(task) {
21
+ switch (task.kind) {
22
+ case "implement_plan":
23
+ case "write_plan":
24
+ case "respond_pr_comment":
25
+ return task.context.localRepoPath ?? null;
26
+ case "prepare_pr_for_merge":
27
+ return task.context.prepareForMerge?.persistentWorktreePath ?? null;
28
+ default:
29
+ // Other task kinds (triage, review, breakdown, sync preflight) are either
30
+ // read-only or do not own a dedicated persistent worktree.
31
+ return null;
32
+ }
33
+ }
34
+ /**
35
+ * Resolves the branch name associated with a task for recovery purposes.
36
+ *
37
+ * @param task The task record to inspect.
38
+ * @returns The branch name, or null if none can be resolved.
39
+ */
40
+ resolveTaskBranch(task) {
41
+ if (task.kind === "prepare_pr_for_merge") {
42
+ return task.context.prepareForMerge?.headBranch ?? null;
43
+ }
44
+ return task.context.branchName ?? null;
45
+ }
46
+ /**
47
+ * Returns true if the task association requires workspace recovery.
48
+ *
49
+ * Triggers:
50
+ * 1. Task status is "failed" (traditional technical failure) AND it has a path.
51
+ * 2. Task context has "recoveryNeeded: true" (explicitly set during retry derivation).
52
+ *
53
+ * @param task The task record to inspect.
54
+ */
55
+ isRecoveryNeeded(task) {
56
+ const explicitRecovery = task.context.recoveryNeeded === true;
57
+ if (explicitRecovery) {
58
+ return true;
59
+ }
60
+ // Explicitly recovered in the current cycle or a previous turn.
61
+ if (task.context.recoveryNeeded === false) {
62
+ return false;
63
+ }
64
+ const hasPath = this.resolveTaskWorkspacePath(task) !== null;
65
+ const isFailed = task.status === "failed";
66
+ return isFailed && hasPath;
67
+ }
68
+ /**
69
+ * Performs the recovery of a task's workspace.
70
+ *
71
+ * 1. Validates that the workspace path is owned by the orchestrator.
72
+ * 2. Verifies it is a valid git working tree.
73
+ * 3. Aborts any in-progress merge.
74
+ * 4. Ensures the correct branch is checked out.
75
+ * 5. Performs a hard reset to origin/<branch> (branch may be derived from
76
+ * the workspace's current branch if the task context has none; fails
77
+ * explicitly if no branch can be determined).
78
+ * 6. Cleans untracked files and directories.
79
+ *
80
+ * @param opts Recovery options.
81
+ * @returns A record summarizing the recovery outcome.
82
+ */
83
+ async recoverWorkspace(opts) {
84
+ const { task, codeRoot, gitGateway } = opts;
85
+ const workspacePath = this.resolveTaskWorkspacePath(task);
86
+ if (!workspacePath) {
87
+ return {
88
+ taskId: task.id,
89
+ taskKind: task.kind,
90
+ outcome: "skipped",
91
+ reason: "Task has no associated workspace path.",
92
+ recoveredAt: this.now(),
93
+ };
94
+ }
95
+ const ownership = validateWorkspaceOwnership(workspacePath, codeRoot);
96
+ if (!ownership.valid) {
97
+ return {
98
+ taskId: task.id,
99
+ taskKind: task.kind,
100
+ workspacePath,
101
+ outcome: "rejected",
102
+ reason: ownership.reason,
103
+ recoveredAt: this.now(),
104
+ };
105
+ }
106
+ try {
107
+ const isGit = await gitGateway.isInsideWorkingTree(workspacePath);
108
+ if (!isGit) {
109
+ return {
110
+ taskId: task.id,
111
+ taskKind: task.kind,
112
+ workspacePath,
113
+ outcome: "failed",
114
+ reason: `Path "${workspacePath}" is not a git working tree.`,
115
+ recoveredAt: this.now(),
116
+ };
117
+ }
118
+ await gitGateway.fetchRepository(workspacePath);
119
+ // Abort any in-progress merge state before resetting.
120
+ const mergeState = await gitGateway.getActiveMergeState(workspacePath);
121
+ if (mergeState === "merging") {
122
+ await gitGateway.abortMerge(workspacePath);
123
+ }
124
+ else if (mergeState === "rebasing") {
125
+ await gitGateway.abortRebase(workspacePath);
126
+ }
127
+ else if (mergeState === "cherry-picking") {
128
+ await gitGateway.abortCherryPick(workspacePath);
129
+ }
130
+ else if (mergeState === "reverting") {
131
+ await gitGateway.abortRevert(workspacePath);
132
+ }
133
+ const currentBranch = await gitGateway.getCurrentBranch(workspacePath);
134
+ let branch = this.resolveTaskBranch(task);
135
+ if (!branch) {
136
+ // No branch in task context (e.g. write_plan/implement_plan tasks
137
+ // derived from a repoSyncRecord without an active worktree). Fall back
138
+ // to whatever branch the workspace is currently on so we can reset to
139
+ // its remote ref instead of leaving local-only commits ahead of origin.
140
+ branch = currentBranch;
141
+ }
142
+ if (!branch) {
143
+ return {
144
+ taskId: task.id,
145
+ taskKind: task.kind,
146
+ workspacePath,
147
+ outcome: "failed",
148
+ reason: "Cannot recover: task has no associated branch name and the workspace is not currently on a branch (detached HEAD).",
149
+ recoveredAt: this.now(),
150
+ };
151
+ }
152
+ if (currentBranch !== branch) {
153
+ await gitGateway.checkoutBranch(workspacePath, branch);
154
+ }
155
+ await gitGateway.resetHard(workspacePath, `origin/${branch}`);
156
+ await gitGateway.cleanUntrackedFiles(workspacePath);
157
+ return {
158
+ taskId: task.id,
159
+ taskKind: task.kind,
160
+ workspacePath,
161
+ outcome: "recovered",
162
+ reason: `Workspace at "${workspacePath}" reset and cleaned.`,
163
+ recoveredAt: this.now(),
164
+ };
165
+ }
166
+ catch (err) {
167
+ return {
168
+ taskId: task.id,
169
+ taskKind: task.kind,
170
+ workspacePath,
171
+ outcome: "failed",
172
+ reason: err instanceof Error ? err.message : String(err),
173
+ recoveredAt: this.now(),
174
+ };
175
+ }
176
+ }
177
+ }
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@backburner/cli",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Local GitHub-based agent orchestration CLI",
6
+ "license": "MIT",
7
+ "author": "Elad Aviv",
8
+ "keywords": [
9
+ "cli",
10
+ "github",
11
+ "agents",
12
+ "orchestration",
13
+ "automation"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/eladav/backburner.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/eladav/backburner/issues"
21
+ },
22
+ "homepage": "https://github.com/eladav/backburner#readme",
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "files": [
27
+ "dist/src",
28
+ "README.md"
29
+ ],
30
+ "bin": {
31
+ "backburner": "dist/src/cli/run.js"
32
+ },
33
+ "scripts": {
34
+ "clean": "node scripts/clean-dist.mjs",
35
+ "build": "npm run clean && tsc -p tsconfig.json && node scripts/ensure-npm-bin.mjs",
36
+ "build:backburner": "npm run build",
37
+ "build:github-broker": "npm run build",
38
+ "prepack": "npm run build",
39
+ "link:local": "npm run build && npm link",
40
+ "install:local": "node scripts/install-local-package.mjs",
41
+ "typecheck": "tsc -p tsconfig.json --noEmit",
42
+ "test": "npm run build && node --import ./dist/test/setup.js --test --test-concurrency=1 dist/test/*.test.js",
43
+ "run:backburner": "node dist/src/cli/run.js run",
44
+ "run:github-broker": "node dist/src/cli/run.js broker",
45
+ "smoke:github-broker": "node dist/src/cli/run.js broker-smoke",
46
+ "init": "node dist/src/cli/run.js init"
47
+ },
48
+ "engines": {
49
+ "node": ">=20"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^24.6.0",
53
+ "typescript": "^5.9.3"
54
+ },
55
+ "dependencies": {
56
+ "@smithery/api": "^0.67.0",
57
+ "cross-spawn": "^7.0.6",
58
+ "xstate": "^5.31.0",
59
+ "zod": "^4.4.3"
60
+ }
61
+ }