ai-maestro 1.0.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 (81) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +15 -0
  3. package/README.md +727 -0
  4. package/bin/maestro.mjs +246 -0
  5. package/package.json +29 -0
  6. package/src/checkpoint.mjs +102 -0
  7. package/src/codex-diagnostics.mjs +682 -0
  8. package/src/codex-home-inspect.mjs +252 -0
  9. package/src/codex-home.mjs +258 -0
  10. package/src/commands.mjs +809 -0
  11. package/src/config.mjs +11 -0
  12. package/src/debug.mjs +164 -0
  13. package/src/encoding-guard.mjs +125 -0
  14. package/src/engines/ai-router-engine.mjs +37 -0
  15. package/src/engines/codex-engine.mjs +21 -0
  16. package/src/engines/engine.mjs +21 -0
  17. package/src/engines/registry.mjs +29 -0
  18. package/src/files.mjs +65 -0
  19. package/src/format.mjs +132 -0
  20. package/src/lock.mjs +439 -0
  21. package/src/memory-log.mjs +18 -0
  22. package/src/memory.mjs +1 -0
  23. package/src/orchestration/attempt-chain-runtime.mjs +627 -0
  24. package/src/orchestration/budget-manager.mjs +121 -0
  25. package/src/orchestration/capability-registry.mjs +108 -0
  26. package/src/orchestration/consolidator.mjs +772 -0
  27. package/src/orchestration/delegation-executor.mjs +262 -0
  28. package/src/orchestration/delegation-manager.mjs +116 -0
  29. package/src/orchestration/deployment-intent.mjs +36 -0
  30. package/src/orchestration/engine-history.mjs +110 -0
  31. package/src/orchestration/engine-policy.mjs +73 -0
  32. package/src/orchestration/engine-selector.mjs +187 -0
  33. package/src/orchestration/execution-context.mjs +45 -0
  34. package/src/orchestration/failure-classifier.mjs +136 -0
  35. package/src/orchestration/failure-evidence.mjs +237 -0
  36. package/src/orchestration/fallback-chain-lock.mjs +217 -0
  37. package/src/orchestration/fallback-chain-trail.mjs +218 -0
  38. package/src/orchestration/fallback-executor.mjs +146 -0
  39. package/src/orchestration/fallback-graph.mjs +106 -0
  40. package/src/orchestration/fallback-recommendation.mjs +73 -0
  41. package/src/orchestration/file-conflict-detector.mjs +126 -0
  42. package/src/orchestration/host-validation.mjs +241 -0
  43. package/src/orchestration/orchestration-loop.mjs +1971 -0
  44. package/src/orchestration/orchestration-runtime.mjs +1019 -0
  45. package/src/orchestration/orchestration-scheduler.mjs +135 -0
  46. package/src/orchestration/orchestration-trail.mjs +192 -0
  47. package/src/orchestration/preflight.mjs +212 -0
  48. package/src/orchestration/provider-health.mjs +566 -0
  49. package/src/orchestration/provider-router.mjs +352 -0
  50. package/src/orchestration/rc-check-adapters.mjs +817 -0
  51. package/src/orchestration/rc-check.mjs +544 -0
  52. package/src/orchestration/rc-policy.mjs +200 -0
  53. package/src/orchestration/runtime-gate.mjs +146 -0
  54. package/src/orchestration/sector-managers.mjs +215 -0
  55. package/src/orchestration/self-hosting-canary.mjs +231 -0
  56. package/src/orchestration/self-hosting-readiness.mjs +244 -0
  57. package/src/orchestration/spec-planner.mjs +877 -0
  58. package/src/orchestration/subtask-planner.mjs +176 -0
  59. package/src/orchestration/task-classifier.mjs +241 -0
  60. package/src/orchestration/verifier.mjs +1608 -0
  61. package/src/orchestration-commands.mjs +279 -0
  62. package/src/planner.mjs +38 -0
  63. package/src/project.mjs +1 -0
  64. package/src/run-diagnostics.mjs +641 -0
  65. package/src/runner.mjs +243 -0
  66. package/src/safety.mjs +116 -0
  67. package/src/schema.mjs +371 -0
  68. package/src/session-commands.mjs +521 -0
  69. package/src/shell.mjs +93 -0
  70. package/src/smart-planner.mjs +249 -0
  71. package/src/task-commands.mjs +1182 -0
  72. package/src/tasks.mjs +134 -0
  73. package/src/ui/commands.mjs +76 -0
  74. package/src/ui/events.mjs +45 -0
  75. package/src/ui/public/app.js +600 -0
  76. package/src/ui/public/index.html +88 -0
  77. package/src/ui/public/style.css +460 -0
  78. package/src/ui/server.mjs +112 -0
  79. package/src/ui/state.mjs +504 -0
  80. package/src/usage.mjs +178 -0
  81. package/src/workspace-diff.mjs +228 -0
