@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,51 @@
1
+ import * as path from "node:path";
2
+ import { readJsonFileIfExists, writeJsonFileAtomic } from "../../utils/json.js";
3
+ export class WaveAssessmentSessionStore {
4
+ logDir;
5
+ sessions = new Map();
6
+ constructor(logDir) {
7
+ this.logDir = logDir;
8
+ }
9
+ async saveSession(session) {
10
+ this.sessions.set(session.sessionId, session);
11
+ if (this.logDir) {
12
+ await writeJsonFileAtomic(this.sessionFilePath(session.sessionId), session);
13
+ }
14
+ }
15
+ async getSession(sessionId) {
16
+ const cached = this.sessions.get(sessionId);
17
+ if (cached) {
18
+ return cached;
19
+ }
20
+ if (!this.logDir) {
21
+ return undefined;
22
+ }
23
+ const persisted = await readJsonFileIfExists(this.sessionFilePath(sessionId));
24
+ if (persisted) {
25
+ this.sessions.set(sessionId, persisted);
26
+ }
27
+ return persisted;
28
+ }
29
+ getSessionSync(sessionId) {
30
+ return this.sessions.get(sessionId);
31
+ }
32
+ listSessions() {
33
+ return Array.from(this.sessions.values());
34
+ }
35
+ sessionFilePath(sessionId) {
36
+ if (!this.logDir) {
37
+ throw new Error("Cannot resolve session file path without a log directory.");
38
+ }
39
+ assertSafeSessionId(sessionId);
40
+ return path.join(this.logDir, "wave-assessment-sessions", `${sessionId}.json`);
41
+ }
42
+ }
43
+ function assertSafeSessionId(sessionId) {
44
+ if (sessionId.trim() === "" ||
45
+ sessionId.includes("/") ||
46
+ sessionId.includes("\\") ||
47
+ sessionId === "." ||
48
+ sessionId === "..") {
49
+ throw new Error(`Invalid wave assessment session ID: ${sessionId}`);
50
+ }
51
+ }
@@ -0,0 +1,419 @@
1
+ import { WAVE_ASSESSMENT_DECISIONS, WAVE_ASSESSMENT_FINDING_CATEGORIES, addWaveAssessmentFinding, completeWaveAssessmentSession, createWaveAssessmentSession, exportWaveAssessmentSession } from "./session.js";
2
+ import { validateWaveAssessmentSession } from "./validate.js";
3
+ export class WaveAssessmentToolService {
4
+ store;
5
+ logger;
6
+ namespace = "wave_assessment";
7
+ constructor(store, logger) {
8
+ this.store = store;
9
+ this.logger = logger;
10
+ }
11
+ listTools() {
12
+ return TOOL_DEFINITIONS;
13
+ }
14
+ async callTool(name, rawArguments) {
15
+ const startedAt = new Date().toISOString();
16
+ try {
17
+ switch (name) {
18
+ case "start_session": {
19
+ const args = validateStartSessionArgs(rawArguments);
20
+ const existing = await this.store.getSession(args.sessionId);
21
+ if (existing) {
22
+ const identityIssues = validateWaveAssessmentSession(existing, {
23
+ currentIdentity: {
24
+ workstreamId: args.workstreamId,
25
+ waveId: args.waveId,
26
+ parentPrNumber: args.parentPrNumber,
27
+ parentHeadSha: args.assessedParentHeadSha,
28
+ planRevision: args.currentPlanRevision
29
+ }
30
+ }).filter((issue) => issue.code.startsWith("stale-"));
31
+ if (identityIssues.length > 0 ||
32
+ existing.originalPlanningSessionId !== args.originalPlanningSessionId ||
33
+ !arraysEqual(existing.planNodeIds, args.planNodeIds)) {
34
+ return invalidSessionFailure(name, args.sessionId, [
35
+ ...identityIssues,
36
+ ...(existing.originalPlanningSessionId !== args.originalPlanningSessionId
37
+ ? [{
38
+ severity: "error",
39
+ code: "stale-plan-revision",
40
+ message: `Original planning session ${args.originalPlanningSessionId} does not match existing session ${existing.originalPlanningSessionId}.`,
41
+ path: ["originalPlanningSessionId"]
42
+ }]
43
+ : []),
44
+ ...(!arraysEqual(existing.planNodeIds, args.planNodeIds)
45
+ ? [{
46
+ severity: "error",
47
+ code: "unknown-node-reference",
48
+ message: "Provided plan node IDs do not match the existing session.",
49
+ path: ["planNodeIds"]
50
+ }]
51
+ : [])
52
+ ]);
53
+ }
54
+ this.logInvocation(startedAt, name, { sessionId: args.sessionId }, "ok");
55
+ return success(name, `Loaded existing wave assessment session: ${args.sessionId}`, {
56
+ sessionId: args.sessionId,
57
+ status: existing.status
58
+ });
59
+ }
60
+ const session = createWaveAssessmentSession(args);
61
+ await this.store.saveSession(session);
62
+ this.logInvocation(startedAt, name, { sessionId: session.sessionId }, "ok");
63
+ return success(name, `Started wave assessment session: ${session.sessionId}`, {
64
+ sessionId: session.sessionId,
65
+ status: session.status
66
+ });
67
+ }
68
+ case "add_finding": {
69
+ const args = validateAddFindingArgs(rawArguments);
70
+ const session = await this.requireSession(args.sessionId);
71
+ ensureOpenSession(session);
72
+ assertAffectedPlanNodeIdsKnown(session, args.affectedPlanNodeIds);
73
+ const updatedSession = addWaveAssessmentFinding(session, args);
74
+ await this.store.saveSession(updatedSession);
75
+ this.logInvocation(startedAt, name, { sessionId: args.sessionId }, "ok");
76
+ return success(name, `Added finding to wave assessment session: ${args.sessionId}`, {
77
+ sessionId: args.sessionId,
78
+ findingCount: updatedSession.findings.length
79
+ });
80
+ }
81
+ case "complete_session": {
82
+ const args = validateCompleteSessionArgs(rawArguments);
83
+ const session = await this.requireSession(args.sessionId);
84
+ ensureOpenSession(session);
85
+ const issues = validateWaveAssessmentSession(session, {
86
+ decision: args.decision,
87
+ summary: args.summary,
88
+ currentIdentity: args.currentIdentity
89
+ });
90
+ if (issues.length > 0) {
91
+ const updatedSession = { ...session, validationIssues: issues };
92
+ await this.store.saveSession(updatedSession);
93
+ return invalidSessionFailure(name, args.sessionId, issues);
94
+ }
95
+ const updatedSession = completeWaveAssessmentSession(session, args);
96
+ await this.store.saveSession(updatedSession);
97
+ this.logInvocation(startedAt, name, { sessionId: args.sessionId }, "ok");
98
+ return success(name, `Completed wave assessment session: ${args.sessionId}`, {
99
+ sessionId: args.sessionId,
100
+ decision: updatedSession.decision,
101
+ findingCount: updatedSession.findings.length
102
+ });
103
+ }
104
+ case "export_session": {
105
+ const args = validateExportSessionArgs(rawArguments);
106
+ const session = await this.requireSession(args.sessionId);
107
+ const issues = validateWaveAssessmentSession(session, {
108
+ currentIdentity: args.currentIdentity
109
+ });
110
+ if (issues.length > 0) {
111
+ return invalidSessionFailure(name, args.sessionId, issues);
112
+ }
113
+ if (session.status !== "completed") {
114
+ return {
115
+ ok: false,
116
+ tool: name,
117
+ error: {
118
+ code: "session_not_completed",
119
+ message: `Cannot export incomplete wave assessment session ${args.sessionId}.`
120
+ }
121
+ };
122
+ }
123
+ const result = exportWaveAssessmentSession(session);
124
+ this.logInvocation(startedAt, name, { sessionId: args.sessionId }, "ok");
125
+ return success(name, `Exported wave assessment session: ${args.sessionId}`, result);
126
+ }
127
+ default:
128
+ return {
129
+ ok: false,
130
+ tool: name,
131
+ error: {
132
+ code: "tool_not_found",
133
+ message: `Unknown wave assessment tool "${name}"`
134
+ }
135
+ };
136
+ }
137
+ }
138
+ catch (error) {
139
+ const failure = toFailure(name, error);
140
+ this.logInvocation(startedAt, name, rawArguments, "error", failure.error.message);
141
+ return failure;
142
+ }
143
+ }
144
+ async requireSession(sessionId) {
145
+ const session = await this.store.getSession(sessionId);
146
+ if (!session) {
147
+ throw new WaveAssessmentToolError("session_not_found", `Session not found: ${sessionId}`);
148
+ }
149
+ return session;
150
+ }
151
+ logInvocation(timestamp, toolName, args, status, message) {
152
+ this.logger.info(JSON.stringify({
153
+ component: "wave-assessment-tool-service",
154
+ timestamp,
155
+ toolName,
156
+ resultStatus: status,
157
+ arguments: args,
158
+ ...(message ? { message } : {})
159
+ }));
160
+ }
161
+ }
162
+ class WaveAssessmentToolError extends Error {
163
+ code;
164
+ constructor(code, message) {
165
+ super(message);
166
+ this.code = code;
167
+ }
168
+ }
169
+ function success(tool, summary, result) {
170
+ return { ok: true, tool, summary, result };
171
+ }
172
+ function invalidSessionFailure(tool, sessionId, issues) {
173
+ return {
174
+ ok: false,
175
+ tool,
176
+ error: {
177
+ code: "invalid_session",
178
+ message: `Wave assessment session ${sessionId} failed validation: ${issues.map((issue) => issue.code).join(", ")}`
179
+ }
180
+ };
181
+ }
182
+ function toFailure(name, error) {
183
+ if (error instanceof WaveAssessmentToolError) {
184
+ return {
185
+ ok: false,
186
+ tool: name,
187
+ error: {
188
+ code: error.code,
189
+ message: error.message
190
+ }
191
+ };
192
+ }
193
+ return {
194
+ ok: false,
195
+ tool: name,
196
+ error: {
197
+ code: "internal_error",
198
+ message: error instanceof Error ? error.message : String(error)
199
+ }
200
+ };
201
+ }
202
+ function validateStartSessionArgs(args) {
203
+ const obj = expectObject(args);
204
+ return {
205
+ sessionId: expectString(obj.sessionId, "sessionId"),
206
+ workstreamId: expectString(obj.workstreamId, "workstreamId"),
207
+ waveId: expectString(obj.waveId, "waveId"),
208
+ parentPrNumber: expectPositiveInteger(obj.parentPrNumber, "parentPrNumber"),
209
+ assessedParentHeadSha: expectString(obj.assessedParentHeadSha, "assessedParentHeadSha"),
210
+ originalPlanningSessionId: expectString(obj.originalPlanningSessionId, "originalPlanningSessionId"),
211
+ currentPlanRevision: expectStringOrNumber(obj.currentPlanRevision, "currentPlanRevision"),
212
+ planNodeIds: expectUniqueStringArray(obj.planNodeIds, "planNodeIds")
213
+ };
214
+ }
215
+ function validateAddFindingArgs(args) {
216
+ const obj = expectObject(args);
217
+ return {
218
+ sessionId: expectString(obj.sessionId, "sessionId"),
219
+ category: expectEnum(obj.category, "category", WAVE_ASSESSMENT_FINDING_CATEGORIES),
220
+ summary: expectString(obj.summary, "summary"),
221
+ details: expectString(obj.details, "details"),
222
+ affectedPlanNodeIds: expectStringArray(obj.affectedPlanNodeIds ?? [], "affectedPlanNodeIds")
223
+ };
224
+ }
225
+ function validateCompleteSessionArgs(args) {
226
+ const obj = expectObject(args);
227
+ return {
228
+ sessionId: expectString(obj.sessionId, "sessionId"),
229
+ decision: expectEnum(obj.decision, "decision", WAVE_ASSESSMENT_DECISIONS),
230
+ summary: expectString(obj.summary, "summary"),
231
+ currentIdentity: expectCurrentIdentity(obj.currentIdentity)
232
+ };
233
+ }
234
+ function validateExportSessionArgs(args) {
235
+ const obj = expectObject(args);
236
+ return {
237
+ sessionId: expectString(obj.sessionId, "sessionId"),
238
+ currentIdentity: expectCurrentIdentity(obj.currentIdentity)
239
+ };
240
+ }
241
+ function expectCurrentIdentity(value) {
242
+ if (value === undefined) {
243
+ return undefined;
244
+ }
245
+ const obj = expectObject(value, "currentIdentity");
246
+ const result = {};
247
+ if (obj.workstreamId !== undefined)
248
+ result.workstreamId = expectString(obj.workstreamId, "currentIdentity.workstreamId");
249
+ if (obj.waveId !== undefined)
250
+ result.waveId = expectString(obj.waveId, "currentIdentity.waveId");
251
+ if (obj.parentPrNumber !== undefined)
252
+ result.parentPrNumber = expectPositiveInteger(obj.parentPrNumber, "currentIdentity.parentPrNumber");
253
+ if (obj.parentHeadSha !== undefined)
254
+ result.parentHeadSha = expectString(obj.parentHeadSha, "currentIdentity.parentHeadSha");
255
+ if (obj.planRevision !== undefined)
256
+ result.planRevision = expectStringOrNumber(obj.planRevision, "currentIdentity.planRevision");
257
+ return result;
258
+ }
259
+ function ensureOpenSession(session) {
260
+ if (session.status !== "open") {
261
+ throw new WaveAssessmentToolError("session_already_completed", `Wave assessment session ${session.sessionId} is already completed.`);
262
+ }
263
+ }
264
+ function assertAffectedPlanNodeIdsKnown(session, affectedPlanNodeIds) {
265
+ const knownPlanNodeIds = new Set(session.planNodeIds);
266
+ const unknownNodeIds = affectedPlanNodeIds.filter((nodeId) => !knownPlanNodeIds.has(nodeId));
267
+ if (unknownNodeIds.length > 0) {
268
+ throw new WaveAssessmentToolError("unknown_node_reference", `Unknown affected plan node IDs: ${unknownNodeIds.join(", ")}`);
269
+ }
270
+ }
271
+ function arraysEqual(left, right) {
272
+ return left.length === right.length && left.every((value, index) => value === right[index]);
273
+ }
274
+ function expectObject(value, name = "arguments") {
275
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
276
+ throw new WaveAssessmentToolError("invalid_arguments", `Expected ${name} to be an object.`);
277
+ }
278
+ return value;
279
+ }
280
+ function expectString(value, name) {
281
+ if (typeof value !== "string" || value.trim() === "") {
282
+ throw new WaveAssessmentToolError("invalid_arguments", `Expected ${name} to be a non-empty string.`);
283
+ }
284
+ return value.trim();
285
+ }
286
+ function expectStringOrNumber(value, name) {
287
+ if (typeof value === "number" && Number.isFinite(value)) {
288
+ return String(value);
289
+ }
290
+ return expectString(value, name);
291
+ }
292
+ function expectPositiveInteger(value, name) {
293
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
294
+ throw new WaveAssessmentToolError("invalid_arguments", `Expected ${name} to be a positive integer.`);
295
+ }
296
+ return value;
297
+ }
298
+ function expectStringArray(value, name) {
299
+ if (!Array.isArray(value)) {
300
+ throw new WaveAssessmentToolError("invalid_arguments", `Expected ${name} to be an array.`);
301
+ }
302
+ return value.map((item, index) => expectString(item, `${name}[${index}]`));
303
+ }
304
+ function expectUniqueStringArray(value, name) {
305
+ const items = expectStringArray(value, name);
306
+ const seen = new Set();
307
+ for (const item of items) {
308
+ if (seen.has(item)) {
309
+ throw new WaveAssessmentToolError("invalid_arguments", `Expected ${name} to contain unique values. Duplicate: ${item}`);
310
+ }
311
+ seen.add(item);
312
+ }
313
+ return items;
314
+ }
315
+ function expectEnum(value, name, choices) {
316
+ const str = expectString(value, name);
317
+ if (!choices.includes(str)) {
318
+ throw new WaveAssessmentToolError("invalid_arguments", `Expected ${name} to be one of: ${choices.join(", ")}.`);
319
+ }
320
+ return str;
321
+ }
322
+ const TOOL_DEFINITIONS = [
323
+ {
324
+ name: "start_session",
325
+ title: "Start Wave Assessment Session",
326
+ description: "Start or load a wave assessment session for one parent PR head and plan revision.",
327
+ inputSchema: {
328
+ type: "object",
329
+ additionalProperties: false,
330
+ required: [
331
+ "sessionId",
332
+ "workstreamId",
333
+ "waveId",
334
+ "parentPrNumber",
335
+ "assessedParentHeadSha",
336
+ "originalPlanningSessionId",
337
+ "currentPlanRevision",
338
+ "planNodeIds"
339
+ ],
340
+ properties: {
341
+ sessionId: { type: "string" },
342
+ workstreamId: { type: "string" },
343
+ waveId: { type: "string" },
344
+ parentPrNumber: { type: "number" },
345
+ assessedParentHeadSha: { type: "string" },
346
+ originalPlanningSessionId: { type: "string" },
347
+ currentPlanRevision: { oneOf: [{ type: "string" }, { type: "number" }] },
348
+ planNodeIds: { type: "array", items: { type: "string" } }
349
+ }
350
+ }
351
+ },
352
+ {
353
+ name: "add_finding",
354
+ title: "Add Wave Assessment Finding",
355
+ description: "Record a finding discovered while assessing the integrated parent PR.",
356
+ inputSchema: {
357
+ type: "object",
358
+ additionalProperties: false,
359
+ required: ["sessionId", "category", "summary", "details", "affectedPlanNodeIds"],
360
+ properties: {
361
+ sessionId: { type: "string" },
362
+ category: { type: "string", enum: [...WAVE_ASSESSMENT_FINDING_CATEGORIES] },
363
+ summary: { type: "string" },
364
+ details: { type: "string" },
365
+ affectedPlanNodeIds: { type: "array", items: { type: "string" } }
366
+ }
367
+ }
368
+ },
369
+ {
370
+ name: "complete_session",
371
+ title: "Complete Wave Assessment Session",
372
+ description: "Finalize a wave assessment decision after validating summary, findings, plan nodes, and optional freshness identity.",
373
+ inputSchema: {
374
+ type: "object",
375
+ additionalProperties: false,
376
+ required: ["sessionId", "decision", "summary"],
377
+ properties: {
378
+ sessionId: { type: "string" },
379
+ decision: { type: "string", enum: [...WAVE_ASSESSMENT_DECISIONS] },
380
+ summary: { type: "string" },
381
+ currentIdentity: {
382
+ type: "object",
383
+ additionalProperties: false,
384
+ properties: {
385
+ workstreamId: { type: "string" },
386
+ waveId: { type: "string" },
387
+ parentPrNumber: { type: "number" },
388
+ parentHeadSha: { type: "string" },
389
+ planRevision: { oneOf: [{ type: "string" }, { type: "number" }] }
390
+ }
391
+ }
392
+ }
393
+ }
394
+ },
395
+ {
396
+ name: "export_session",
397
+ title: "Export Wave Assessment Session",
398
+ description: "Export a completed and still-fresh wave assessment result.",
399
+ inputSchema: {
400
+ type: "object",
401
+ additionalProperties: false,
402
+ required: ["sessionId"],
403
+ properties: {
404
+ sessionId: { type: "string" },
405
+ currentIdentity: {
406
+ type: "object",
407
+ additionalProperties: false,
408
+ properties: {
409
+ workstreamId: { type: "string" },
410
+ waveId: { type: "string" },
411
+ parentPrNumber: { type: "number" },
412
+ parentHeadSha: { type: "string" },
413
+ planRevision: { oneOf: [{ type: "string" }, { type: "number" }] }
414
+ }
415
+ }
416
+ }
417
+ }
418
+ }
419
+ ];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,78 @@
1
+ export function validateWaveAssessmentSession(session, options) {
2
+ const issues = [];
3
+ const decision = options?.decision ?? session.decision;
4
+ const summary = options?.summary ?? session.summary;
5
+ if (summary.trim() === "") {
6
+ issues.push({
7
+ severity: "error",
8
+ code: "missing-summary",
9
+ message: "A non-empty assessment summary is required.",
10
+ path: ["summary"]
11
+ });
12
+ }
13
+ if (decision === "replan" && session.findings.length === 0) {
14
+ issues.push({
15
+ severity: "error",
16
+ code: "missing-findings-for-replan",
17
+ message: "A replan decision requires at least one finding.",
18
+ path: ["findings"]
19
+ });
20
+ }
21
+ const knownPlanNodeIds = new Set(session.planNodeIds);
22
+ session.findings.forEach((finding, findingIndex) => {
23
+ finding.affectedPlanNodeIds.forEach((nodeId, nodeIndex) => {
24
+ if (!knownPlanNodeIds.has(nodeId)) {
25
+ issues.push({
26
+ severity: "error",
27
+ code: "unknown-node-reference",
28
+ message: `Finding ${finding.id} references unknown plan node "${nodeId}".`,
29
+ path: ["findings", String(findingIndex), "affectedPlanNodeIds", String(nodeIndex)]
30
+ });
31
+ }
32
+ });
33
+ });
34
+ const currentIdentity = options?.currentIdentity;
35
+ if (currentIdentity) {
36
+ if (currentIdentity.workstreamId !== undefined && currentIdentity.workstreamId !== session.workstreamId) {
37
+ issues.push({
38
+ severity: "error",
39
+ code: "stale-workstream",
40
+ message: `Current workstream ${currentIdentity.workstreamId} does not match session workstream ${session.workstreamId}.`,
41
+ path: ["currentIdentity", "workstreamId"]
42
+ });
43
+ }
44
+ if (currentIdentity.waveId !== undefined && currentIdentity.waveId !== session.waveId) {
45
+ issues.push({
46
+ severity: "error",
47
+ code: "stale-wave",
48
+ message: `Current wave ${currentIdentity.waveId} does not match session wave ${session.waveId}.`,
49
+ path: ["currentIdentity", "waveId"]
50
+ });
51
+ }
52
+ if (currentIdentity.parentPrNumber !== undefined && currentIdentity.parentPrNumber !== session.parentPrNumber) {
53
+ issues.push({
54
+ severity: "error",
55
+ code: "stale-parent-pr",
56
+ message: `Current parent PR ${currentIdentity.parentPrNumber} does not match session parent PR ${session.parentPrNumber}.`,
57
+ path: ["currentIdentity", "parentPrNumber"]
58
+ });
59
+ }
60
+ if (currentIdentity.parentHeadSha !== undefined && currentIdentity.parentHeadSha !== session.assessedParentHeadSha) {
61
+ issues.push({
62
+ severity: "error",
63
+ code: "stale-parent-head",
64
+ message: `Current parent head ${currentIdentity.parentHeadSha} does not match assessed head ${session.assessedParentHeadSha}.`,
65
+ path: ["currentIdentity", "parentHeadSha"]
66
+ });
67
+ }
68
+ if (currentIdentity.planRevision !== undefined && currentIdentity.planRevision !== session.currentPlanRevision) {
69
+ issues.push({
70
+ severity: "error",
71
+ code: "stale-plan-revision",
72
+ message: `Current plan revision ${currentIdentity.planRevision} does not match session plan revision ${session.currentPlanRevision}.`,
73
+ path: ["currentIdentity", "planRevision"]
74
+ });
75
+ }
76
+ }
77
+ return issues;
78
+ }