gaslighting-engine 0.2.2 → 0.3.1

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 (36) hide show
  1. package/.agents/prompts/gaslighting.md +2 -1
  2. package/.agents/skills/gaslighting/SKILL.md +4 -1
  3. package/.codex/prompts/gaslighting.md +2 -1
  4. package/.codex/skills/gaslighting/SKILL.md +4 -1
  5. package/README.md +50 -1
  6. package/dist/cli.js +41 -1
  7. package/dist/commands/cockpit.js +197 -0
  8. package/dist/commands/doctor.js +1 -0
  9. package/dist/core/cockpitHtml.js +598 -0
  10. package/dist/core/codexRuntime.js +103 -0
  11. package/dist/core/generateDocs.js +5 -3
  12. package/dist/core/generateOtherMarkdown.js +68 -4
  13. package/dist/index.js +1 -1
  14. package/dist/utils/logger.js +1 -1
  15. package/dist/version.js +1 -1
  16. package/docs/codex-usage.md +24 -4
  17. package/docs/examples.md +3 -0
  18. package/examples/ecommerce/.codex/prompts/gaslighting.md +2 -1
  19. package/examples/ecommerce/.codex/skills/gaslighting/SKILL.md +4 -1
  20. package/examples/ecommerce/.gaslighting/AGENTS.md +3 -1
  21. package/examples/ecommerce/.gaslighting/AGENT_RUNTIME.md +54 -0
  22. package/examples/ecommerce/.gaslighting/CODEX_PROMPT.md +2 -1
  23. package/examples/ecommerce/AGENTS.md +3 -2
  24. package/examples/hospital-homepage/.codex/prompts/gaslighting.md +2 -1
  25. package/examples/hospital-homepage/.codex/skills/gaslighting/SKILL.md +4 -1
  26. package/examples/hospital-homepage/.gaslighting/AGENTS.md +3 -1
  27. package/examples/hospital-homepage/.gaslighting/AGENT_RUNTIME.md +54 -0
  28. package/examples/hospital-homepage/.gaslighting/CODEX_PROMPT.md +2 -1
  29. package/examples/hospital-homepage/AGENTS.md +3 -2
  30. package/examples/landing-page/.codex/prompts/gaslighting.md +2 -1
  31. package/examples/landing-page/.codex/skills/gaslighting/SKILL.md +4 -1
  32. package/examples/landing-page/.gaslighting/AGENTS.md +3 -1
  33. package/examples/landing-page/.gaslighting/AGENT_RUNTIME.md +54 -0
  34. package/examples/landing-page/.gaslighting/CODEX_PROMPT.md +2 -1
  35. package/examples/landing-page/AGENTS.md +3 -2
  36. package/package.json +1 -1
