@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 @@
1
+ {"version":3,"file":"run-manager.d.ts","sourceRoot":"","sources":["../src/run-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EACV,QAAQ,EAER,SAAS,EACT,KAAK,EAEL,aAAa,EACd,MAAM,YAAY,CAAC;AAiCpB,qBAAa,UAAU;IAKT,OAAO,CAAC,QAAQ,CAAC,WAAW;IAJxC,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAAuB;gBAEZ,WAAW,EAAE,MAAM;IAEhD,qCAAqC;IACrC,OAAO,KAAK,KAAK,GAEhB;IAED,yEAAyE;IACzE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,QAAQ;IA8E1D,iCAAiC;IACjC,OAAO,IAAI,QAAQ;IAOnB,sCAAsC;IACtC,IAAI,OAAO,IAAI,QAAQ,GAAG,IAAI,CAE7B;IAED,6BAA6B;IAC7B,IAAI,GAAG,IAAI,MAAM,GAAG,IAAI,CAEvB;IAMD,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAY5B,sDAAsD;IACtD,aAAa,IAAI,IAAI;IAQrB,sCAAsC;IACtC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAYpC,oCAAoC;IACpC,cAAc,IAAI,IAAI;IAYtB,iCAAiC;IACjC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAYhC,iBAAiB;IACjB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAO3B,2CAA2C;IAC3C,YAAY,IAAI,MAAM,EAAE;IAWxB,kBAAkB;IAClB,GAAG,IAAI,QAAQ;IAmBf,uCAAuC;IACvC,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAcpE,4BAA4B;IAC5B,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAMrD,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAU7D,uCAAuC;IACvC,iBAAiB,IAAI;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAkB3D,+CAA+C;IAC/C,iBAAiB,IAAI,MAAM,EAAE;IAe7B,6CAA6C;IAC7C,WAAW,IAAI;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,iBAAiB,EAAE,MAAM,CAAC;QAC1B,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC;QACzB,uBAAuB,EAAE,MAAM,CAAC;QAChC,YAAY,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QACjD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC;IAsBD,OAAO,CAAC,YAAY;CAKrB"}
@@ -0,0 +1,315 @@
1
+ /**
2
+ * Run Manager — creates and manages run folders on disk.
3
+ *
4
+ * Each run lives at `.blockspool/runs/<run_id>/` and contains:
5
+ * - state.json — current RunState (overwritten on every change)
6
+ * - events.ndjson — append-only event log
7
+ * - diffs/ — patch files per step
8
+ * - artifacts/ — QA logs, scout proposals, etc.
9
+ */
10
+ import * as fs from 'node:fs';
11
+ import * as path from 'node:path';
12
+ import { prefixedId } from '@blockspool/core';
13
+ import { checkSpindle } from './spindle.js';
14
+ // ---------------------------------------------------------------------------
15
+ // Defaults
16
+ // ---------------------------------------------------------------------------
17
+ const DEFAULT_STEP_BUDGET = 200;
18
+ const DEFAULT_TICKET_STEP_BUDGET = 12;
19
+ const DEFAULT_MAX_LINES_PER_TICKET = 500;
20
+ const DEFAULT_MAX_TOOL_CALLS_PER_TICKET = 50;
21
+ const DEFAULT_MAX_PRS = 5;
22
+ const DEFAULT_MIN_CONFIDENCE = 70;
23
+ const DEFAULT_MAX_PROPOSALS_PER_SCOUT = 5;
24
+ const DEFAULT_SCOPE = 'src/**';
25
+ const DEFAULT_CATEGORIES = ['refactor', 'docs', 'test', 'perf', 'security'];
26
+ function emptySpindle() {
27
+ return {
28
+ output_hashes: [],
29
+ diff_hashes: [],
30
+ iterations_since_change: 0,
31
+ total_output_chars: 0,
32
+ total_change_chars: 0,
33
+ failing_command_signatures: [],
34
+ plan_hashes: [],
35
+ };
36
+ }
37
+ // ---------------------------------------------------------------------------
38
+ // RunManager
39
+ // ---------------------------------------------------------------------------
40
+ export class RunManager {
41
+ projectPath;
42
+ state = null;
43
+ runDir = null;
44
+ eventsPath = null;
45
+ constructor(projectPath) {
46
+ this.projectPath = projectPath;
47
+ }
48
+ /** The base .blockspool directory */
49
+ get bsDir() {
50
+ return path.join(this.projectPath, '.blockspool');
51
+ }
52
+ /** Create a new run, write initial state.json and SESSION_START event */
53
+ create(projectId, config) {
54
+ if (this.state) {
55
+ throw new Error('Run already active. End it first.');
56
+ }
57
+ const runId = prefixedId('run');
58
+ const sessionId = prefixedId('ses');
59
+ const now = new Date();
60
+ const expiresAt = config.hours
61
+ ? new Date(now.getTime() + config.hours * 60 * 60 * 1000).toISOString()
62
+ : null;
63
+ this.state = {
64
+ run_id: runId,
65
+ session_id: sessionId,
66
+ project_id: projectId,
67
+ phase: 'SCOUT',
68
+ phase_entry_step: 0,
69
+ step_count: 0,
70
+ step_budget: config.step_budget ?? DEFAULT_STEP_BUDGET,
71
+ ticket_step_count: 0,
72
+ ticket_step_budget: config.ticket_step_budget ?? DEFAULT_TICKET_STEP_BUDGET,
73
+ total_lines_changed: 0,
74
+ max_lines_per_ticket: DEFAULT_MAX_LINES_PER_TICKET,
75
+ total_tool_calls: 0,
76
+ max_tool_calls_per_ticket: DEFAULT_MAX_TOOL_CALLS_PER_TICKET,
77
+ tickets_completed: 0,
78
+ tickets_failed: 0,
79
+ tickets_blocked: 0,
80
+ prs_created: 0,
81
+ scout_cycles: 0,
82
+ max_cycles: config.max_cycles ?? 1,
83
+ max_prs: config.max_prs ?? DEFAULT_MAX_PRS,
84
+ current_ticket_id: null,
85
+ current_ticket_plan: null,
86
+ plan_approved: false,
87
+ plan_rejections: 0,
88
+ qa_retries: 0,
89
+ started_at: now.toISOString(),
90
+ expires_at: expiresAt,
91
+ scope: config.scope ?? DEFAULT_SCOPE,
92
+ formula: config.formula ?? null,
93
+ categories: config.categories ?? DEFAULT_CATEGORIES,
94
+ min_confidence: config.min_confidence ?? DEFAULT_MIN_CONFIDENCE,
95
+ max_proposals_per_scout: config.max_proposals ?? DEFAULT_MAX_PROPOSALS_PER_SCOUT,
96
+ draft_prs: config.draft_prs ?? true,
97
+ hints: [],
98
+ spindle: emptySpindle(),
99
+ recent_intent_hashes: [],
100
+ };
101
+ // Create run folder
102
+ const runsDir = path.join(this.bsDir, 'runs');
103
+ this.runDir = path.join(runsDir, runId);
104
+ this.eventsPath = path.join(this.runDir, 'events.ndjson');
105
+ fs.mkdirSync(path.join(this.runDir, 'diffs'), { recursive: true });
106
+ fs.mkdirSync(path.join(this.runDir, 'artifacts'), { recursive: true });
107
+ // Write initial state
108
+ this.persistState();
109
+ // Log session start
110
+ this.appendEvent('SESSION_START', {
111
+ config,
112
+ project_id: projectId,
113
+ });
114
+ return this.state;
115
+ }
116
+ /** Get current state or throw */
117
+ require() {
118
+ if (!this.state) {
119
+ throw new Error('No active run. Call blockspool_start_session first.');
120
+ }
121
+ return this.state;
122
+ }
123
+ /** Get current state (may be null) */
124
+ get current() {
125
+ return this.state;
126
+ }
127
+ /** Get run directory path */
128
+ get dir() {
129
+ return this.runDir;
130
+ }
131
+ // -----------------------------------------------------------------------
132
+ // State mutations (all persist + log)
133
+ // -----------------------------------------------------------------------
134
+ /** Transition to a new phase */
135
+ setPhase(phase) {
136
+ const s = this.require();
137
+ const oldPhase = s.phase;
138
+ s.phase = phase;
139
+ s.phase_entry_step = s.step_count;
140
+ this.persistState();
141
+ this.appendEvent('ADVANCE_RETURNED', {
142
+ old_phase: oldPhase,
143
+ new_phase: phase,
144
+ });
145
+ }
146
+ /** Increment step counter (called on each advance) */
147
+ incrementStep() {
148
+ const s = this.require();
149
+ s.step_count++;
150
+ s.ticket_step_count++;
151
+ s.total_tool_calls++;
152
+ this.persistState();
153
+ }
154
+ /** Assign a ticket as current work */
155
+ assignTicket(ticketId) {
156
+ const s = this.require();
157
+ s.current_ticket_id = ticketId;
158
+ s.ticket_step_count = 0;
159
+ s.plan_approved = false;
160
+ s.current_ticket_plan = null;
161
+ s.plan_rejections = 0;
162
+ s.qa_retries = 0;
163
+ this.persistState();
164
+ this.appendEvent('TICKET_ASSIGNED', { ticket_id: ticketId });
165
+ }
166
+ /** Mark current ticket completed */
167
+ completeTicket() {
168
+ const s = this.require();
169
+ s.tickets_completed++;
170
+ const ticketId = s.current_ticket_id;
171
+ s.current_ticket_id = null;
172
+ s.current_ticket_plan = null;
173
+ s.plan_approved = false;
174
+ s.ticket_step_count = 0;
175
+ this.persistState();
176
+ this.appendEvent('TICKET_COMPLETED', { ticket_id: ticketId });
177
+ }
178
+ /** Mark current ticket failed */
179
+ failTicket(reason) {
180
+ const s = this.require();
181
+ s.tickets_failed++;
182
+ const ticketId = s.current_ticket_id;
183
+ s.current_ticket_id = null;
184
+ s.current_ticket_plan = null;
185
+ s.plan_approved = false;
186
+ s.ticket_step_count = 0;
187
+ this.persistState();
188
+ this.appendEvent('TICKET_FAILED', { ticket_id: ticketId, reason });
189
+ }
190
+ /** Add a hint */
191
+ addHint(hint) {
192
+ const s = this.require();
193
+ s.hints.push(hint);
194
+ this.persistState();
195
+ this.appendEvent('USER_OVERRIDE', { hint });
196
+ }
197
+ /** Consume and return all pending hints */
198
+ consumeHints() {
199
+ const s = this.require();
200
+ const hints = [...s.hints];
201
+ s.hints = [];
202
+ this.persistState();
203
+ for (const h of hints) {
204
+ this.appendEvent('HINT_CONSUMED', { hint: h });
205
+ }
206
+ return hints;
207
+ }
208
+ /** End the run */
209
+ end() {
210
+ const s = this.require();
211
+ this.appendEvent('SESSION_END', {
212
+ tickets_completed: s.tickets_completed,
213
+ tickets_failed: s.tickets_failed,
214
+ prs_created: s.prs_created,
215
+ step_count: s.step_count,
216
+ });
217
+ const finalState = { ...s };
218
+ this.state = null;
219
+ this.runDir = null;
220
+ this.eventsPath = null;
221
+ return finalState;
222
+ }
223
+ // -----------------------------------------------------------------------
224
+ // Event logging
225
+ // -----------------------------------------------------------------------
226
+ /** Append an event to events.ndjson */
227
+ appendEvent(type, payload) {
228
+ const step = this.state?.step_count ?? 0;
229
+ const event = {
230
+ ts: new Date().toISOString(),
231
+ step,
232
+ type,
233
+ payload,
234
+ };
235
+ if (this.eventsPath) {
236
+ fs.appendFileSync(this.eventsPath, JSON.stringify(event) + '\n', 'utf8');
237
+ }
238
+ }
239
+ /** Save an artifact file */
240
+ saveArtifact(filename, content) {
241
+ if (!this.runDir)
242
+ return;
243
+ const artifactPath = path.join(this.runDir, 'artifacts', filename);
244
+ fs.writeFileSync(artifactPath, content, 'utf8');
245
+ }
246
+ /** Save a diff/patch file */
247
+ saveDiff(step, ticketId, patch) {
248
+ if (!this.runDir)
249
+ return;
250
+ const diffPath = path.join(this.runDir, 'diffs', `${step}-${ticketId}.patch`);
251
+ fs.writeFileSync(diffPath, patch, 'utf8');
252
+ }
253
+ // -----------------------------------------------------------------------
254
+ // Helpers
255
+ // -----------------------------------------------------------------------
256
+ /** Check if any budget is exhausted */
257
+ isBudgetExhausted() {
258
+ const s = this.require();
259
+ if (s.step_count >= s.step_budget) {
260
+ return { exhausted: true, which: 'step_budget' };
261
+ }
262
+ if (s.ticket_step_count >= s.ticket_step_budget) {
263
+ return { exhausted: true, which: 'ticket_step_budget' };
264
+ }
265
+ if (s.prs_created >= s.max_prs) {
266
+ return { exhausted: true, which: 'max_prs' };
267
+ }
268
+ if (s.expires_at && new Date() > new Date(s.expires_at)) {
269
+ return { exhausted: true, which: 'time_budget' };
270
+ }
271
+ return { exhausted: false };
272
+ }
273
+ /** Check if approaching budget limits (80%) */
274
+ getBudgetWarnings() {
275
+ const s = this.require();
276
+ const warnings = [];
277
+ if (s.step_count >= s.step_budget * 0.8) {
278
+ warnings.push(`step_budget: ${s.step_count}/${s.step_budget}`);
279
+ }
280
+ if (s.ticket_step_count >= s.ticket_step_budget * 0.8) {
281
+ warnings.push(`ticket_step_budget: ${s.ticket_step_count}/${s.ticket_step_budget}`);
282
+ }
283
+ if (s.prs_created >= s.max_prs * 0.8) {
284
+ warnings.push(`max_prs: ${s.prs_created}/${s.max_prs}`);
285
+ }
286
+ return warnings;
287
+ }
288
+ /** Build the digest for advance responses */
289
+ buildDigest() {
290
+ const s = this.require();
291
+ const timeRemainingMs = s.expires_at
292
+ ? Math.max(0, new Date(s.expires_at).getTime() - Date.now())
293
+ : null;
294
+ return {
295
+ step: s.step_count,
296
+ phase: s.phase,
297
+ tickets_completed: s.tickets_completed,
298
+ tickets_failed: s.tickets_failed,
299
+ budget_remaining: s.step_budget - s.step_count,
300
+ ticket_budget_remaining: s.ticket_step_budget - s.ticket_step_count,
301
+ spindle_risk: checkSpindle(s.spindle).risk,
302
+ time_remaining_ms: timeRemainingMs,
303
+ };
304
+ }
305
+ // -----------------------------------------------------------------------
306
+ // Persistence
307
+ // -----------------------------------------------------------------------
308
+ persistState() {
309
+ if (!this.runDir || !this.state)
310
+ return;
311
+ const statePath = path.join(this.runDir, 'state.json');
312
+ fs.writeFileSync(statePath, JSON.stringify(this.state, null, 2) + '\n', 'utf8');
313
+ }
314
+ }
315
+ //# sourceMappingURL=run-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-manager.js","sourceRoot":"","sources":["../src/run-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Scope Policy — derives and enforces file-level constraints for tickets.
3
+ *
4
+ * Used by the plan validator and PreToolUse hook to ensure
5
+ * agents only touch files they're allowed to.
6
+ */
7
+ export interface ScopePolicy {
8
+ allowed_paths: string[];
9
+ denied_paths: string[];
10
+ denied_patterns: RegExp[];
11
+ max_files: number;
12
+ max_lines: number;
13
+ plan_required: boolean;
14
+ }
15
+ export interface DeriveScopeInput {
16
+ allowedPaths: string[];
17
+ category: string;
18
+ maxLinesPerTicket: number;
19
+ }
20
+ export declare function deriveScopePolicy(input: DeriveScopeInput): ScopePolicy;
21
+ export interface PlanFile {
22
+ path: string;
23
+ action: string;
24
+ reason: string;
25
+ }
26
+ export interface PlanValidationResult {
27
+ valid: boolean;
28
+ reason: string | null;
29
+ }
30
+ export declare function validatePlanScope(files: PlanFile[], estimatedLines: number, riskLevel: string, policy: ScopePolicy): PlanValidationResult;
31
+ export declare function isFileAllowed(filePath: string, policy: ScopePolicy): boolean;
32
+ export declare function containsCredentials(content: string): string | null;
33
+ export declare function serializeScopePolicy(policy: ScopePolicy): Record<string, unknown>;
34
+ //# sourceMappingURL=scope-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-policy.d.ts","sourceRoot":"","sources":["../src/scope-policy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;CACxB;AAuCD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,WAAW,CAStE;AAMD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,QAAQ,EAAE,EACjB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,GAClB,oBAAoB,CA6DtB;AAMD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAuB5E;AAMD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQlE;AAMD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASjF"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Scope Policy — derives and enforces file-level constraints for tickets.
3
+ *
4
+ * Used by the plan validator and PreToolUse hook to ensure
5
+ * agents only touch files they're allowed to.
6
+ */
7
+ import { minimatch } from 'minimatch';
8
+ // ---------------------------------------------------------------------------
9
+ // Always-denied paths (build artifacts, lockfiles, VCS internals)
10
+ // ---------------------------------------------------------------------------
11
+ const ALWAYS_DENIED_PATHS = [
12
+ '.env', '.env.*',
13
+ 'node_modules/**', '.git/**',
14
+ 'dist/**', 'build/**', 'coverage/**',
15
+ '*.lock', 'package-lock.json',
16
+ ];
17
+ // ---------------------------------------------------------------------------
18
+ // Credential / secret deny patterns
19
+ // ---------------------------------------------------------------------------
20
+ const CREDENTIAL_PATTERNS = [
21
+ /AKIA[0-9A-Z]{16}/, // AWS access key
22
+ /-----BEGIN.*PRIVATE KEY-----/, // PEM keys
23
+ /ghp_[a-zA-Z0-9]{36}/, // GitHub PAT
24
+ /sk-[a-zA-Z0-9]{48}/, // OpenAI key
25
+ /password\s*[:=]\s*['"][^'"]+/i, // hardcoded passwords
26
+ ];
27
+ // ---------------------------------------------------------------------------
28
+ // Path deny patterns (file names that shouldn't be touched)
29
+ // ---------------------------------------------------------------------------
30
+ const FILE_DENY_PATTERNS = [
31
+ /\.(env|pem|key)$/,
32
+ /credentials/i,
33
+ /secret/i,
34
+ ];
35
+ export function deriveScopePolicy(input) {
36
+ return {
37
+ allowed_paths: input.allowedPaths,
38
+ denied_paths: ALWAYS_DENIED_PATHS,
39
+ denied_patterns: FILE_DENY_PATTERNS,
40
+ max_files: 10,
41
+ max_lines: input.category === 'test' ? 1000 : input.maxLinesPerTicket,
42
+ plan_required: input.category !== 'docs',
43
+ };
44
+ }
45
+ export function validatePlanScope(files, estimatedLines, riskLevel, policy) {
46
+ // 1. Must have files
47
+ if (!files || files.length === 0) {
48
+ return { valid: false, reason: 'Plan must include at least one file to touch' };
49
+ }
50
+ // 2. Check estimated lines
51
+ if (estimatedLines > policy.max_lines) {
52
+ return {
53
+ valid: false,
54
+ reason: `Estimated lines (${estimatedLines}) exceeds max (${policy.max_lines})`,
55
+ };
56
+ }
57
+ // 3. Check max files
58
+ if (files.length > policy.max_files) {
59
+ return {
60
+ valid: false,
61
+ reason: `Plan touches ${files.length} files, max allowed is ${policy.max_files}`,
62
+ };
63
+ }
64
+ // 4. Valid risk level
65
+ if (!riskLevel || !['low', 'medium', 'high'].includes(riskLevel)) {
66
+ return { valid: false, reason: 'Plan must specify risk_level: low, medium, or high' };
67
+ }
68
+ // 5. Check each file against denied paths
69
+ for (const f of files) {
70
+ for (const deniedGlob of policy.denied_paths) {
71
+ if (minimatch(f.path, deniedGlob, { dot: true })) {
72
+ return { valid: false, reason: `Plan touches denied path: ${f.path} (matches ${deniedGlob})` };
73
+ }
74
+ }
75
+ }
76
+ // 6. Check each file against denied patterns
77
+ for (const f of files) {
78
+ for (const pattern of policy.denied_patterns) {
79
+ if (pattern.test(f.path)) {
80
+ return { valid: false, reason: `Plan touches sensitive file: ${f.path}` };
81
+ }
82
+ }
83
+ }
84
+ // 7. Check each file is within allowed_paths (if any specified)
85
+ if (policy.allowed_paths.length > 0) {
86
+ for (const f of files) {
87
+ const isAllowed = policy.allowed_paths.some(glob => minimatch(f.path, glob, { dot: true }));
88
+ if (!isAllowed) {
89
+ return {
90
+ valid: false,
91
+ reason: `File ${f.path} is outside allowed paths: ${policy.allowed_paths.join(', ')}`,
92
+ };
93
+ }
94
+ }
95
+ }
96
+ return { valid: true, reason: null };
97
+ }
98
+ // ---------------------------------------------------------------------------
99
+ // Check a single file path (used by PreToolUse hook)
100
+ // ---------------------------------------------------------------------------
101
+ export function isFileAllowed(filePath, policy) {
102
+ // Check denied paths
103
+ for (const deniedGlob of policy.denied_paths) {
104
+ if (minimatch(filePath, deniedGlob, { dot: true })) {
105
+ return false;
106
+ }
107
+ }
108
+ // Check denied patterns
109
+ for (const pattern of policy.denied_patterns) {
110
+ if (pattern.test(filePath)) {
111
+ return false;
112
+ }
113
+ }
114
+ // Check allowed paths (empty = everything allowed)
115
+ if (policy.allowed_paths.length > 0) {
116
+ return policy.allowed_paths.some(glob => minimatch(filePath, glob, { dot: true }));
117
+ }
118
+ return true;
119
+ }
120
+ // ---------------------------------------------------------------------------
121
+ // Check file content for credential patterns
122
+ // ---------------------------------------------------------------------------
123
+ export function containsCredentials(content) {
124
+ for (const pattern of CREDENTIAL_PATTERNS) {
125
+ const match = content.match(pattern);
126
+ if (match) {
127
+ return `Content contains potential credential: ${pattern.source}`;
128
+ }
129
+ }
130
+ return null;
131
+ }
132
+ // ---------------------------------------------------------------------------
133
+ // Serialize policy for MCP tool response (RegExp → string)
134
+ // ---------------------------------------------------------------------------
135
+ export function serializeScopePolicy(policy) {
136
+ return {
137
+ allowed_paths: policy.allowed_paths,
138
+ denied_paths: policy.denied_paths,
139
+ denied_patterns: policy.denied_patterns.map(r => r.source),
140
+ max_files: policy.max_files,
141
+ max_lines: policy.max_lines,
142
+ plan_required: policy.plan_required,
143
+ };
144
+ }
145
+ //# sourceMappingURL=scope-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-policy.js","sourceRoot":"","sources":["../src/scope-policy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAetC,8EAA8E;AAC9E,kEAAkE;AAClE,8EAA8E;AAE9E,MAAM,mBAAmB,GAAG;IAC1B,MAAM,EAAE,QAAQ;IAChB,iBAAiB,EAAE,SAAS;IAC5B,SAAS,EAAE,UAAU,EAAE,aAAa;IACpC,QAAQ,EAAE,mBAAmB;CAC9B,CAAC;AAEF,8EAA8E;AAC9E,oCAAoC;AACpC,8EAA8E;AAE9E,MAAM,mBAAmB,GAAa;IACpC,kBAAkB,EAAyB,iBAAiB;IAC5D,8BAA8B,EAAe,WAAW;IACxD,qBAAqB,EAAuB,aAAa;IACzD,oBAAoB,EAAwB,aAAa;IACzD,+BAA+B,EAAa,sBAAsB;CACnE,CAAC;AAEF,8EAA8E;AAC9E,4DAA4D;AAC5D,8EAA8E;AAE9E,MAAM,kBAAkB,GAAa;IACnC,kBAAkB;IAClB,cAAc;IACd,SAAS;CACV,CAAC;AAYF,MAAM,UAAU,iBAAiB,CAAC,KAAuB;IACvD,OAAO;QACL,aAAa,EAAE,KAAK,CAAC,YAAY;QACjC,YAAY,EAAE,mBAAmB;QACjC,eAAe,EAAE,kBAAkB;QACnC,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,iBAAiB;QACrE,aAAa,EAAE,KAAK,CAAC,QAAQ,KAAK,MAAM;KACzC,CAAC;AACJ,CAAC;AAiBD,MAAM,UAAU,iBAAiB,CAC/B,KAAiB,EACjB,cAAsB,EACtB,SAAiB,EACjB,MAAmB;IAEnB,qBAAqB;IACrB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,8CAA8C,EAAE,CAAC;IAClF,CAAC;IAED,2BAA2B;IAC3B,IAAI,cAAc,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;QACtC,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,oBAAoB,cAAc,kBAAkB,MAAM,CAAC,SAAS,GAAG;SAChF,CAAC;IACJ,CAAC;IAED,qBAAqB;IACrB,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;QACpC,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,gBAAgB,KAAK,CAAC,MAAM,0BAA0B,MAAM,CAAC,SAAS,EAAE;SACjF,CAAC;IACJ,CAAC;IAED,sBAAsB;IACtB,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACjE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,oDAAoD,EAAE,CAAC;IACxF,CAAC;IAED,0CAA0C;IAC1C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YAC7C,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;gBACjD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,6BAA6B,CAAC,CAAC,IAAI,aAAa,UAAU,GAAG,EAAE,CAAC;YACjG,CAAC;QACH,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;YAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,gCAAgC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAC5E,CAAC;QACH,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACjD,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CACvC,CAAC;YACF,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO;oBACL,KAAK,EAAE,KAAK;oBACZ,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,8BAA8B,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;iBACtF,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC;AAED,8EAA8E;AAC9E,qDAAqD;AACrD,8EAA8E;AAE9E,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,MAAmB;IACjE,qBAAqB;IACrB,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QAC7C,IAAI,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACnD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,wBAAwB;IACxB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,OAAO,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACtC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CACzC,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8EAA8E;AAC9E,6CAA6C;AAC7C,8EAA8E;AAE9E,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,KAAK,MAAM,OAAO,IAAI,mBAAmB,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,0CAA0C,OAAO,CAAC,MAAM,EAAE,CAAC;QACpE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8EAA8E;AAC9E,2DAA2D;AAC3D,8EAA8E;AAE9E,MAAM,UAAU,oBAAoB,CAAC,MAAmB;IACtD,OAAO;QACL,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,eAAe,EAAE,MAAM,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;QAC1D,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,aAAa,EAAE,MAAM,CAAC,aAAa;KACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * MCP Server setup and tool registration
3
+ */
4
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import type { DatabaseAdapter } from '@blockspool/core';
6
+ import { SessionManager } from './state.js';
7
+ export interface ServerOptions {
8
+ db: DatabaseAdapter;
9
+ projectPath: string;
10
+ projectName?: string;
11
+ }
12
+ export declare function createServer(options: ServerOptions): Promise<{
13
+ server: McpServer;
14
+ state: SessionManager;
15
+ }>;
16
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAM5C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,eAAe,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC;IAClE,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,cAAc,CAAC;CACvB,CAAC,CAwBD"}
package/dist/server.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * MCP Server setup and tool registration
3
+ */
4
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import { repos } from '@blockspool/core';
6
+ import { SessionManager } from './state.js';
7
+ import { registerSessionTools } from './tools/session.js';
8
+ import { registerScoutTools } from './tools/scout.js';
9
+ import { registerExecuteTools } from './tools/execute.js';
10
+ import { registerGitTools } from './tools/git.js';
11
+ export async function createServer(options) {
12
+ const { db, projectPath, projectName } = options;
13
+ // Ensure project exists
14
+ const project = await repos.projects.ensureForRepo(db, {
15
+ name: projectName ?? projectPath.split('/').pop() ?? 'unknown',
16
+ rootPath: projectPath,
17
+ });
18
+ const state = new SessionManager(db, project, projectPath);
19
+ const getState = () => state;
20
+ const server = new McpServer({
21
+ name: 'blockspool',
22
+ version: '0.2.0',
23
+ });
24
+ // Register tool groups
25
+ registerSessionTools(server, getState);
26
+ registerScoutTools(server, getState);
27
+ registerExecuteTools(server, getState);
28
+ registerGitTools(server, getState);
29
+ return { server, state };
30
+ }
31
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAQlD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAsB;IAIvD,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAEjD,wBAAwB;IACxB,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,EAAE;QACrD,IAAI,EAAE,WAAW,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,SAAS;QAC9D,QAAQ,EAAE,WAAW;KACtB,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC;IAE7B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,uBAAuB;IACvB,oBAAoB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvC,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrC,oBAAoB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvC,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEnC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Spindle Loop Detection — MCP server adaptation.
3
+ *
4
+ * Monitors agent execution for unproductive patterns:
5
+ * - Oscillation: diffs flip-flopping (A→B→A)
6
+ * - Repetition: similar outputs repeated
7
+ * - Stalling: no file changes for N iterations
8
+ * - QA ping-pong: alternating test/lint failures
9
+ * - Command signature: same command fails repeatedly
10
+ *
11
+ * Ported from packages/cli/src/lib/spindle.ts, adapted for the
12
+ * event-driven MCP architecture.
13
+ */
14
+ import type { SpindleState } from './types.js';
15
+ export interface SpindleConfig {
16
+ similarityThreshold: number;
17
+ maxSimilarOutputs: number;
18
+ maxStallIterations: number;
19
+ maxCommandFailures: number;
20
+ maxQaPingPong: number;
21
+ maxFileEdits: number;
22
+ }
23
+ export declare const DEFAULT_SPINDLE_CONFIG: SpindleConfig;
24
+ export type SpindleReason = 'oscillation' | 'repetition' | 'stalling' | 'qa_ping_pong' | 'command_failure';
25
+ export interface SpindleCheckResult {
26
+ shouldAbort: boolean;
27
+ shouldBlock: boolean;
28
+ reason?: SpindleReason;
29
+ confidence: number;
30
+ diagnostics: Record<string, unknown>;
31
+ risk: 'none' | 'low' | 'medium' | 'high';
32
+ }
33
+ export declare function checkSpindle(spindle: SpindleState, config?: SpindleConfig): SpindleCheckResult;
34
+ export declare function recordOutput(spindle: SpindleState, output: string): void;
35
+ export declare function recordDiff(spindle: SpindleState, diff: string | null): void;
36
+ export declare function recordCommandFailure(spindle: SpindleState, command: string, error: string): void;
37
+ export declare function recordPlanHash(spindle: SpindleState, plan: unknown): void;
38
+ export declare function recordTicketResult(spindle: SpindleState, changedFiles: string[], diff: string | null): void;
39
+ /** Get file edit frequency warnings */
40
+ export declare function getFileEditWarnings(spindle: SpindleState, threshold?: number): string[];
41
+ //# sourceMappingURL=spindle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spindle.d.ts","sourceRoot":"","sources":["../src/spindle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM/C,MAAM,WAAW,aAAa;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,sBAAsB,EAAE,aAOpC,CAAC;AAMF,MAAM,MAAM,aAAa,GACrB,aAAa,GACb,YAAY,GACZ,UAAU,GACV,cAAc,GACd,iBAAiB,CAAC;AAEtB,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC1C;AAcD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,YAAY,EACrB,MAAM,GAAE,aAAsC,GAC7C,kBAAkB,CA8EpB;AAMD,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAKxE;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAmB3E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIhG;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAIzE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE3G;AAwID,uCAAuC;AACvC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,EAAE,SAAS,GAAE,MAAU,GAAG,MAAM,EAAE,CAS1F"}