@blockspool/mcp 0.3.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 (66) hide show
  1. package/README.md +174 -0
  2. package/dist/advance.d.ts +30 -0
  3. package/dist/advance.d.ts.map +1 -0
  4. package/dist/advance.js +514 -0
  5. package/dist/advance.js.map +1 -0
  6. package/dist/direct-client.d.ts +57 -0
  7. package/dist/direct-client.d.ts.map +1 -0
  8. package/dist/direct-client.js +92 -0
  9. package/dist/direct-client.js.map +1 -0
  10. package/dist/event-processor.d.ts +17 -0
  11. package/dist/event-processor.d.ts.map +1 -0
  12. package/dist/event-processor.js +360 -0
  13. package/dist/event-processor.js.map +1 -0
  14. package/dist/formulas.d.ts +37 -0
  15. package/dist/formulas.d.ts.map +1 -0
  16. package/dist/formulas.js +245 -0
  17. package/dist/formulas.js.map +1 -0
  18. package/dist/index.d.ts +9 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +42 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/proposals.d.ts +51 -0
  23. package/dist/proposals.d.ts.map +1 -0
  24. package/dist/proposals.js +207 -0
  25. package/dist/proposals.js.map +1 -0
  26. package/dist/run-manager.d.ts +69 -0
  27. package/dist/run-manager.d.ts.map +1 -0
  28. package/dist/run-manager.js +315 -0
  29. package/dist/run-manager.js.map +1 -0
  30. package/dist/scope-policy.d.ts +34 -0
  31. package/dist/scope-policy.d.ts.map +1 -0
  32. package/dist/scope-policy.js +145 -0
  33. package/dist/scope-policy.js.map +1 -0
  34. package/dist/server.d.ts +16 -0
  35. package/dist/server.d.ts.map +1 -0
  36. package/dist/server.js +31 -0
  37. package/dist/server.js.map +1 -0
  38. package/dist/spindle.d.ts +41 -0
  39. package/dist/spindle.d.ts.map +1 -0
  40. package/dist/spindle.js +279 -0
  41. package/dist/spindle.js.map +1 -0
  42. package/dist/state.d.ts +36 -0
  43. package/dist/state.d.ts.map +1 -0
  44. package/dist/state.js +50 -0
  45. package/dist/state.js.map +1 -0
  46. package/dist/tools/execute.d.ts +7 -0
  47. package/dist/tools/execute.d.ts.map +1 -0
  48. package/dist/tools/execute.js +238 -0
  49. package/dist/tools/execute.js.map +1 -0
  50. package/dist/tools/git.d.ts +7 -0
  51. package/dist/tools/git.d.ts.map +1 -0
  52. package/dist/tools/git.js +67 -0
  53. package/dist/tools/git.js.map +1 -0
  54. package/dist/tools/scout.d.ts +7 -0
  55. package/dist/tools/scout.d.ts.map +1 -0
  56. package/dist/tools/scout.js +199 -0
  57. package/dist/tools/scout.js.map +1 -0
  58. package/dist/tools/session.d.ts +7 -0
  59. package/dist/tools/session.d.ts.map +1 -0
  60. package/dist/tools/session.js +296 -0
  61. package/dist/tools/session.js.map +1 -0
  62. package/dist/types.d.ts +116 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +13 -0
  65. package/dist/types.js.map +1 -0
  66. package/package.json +63 -0
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Direct Client — programmatic adapter for the BlockSpool canonical loop.
3
+ *
4
+ * Proves the MCP tools work without Claude Code or stdio transport.
5
+ * Any LLM (or test harness) can drive the loop:
6
+ *
7
+ * const client = await DirectClient.create({ projectPath });
8
+ * const session = client.startSession({ scope: 'src/**' });
9
+ * while (true) {
10
+ * const resp = await client.advance();
11
+ * if (resp.next_action === 'STOP') break;
12
+ * // ... call LLM with resp.prompt ...
13
+ * await client.ingestEvent(type, payload);
14
+ * }
15
+ * client.endSession();
16
+ * await client.close();
17
+ */
18
+ import * as path from 'node:path';
19
+ import * as fs from 'node:fs';
20
+ import { createSQLiteAdapter } from '@blockspool/sqlite';
21
+ import { repos } from '@blockspool/core';
22
+ import { RunManager } from './run-manager.js';
23
+ import { advance } from './advance.js';
24
+ import { processEvent } from './event-processor.js';
25
+ export class DirectClient {
26
+ run;
27
+ db;
28
+ project;
29
+ ownsDb;
30
+ constructor(run, db, project, ownsDb) {
31
+ this.run = run;
32
+ this.db = db;
33
+ this.project = project;
34
+ this.ownsDb = ownsDb;
35
+ }
36
+ /**
37
+ * Create a DirectClient. Initializes SQLite and ensures the project exists.
38
+ */
39
+ static async create(options) {
40
+ const { projectPath, projectName } = options;
41
+ let db = options.db;
42
+ let ownsDb = false;
43
+ if (!db) {
44
+ const bsDir = path.join(projectPath, '.blockspool');
45
+ if (!fs.existsSync(bsDir)) {
46
+ fs.mkdirSync(bsDir, { recursive: true });
47
+ }
48
+ db = await createSQLiteAdapter({ url: path.join(bsDir, 'state.sqlite') });
49
+ ownsDb = true;
50
+ }
51
+ const project = await repos.projects.ensureForRepo(db, {
52
+ name: projectName ?? path.basename(projectPath),
53
+ rootPath: projectPath,
54
+ });
55
+ const run = new RunManager(projectPath);
56
+ return new DirectClient(run, db, project, ownsDb);
57
+ }
58
+ /** Start a new session. Returns the initial RunState. */
59
+ startSession(config = {}) {
60
+ return this.run.create(this.project.id, config);
61
+ }
62
+ /** Get the next action. This is the core loop driver. */
63
+ async advance() {
64
+ return advance({ run: this.run, db: this.db, project: this.project });
65
+ }
66
+ /** Report an event back. Triggers state transitions. */
67
+ async ingestEvent(type, payload) {
68
+ // Log the raw event (matches what blockspool_ingest_event tool does)
69
+ this.run.appendEvent(type, payload);
70
+ const result = await processEvent(this.run, this.db, type, payload);
71
+ return { processed: result.processed, message: result.message };
72
+ }
73
+ /** Get current run state. */
74
+ getState() {
75
+ return this.run.require();
76
+ }
77
+ /** End the session. */
78
+ endSession() {
79
+ return this.run.end();
80
+ }
81
+ /** Clean up resources. */
82
+ async close() {
83
+ if (this.ownsDb) {
84
+ await this.db.close();
85
+ }
86
+ }
87
+ /** Expose internals for testing */
88
+ get _run() { return this.run; }
89
+ get _db() { return this.db; }
90
+ get _project() { return this.project; }
91
+ }
92
+ //# sourceMappingURL=direct-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direct-client.js","sourceRoot":"","sources":["../src/direct-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Event Processor — handles ingested events and triggers state transitions.
3
+ *
4
+ * When the client calls `blockspool_ingest_event`, this module processes
5
+ * the event and updates RunState accordingly (phase transitions, counters, etc).
6
+ */
7
+ import type { DatabaseAdapter } from '@blockspool/core';
8
+ import { RunManager } from './run-manager.js';
9
+ import type { EventType } from './types.js';
10
+ export interface ProcessResult {
11
+ processed: boolean;
12
+ phase_changed: boolean;
13
+ new_phase?: string;
14
+ message: string;
15
+ }
16
+ export declare function processEvent(run: RunManager, db: DatabaseAdapter, type: EventType, payload: Record<string, unknown>): Promise<ProcessResult>;
17
+ //# sourceMappingURL=event-processor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-processor.d.ts","sourceRoot":"","sources":["../src/event-processor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AAMxD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,UAAU,EACf,EAAE,EAAE,eAAe,EACnB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,aAAa,CAAC,CAiaxB"}
@@ -0,0 +1,360 @@
1
+ /**
2
+ * Event Processor — handles ingested events and triggers state transitions.
3
+ *
4
+ * When the client calls `blockspool_ingest_event`, this module processes
5
+ * the event and updates RunState accordingly (phase transitions, counters, etc).
6
+ */
7
+ import { repos } from '@blockspool/core';
8
+ import { filterAndCreateTickets } from './proposals.js';
9
+ import { deriveScopePolicy, validatePlanScope } from './scope-policy.js';
10
+ import { recordDiff, recordCommandFailure, recordPlanHash } from './spindle.js';
11
+ export async function processEvent(run, db, type, payload) {
12
+ const s = run.require();
13
+ switch (type) {
14
+ // -----------------------------------------------------------------
15
+ // Scout events
16
+ // -----------------------------------------------------------------
17
+ case 'SCOUT_OUTPUT': {
18
+ if (s.phase !== 'SCOUT') {
19
+ return { processed: true, phase_changed: false, message: 'Scout output outside SCOUT phase, ignored' };
20
+ }
21
+ // Extract proposals from payload
22
+ const rawProposals = (payload['proposals'] ?? []);
23
+ if (rawProposals.length === 0) {
24
+ // No proposals found — done
25
+ run.setPhase('DONE');
26
+ return {
27
+ processed: true,
28
+ phase_changed: true,
29
+ new_phase: 'DONE',
30
+ message: 'No proposals in scout output, transitioning to DONE',
31
+ };
32
+ }
33
+ // Filter, dedup, score, create tickets
34
+ const result = await filterAndCreateTickets(run, db, rawProposals);
35
+ // Save proposals artifact
36
+ run.saveArtifact(`${s.step_count}-scout-proposals.json`, JSON.stringify({ raw: rawProposals, result }, null, 2));
37
+ if (result.created_ticket_ids.length > 0) {
38
+ run.setPhase('NEXT_TICKET');
39
+ return {
40
+ processed: true,
41
+ phase_changed: true,
42
+ new_phase: 'NEXT_TICKET',
43
+ message: `Created ${result.created_ticket_ids.length} tickets from ${rawProposals.length} proposals (${result.rejected.length} rejected)`,
44
+ };
45
+ }
46
+ // All proposals rejected — done
47
+ run.setPhase('DONE');
48
+ return {
49
+ processed: true,
50
+ phase_changed: true,
51
+ new_phase: 'DONE',
52
+ message: `All ${rawProposals.length} proposals rejected: ${result.rejected.map(r => r.reason).join('; ')}`,
53
+ };
54
+ }
55
+ case 'PROPOSALS_FILTERED': {
56
+ // Legacy event from blockspool_submit_proposals tool.
57
+ // Check if we have ready tickets now.
58
+ const readyCount = await repos.tickets.countByStatus(db, s.project_id);
59
+ const ready = readyCount['ready'] ?? 0;
60
+ if (ready > 0 && s.phase === 'SCOUT') {
61
+ run.setPhase('NEXT_TICKET');
62
+ return {
63
+ processed: true,
64
+ phase_changed: true,
65
+ new_phase: 'NEXT_TICKET',
66
+ message: `${ready} tickets ready, transitioning to NEXT_TICKET`,
67
+ };
68
+ }
69
+ if (ready === 0 && s.phase === 'SCOUT') {
70
+ run.setPhase('DONE');
71
+ return {
72
+ processed: true,
73
+ phase_changed: true,
74
+ new_phase: 'DONE',
75
+ message: 'No proposals accepted, transitioning to DONE',
76
+ };
77
+ }
78
+ return { processed: true, phase_changed: false, message: 'Proposals filtered' };
79
+ }
80
+ // -----------------------------------------------------------------
81
+ // Plan events
82
+ // -----------------------------------------------------------------
83
+ case 'PLAN_SUBMITTED': {
84
+ if (s.phase !== 'PLAN') {
85
+ return { processed: true, phase_changed: false, message: 'Plan submitted outside PLAN phase, ignored' };
86
+ }
87
+ const raw = payload;
88
+ // Coerce files_to_touch if Claude sends plain strings instead of objects
89
+ const rawFiles = Array.isArray(raw.files_to_touch) ? raw.files_to_touch : [];
90
+ const files_to_touch = rawFiles.map((f) => {
91
+ if (typeof f === 'string')
92
+ return { path: f, action: 'modify', reason: '' };
93
+ if (f && typeof f === 'object' && 'path' in f)
94
+ return f;
95
+ return { path: String(f), action: 'modify', reason: '' };
96
+ });
97
+ const plan = {
98
+ ticket_id: String(raw.ticket_id ?? s.current_ticket_id ?? ''),
99
+ files_to_touch,
100
+ expected_tests: Array.isArray(raw.expected_tests) ? raw.expected_tests.map(String) : [],
101
+ risk_level: (raw.risk_level === 'low' || raw.risk_level === 'medium' || raw.risk_level === 'high')
102
+ ? raw.risk_level : 'low',
103
+ estimated_lines: typeof raw.estimated_lines === 'number' ? raw.estimated_lines : 50,
104
+ };
105
+ // Derive scope policy for the current ticket
106
+ const ticket = s.current_ticket_id
107
+ ? await repos.tickets.getById(db, s.current_ticket_id)
108
+ : null;
109
+ const policy = deriveScopePolicy({
110
+ allowedPaths: ticket?.allowedPaths ?? [],
111
+ category: ticket?.category ?? 'refactor',
112
+ maxLinesPerTicket: s.max_lines_per_ticket,
113
+ });
114
+ // Validate plan against scope policy
115
+ const scopeResult = validatePlanScope(plan.files_to_touch, plan.estimated_lines, plan.risk_level, policy);
116
+ if (!scopeResult.valid) {
117
+ s.plan_rejections++;
118
+ run.appendEvent('PLAN_REJECTED', { reason: scopeResult.reason, attempt: s.plan_rejections });
119
+ return {
120
+ processed: true,
121
+ phase_changed: false,
122
+ message: `Plan rejected: ${scopeResult.reason} (attempt ${s.plan_rejections}/${3})`,
123
+ };
124
+ }
125
+ // Plan passed validation
126
+ s.current_ticket_plan = plan;
127
+ recordPlanHash(s.spindle, plan);
128
+ // High-risk plans → BLOCKED_NEEDS_HUMAN
129
+ if (plan.risk_level === 'high') {
130
+ run.appendEvent('PLAN_REJECTED', { reason: 'High-risk plan requires human approval', risk_level: 'high' });
131
+ run.setPhase('BLOCKED_NEEDS_HUMAN');
132
+ return {
133
+ processed: true,
134
+ phase_changed: true,
135
+ new_phase: 'BLOCKED_NEEDS_HUMAN',
136
+ message: 'High-risk plan requires human approval',
137
+ };
138
+ }
139
+ // Low/medium risk — auto-approve
140
+ s.plan_approved = true;
141
+ run.appendEvent('PLAN_APPROVED', { risk_level: plan.risk_level, auto: true });
142
+ run.setPhase('EXECUTE');
143
+ return {
144
+ processed: true,
145
+ phase_changed: true,
146
+ new_phase: 'EXECUTE',
147
+ message: `${plan.risk_level}-risk plan auto-approved, moving to EXECUTE`,
148
+ };
149
+ }
150
+ // -----------------------------------------------------------------
151
+ // Execution events
152
+ // -----------------------------------------------------------------
153
+ case 'TICKET_RESULT': {
154
+ if (s.phase !== 'EXECUTE') {
155
+ return { processed: true, phase_changed: false, message: 'Ticket result outside EXECUTE phase' };
156
+ }
157
+ const status = payload['status'];
158
+ if (status === 'done') {
159
+ // Validate changed_files against plan (if plan exists)
160
+ const changedFiles = (payload['changed_files'] ?? []);
161
+ const linesAdded = (payload['lines_added'] ?? 0);
162
+ const linesRemoved = (payload['lines_removed'] ?? 0);
163
+ const totalLines = linesAdded + linesRemoved;
164
+ // Save ticket result artifact
165
+ run.saveArtifact(`${s.step_count}-ticket-result.json`, JSON.stringify({
166
+ status,
167
+ changed_files: changedFiles,
168
+ lines_added: linesAdded,
169
+ lines_removed: linesRemoved,
170
+ summary: payload['summary'],
171
+ }, null, 2));
172
+ // Validate changed files against approved plan
173
+ if (s.current_ticket_plan) {
174
+ const plannedPaths = new Set(s.current_ticket_plan.files_to_touch.map(f => f.path));
175
+ const surpriseFiles = changedFiles.filter(f => !plannedPaths.has(f));
176
+ if (surpriseFiles.length > 0) {
177
+ run.appendEvent('SCOPE_BLOCKED', {
178
+ ticket_id: s.current_ticket_id,
179
+ surprise_files: surpriseFiles,
180
+ planned_files: [...plannedPaths],
181
+ });
182
+ return {
183
+ processed: true,
184
+ phase_changed: false,
185
+ message: `Changed files not in plan: ${surpriseFiles.join(', ')}. Revert those changes and re-submit.`,
186
+ };
187
+ }
188
+ // Validate lines against budget
189
+ if (totalLines > s.max_lines_per_ticket) {
190
+ return {
191
+ processed: true,
192
+ phase_changed: false,
193
+ message: `Lines changed (${totalLines}) exceeds budget (${s.max_lines_per_ticket}). Reduce changes.`,
194
+ };
195
+ }
196
+ }
197
+ // Track lines
198
+ s.total_lines_changed += totalLines;
199
+ // Update spindle state with diff info
200
+ const diff = (payload['diff'] ?? null);
201
+ recordDiff(s.spindle, diff ?? (changedFiles.length > 0 ? changedFiles.join('\n') : null));
202
+ // Move to QA
203
+ run.setPhase('QA');
204
+ return {
205
+ processed: true,
206
+ phase_changed: true,
207
+ new_phase: 'QA',
208
+ message: `Ticket result accepted (${changedFiles.length} files, ${totalLines} lines), moving to QA`,
209
+ };
210
+ }
211
+ if (status === 'failed') {
212
+ // Fail the ticket, move to next
213
+ if (s.current_ticket_id) {
214
+ await repos.tickets.updateStatus(db, s.current_ticket_id, 'blocked');
215
+ run.failTicket(payload['reason'] ?? 'Execution failed');
216
+ }
217
+ run.setPhase('NEXT_TICKET');
218
+ return {
219
+ processed: true,
220
+ phase_changed: true,
221
+ new_phase: 'NEXT_TICKET',
222
+ message: 'Ticket failed, moving to NEXT_TICKET',
223
+ };
224
+ }
225
+ return { processed: true, phase_changed: false, message: `Ticket result: ${status}` };
226
+ }
227
+ // -----------------------------------------------------------------
228
+ // QA events
229
+ // -----------------------------------------------------------------
230
+ case 'QA_COMMAND_RESULT': {
231
+ if (s.phase !== 'QA') {
232
+ return { processed: true, phase_changed: false, message: 'QA command result outside QA phase' };
233
+ }
234
+ const command = payload['command'];
235
+ const success = payload['success'];
236
+ const output = (payload['output'] ?? '');
237
+ // Record command failure in spindle state
238
+ if (!success) {
239
+ recordCommandFailure(s.spindle, command, output);
240
+ }
241
+ // Save command output as artifact
242
+ const cmdSlug = command.replace(/[^a-z0-9]/gi, '-').slice(0, 30);
243
+ run.saveArtifact(`${s.step_count}-qa-${cmdSlug}-${success ? 'pass' : 'fail'}.log`, `$ ${command}\n\n${output}`);
244
+ return {
245
+ processed: true,
246
+ phase_changed: false,
247
+ message: `QA command ${success ? 'passed' : 'failed'}: ${command}`,
248
+ };
249
+ }
250
+ case 'QA_PASSED': {
251
+ if (s.phase !== 'QA') {
252
+ return { processed: true, phase_changed: false, message: 'QA passed outside QA phase' };
253
+ }
254
+ // Mark ticket done in DB
255
+ if (s.current_ticket_id) {
256
+ await repos.tickets.updateStatus(db, s.current_ticket_id, 'done');
257
+ }
258
+ // Save QA summary artifact
259
+ run.saveArtifact(`${s.step_count}-qa-summary.json`, JSON.stringify({
260
+ ticket_id: s.current_ticket_id,
261
+ status: 'passed',
262
+ attempt: s.qa_retries + 1,
263
+ ...payload,
264
+ }, null, 2));
265
+ // Move to PR
266
+ run.setPhase('PR');
267
+ return {
268
+ processed: true,
269
+ phase_changed: true,
270
+ new_phase: 'PR',
271
+ message: 'QA passed, moving to PR',
272
+ };
273
+ }
274
+ case 'QA_FAILED': {
275
+ if (s.phase !== 'QA') {
276
+ return { processed: true, phase_changed: false, message: 'QA failed outside QA phase' };
277
+ }
278
+ // Record QA failure in spindle (for stall detection — no progress)
279
+ recordDiff(s.spindle, null);
280
+ // Save failure artifact
281
+ run.saveArtifact(`${s.step_count}-qa-failed-attempt-${s.qa_retries + 1}.json`, JSON.stringify({
282
+ ticket_id: s.current_ticket_id,
283
+ attempt: s.qa_retries + 1,
284
+ ...payload,
285
+ }, null, 2));
286
+ s.qa_retries++;
287
+ if (s.qa_retries >= 3) {
288
+ // Give up on this ticket
289
+ if (s.current_ticket_id) {
290
+ await repos.tickets.updateStatus(db, s.current_ticket_id, 'blocked');
291
+ run.failTicket(`QA failed ${s.qa_retries} times`);
292
+ }
293
+ run.setPhase('NEXT_TICKET');
294
+ return {
295
+ processed: true,
296
+ phase_changed: true,
297
+ new_phase: 'NEXT_TICKET',
298
+ message: `QA failed ${s.qa_retries} times, giving up on ticket`,
299
+ };
300
+ }
301
+ // Retry: go back to EXECUTE to fix
302
+ run.setPhase('EXECUTE');
303
+ return {
304
+ processed: true,
305
+ phase_changed: true,
306
+ new_phase: 'EXECUTE',
307
+ message: `QA failed (attempt ${s.qa_retries}/3), retrying execution`,
308
+ };
309
+ }
310
+ // -----------------------------------------------------------------
311
+ // PR events
312
+ // -----------------------------------------------------------------
313
+ case 'PR_CREATED': {
314
+ if (s.phase !== 'PR') {
315
+ return { processed: true, phase_changed: false, message: 'PR created outside PR phase' };
316
+ }
317
+ // Save PR artifact
318
+ run.saveArtifact(`${s.step_count}-pr-created.json`, JSON.stringify({
319
+ ticket_id: s.current_ticket_id,
320
+ pr_number: s.prs_created + 1,
321
+ ...payload,
322
+ }, null, 2));
323
+ s.prs_created++;
324
+ run.completeTicket();
325
+ run.appendEvent('PR_CREATED', payload);
326
+ run.setPhase('NEXT_TICKET');
327
+ return {
328
+ processed: true,
329
+ phase_changed: true,
330
+ new_phase: 'NEXT_TICKET',
331
+ message: `PR created (${s.prs_created}/${s.max_prs}), moving to NEXT_TICKET`,
332
+ };
333
+ }
334
+ // -----------------------------------------------------------------
335
+ // User overrides
336
+ // -----------------------------------------------------------------
337
+ case 'USER_OVERRIDE': {
338
+ if (typeof payload['hint'] === 'string') {
339
+ run.addHint(payload['hint']);
340
+ return { processed: true, phase_changed: false, message: 'Hint added' };
341
+ }
342
+ if (payload['cancel'] === true) {
343
+ run.setPhase('DONE');
344
+ return {
345
+ processed: true,
346
+ phase_changed: true,
347
+ new_phase: 'DONE',
348
+ message: 'Session cancelled by user',
349
+ };
350
+ }
351
+ return { processed: true, phase_changed: false, message: 'User override recorded' };
352
+ }
353
+ // -----------------------------------------------------------------
354
+ // Default: just record
355
+ // -----------------------------------------------------------------
356
+ default:
357
+ return { processed: true, phase_changed: false, message: `Event ${type} recorded` };
358
+ }
359
+ }
360
+ //# sourceMappingURL=event-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-processor.js","sourceRoot":"","sources":["../src/event-processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Formulas — typed, versioned, testable sweep recipes.
3
+ *
4
+ * A formula configures what a scout session looks for and how strictly
5
+ * it gates execution. Built-in formulas mirror the CLI's set; users can
6
+ * add custom formulas in `.blockspool/formulas/` as simple YAML files.
7
+ */
8
+ import type { SessionConfig } from './types.js';
9
+ export interface Formula {
10
+ name: string;
11
+ version: number;
12
+ description: string;
13
+ scope?: string;
14
+ categories?: string[];
15
+ min_confidence?: number;
16
+ prompt?: string;
17
+ max_prs?: number;
18
+ model?: string;
19
+ risk_tolerance?: 'low' | 'medium' | 'high';
20
+ tags?: string[];
21
+ }
22
+ export declare const BUILTIN_FORMULAS: Formula[];
23
+ /**
24
+ * Load a formula by name.
25
+ * Search order: user formulas in `.blockspool/formulas/`, then built-ins.
26
+ */
27
+ export declare function loadFormula(name: string, projectPath?: string): Formula | null;
28
+ /**
29
+ * List all available formulas (user + built-in, user overrides built-in).
30
+ */
31
+ export declare function listFormulas(projectPath?: string): Formula[];
32
+ /**
33
+ * Apply a formula's settings to a SessionConfig.
34
+ * Formula values are defaults — explicit config values take precedence.
35
+ */
36
+ export declare function applyFormula(formula: Formula, config: SessionConfig): SessionConfig;
37
+ //# sourceMappingURL=formulas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formulas.d.ts","sourceRoot":"","sources":["../src/formulas.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAMhD,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC3C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAMD,eAAO,MAAM,gBAAgB,EAAE,OAAO,EA8FrC,CAAC;AAMF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAI9E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAK5D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,aAAa,CAUnF"}