@@ -0,0 +1,598 @@
1
+ export function renderCockpitHtml() {
2
+ return `<!doctype html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <title>Gaslighting Mission Control</title>
8
+ <style>
9
+ :root {
10
+ color-scheme: light;
11
+ --bg: #eef3f8;
12
+ --surface: #ffffff;
13
+ --surface-soft: #f6f7f9;
14
+ --line: #dfe4ea;
15
+ --text: #171a1f;
16
+ --muted: #65717f;
17
+ --green: #168a50;
18
+ --orange: #b76b00;
19
+ --red: #b42318;
20
+ --blue: #2563eb;
21
+ --shadow: 0 18px 50px rgba(19, 29, 45, 0.08);
22
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
23
+ }
24
+ * { box-sizing: border-box; }
25
+ body {
26
+ margin: 0;
27
+ min-height: 100vh;
28
+ background: var(--bg);
29
+ color: var(--text);
30
+ letter-spacing: 0;
31
+ overflow: hidden;
32
+ }
33
+ button, input, textarea { font: inherit; }
34
+ button {
35
+ border: 1px solid var(--line);
36
+ background: var(--surface);
37
+ color: var(--text);
38
+ border-radius: 8px;
39
+ min-height: 38px;
40
+ padding: 0 12px;
41
+ cursor: pointer;
42
+ }
43
+ button:hover { border-color: #b8c2cc; background: #f9fafb; }
44
+ button.primary { background: #111827; border-color: #111827; color: white; }
45
+ button.primary:hover { background: #242b38; }
46
+ button.ghost { background: transparent; }
47
+ input, textarea {
48
+ width: 100%;
49
+ border: 1px solid var(--line);
50
+ background: var(--surface);
51
+ color: var(--text);
52
+ border-radius: 8px;
53
+ padding: 10px 12px;
54
+ outline: none;
55
+ }
56
+ textarea { min-height: 92px; resize: vertical; line-height: 1.45; }
57
+ input:focus, textarea:focus { border-color: #7aa2ff; box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12); }
58
+ .app {
59
+ display: grid;
60
+ grid-template-columns: 286px minmax(520px, 1fr) 330px;
61
+ height: 100vh;
62
+ }
63
+ .sidebar {
64
+ border-right: 1px solid var(--line);
65
+ padding: 18px 14px;
66
+ background: #e8eef5;
67
+ overflow: auto;
68
+ }
69
+ .brand {
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 10px;
73
+ height: 42px;
74
+ padding: 0 10px;
75
+ font-weight: 700;
76
+ }
77
+ .mark {
78
+ width: 28px;
79
+ height: 28px;
80
+ display: grid;
81
+ place-items: center;
82
+ border: 1px solid #cbd5e1;
83
+ border-radius: 8px;
84
+ background: #111827;
85
+ color: white;
86
+ font-size: 13px;
87
+ }
88
+ .nav-title {
89
+ margin: 22px 10px 8px;
90
+ color: #8994a3;
91
+ font-size: 12px;
92
+ text-transform: uppercase;
93
+ letter-spacing: 0.08em;
94
+ }
95
+ .nav-item {
96
+ display: flex;
97
+ align-items: center;
98
+ justify-content: space-between;
99
+ gap: 10px;
100
+ min-height: 34px;
101
+ padding: 0 10px;
102
+ border-radius: 8px;
103
+ color: #343b46;
104
+ font-size: 14px;
105
+ }
106
+ .nav-item.active { background: #dde5ee; color: #111827; font-weight: 650; }
107
+ .nav-dot { width: 8px; height: 8px; border-radius: 99px; background: #94a3b8; }
108
+ .nav-dot.ok { background: var(--green); }
109
+ .nav-dot.warn { background: var(--orange); }
110
+ .main {
111
+ display: grid;
112
+ grid-template-rows: 64px 1fr 156px;
113
+ background: #fbfcfd;
114
+ min-width: 0;
115
+ }
116
+ .topbar {
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: space-between;
120
+ border-bottom: 1px solid var(--line);
121
+ padding: 0 22px;
122
+ background: var(--surface);
123
+ }
124
+ .title-block { min-width: 0; }
125
+ .title {
126
+ font-size: 15px;
127
+ font-weight: 700;
128
+ white-space: nowrap;
129
+ overflow: hidden;
130
+ text-overflow: ellipsis;
131
+ }
132
+ .subtitle { color: var(--muted); font-size: 12px; margin-top: 2px; }
133
+ .top-actions { display: flex; gap: 8px; align-items: center; }
134
+ .content {
135
+ overflow: auto;
136
+ padding: 24px 24px 30px;
137
+ }
138
+ .mission {
139
+ max-width: 920px;
140
+ margin: 0 auto;
141
+ }
142
+ .hero-panel {
143
+ background: var(--surface);
144
+ border: 1px solid var(--line);
145
+ border-radius: 8px;
146
+ box-shadow: var(--shadow);
147
+ padding: 18px;
148
+ margin-bottom: 16px;
149
+ }
150
+ .hero-grid {
151
+ display: grid;
152
+ grid-template-columns: minmax(0, 1fr) 210px;
153
+ gap: 18px;
154
+ align-items: start;
155
+ }
156
+ .label {
157
+ color: var(--muted);
158
+ font-size: 12px;
159
+ text-transform: uppercase;
160
+ letter-spacing: 0.08em;
161
+ margin-bottom: 8px;
162
+ }
163
+ .request { font-size: 20px; font-weight: 750; line-height: 1.35; margin-bottom: 12px; }
164
+ .meta-grid {
165
+ display: grid;
166
+ grid-template-columns: repeat(3, minmax(0, 1fr));
167
+ gap: 8px;
168
+ }
169
+ .meta {
170
+ background: var(--surface-soft);
171
+ border: 1px solid var(--line);
172
+ border-radius: 8px;
173
+ padding: 10px;
174
+ min-height: 64px;
175
+ }
176
+ .meta strong { display: block; font-size: 13px; margin-bottom: 4px; }
177
+ .meta span { color: var(--muted); font-size: 12px; overflow-wrap: anywhere; }
178
+ .ring {
179
+ height: 164px;
180
+ border: 1px solid var(--line);
181
+ border-radius: 8px;
182
+ display: grid;
183
+ place-items: center;
184
+ background: #f8fafc;
185
+ }
186
+ .score { text-align: center; }
187
+ .score b { font-size: 34px; display: block; }
188
+ .score span { color: var(--muted); font-size: 12px; }
189
+ .section {
190
+ background: var(--surface);
191
+ border: 1px solid var(--line);
192
+ border-radius: 8px;
193
+ padding: 16px;
194
+ margin-bottom: 14px;
195
+ }
196
+ .section-head {
197
+ display: flex;
198
+ align-items: center;
199
+ justify-content: space-between;
200
+ gap: 12px;
201
+ margin-bottom: 12px;
202
+ }
203
+ .section h2 { margin: 0; font-size: 15px; }
204
+ .pill {
205
+ display: inline-flex;
206
+ align-items: center;
207
+ min-height: 24px;
208
+ padding: 0 8px;
209
+ border-radius: 999px;
210
+ border: 1px solid var(--line);
211
+ color: var(--muted);
212
+ font-size: 12px;
213
+ background: #fbfcfd;
214
+ }
215
+ .list { display: grid; gap: 8px; }
216
+ .item {
217
+ display: grid;
218
+ grid-template-columns: 18px minmax(0, 1fr);
219
+ gap: 8px;
220
+ line-height: 1.38;
221
+ color: #2c3440;
222
+ font-size: 14px;
223
+ }
224
+ .checkmark {
225
+ width: 18px;
226
+ height: 18px;
227
+ border-radius: 5px;
228
+ display: grid;
229
+ place-items: center;
230
+ background: #eaf7ef;
231
+ color: var(--green);
232
+ font-size: 12px;
233
+ margin-top: 1px;
234
+ }
235
+ .checkmark.warn { background: #fff7e8; color: var(--orange); }
236
+ .doc-grid {
237
+ display: grid;
238
+ grid-template-columns: repeat(2, minmax(0, 1fr));
239
+ gap: 8px;
240
+ }
241
+ .doc-row {
242
+ min-height: 38px;
243
+ display: flex;
244
+ align-items: center;
245
+ justify-content: space-between;
246
+ gap: 10px;
247
+ border: 1px solid var(--line);
248
+ border-radius: 8px;
249
+ padding: 0 10px;
250
+ font-size: 13px;
251
+ background: #fbfcfd;
252
+ }
253
+ .bottom {
254
+ border-top: 1px solid var(--line);
255
+ background: var(--surface);
256
+ padding: 16px 22px;
257
+ overflow-x: auto;
258
+ }
259
+ .cards {
260
+ display: grid;
261
+ grid-template-columns: repeat(4, minmax(170px, 1fr));
262
+ gap: 12px;
263
+ max-width: 920px;
264
+ margin: 0 auto;
265
+ }
266
+ .action-card {
267
+ border: 1px solid var(--line);
268
+ background: var(--surface-soft);
269
+ border-radius: 8px;
270
+ padding: 12px;
271
+ min-height: 116px;
272
+ display: grid;
273
+ align-content: space-between;
274
+ gap: 10px;
275
+ }
276
+ .action-card strong { font-size: 14px; }
277
+ .action-card span { color: var(--muted); font-size: 12px; line-height: 1.35; }
278
+ .side {
279
+ border-left: 1px solid var(--line);
280
+ background: var(--surface);
281
+ padding: 18px;
282
+ overflow: auto;
283
+ }
284
+ .side-panel {
285
+ border: 1px solid var(--line);
286
+ border-radius: 8px;
287
+ padding: 14px;
288
+ margin-bottom: 14px;
289
+ background: #ffffff;
290
+ }
291
+ .side-panel h3 { margin: 0 0 12px; font-size: 14px; }
292
+ .status-row {
293
+ display: flex;
294
+ align-items: flex-start;
295
+ gap: 9px;
296
+ padding: 8px 0;
297
+ border-top: 1px solid #edf0f3;
298
+ font-size: 13px;
299
+ }
300
+ .status-row:first-of-type { border-top: 0; padding-top: 0; }
301
+ .status-row small { display: block; color: var(--muted); line-height: 1.35; margin-top: 2px; }
302
+ .badge {
303
+ width: 18px;
304
+ height: 18px;
305
+ border-radius: 99px;
306
+ display: grid;
307
+ place-items: center;
308
+ flex: 0 0 auto;
309
+ color: white;
310
+ font-size: 11px;
311
+ margin-top: 1px;
312
+ background: var(--green);
313
+ }
314
+ .badge.warn { background: var(--orange); }
315
+ .console {
316
+ background: #111827;
317
+ color: #e5e7eb;
318
+ border-radius: 8px;
319
+ padding: 12px;
320
+ min-height: 84px;
321
+ max-height: 160px;
322
+ overflow: auto;
323
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
324
+ font-size: 12px;
325
+ line-height: 1.45;
326
+ white-space: pre-wrap;
327
+ overflow-wrap: anywhere;
328
+ }
329
+ .split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }
330
+ .hidden { display: none; }
331
+ @media (max-width: 1180px) {
332
+ body { overflow: auto; }
333
+ .app { grid-template-columns: 240px minmax(0, 1fr); height: auto; min-height: 100vh; }
334
+ .side { grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--line); }
335
+ .main { min-height: 760px; }
336
+ }
337
+ @media (max-width: 820px) {
338
+ .app { display: block; }
339
+ .sidebar { border-right: 0; border-bottom: 1px solid var(--line); }
340
+ .main { display: block; }
341
+ .topbar, .bottom { position: static; }
342
+ .hero-grid, .meta-grid, .split, .cards, .doc-grid { grid-template-columns: 1fr; }
343
+ body { overflow: auto; }
344
+ }
345
+ </style>
346
+ </head>
347
+ <body>
348
+ <div class="app">
349
+ <aside class="sidebar">
350
+ <div class="brand"><div class="mark">GE</div><div>Gaslighting<br />Mission Control</div></div>
351
+ <div class="nav-title">Project</div>
352
+ <div class="nav-item active"><span id="navProject">Loading project</span><span class="nav-dot" id="navDot"></span></div>
353
+ <div class="nav-item"><span>Discipline Docs</span><span class="nav-dot ok"></span></div>
354
+ <div class="nav-item"><span>Codex Runtime</span><span class="nav-dot warn" id="runtimeDot"></span></div>
355
+ <div class="nav-title">Workflow</div>
356
+ <div class="nav-item"><span>1. Confirm mission</span></div>
357
+ <div class="nav-item"><span>2. Fix care risks</span></div>
358
+ <div class="nav-item"><span>3. Start Codex</span></div>
359
+ <div class="nav-title">Root</div>
360
+ <div class="nav-item"><span id="rootPath">.</span></div>
361
+ </aside>
362
+
363
+ <main class="main">
364
+ <header class="topbar">
365
+ <div class="title-block">
366
+ <div class="title" id="title">Mission Control</div>
367
+ <div class="subtitle" id="subtitle">Codex-first project discipline cockpit</div>
368
+ </div>
369
+ <div class="top-actions">
370
+ <button class="ghost" id="refreshBtn">Refresh</button>
371
+ <button class="primary" id="startBtn">Start Codex</button>
372
+ </div>
373
+ </header>
374
+
375
+ <section class="content">
376
+ <div class="mission">
377
+ <div class="hero-panel">
378
+ <div class="hero-grid">
379
+ <div>
380
+ <div class="label">Current Request</div>
381
+ <div class="request" id="requestText">Loading</div>
382
+ <div class="meta-grid">
383
+ <div class="meta"><strong>Type</strong><span id="projectType">-</span></div>
384
+ <div class="meta"><strong>Confidence</strong><span id="confidence">-</span></div>
385
+ <div class="meta"><strong>Stack</strong><span id="stackHints">-</span></div>
386
+ </div>
387
+ </div>
388
+ <div class="ring">
389
+ <div class="score"><b id="readinessScore">0%</b><span>readiness</span></div>
390
+ </div>
391
+ </div>
392
+ </div>
393
+
394
+ <div class="section">
395
+ <div class="section-head"><h2>Mission Purpose</h2><span class="pill">must not drift</span></div>
396
+ <div class="list" id="purposeList"></div>
397
+ </div>
398
+
399
+ <div class="split">
400
+ <div class="section">
401
+ <div class="section-head"><h2>Expected Pages</h2><span class="pill" id="pageCount">0</span></div>
402
+ <div class="list" id="pageList"></div>
403
+ </div>
404
+ <div class="section">
405
+ <div class="section-head"><h2>Missing Info</h2><span class="pill">non-blocking first</span></div>
406
+ <div class="list" id="missingList"></div>
407
+ </div>
408
+ </div>
409
+
410
+ <div class="section">
411
+ <div class="section-head"><h2>Generated Control Files</h2><span class="pill">Codex reads these</span></div>
412
+ <div class="doc-grid" id="docGrid"></div>
413
+ </div>
414
+
415
+ <div class="section">
416
+ <div class="section-head"><h2>Codex Launch Prompt</h2><span class="pill">editable before launch</span></div>
417
+ <textarea id="requestInput"></textarea>
418
+ </div>
419
+ </div>
420
+ </section>
421
+
422
+ <footer class="bottom">
423
+ <div class="cards">
424
+ <div class="action-card"><div><strong>Refresh Docs</strong><br /><span>Regenerate .gaslighting and root AGENTS.md from the current request.</span></div><button id="generateBtn">Generate</button></div>
425
+ <div class="action-card"><div><strong>Connect GitHub</strong><br /><span>Initialize Git if needed and connect origin without blocking implementation.</span></div><button id="githubBtn">Connect</button></div>
426
+ <div class="action-card"><div><strong>Doctor</strong><br /><span>Check discipline documents and operational care state.</span></div><button id="doctorBtn">Check</button></div>
427
+ <div class="action-card"><div><strong>Start Codex</strong><br /><span>Close the cockpit server and continue Codex in this same terminal.</span></div><button class="primary" id="startCardBtn">Start</button></div>
428
+ </div>
429
+ </footer>
430
+ </main>
431
+
432
+ <aside class="side">
433
+ <div class="side-panel">
434
+ <h3>Environment</h3>
435
+ <div id="careChecks"></div>
436
+ </div>
437
+ <div class="side-panel">
438
+ <h3>Runtime</h3>
439
+ <div class="status-row"><span class="badge">T</span><div><strong>Launch mode</strong><small id="launchMode">-</small></div></div>
440
+ <div class="status-row"><span class="badge">C</span><div><strong>Codex args</strong><small id="codexArgs">-</small></div></div>
441
+ <div class="status-row"><span class="badge warn">M</span><div><strong>Model strategy</strong><small>Codex-first now. Multi-model slots can be added later.</small></div></div>
442
+ </div>
443
+ <div class="side-panel">
444
+ <h3>GitHub Remote</h3>
445
+ <input id="githubInput" placeholder="https://github.com/user/repo.git" />
446
+ </div>
447
+ <div class="side-panel">
448
+ <h3>Console</h3>
449
+ <div class="console" id="consoleBox">Waiting for mission state...</div>
450
+ </div>
451
+ </aside>
452
+ </div>
453
+
454
+ <script>
455
+ var state = null;
456
+ var docs = [
457
+ 'AGENTS.md',
458
+ '.gaslighting/GASLIGHTING.md',
459
+ '.gaslighting/PRD.md',
460
+ '.gaslighting/MISSING_INFO.md',
461
+ '.gaslighting/ASSUMPTIONS.md',
462
+ '.gaslighting/DECISION_LOG.md',
463
+ '.gaslighting/MEMORY.md',
464
+ '.gaslighting/AGENT_RUNTIME.md',
465
+ '.gaslighting/PROJECT_CARE.md'
466
+ ];
467
+
468
+ function el(id) { return document.getElementById(id); }
469
+ function log(message) { el('consoleBox').textContent = message; }
470
+ function item(text, warn) {
471
+ var row = document.createElement('div');
472
+ row.className = 'item';
473
+ var mark = document.createElement('div');
474
+ mark.className = warn ? 'checkmark warn' : 'checkmark';
475
+ mark.textContent = warn ? '!' : 'OK';
476
+ var body = document.createElement('div');
477
+ body.textContent = text;
478
+ row.appendChild(mark);
479
+ row.appendChild(body);
480
+ return row;
481
+ }
482
+ function statusRow(check) {
483
+ var row = document.createElement('div');
484
+ row.className = 'status-row';
485
+ var badge = document.createElement('span');
486
+ badge.className = check.ok ? 'badge' : 'badge warn';
487
+ badge.textContent = check.ok ? 'OK' : '!';
488
+ var body = document.createElement('div');
489
+ var strong = document.createElement('strong');
490
+ strong.textContent = check.label;
491
+ var small = document.createElement('small');
492
+ small.textContent = check.message;
493
+ body.appendChild(strong);
494
+ body.appendChild(small);
495
+ row.appendChild(badge);
496
+ row.appendChild(body);
497
+ return row;
498
+ }
499
+ function render(next) {
500
+ state = next;
501
+ el('title').textContent = 'Gaslighting Mission Control';
502
+ el('subtitle').textContent = state.root;
503
+ el('rootPath').textContent = state.root;
504
+ el('navProject').textContent = state.analysis.projectType;
505
+ el('requestText').textContent = state.request;
506
+ el('requestInput').value = state.request;
507
+ el('projectType').textContent = state.analysis.projectType;
508
+ el('confidence').textContent = state.analysis.confidence;
509
+ el('stackHints').textContent = state.analysis.detectedStackHints.length ? state.analysis.detectedStackHints.join(', ') : 'default web stack';
510
+ el('codexArgs').textContent = state.codexArgs;
511
+ el('launchMode').textContent = state.launchMode === 'new_terminal' ? 'New terminal window' : 'Same terminal handoff';
512
+
513
+ var warningCount = state.care.checks.filter(function (check) { return !check.ok; }).length;
514
+ var score = Math.max(0, Math.round(((state.care.checks.length - warningCount) / Math.max(1, state.care.checks.length)) * 100));
515
+ el('readinessScore').textContent = score + '%';
516
+ el('navDot').className = warningCount ? 'nav-dot warn' : 'nav-dot ok';
517
+ el('runtimeDot').className = state.codexAvailable ? 'nav-dot ok' : 'nav-dot warn';
518
+
519
+ el('purposeList').replaceChildren.apply(el('purposeList'), state.purpose.map(function (text) { return item(text, false); }));
520
+ el('pageCount').textContent = String(state.pages.length);
521
+ el('pageList').replaceChildren.apply(el('pageList'), state.pages.map(function (text) { return item(text, false); }));
522
+ el('missingList').replaceChildren.apply(el('missingList'), state.analysis.missingInfo.map(function (info) { return item(info.item + ': ' + info.temporaryAssumption, info.blocking !== 'non_blocking'); }));
523
+
524
+ var careNodes = state.care.checks.map(statusRow);
525
+ el('careChecks').replaceChildren.apply(el('careChecks'), careNodes);
526
+
527
+ var docNodes = docs.map(function (name) {
528
+ var row = document.createElement('div');
529
+ row.className = 'doc-row';
530
+ var left = document.createElement('span');
531
+ left.textContent = name;
532
+ var right = document.createElement('span');
533
+ right.className = 'pill';
534
+ right.textContent = state.docsPresent[name] ? 'ready' : 'missing';
535
+ row.appendChild(left);
536
+ row.appendChild(right);
537
+ return row;
538
+ });
539
+ el('docGrid').replaceChildren.apply(el('docGrid'), docNodes);
540
+ log('Mission loaded. Review the warnings, then start Codex.');
541
+ }
542
+ async function api(path, options) {
543
+ var response = await fetch(path, options);
544
+ var data = await response.json();
545
+ if (!response.ok) throw new Error(data.message || 'Request failed.');
546
+ return data;
547
+ }
548
+ async function refresh() {
549
+ render(await api('/api/state'));
550
+ }
551
+ async function generate() {
552
+ log('Generating control documents...');
553
+ var data = await api('/api/generate', {
554
+ method: 'POST',
555
+ headers: { 'content-type': 'application/json' },
556
+ body: JSON.stringify({ request: el('requestInput').value })
557
+ });
558
+ render(data.state);
559
+ log('Documents regenerated. Codex can now start from the updated mission.');
560
+ }
561
+ async function startCodex() {
562
+ log('Starting Codex...');
563
+ var data = await api('/api/start-codex', {
564
+ method: 'POST',
565
+ headers: { 'content-type': 'application/json' },
566
+ body: JSON.stringify({ request: el('requestInput').value })
567
+ });
568
+ log(data.message + '\\n\\n' + data.command + '\\n\\nThe original terminal will continue as Codex.');
569
+ }
570
+ async function connectGithub() {
571
+ var githubUrl = el('githubInput').value.trim();
572
+ if (!githubUrl) {
573
+ log('Paste a GitHub remote URL first.');
574
+ return;
575
+ }
576
+ var data = await api('/api/connect-github', {
577
+ method: 'POST',
578
+ headers: { 'content-type': 'application/json' },
579
+ body: JSON.stringify({ githubUrl: githubUrl })
580
+ });
581
+ render(data.state);
582
+ log(data.results.map(function (result) { return (result.ok ? 'PASS ' : 'WARN ') + result.step + ': ' + result.message; }).join('\\n'));
583
+ }
584
+ el('refreshBtn').addEventListener('click', refresh);
585
+ el('generateBtn').addEventListener('click', generate);
586
+ el('startBtn').addEventListener('click', startCodex);
587
+ el('startCardBtn').addEventListener('click', startCodex);
588
+ el('githubBtn').addEventListener('click', connectGithub);
589
+ el('doctorBtn').addEventListener('click', function () {
590
+ if (!state) return;
591
+ var warnings = state.care.checks.filter(function (check) { return !check.ok; });
592
+ log(warnings.length ? warnings.map(function (check) { return 'WARN ' + check.label + ': ' + check.action; }).join('\\n') : 'PASS care checks look stable.');
593
+ });
594
+ refresh().catch(function (error) { log(error.message); });
595
+ </script>
596
+ </body>
597
+ </html>`;
598
+ }