iriai-build 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 (80) hide show
  1. package/bin/iriai-build.js +78 -0
  2. package/bridge-v3.js +98 -0
  3. package/cli/bootstrap.js +83 -0
  4. package/cli/commands/implementation.js +64 -0
  5. package/cli/commands/index.js +46 -0
  6. package/cli/commands/launch.js +153 -0
  7. package/cli/commands/plan.js +117 -0
  8. package/cli/commands/setup.js +80 -0
  9. package/cli/commands/slack.js +97 -0
  10. package/cli/commands/transfer.js +111 -0
  11. package/cli/config.js +92 -0
  12. package/cli/display.js +121 -0
  13. package/cli/terminal-input.js +666 -0
  14. package/cli/wait.js +82 -0
  15. package/index.js +1488 -0
  16. package/lib/agent-process.js +170 -0
  17. package/lib/bridge-state.js +126 -0
  18. package/lib/constants.js +137 -0
  19. package/lib/health-monitor.js +113 -0
  20. package/lib/prompt-builder.js +565 -0
  21. package/lib/signal-watcher.js +215 -0
  22. package/lib/slack-helpers.js +224 -0
  23. package/lib/state-machines/feature-lead.js +408 -0
  24. package/lib/state-machines/operator-agent.js +173 -0
  25. package/lib/state-machines/planning-role.js +161 -0
  26. package/lib/state-machines/role-agent.js +186 -0
  27. package/lib/state-machines/team-orchestrator.js +160 -0
  28. package/package.json +31 -0
  29. package/v3/.handover-html-evidence.md +35 -0
  30. package/v3/KICKOFF-HTML-EVIDENCE.md +98 -0
  31. package/v3/PLAN-HTML-EVIDENCE-HARDENING.md +603 -0
  32. package/v3/adapters/desktop-adapter.js +78 -0
  33. package/v3/adapters/interface.js +146 -0
  34. package/v3/adapters/slack-adapter.js +608 -0
  35. package/v3/adapters/slack-helpers.js +179 -0
  36. package/v3/adapters/terminal-adapter.js +249 -0
  37. package/v3/agent-supervisor.js +320 -0
  38. package/v3/artifact-portal.js +1184 -0
  39. package/v3/bridge.db +0 -0
  40. package/v3/constants.js +170 -0
  41. package/v3/db.js +76 -0
  42. package/v3/file-io.js +216 -0
  43. package/v3/helpers.js +174 -0
  44. package/v3/operator.js +364 -0
  45. package/v3/orchestrator.js +2886 -0
  46. package/v3/plan-compiler.js +440 -0
  47. package/v3/prompt-builder.js +849 -0
  48. package/v3/queries.js +461 -0
  49. package/v3/recovery.js +508 -0
  50. package/v3/review-sessions.js +360 -0
  51. package/v3/roles/accessibility-auditor/CLAUDE.md +50 -0
  52. package/v3/roles/analytics-engineer/CLAUDE.md +40 -0
  53. package/v3/roles/architect/CLAUDE.md +809 -0
  54. package/v3/roles/backend-implementer/CLAUDE.md +97 -0
  55. package/v3/roles/code-reviewer/CLAUDE.md +89 -0
  56. package/v3/roles/database-implementer/CLAUDE.md +97 -0
  57. package/v3/roles/deployer/CLAUDE.md +42 -0
  58. package/v3/roles/designer/CLAUDE.md +386 -0
  59. package/v3/roles/documentation/CLAUDE.md +40 -0
  60. package/v3/roles/feature-lead/CLAUDE.md +233 -0
  61. package/v3/roles/frontend-implementer/CLAUDE.md +97 -0
  62. package/v3/roles/implementer/CLAUDE.md +97 -0
  63. package/v3/roles/integration-tester/CLAUDE.md +174 -0
  64. package/v3/roles/observability-engineer/CLAUDE.md +40 -0
  65. package/v3/roles/operator/CLAUDE.md +322 -0
  66. package/v3/roles/orchestrator/CLAUDE.md +288 -0
  67. package/v3/roles/package-implementer/CLAUDE.md +47 -0
  68. package/v3/roles/performance-analyst/CLAUDE.md +49 -0
  69. package/v3/roles/plan-compiler/CLAUDE.md +163 -0
  70. package/v3/roles/planning-lead/CLAUDE.md +41 -0
  71. package/v3/roles/pm/CLAUDE.md +806 -0
  72. package/v3/roles/regression-tester/CLAUDE.md +135 -0
  73. package/v3/roles/release-manager/CLAUDE.md +43 -0
  74. package/v3/roles/security-auditor/CLAUDE.md +90 -0
  75. package/v3/roles/smoke-tester/CLAUDE.md +97 -0
  76. package/v3/roles/test-author/CLAUDE.md +42 -0
  77. package/v3/roles/verifier/CLAUDE.md +90 -0
  78. package/v3/schema.sql +134 -0
  79. package/v3/slack-adapter.js +510 -0
  80. package/v3/slack-helpers.js +346 -0