@@ -0,0 +1,460 @@
1
+ :root {
2
+ color-scheme: dark;
3
+ --bg: #0a0d10;
4
+ --bg-2: #0f1418;
5
+ --surface: #12181d;
6
+ --surface-2: #182027;
7
+ --surface-3: #1d2730;
8
+ --line: #2a3640;
9
+ --text: #eef3f4;
10
+ --muted: #98a7af;
11
+ --accent: #63d6a5;
12
+ --accent-2: #6fb2ff;
13
+ --warning: #efc15d;
14
+ --danger: #ff6e6e;
15
+ --cyan: #61d4d0;
16
+ }
17
+
18
+ * {
19
+ box-sizing: border-box;
20
+ }
21
+
22
+ html, body {
23
+ min-height: 100%;
24
+ }
25
+
26
+ body {
27
+ margin: 0;
28
+ color: var(--text);
29
+ font-family: Inter, "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
30
+ background:
31
+ linear-gradient(180deg, rgba(12, 16, 19, 0.98), rgba(8, 10, 12, 0.98)),
32
+ linear-gradient(120deg, rgba(99, 214, 165, 0.07), transparent 35%, rgba(111, 178, 255, 0.05) 70%);
33
+ }
34
+
35
+ body::before {
36
+ content: "";
37
+ position: fixed;
38
+ inset: 0;
39
+ pointer-events: none;
40
+ background-image:
41
+ linear-gradient(rgba(255, 255, 255, 0.025) 1px, transparent 1px),
42
+ linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
43
+ background-size: 44px 44px;
44
+ opacity: 0.35;
45
+ }
46
+
47
+ h1, h2, h3, p {
48
+ margin: 0;
49
+ }
50
+
51
+ .topbar {
52
+ position: sticky;
53
+ top: 0;
54
+ z-index: 10;
55
+ display: flex;
56
+ justify-content: space-between;
57
+ gap: 20px;
58
+ align-items: flex-start;
59
+ padding: 18px 22px;
60
+ border-bottom: 1px solid rgba(255, 255, 255, 0.06);
61
+ backdrop-filter: blur(12px);
62
+ background: rgba(10, 13, 16, 0.86);
63
+ }
64
+
65
+ .brand {
66
+ display: grid;
67
+ gap: 4px;
68
+ }
69
+
70
+ .eyebrow {
71
+ color: var(--muted);
72
+ font-size: 11px;
73
+ letter-spacing: 0.08em;
74
+ }
75
+
76
+ .topbar h1 {
77
+ font-size: 26px;
78
+ line-height: 1.1;
79
+ font-weight: 700;
80
+ }
81
+
82
+ .topbar-actions {
83
+ display: flex;
84
+ flex-wrap: wrap;
85
+ justify-content: flex-end;
86
+ align-items: center;
87
+ gap: 10px;
88
+ }
89
+
90
+ .shell {
91
+ position: relative;
92
+ display: grid;
93
+ gap: 16px;
94
+ padding: 16px;
95
+ }
96
+
97
+ .cards {
98
+ display: grid;
99
+ grid-template-columns: repeat(3, minmax(0, 1fr));
100
+ gap: 14px;
101
+ }
102
+
103
+ .workspace {
104
+ display: grid;
105
+ grid-template-columns: minmax(0, 1.2fr) minmax(320px, 0.8fr);
106
+ gap: 16px;
107
+ align-items: start;
108
+ }
109
+
110
+ .panel,
111
+ .card {
112
+ border: 1px solid var(--line);
113
+ border-radius: 8px;
114
+ background: linear-gradient(180deg, rgba(20, 26, 31, 0.98), rgba(16, 21, 26, 0.98));
115
+ box-shadow: 0 18px 40px rgba(0, 0, 0, 0.18);
116
+ }
117
+
118
+ .card {
119
+ padding: 14px;
120
+ display: grid;
121
+ gap: 10px;
122
+ }
123
+
124
+ .card-head,
125
+ .panel-head,
126
+ .microterminal-head,
127
+ .task-row__head,
128
+ .timeline-row__head {
129
+ display: flex;
130
+ justify-content: space-between;
131
+ gap: 12px;
132
+ align-items: center;
133
+ }
134
+
135
+ .card h3,
136
+ .panel h2 {
137
+ font-size: 15px;
138
+ font-weight: 700;
139
+ }
140
+
141
+ .card-detail,
142
+ .timeline-row__detail,
143
+ .task-row__meta,
144
+ .meta {
145
+ color: var(--muted);
146
+ font-size: 12px;
147
+ }
148
+
149
+ .card-event,
150
+ .task-row__title,
151
+ .timeline-row__summary {
152
+ font-size: 14px;
153
+ line-height: 1.35;
154
+ }
155
+
156
+ .panel {
157
+ padding: 14px;
158
+ }
159
+
160
+ .tasks-panel,
161
+ .operations-panel,
162
+ .timeline-panel {
163
+ min-width: 0;
164
+ }
165
+
166
+ .tasks-panel,
167
+ .operations-panel {
168
+ display: grid;
169
+ gap: 12px;
170
+ }
171
+
172
+ .task-list,
173
+ .timeline-list {
174
+ display: grid;
175
+ gap: 10px;
176
+ }
177
+
178
+ .task-row,
179
+ .timeline-row {
180
+ border: 1px solid rgba(255, 255, 255, 0.06);
181
+ border-radius: 8px;
182
+ background: rgba(8, 11, 13, 0.55);
183
+ padding: 12px;
184
+ display: grid;
185
+ gap: 10px;
186
+ }
187
+
188
+ .task-row__title {
189
+ font-weight: 600;
190
+ }
191
+
192
+ .filters,
193
+ .command-buttons,
194
+ .inline-actions {
195
+ display: flex;
196
+ flex-wrap: wrap;
197
+ gap: 8px;
198
+ }
199
+
200
+ .command-buttons {
201
+ margin-bottom: 6px;
202
+ }
203
+
204
+ .chip,
205
+ .filter,
206
+ button,
207
+ .action-button {
208
+ border-radius: 999px;
209
+ border: 1px solid rgba(255, 255, 255, 0.08);
210
+ min-height: 32px;
211
+ padding: 0 11px;
212
+ color: var(--text);
213
+ background: rgba(255, 255, 255, 0.02);
214
+ font: inherit;
215
+ }
216
+
217
+ button {
218
+ cursor: pointer;
219
+ }
220
+
221
+ button:hover,
222
+ .filter:hover,
223
+ .action-button:hover {
224
+ border-color: rgba(255, 255, 255, 0.2);
225
+ }
226
+
227
+ button:disabled,
228
+ .action-button:disabled {
229
+ cursor: not-allowed;
230
+ opacity: 0.5;
231
+ }
232
+
233
+ .filter--active {
234
+ border-color: rgba(99, 214, 165, 0.7);
235
+ background: rgba(99, 214, 165, 0.08);
236
+ }
237
+
238
+ .chip {
239
+ display: inline-flex;
240
+ align-items: center;
241
+ gap: 6px;
242
+ font-size: 12px;
243
+ white-space: nowrap;
244
+ }
245
+
246
+ .chip--neutral,
247
+ .chip--idle {
248
+ color: var(--muted);
249
+ }
250
+
251
+ .chip--running {
252
+ color: var(--accent-2);
253
+ }
254
+
255
+ .chip--thinking {
256
+ color: var(--accent);
257
+ }
258
+
259
+ .chip--success {
260
+ color: var(--accent);
261
+ }
262
+
263
+ .chip--warning {
264
+ color: var(--warning);
265
+ }
266
+
267
+ .chip--blocked,
268
+ .chip--error {
269
+ color: var(--danger);
270
+ }
271
+
272
+ .action-button--primary {
273
+ border-color: rgba(111, 178, 255, 0.28);
274
+ background: rgba(111, 178, 255, 0.08);
275
+ }
276
+
277
+ .action-button--secondary {
278
+ border-color: rgba(239, 193, 93, 0.28);
279
+ background: rgba(239, 193, 93, 0.08);
280
+ }
281
+
282
+ .microterminal {
283
+ display: grid;
284
+ gap: 10px;
285
+ padding: 12px;
286
+ border-radius: 8px;
287
+ background: rgba(7, 10, 12, 0.72);
288
+ border: 1px solid rgba(255, 255, 255, 0.05);
289
+ }
290
+
291
+ .microterminal-actions {
292
+ display: flex;
293
+ flex-wrap: wrap;
294
+ gap: 8px;
295
+ }
296
+
297
+ .terminal-summary {
298
+ color: var(--text);
299
+ font-size: 13px;
300
+ line-height: 1.4;
301
+ }
302
+
303
+ .terminal {
304
+ margin: 0;
305
+ max-height: 240px;
306
+ overflow: auto;
307
+ white-space: pre-wrap;
308
+ word-break: break-word;
309
+ border-radius: 8px;
310
+ background: #07090b;
311
+ border: 1px solid rgba(255, 255, 255, 0.05);
312
+ color: #dce5e6;
313
+ padding: 12px;
314
+ font: 12px/1.5 Consolas, "SFMono-Regular", monospace;
315
+ }
316
+
317
+ .terminal--card {
318
+ max-height: 160px;
319
+ }
320
+
321
+ .terminal--timeline {
322
+ max-height: 180px;
323
+ }
324
+
325
+ .terminal--hidden {
326
+ display: none;
327
+ }
328
+
329
+ .meta {
330
+ white-space: nowrap;
331
+ }
332
+
333
+ .toast {
334
+ position: fixed;
335
+ right: 16px;
336
+ bottom: 16px;
337
+ padding: 10px 14px;
338
+ border-radius: 8px;
339
+ border: 1px solid rgba(255, 255, 255, 0.08);
340
+ background: rgba(13, 17, 20, 0.95);
341
+ color: var(--text);
342
+ box-shadow: 0 18px 36px rgba(0, 0, 0, 0.24);
343
+ opacity: 0;
344
+ transform: translateY(6px);
345
+ transition: opacity 140ms ease, transform 140ms ease;
346
+ pointer-events: none;
347
+ }
348
+
349
+ .toast--visible {
350
+ opacity: 1;
351
+ transform: translateY(0);
352
+ }
353
+
354
+ @media (max-width: 1100px) {
355
+ .workspace {
356
+ grid-template-columns: 1fr;
357
+ }
358
+ }
359
+
360
+ @media (max-width: 900px) {
361
+ .cards {
362
+ grid-template-columns: repeat(2, minmax(0, 1fr));
363
+ }
364
+ }
365
+
366
+ @media (max-width: 640px) {
367
+ .topbar {
368
+ position: static;
369
+ flex-direction: column;
370
+ }
371
+
372
+ .topbar-actions {
373
+ justify-content: flex-start;
374
+ }
375
+
376
+ .cards {
377
+ grid-template-columns: 1fr;
378
+ }
379
+
380
+ .shell {
381
+ padding: 12px;
382
+ }
383
+ }
384
+
385
+ .orchestra-engines {
386
+ display: grid;
387
+ gap: 10px;
388
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
389
+ margin-bottom: 14px;
390
+ }
391
+
392
+ .orchestra-engine {
393
+ border: 1px solid rgba(255, 255, 255, 0.06);
394
+ border-radius: 8px;
395
+ background: rgba(8, 11, 13, 0.55);
396
+ padding: 10px 12px;
397
+ display: grid;
398
+ gap: 6px;
399
+ }
400
+
401
+ .orchestra-engine__head {
402
+ display: flex;
403
+ align-items: center;
404
+ gap: 6px;
405
+ flex-wrap: wrap;
406
+ }
407
+
408
+ .orchestra-engine__name {
409
+ font-weight: 600;
410
+ font-size: 13px;
411
+ margin-right: auto;
412
+ }
413
+
414
+ .orchestra-engine__detail {
415
+ font-size: 12px;
416
+ opacity: 0.75;
417
+ line-height: 1.4;
418
+ }
419
+
420
+ .orchestra-grid {
421
+ display: grid;
422
+ gap: 14px;
423
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
424
+ }
425
+
426
+ .orchestra-block h3 {
427
+ font-size: 13px;
428
+ text-transform: uppercase;
429
+ letter-spacing: 0.04em;
430
+ opacity: 0.7;
431
+ margin: 0 0 8px;
432
+ }
433
+
434
+ .orchestra-kv {
435
+ display: grid;
436
+ gap: 4px;
437
+ }
438
+
439
+ .orchestra-kv-row {
440
+ display: flex;
441
+ justify-content: space-between;
442
+ gap: 12px;
443
+ font-size: 13px;
444
+ border-bottom: 1px dashed rgba(255, 255, 255, 0.06);
445
+ padding-bottom: 4px;
446
+ }
447
+
448
+ .orchestra-kv-label {
449
+ opacity: 0.7;
450
+ }
451
+
452
+ .orchestra-kv-value {
453
+ font-weight: 600;
454
+ }
455
+
456
+ .orchestra-note {
457
+ margin-top: 12px;
458
+ font-size: 12px;
459
+ opacity: 0.6;
460
+ }
@@ -0,0 +1,112 @@
1
+ import http from 'http';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { readHandoffSnapshot, readUiState } from './state.mjs';
6
+ import { handleEvents } from './events.mjs';
7
+ import { runUiCommand } from './commands.mjs';
8
+
9
+ const HOST = '127.0.0.1';
10
+ const DEFAULT_PORT = 4317;
11
+ const PUBLIC_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), 'public');
12
+
13
+ const MIME_TYPES = {
14
+ '.html': 'text/html; charset=utf-8',
15
+ '.js': 'text/javascript; charset=utf-8',
16
+ '.css': 'text/css; charset=utf-8',
17
+ '.json': 'application/json; charset=utf-8'
18
+ };
19
+
20
+ function sendJson(res, status, value) {
21
+ res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
22
+ res.end(JSON.stringify(value));
23
+ }
24
+
25
+ async function readRequestJson(req) {
26
+ let body = '';
27
+ for await (const chunk of req) {
28
+ body += chunk.toString();
29
+ if (body.length > 4096) throw new Error('Request body too large');
30
+ }
31
+ return body ? JSON.parse(body) : {};
32
+ }
33
+
34
+ async function serveStatic(req, res) {
35
+ const url = new URL(req.url, 'http://' + HOST);
36
+ const pathname = url.pathname === '/' ? '/index.html' : url.pathname;
37
+ const target = path.normalize(path.join(PUBLIC_DIR, pathname));
38
+ if (!target.startsWith(PUBLIC_DIR)) {
39
+ res.writeHead(403);
40
+ res.end('Forbidden');
41
+ return;
42
+ }
43
+ try {
44
+ const content = await fs.readFile(target);
45
+ res.writeHead(200, { 'Content-Type': MIME_TYPES[path.extname(target)] || 'application/octet-stream' });
46
+ res.end(content);
47
+ } catch (error) {
48
+ res.writeHead(error.code === 'ENOENT' ? 404 : 500);
49
+ res.end(error.code === 'ENOENT' ? 'Not found' : error.message);
50
+ }
51
+ }
52
+
53
+ export function createUiServer(projectRoot) {
54
+ return http.createServer(async (req, res) => {
55
+ try {
56
+ const url = new URL(req.url, 'http://' + HOST);
57
+ if (req.method === 'GET' && url.pathname === '/api/state') {
58
+ return sendJson(res, 200, await readUiState(projectRoot));
59
+ }
60
+ if (req.method === 'GET' && url.pathname === '/api/handoff') {
61
+ return sendJson(res, 200, await readHandoffSnapshot(projectRoot));
62
+ }
63
+ if (req.method === 'GET' && url.pathname === '/api/events') {
64
+ return handleEvents(req, res, projectRoot);
65
+ }
66
+ if (req.method === 'POST' && url.pathname === '/api/command') {
67
+ let body;
68
+ try {
69
+ body = await readRequestJson(req);
70
+ } catch (error) {
71
+ return sendJson(res, 400, { error: 'Invalid JSON body: ' + error.message });
72
+ }
73
+ if (!body.command || typeof body.command !== 'string') {
74
+ return sendJson(res, 400, { error: 'Missing command' });
75
+ }
76
+ try {
77
+ return sendJson(res, 200, await runUiCommand(body.command, body, projectRoot));
78
+ } catch (error) {
79
+ return sendJson(res, /Unknown UI command|taskId must be a positive integer/.test(error.message) ? 400 : 500, { error: error.message });
80
+ }
81
+ }
82
+ if (req.method === 'GET') return serveStatic(req, res);
83
+ sendJson(res, 405, { error: 'Method not allowed' });
84
+ } catch (error) {
85
+ sendJson(res, 500, { error: error.message });
86
+ }
87
+ });
88
+ }
89
+
90
+ export async function startUiServer({ projectRoot = process.cwd(), port = DEFAULT_PORT } = {}) {
91
+ let currentPort = port;
92
+ const maxAttempts = port === 0 ? 1 : 50;
93
+ while (maxAttempts === 1 || currentPort < port + 50) {
94
+ const server = createUiServer(projectRoot);
95
+ try {
96
+ const actualPort = await new Promise((resolve, reject) => {
97
+ server.once('error', reject);
98
+ server.listen(currentPort, HOST, () => {
99
+ const address = server.address();
100
+ resolve(address && typeof address === 'object' ? address.port : currentPort);
101
+ });
102
+ });
103
+ return { server, host: HOST, port: actualPort, url: 'http://' + HOST + ':' + actualPort };
104
+ } catch (error) {
105
+ server.close();
106
+ if (error.code !== 'EADDRINUSE') throw error;
107
+ currentPort++;
108
+ if (maxAttempts === 1) throw error;
109
+ }
110
+ }
111
+ throw new Error('No free Maestro UI port found near ' + port);
112
+ }