@@ -0,0 +1,440 @@
1
+ // plan-compiler.js — Compiles planning artifacts into a tabbed HTML document.
2
+ // Used for plan approval reviews. Includes Mermaid.js support for diagrams.
3
+
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+
7
+ /**
8
+ * Compile all planning artifacts from planDir into a single tabbed HTML document.
9
+ * Writes the output to `{planDir}/.plan-review.html` and returns the path.
10
+ *
11
+ * Tabs: PRD | Design Decisions | Architecture (context.md) | Phase Overview (plan.yaml)
12
+ */
13
+ export function compilePlanReviewHtml(planDir) {
14
+ const outputPath = path.join(planDir, ".plan-review.html");
15
+
16
+ const tabs = [];
17
+
18
+ // PRD
19
+ const prdContent = readArtifact(planDir, "prd");
20
+ if (prdContent) {
21
+ tabs.push({ id: "prd", label: "PRD", content: prdContent });
22
+ }
23
+
24
+ // Design Decisions
25
+ const designContent = readArtifact(planDir, "design-decisions");
26
+ if (designContent) {
27
+ tabs.push({ id: "design", label: "Design Decisions", content: designContent });
28
+ }
29
+
30
+ // Architecture (context.md)
31
+ const contextContent = readArtifact(planDir, "context");
32
+ if (contextContent) {
33
+ tabs.push({ id: "architecture", label: "Architecture", content: contextContent });
34
+ }
35
+
36
+ // Plan.yaml (rendered as code block)
37
+ const planYamlPath = path.join(planDir, "plan.yaml");
38
+ if (fs.existsSync(planYamlPath)) {
39
+ const yamlContent = fs.readFileSync(planYamlPath, "utf-8");
40
+ tabs.push({
41
+ id: "plan",
42
+ label: "Phase Overview",
43
+ content: "```yaml\n" + yamlContent + "\n```",
44
+ });
45
+ }
46
+
47
+ if (tabs.length === 0) {
48
+ return null;
49
+ }
50
+
51
+ const html = renderTabbedHtml(tabs);
52
+ fs.writeFileSync(outputPath, html, "utf-8");
53
+ return outputPath;
54
+ }
55
+
56
+ function readArtifact(planDir, pattern) {
57
+ try {
58
+ const files = fs.readdirSync(planDir);
59
+ const match = files.find(f => f.includes(pattern) && f.endsWith(".md"));
60
+ if (match) {
61
+ return fs.readFileSync(path.join(planDir, match), "utf-8");
62
+ }
63
+ } catch {
64
+ // planDir doesn't exist
65
+ }
66
+ return null;
67
+ }
68
+
69
+ function renderTabbedHtml(tabs) {
70
+ const tabButtons = tabs.map((t, i) =>
71
+ `<button class="tab-btn${i === 0 ? " active" : ""}" onclick="showTab('${t.id}')" id="btn-${t.id}">${t.label}</button>`
72
+ ).join("\n ");
73
+
74
+ const tabPanels = tabs.map((t, i) =>
75
+ `<div class="tab-panel${i === 0 ? " active" : ""}" id="panel-${t.id}">
76
+ <div class="markdown-body">${escapeForTemplate(t.content)}</div>
77
+ <nav class="tab-toc" id="toc-${t.id}"></nav>
78
+ </div>`
79
+ ).join("\n ");
80
+
81
+ return `<!DOCTYPE html>
82
+ <html lang="en">
83
+ <head>
84
+ <meta charset="UTF-8">
85
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
86
+ <title>Plan Review</title>
87
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"><\/script>
88
+ <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"><\/script>
89
+ <style>
90
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
91
+
92
+ :root {
93
+ --text-primary: #1a1a2e;
94
+ --text-secondary: #555;
95
+ --text-muted: #888;
96
+ --bg-page: #faf9f7;
97
+ --bg-header: rgba(250, 249, 247, 0.92);
98
+ --border-subtle: rgba(0, 0, 0, 0.06);
99
+ --accent: #2d5be3;
100
+ --accent-faint: rgba(45, 91, 227, 0.06);
101
+ --content-width: 720px;
102
+ --layout-width: 1080px;
103
+ --gutter: clamp(20px, 5vw, 48px);
104
+ }
105
+
106
+ html {
107
+ font-size: 18px;
108
+ -webkit-font-smoothing: antialiased;
109
+ text-rendering: optimizeLegibility;
110
+ scroll-behavior: smooth;
111
+ }
112
+
113
+ body {
114
+ font-family: Charter, Georgia, 'Times New Roman', serif;
115
+ color: var(--text-primary);
116
+ background: var(--bg-page);
117
+ line-height: 1.72;
118
+ min-height: 100vh;
119
+ }
120
+
121
+ ::selection {
122
+ background: rgba(45, 91, 227, 0.18);
123
+ color: inherit;
124
+ }
125
+
126
+ /* ── Header ──────────────────────────────────────────────── */
127
+ .header {
128
+ background: var(--bg-header);
129
+ backdrop-filter: blur(16px);
130
+ border-bottom: 1px solid var(--border-subtle);
131
+ padding: 0 var(--gutter);
132
+ position: sticky;
133
+ top: 0;
134
+ z-index: 100;
135
+ }
136
+
137
+ .header-inner {
138
+ max-width: var(--layout-width);
139
+ margin: 0 auto;
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: space-between;
143
+ height: 56px;
144
+ }
145
+
146
+ .header h1 {
147
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
148
+ font-size: 16px;
149
+ font-weight: 600;
150
+ color: var(--text-primary);
151
+ letter-spacing: -0.01em;
152
+ }
153
+
154
+ .header .badge {
155
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
156
+ background: #238636;
157
+ color: #fff;
158
+ padding: 3px 10px;
159
+ border-radius: 12px;
160
+ font-size: 12px;
161
+ font-weight: 500;
162
+ }
163
+
164
+ /* ── Tabs ─────────────────────────────────────────────────── */
165
+ .tabs {
166
+ display: flex;
167
+ gap: 0;
168
+ background: var(--bg-header);
169
+ border-bottom: 1px solid var(--border-subtle);
170
+ padding: 0 var(--gutter);
171
+ position: sticky;
172
+ top: 56px;
173
+ z-index: 99;
174
+ }
175
+
176
+ .tabs-inner {
177
+ max-width: var(--layout-width);
178
+ margin: 0 auto;
179
+ display: flex;
180
+ width: 100%;
181
+ }
182
+
183
+ .tab-btn {
184
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
185
+ background: none;
186
+ border: none;
187
+ color: var(--text-muted);
188
+ padding: 12px 20px;
189
+ font-size: 14px;
190
+ cursor: pointer;
191
+ border-bottom: 2px solid transparent;
192
+ transition: all 0.15s;
193
+ white-space: nowrap;
194
+ }
195
+
196
+ .tab-btn:hover { color: var(--text-primary); }
197
+ .tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
198
+
199
+ /* ── Tab Panels ───────────────────────────────────────────── */
200
+ .tab-panel {
201
+ display: none;
202
+ max-width: var(--layout-width);
203
+ margin: 0 auto;
204
+ padding: 0 var(--gutter);
205
+ }
206
+
207
+ .tab-panel.active {
208
+ display: grid;
209
+ grid-template-columns: var(--content-width) 1fr;
210
+ gap: 0;
211
+ }
212
+
213
+ /* ── Per-tab TOC ──────────────────────────────────────────── */
214
+ .tab-toc {
215
+ position: sticky;
216
+ top: 128px;
217
+ align-self: start;
218
+ max-height: calc(100vh - 144px);
219
+ overflow-y: auto;
220
+ padding: 36px 0 36px 32px;
221
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
222
+ border-left: 1px solid var(--border-subtle);
223
+ }
224
+
225
+ .tab-toc:empty { display: none; }
226
+
227
+ .tab-toc .toc-title {
228
+ font-size: 11px;
229
+ font-weight: 700;
230
+ text-transform: uppercase;
231
+ letter-spacing: 0.08em;
232
+ color: var(--text-muted);
233
+ margin-bottom: 12px;
234
+ }
235
+
236
+ .tab-toc ol { list-style: none; padding: 0; margin: 0; }
237
+ .tab-toc li { margin: 0; }
238
+
239
+ .tab-toc a {
240
+ display: block;
241
+ padding: 4px 0 4px 12px;
242
+ font-size: 13px;
243
+ line-height: 1.4;
244
+ color: var(--text-muted);
245
+ text-decoration: none;
246
+ border-left: 2px solid transparent;
247
+ margin-left: -1px;
248
+ transition: color 0.15s;
249
+ }
250
+
251
+ .tab-toc a:hover { color: var(--text-primary); }
252
+ .tab-toc a.active { color: var(--accent); border-left-color: var(--accent); font-weight: 600; }
253
+ .tab-toc .toc-h3 { padding-left: 24px; font-size: 12px; }
254
+
255
+ .tab-toc::-webkit-scrollbar { width: 3px; }
256
+ .tab-toc::-webkit-scrollbar-thumb { background: var(--border-subtle); border-radius: 3px; }
257
+
258
+ /* ── Markdown body ────────────────────────────────────────── */
259
+ .markdown-body {
260
+ padding: 36px 0 120px;
261
+ min-width: 0;
262
+ }
263
+
264
+ .markdown-body h1 {
265
+ font-size: 1.9rem; font-weight: 700; line-height: 1.25;
266
+ letter-spacing: -0.015em; margin: 2.4em 0 0.6em;
267
+ padding-bottom: 0.3em; border-bottom: 1px solid var(--border-subtle);
268
+ color: var(--text-primary);
269
+ }
270
+ .markdown-body h1:first-child { margin-top: 0; }
271
+
272
+ .markdown-body h2 {
273
+ font-size: 1.45rem; font-weight: 700; line-height: 1.3;
274
+ letter-spacing: -0.01em; margin: 2.2em 0 0.5em;
275
+ padding-bottom: 0.25em; border-bottom: 1px solid var(--border-subtle);
276
+ color: var(--text-primary);
277
+ }
278
+
279
+ .markdown-body h3 {
280
+ font-size: 1.15rem; font-weight: 700; line-height: 1.35;
281
+ margin: 1.8em 0 0.4em; color: var(--text-primary);
282
+ }
283
+
284
+ .markdown-body h4, .markdown-body h5, .markdown-body h6 {
285
+ font-weight: 700; line-height: 1.4; margin: 1.6em 0 0.4em;
286
+ color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.04em;
287
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
288
+ font-size: 0.78rem;
289
+ }
290
+
291
+ .markdown-body p { margin-bottom: 1.4em; }
292
+
293
+ .markdown-body a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }
294
+ .markdown-body a:hover { color: #1a3fa0; }
295
+
296
+ .markdown-body strong { font-weight: 700; }
297
+ .markdown-body em { font-style: italic; }
298
+
299
+ .markdown-body blockquote {
300
+ margin: 1.6em 0; padding: 12px 0 12px 24px;
301
+ border-left: 3px solid var(--accent); color: var(--text-secondary);
302
+ font-style: italic; background: var(--accent-faint); border-radius: 0 8px 8px 0;
303
+ }
304
+ .markdown-body blockquote p:last-child { margin-bottom: 0; }
305
+
306
+ .markdown-body ul, .markdown-body ol { margin: 0 0 1.4em; padding-left: 1.6em; }
307
+ .markdown-body li { margin-bottom: 0.4em; }
308
+ .markdown-body li > ul, .markdown-body li > ol { margin-top: 0.4em; margin-bottom: 0; }
309
+ .markdown-body li::marker { color: var(--text-muted); }
310
+
311
+ .markdown-body code {
312
+ font-family: 'SF Mono', Menlo, Consolas, monospace; font-size: 0.85em;
313
+ background: var(--accent-faint); padding: 2px 6px; border-radius: 4px;
314
+ color: #1a3fa0; word-break: break-word;
315
+ }
316
+ .markdown-body pre {
317
+ margin: 1.6em 0; padding: 20px 24px; background: #1a1a2e;
318
+ border-radius: 10px; overflow-x: auto; line-height: 1.55;
319
+ }
320
+ .markdown-body pre code { background: none; padding: 0; color: #c8d0e0; font-size: 0.82rem; }
321
+
322
+ .markdown-body hr { border: none; height: 1px; background: var(--border-subtle); margin: 2.4em 0; }
323
+
324
+ .markdown-body table {
325
+ width: 100%; border-collapse: collapse; margin: 1.6em 0;
326
+ font-size: 0.92rem; border: 1px solid var(--border-subtle); border-radius: 8px; overflow: hidden;
327
+ }
328
+ .markdown-body th, .markdown-body td { padding: 10px 14px; text-align: left; border-bottom: 1px solid var(--border-subtle); }
329
+ .markdown-body th {
330
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
331
+ font-weight: 600; font-size: 0.78rem; text-transform: uppercase;
332
+ letter-spacing: 0.04em; color: var(--text-muted); background: rgba(0,0,0,0.02);
333
+ }
334
+ .markdown-body tr:last-child td { border-bottom: none; }
335
+ .markdown-body tr:hover td { background: rgba(45, 91, 227, 0.02); }
336
+
337
+ .mermaid { background: #1a1a2e; padding: 24px; border-radius: 10px; margin: 1.6em 0; }
338
+
339
+ /* ── Reading progress ────────────────────────────────────── */
340
+ .progress { position: fixed; top: 56px; left: 0; width: 0%; height: 2px; background: var(--accent); z-index: 101; transition: width 0.1s linear; }
341
+
342
+ /* ── Responsive ──────────────────────────────────────────── */
343
+ @media (max-width: 960px) {
344
+ .tab-panel.active { grid-template-columns: 1fr; }
345
+ .tab-toc { display: none; }
346
+ }
347
+
348
+ @media (max-width: 600px) {
349
+ html { font-size: 16px; }
350
+ .header-inner { height: 48px; }
351
+ .tabs { top: 48px; }
352
+ .tab-btn { padding: 10px 14px; font-size: 13px; }
353
+ }
354
+
355
+ /* ── Entrance animation ──────────────────────────────────── */
356
+ @keyframes fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
357
+ .tab-panel.active { animation: fade-up 0.4s ease both; }
358
+ </style>
359
+ </head>
360
+ <body>
361
+ <div class="progress" id="progress"></div>
362
+
363
+ <div class="header">
364
+ <div class="header-inner">
365
+ <h1>Plan Review</h1>
366
+ <span class="badge">Awaiting Approval</span>
367
+ </div>
368
+ </div>
369
+
370
+ <div class="tabs">
371
+ <div class="tabs-inner">
372
+ ${tabButtons}
373
+ </div>
374
+ </div>
375
+
376
+ <div class="content">
377
+ ${tabPanels}
378
+ </div>
379
+
380
+ <script>
381
+ mermaid.initialize({ startOnLoad: false, theme: 'dark' });
382
+
383
+ function showTab(id) {
384
+ document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
385
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
386
+ document.getElementById('panel-' + id).classList.add('active');
387
+ document.getElementById('btn-' + id).classList.add('active');
388
+ }
389
+
390
+ // Render all markdown content + build per-tab TOC
391
+ document.querySelectorAll('.tab-panel').forEach(panel => {
392
+ var body = panel.querySelector('.markdown-body');
393
+ var tocEl = panel.querySelector('.tab-toc');
394
+ if (!body) return;
395
+
396
+ var raw = body.textContent;
397
+ body.innerHTML = marked.parse(raw);
398
+
399
+ // Render mermaid blocks
400
+ body.querySelectorAll('pre code.language-mermaid').forEach(code => {
401
+ var pre = code.parentElement;
402
+ var div = document.createElement('div');
403
+ div.className = 'mermaid';
404
+ div.textContent = code.textContent;
405
+ pre.replaceWith(div);
406
+ });
407
+
408
+ // Build TOC
409
+ if (!tocEl) return;
410
+ var headings = body.querySelectorAll('h1, h2, h3');
411
+ if (headings.length < 3) return;
412
+
413
+ var html = '<div class="toc-title">On this page</div><ol>';
414
+ headings.forEach(function(h, i) {
415
+ var id = panel.id + '-s' + i;
416
+ h.setAttribute('id', id);
417
+ var level = h.tagName.toLowerCase();
418
+ var cls = level === 'h3' ? ' class="toc-h3"' : '';
419
+ html += '<li><a href="#' + id + '"' + cls + '>' + h.textContent + '</a></li>';
420
+ });
421
+ html += '</ol>';
422
+ tocEl.innerHTML = html;
423
+ });
424
+
425
+ mermaid.run();
426
+
427
+ // Reading progress
428
+ window.addEventListener('scroll', function() {
429
+ var h = document.documentElement.scrollHeight - window.innerHeight;
430
+ document.getElementById('progress').style.width = h > 0 ? ((window.scrollY / h) * 100) + '%' : '0%';
431
+ }, { passive: true });
432
+ <\/script>
433
+ </body>
434
+ </html>`;
435
+ }
436
+
437
+ function escapeForTemplate(content) {
438
+ // Escape </script> tags in content so they don't break the HTML
439
+ return content.replace(/<\/script>/gi, "<\\/script>");
440
+ }