0xray 4.0.32 → 4.0.34

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 (88) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.opencode/agents/api-design.yml +31 -0
  3. package/.opencode/agents/architect.yml +15 -0
  4. package/.opencode/agents/architecture-patterns.yml +14 -0
  5. package/.opencode/agents/auto-format.yml +14 -0
  6. package/.opencode/agents/backend-engineer.yml +12 -0
  7. package/.opencode/agents/boot-orchestrator.yml +14 -0
  8. package/.opencode/agents/bug-triage-specialist.yml +15 -0
  9. package/.opencode/agents/code-analyzer.yml +12 -0
  10. package/.opencode/agents/code-reviewer.yml +14 -0
  11. package/.opencode/agents/content-creator.yml +12 -0
  12. package/.opencode/agents/database-engineer.yml +12 -0
  13. package/.opencode/agents/devops-engineer.yml +12 -0
  14. package/.opencode/agents/framework-compliance-audit.yml +14 -0
  15. package/.opencode/agents/frontend-engineer.yml +12 -0
  16. package/.opencode/agents/frontend-ui-ux-engineer.yml +12 -0
  17. package/.opencode/agents/git-workflow.yml +15 -0
  18. package/.opencode/agents/growth-strategist.yml +12 -0
  19. package/.opencode/agents/hermes-agent.yml +12 -0
  20. package/.opencode/agents/inference-improve.yml +12 -0
  21. package/.opencode/agents/lint.yml +14 -0
  22. package/.opencode/agents/log-monitor.yml +12 -0
  23. package/.opencode/agents/mobile-developer.yml +12 -0
  24. package/.opencode/agents/model-health-check.yml +12 -0
  25. package/.opencode/agents/multimodal-looker.yml +12 -0
  26. package/.opencode/agents/performance-analysis.yml +12 -0
  27. package/.opencode/agents/performance-engineer.yml +12 -0
  28. package/.opencode/agents/performance-optimization.yml +12 -0
  29. package/.opencode/agents/processor-pipeline.yml +14 -0
  30. package/.opencode/agents/project-analysis.yml +12 -0
  31. package/.opencode/agents/refactorer.yml +12 -0
  32. package/.opencode/agents/researcher.yml +12 -0
  33. package/.opencode/agents/security-auditor.yml +12 -0
  34. package/.opencode/agents/security-scan.yml +12 -0
  35. package/.opencode/agents/seo-consultant.yml +12 -0
  36. package/.opencode/agents/session-management.yml +12 -0
  37. package/.opencode/agents/state-manager.yml +12 -0
  38. package/.opencode/agents/storyteller.yml +12 -0
  39. package/.opencode/agents/strategist.yml +12 -0
  40. package/.opencode/agents/tech-writer.yml +12 -0
  41. package/.opencode/agents/testing-best-practices.yml +12 -0
  42. package/.opencode/agents/testing-lead.yml +12 -0
  43. package/.opencode/agents/ui-ux-design.yml +12 -0
  44. package/.opencode/codex.codex +8 -0
  45. package/.opencode/commands/auto-format.md +99 -0
  46. package/.opencode/commands/auto-summary-capture.md +90 -0
  47. package/.opencode/commands/dependency-audit.md +184 -0
  48. package/.opencode/commands/enforcer-daily-scan.md +137 -0
  49. package/.opencode/commands/framework-compliance-audit.md +205 -0
  50. package/.opencode/commands/interactive-validator.md +75 -0
  51. package/.opencode/commands/job-summary-logger.md +68 -0
  52. package/.opencode/commands/lint.md +11 -0
  53. package/.opencode/commands/mode-switch.md +95 -0
  54. package/.opencode/commands/model-health-check.md +186 -0
  55. package/.opencode/commands/performance-analysis.md +144 -0
  56. package/.opencode/commands/pre-commit-introspection.md +185 -0
  57. package/.opencode/commands/pre-commit-introspection.sh +133 -0
  58. package/.opencode/commands/security-scan.md +157 -0
  59. package/.opencode/commands/sisyphus-validation.md +128 -0
  60. package/.opencode/commands/summary-logger.md +83 -0
  61. package/.opencode/enforcer-config.json +282 -0
  62. package/.opencode/skills/orchestrator/SKILL.md +3 -3
  63. package/.opencode/workflows/post-deployment-audit.yml +123 -0
  64. package/.xray/features.json +1 -1
  65. package/dist/CHANGELOG.md +17 -0
  66. package/dist/cli/goggles-status.js +1 -1
  67. package/dist/governance/governance-service.js +11 -1
  68. package/dist/integrations/grok/hooks/grok-hook-utils.js +7 -4
  69. package/dist/integrations/grok/hooks/pre-tool-use.js +7 -1
  70. package/dist/integrations/grok/hooks/session-start.js +5 -2
  71. package/dist/integrations/hooks/goggles-mcp.mjs +3 -36
  72. package/dist/integrations/hooks/goggles-pipeline.d.mts +2 -0
  73. package/dist/integrations/hooks/goggles-pipeline.mjs +438 -82
  74. package/dist/integrations/hooks/goggles-planes.json +23 -0
  75. package/dist/integrations/hooks/plates.cjs +15 -1
  76. package/dist/integrations/hooks/station-hook-runtime.cjs +53 -5
  77. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  78. package/dist/mcps/orchestrator/config/agent-capabilities.js +5 -0
  79. package/dist/nucleus/thin-dispatch.d.ts +4 -0
  80. package/dist/nucleus/thin-dispatch.js +9 -1
  81. package/dist/skills/orchestrator/SKILL.md +3 -3
  82. package/package.json +1 -1
  83. package/scripts/foundry/mint-suit.cjs +6 -1
  84. package/scripts/node/install-bridges.cjs +24 -7
  85. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  86. package/src/mcps/orchestrator/config/agent-capabilities.ts +4 -0
  87. package/src/skills/orchestrator/SKILL.md +3 -3
  88. package/xray/features.json +1 -1
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Goggles. Kind 0 name-checks the six outer names.
3
- * Digest and triage return one card. Empty stays empty.
3
+ * A named plane returns its view. Field pipes fill what they can see.
4
+ * The view keeps every field for the next look.
4
5
  * A held plane stays quiet. A leave is denied.
5
6
  */
6
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
7
+ import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs';
8
+ import { createRequire } from 'node:module';
7
9
  import { dirname, join, resolve } from 'node:path';
8
10
  import { fileURLToPath } from 'node:url';
9
11
 
@@ -204,10 +206,20 @@ function gogglesAnswer(argv) {
204
206
  return { text: cards.map((card) => formatCardText(card)).join('\n\n'), cards };
205
207
  }
206
208
  const planes = acceptedPlanes().filter((name) => lower.includes(name));
207
- if (planes.length !== 1) return none('');
209
+ if (planes.length !== 1) {
210
+ if (!planes.length && cardPlanesNamed(lower).length) {
211
+ const cards = collectCards('digest', lower);
212
+ if (cards && cards.length) {
213
+ return { text: cards.map((card) => formatCardText(card)).join('\n\n'), cards };
214
+ }
215
+ }
216
+ return none('');
217
+ }
208
218
  const scopes = findScopes(words.filter((word) => word.toLowerCase() !== planes[0]));
209
219
  if (scopes.length > 1) return none('');
210
- return none(readingLine(planes[0], scopes[0] || ''));
220
+ const line = readingLine(planes[0], scopes[0] || '');
221
+ const file = moduleFile(planes[0]);
222
+ return none(file ? `${line} File: ${file}` : line);
211
223
  }
212
224
 
213
225
  export function readGoggles(argv) {
@@ -222,23 +234,17 @@ function cardPlanesNamed(lower) {
222
234
  return CARD_PLANES.filter((name) => lower.includes(name));
223
235
  }
224
236
 
225
- function listedFiles(plane, scope) {
226
- const files = Array.isArray(plane.files) ? plane.files.map(String) : [];
227
- if (scope === 'one artifact') return files.length === 1 ? [files[0]] : null;
228
- const unpathed = Array.isArray(plane.unpathed) ? plane.unpathed : [];
229
- const parts = files.slice();
230
- for (const name of unpathed) parts.push(name === 'mill' ? 'mill has no path' : String(name));
231
- return parts;
237
+ function listedFiles(plane) {
238
+ return Array.isArray(plane.files) ? plane.files.map(String) : [];
232
239
  }
233
240
 
234
241
  function cardView(plane, scope, flavor, root) {
235
- const listed = listedFiles(plane, scope);
236
- if (!listed) return null;
242
+ const listed = listedFiles(plane);
237
243
  const view = {
238
244
  plane: plane.id,
239
245
  flavor,
240
246
  scope: scope || '',
241
- from: plane.id === 'ground' ? '' : 'ground',
247
+ from: plane.id === 'ground' ? '' : (plane.from || 'ground'),
242
248
  digest: plane.digest || '',
243
249
  plate: plane.plate || '',
244
250
  entry: plane.entry || '',
@@ -248,7 +254,6 @@ function cardView(plane, scope, flavor, root) {
248
254
  setup: plane.setup || '',
249
255
  teardown: plane.teardown || '',
250
256
  worn: plane.worn || '',
251
- narrow: scope === 'one flow' || scope === 'one artifact',
252
257
  };
253
258
  if (flavor === 'triage') {
254
259
  const filled = new Set(filledOf(plane));
@@ -272,10 +277,7 @@ function formatCardText(view) {
272
277
  ['Teardown', view.teardown],
273
278
  ['Worn', view.worn],
274
279
  ];
275
- const shown = view.narrow
276
- ? rows.filter(([key]) => key === 'Plane' || key === 'Digest' || key === 'Files')
277
- : rows;
278
- let text = shown.map(([key, value]) => `${key}: ${value}`.trimEnd()).join('\n');
280
+ let text = rows.map(([key, value]) => `${key}: ${value}`.trimEnd()).join('\n');
279
281
  if (view.flavor === 'triage') {
280
282
  const lines = [];
281
283
  if (view.empty.length) lines.push(`Empty: ${view.empty.join(', ')}`);
@@ -286,23 +288,19 @@ function formatCardText(view) {
286
288
  }
287
289
 
288
290
  export function formatCardPane(view) {
289
- const rows = [['Plane', view.plane]];
290
- if (!view.narrow) rows.push(['From', view.from]);
291
- rows.push(['Digest', view.digest]);
292
- if (!view.narrow) {
293
- rows.push(
294
- ['Plate', view.plate],
295
- ['Entry', view.entry],
296
- ['Exit', view.exit],
297
- ['Files', view.files.join(', ')],
298
- ['Skills', view.skills],
299
- ['Setup', view.setup],
300
- ['Teardown', view.teardown],
301
- ['Worn', view.worn],
302
- );
303
- } else {
304
- rows.push(['Files', view.files.join(', ')]);
305
- }
291
+ const rows = [
292
+ ['Plane', view.plane],
293
+ ['From', view.from],
294
+ ['Digest', view.digest],
295
+ ['Plate', view.plate],
296
+ ['Entry', view.entry],
297
+ ['Exit', view.exit],
298
+ ['Files', view.files.join(', ')],
299
+ ['Skills', view.skills],
300
+ ['Setup', view.setup],
301
+ ['Teardown', view.teardown],
302
+ ['Worn', view.worn],
303
+ ];
306
304
  if (view.flavor === 'triage') {
307
305
  if (Array.isArray(view.empty) && view.empty.length) rows.push(['Empty', view.empty.join(', ')]);
308
306
  if (view.exam) rows.push(['', view.exam]);
@@ -319,20 +317,15 @@ export function formatCardPane(view) {
319
317
  function collectCards(flavor, lower) {
320
318
  const named = cardPlanesNamed(lower);
321
319
  const scopes = findScopes(lower.filter((word) => word !== flavor && !named.includes(word)));
322
- if (named.length > 1 || scopes.length > 1) return null;
320
+ if (scopes.length > 1) return null;
323
321
  const scope = scopes[0] || '';
324
- if (!named.length && (scope === 'part' || scope === 'one flow' || scope === 'one artifact')) return null;
325
322
  const platesDir = findPlatesDir(HERE);
326
- if (!platesDir) return null;
327
323
  const ids = named.length ? named : CARD_PLANES;
328
- const root = repoRootFrom(platesDir);
324
+ const root = sourceRoot(platesDir);
329
325
  const cards = [];
330
326
  for (const id of ids) {
331
- const plane = assemblePlane(id, platesDir);
332
- if (!plane) continue;
333
- const view = cardView(plane, scope, flavor, root);
334
- if (!view) return null;
335
- cards.push(view);
327
+ const plane = growPlane(id, platesDir, stateRoot());
328
+ cards.push(cardView(plane, scope, flavor, root));
336
329
  }
337
330
  return cards;
338
331
  }
@@ -366,6 +359,45 @@ function boxTitles(line) {
366
359
  return titles;
367
360
  }
368
361
 
362
+ function flowEnds(body) {
363
+ if (!body || body.includes('INPUT LAYER')) return null;
364
+ const stages = [];
365
+ let title = '';
366
+ let notes = [];
367
+ let open = false;
368
+ for (const line of String(body).split(/\r?\n/)) {
369
+ if (line.includes('┌')) {
370
+ open = true;
371
+ title = '';
372
+ notes = [];
373
+ continue;
374
+ }
375
+ if (!open) continue;
376
+ if (line.includes('└')) {
377
+ if (title) stages.push({ title, notes: notes.slice() });
378
+ open = false;
379
+ continue;
380
+ }
381
+ if (!/[A-Za-z]/.test(line)) continue;
382
+ const titles = boxTitles(line);
383
+ if (titles.length && !title) {
384
+ title = titles.join(' ');
385
+ continue;
386
+ }
387
+ const note = line.replace(/[│|]/g, ' ').replace(/^[\s·.-]+/, '').trim();
388
+ if (note) notes.push(note);
389
+ }
390
+ if (stages.length < 2) return null;
391
+ const first = stages[0];
392
+ const last = stages[stages.length - 1];
393
+ return {
394
+ entry: first.title,
395
+ exit: last.title,
396
+ setup: first.notes.join(' '),
397
+ teardown: last.notes.join(' '),
398
+ };
399
+ }
400
+
369
401
  function doorsOf(body) {
370
402
  let layer = '';
371
403
  let want = false;
@@ -433,40 +465,336 @@ export function filledOf(plane) {
433
465
  return FIELD_ORDER.filter((name) => flags[name]);
434
466
  }
435
467
 
468
+ function plateSource(id, platesDir) {
469
+ if (!platesDir || id === 'ground') return { body: '', path: '' };
470
+ const abs = join(platesDir, `${id}.md`);
471
+ if (!existsSync(abs)) return { body: '', path: '' };
472
+ return {
473
+ body: readFileSync(abs, 'utf8'),
474
+ path: join('docs-site', 'docs', 'plates', `${id}.md`),
475
+ };
476
+ }
477
+
478
+ function sectionOf(body, title) {
479
+ if (!body) return '';
480
+ const lines = stripFrontmatter(body).split(/\r?\n/);
481
+ const heading = new RegExp(`^#{1,3}\\s+${title}\\s*$`, 'i');
482
+ let on = false;
483
+ const prose = [];
484
+ for (const line of lines) {
485
+ if (heading.test(line)) {
486
+ on = true;
487
+ continue;
488
+ }
489
+ if (!on) continue;
490
+ if (/^#{1,3}\s+/.test(line) || line.startsWith('```')) break;
491
+ if (!line.trim()) {
492
+ if (prose.length) break;
493
+ continue;
494
+ }
495
+ prose.push(line.trim());
496
+ }
497
+ return prose.join(' ');
498
+ }
499
+
436
500
  export function assemblePlane(id, platesDir) {
437
501
  const extra = overlayTable()[id] || {};
438
- let plate = null;
439
- let digest = extra.digest || '';
440
- let doors = { entry: null, exit: null };
441
- if (id !== 'ground') {
442
- const abs = join(platesDir, `${id}.md`);
443
- if (!existsSync(abs)) return null;
444
- const raw = readFileSync(abs, 'utf8');
445
- plate = join('docs-site', 'docs', 'plates', `${id}.md`);
446
- if (!digest) digest = takeOf(stripFrontmatter(raw));
447
- doors = doorsOf(raw);
448
- }
449
- if (!digest) return null;
502
+ const src = plateSource(id, platesDir);
503
+ const doors = src.body ? doorsOf(src.body) : { entry: null, exit: null };
504
+ const flow = src.body ? flowEnds(src.body) : null;
505
+ const digest = extra.digest || (src.body ? takeOf(stripFrontmatter(src.body)) : '');
506
+ const files = Array.isArray(extra.files) ? extra.files.map(String) : [];
450
507
  return {
451
508
  id,
452
509
  digest,
453
- plate,
454
- entry: extra.entry || doors.entry || null,
455
- exit: extra.exit || doors.exit || null,
510
+ plate: src.path,
511
+ entry: extra.entry || doors.entry || (flow && flow.entry) || '',
512
+ exit: extra.exit || doors.exit || (flow && flow.exit) || '',
456
513
  entryIsMark: Boolean(extra.entry),
457
514
  exitIsMark: Boolean(extra.exit),
458
- files: extra.files || null,
515
+ files,
459
516
  unpathed: extra.unpathed || null,
460
- skills: extra.skills || null,
461
- setup: extra.setup || null,
462
- teardown: extra.teardown || null,
463
- worn: extra.worn || null,
517
+ skills: extra.skills || '',
518
+ setup: extra.setup || sectionOf(src.body, 'Setup') || (flow && flow.setup) || '',
519
+ teardown: extra.teardown || sectionOf(src.body, 'Teardown') || (flow && flow.teardown) || '',
520
+ worn: extra.worn || '',
521
+ clues: cluesOf(src.body),
464
522
  mark: extra.mark || null,
465
523
  wornMark: extra.wornMark || null,
466
524
  pick: extra.pick || null,
467
525
  };
468
526
  }
469
527
 
528
+ const VIEW_KEYS = ['from', 'digest', 'plate', 'entry', 'exit', 'files', 'skills', 'setup', 'teardown', 'worn'];
529
+
530
+ function stateRoot() {
531
+ const env = process.env.GOGGLES_ROOT;
532
+ if (typeof env === 'string' && env.trim() && env.trim() !== '.') return resolve(env.trim());
533
+ return process.cwd();
534
+ }
535
+
536
+ function sourceRoot(platesDir) {
537
+ return platesDir ? repoRootFrom(platesDir) : stateRoot();
538
+ }
539
+
540
+ function viewsPath(root) {
541
+ return join(root, '.xray', 'state', 'goggles-views.json');
542
+ }
543
+
544
+ function loadViews(root) {
545
+ try {
546
+ const data = JSON.parse(readFileSync(viewsPath(root), 'utf8'));
547
+ return data && typeof data === 'object' && !Array.isArray(data) ? data : {};
548
+ } catch {
549
+ return {};
550
+ }
551
+ }
552
+
553
+ function saveViews(root, views) {
554
+ const file = viewsPath(root);
555
+ mkdirSync(dirname(file), { recursive: true });
556
+ writeFileSync(file, `${JSON.stringify(views, null, 2)}\n`);
557
+ }
558
+
559
+ function cluesOf(body) {
560
+ if (!body) return [];
561
+ const clues = [];
562
+ for (const match of String(body).matchAll(/`([^`]{4,80})`/g)) clues.push(match[1]);
563
+ for (const line of String(body).split(/\r?\n/)) {
564
+ if (!line.includes('│')) continue;
565
+ for (const title of boxTitles(line)) {
566
+ if (title.length >= 8 && title.length <= 60) clues.push(title);
567
+ }
568
+ }
569
+ return clues;
570
+ }
571
+
572
+ const sourceCache = new Map();
573
+
574
+ function sourceTexts(root) {
575
+ if (sourceCache.has(root)) return sourceCache.get(root);
576
+ const out = [];
577
+ const walk = (dir, depth) => {
578
+ if (depth > 8) return;
579
+ let entries = [];
580
+ try {
581
+ entries = readdirSync(dir, { withFileTypes: true });
582
+ } catch {
583
+ return;
584
+ }
585
+ for (const entry of entries) {
586
+ if (entry.name === 'node_modules' || entry.name === '__tests__' || entry.name === 'dist') continue;
587
+ const abs = join(dir, entry.name);
588
+ if (entry.isDirectory()) {
589
+ walk(abs, depth + 1);
590
+ continue;
591
+ }
592
+ if (!/\.(ts|mjs|cjs)$/.test(entry.name) || entry.name.endsWith('.test.ts')) continue;
593
+ try {
594
+ out.push({ rel: abs.slice(root.length + 1), text: readFileSync(abs, 'utf8') });
595
+ } catch {
596
+ /* unreadable source is not a plane file */
597
+ }
598
+ }
599
+ };
600
+ walk(join(root, 'src'), 0);
601
+ sourceCache.set(root, out);
602
+ return out;
603
+ }
604
+
605
+ function fileFromClues(clues, root, planeId) {
606
+ const texts = sourceTexts(root);
607
+ const votes = new Map();
608
+ for (const clue of clues) {
609
+ const hits = [];
610
+ for (const file of texts) {
611
+ if (!file.text.includes(clue)) continue;
612
+ hits.push(file.rel);
613
+ if (hits.length > 8) break;
614
+ }
615
+ if (!hits.length || hits.length > 8) continue;
616
+ const weight = 9 - hits.length;
617
+ for (const rel of hits) votes.set(rel, (votes.get(rel) || 0) + weight);
618
+ }
619
+ const token = String(planeId || '').split('-')[0].toLowerCase();
620
+ const named = token ? [...votes.keys()].filter((rel) => rel.toLowerCase().includes(token)) : [];
621
+ const pool = new Set(named.length ? named : [...votes.keys()]);
622
+ let best = '';
623
+ let bestRank = -1;
624
+ for (const [rel, score] of votes) {
625
+ if (!pool.has(rel)) continue;
626
+ let rank = score * 10;
627
+ if (token && rel.toLowerCase().includes(token)) rank += 20;
628
+ if (rel.startsWith('src/cli/')) rank -= 3;
629
+ if (rank > bestRank || (rank === bestRank && rel.length < best.length)) {
630
+ best = rel;
631
+ bestRank = rank;
632
+ }
633
+ }
634
+ return best;
635
+ }
636
+
637
+ function skillFor(id, root) {
638
+ const dir = join(root, 'src', 'skills');
639
+ if (!existsSync(dir)) return '';
640
+ let names = [];
641
+ try {
642
+ names = readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
643
+ } catch {
644
+ return '';
645
+ }
646
+ const hit = names.filter((name) => name === id || name.startsWith(`${id}-`));
647
+ if (hit.length !== 1) return '';
648
+ const rel = join('src', 'skills', hit[0], 'SKILL.md');
649
+ return existsSync(join(root, rel)) ? rel : '';
650
+ }
651
+
652
+ function mappedFile(name) {
653
+ try {
654
+ const data = JSON.parse(readFileSync(planesFile(), 'utf8'));
655
+ const entry = data[name];
656
+ if (!entry || typeof entry !== 'object') return '';
657
+ if (typeof entry.file === 'string' && entry.file) return entry.file;
658
+ } catch {
659
+ return '';
660
+ }
661
+ return '';
662
+ }
663
+
664
+ function moduleFile(name) {
665
+ const mapped = mappedFile(name);
666
+ if (mapped) return mapped;
667
+ const platesDir = findPlatesDir(HERE);
668
+ const root = sourceRoot(platesDir);
669
+ const hit = sourceTexts(root).find((file) => file.rel.endsWith(`/${name}.ts`) || file.rel.endsWith(`/${name}.mjs`));
670
+ return hit ? hit.rel : '';
671
+ }
672
+
673
+ function hintLine(name) {
674
+ const cards = lookCards(['digest', name]);
675
+ const files = cards && cards[0] && Array.isArray(cards[0].files) ? cards[0].files : [];
676
+ return files.length ? `${name}: ${files.join(', ')}` : '';
677
+ }
678
+
679
+ export function suitHint(text) {
680
+ const words = intentWords(text);
681
+ const named = CARD_PLANES.filter((name) => words.includes(name));
682
+ if (!named.length) return lookVerb(words) ? 'Name one plane.' : '';
683
+ return named.map(hintLine).filter(Boolean).join('\n');
684
+ }
685
+
686
+ const DEEP_SEARCH = /search_codebase|find_implementation|get_documentation/i;
687
+
688
+ function researchCall(toolName, text) {
689
+ const tool = String(toolName || '');
690
+ const spoken = String(text || '');
691
+ if (/researcher|explorer|deep[- ]?research/i.test(tool) || DEEP_SEARCH.test(tool)) return true;
692
+ return /bash|shell/i.test(tool) && (/researcher|explorer|deep[- ]?research/i.test(spoken) || DEEP_SEARCH.test(spoken));
693
+ }
694
+
695
+ /** One plane: the files the suit continues with. A look with no plane stops. Two planes stay quiet. */
696
+ export function lensPlane(text) {
697
+ const hint = suitHint(String(text || ''));
698
+ if (hint === 'Name one plane.') return { stop: true, files: [] };
699
+ if (!hint || hint.includes('\n')) return { stop: false, files: [] };
700
+ const mark = hint.indexOf(': ');
701
+ if (mark < 0) return { stop: false, files: [] };
702
+ const files = hint.slice(mark + 2).split(',').map((part) => part.trim()).filter(Boolean);
703
+ return { stop: false, files };
704
+ }
705
+
706
+ function lensPage(root, names) {
707
+ if (!names.length) return 'Name one plane.';
708
+ const platesDir = findPlatesDir(HERE);
709
+ const parts = [];
710
+ for (const id of names) {
711
+ const plane = growPlane(id, platesDir, root);
712
+ parts.push(formatCardText(cardView(plane, '', 'digest', sourceRoot(platesDir))));
713
+ }
714
+ return parts.join('\n\n');
715
+ }
716
+
717
+ function stampPlane(root, id) {
718
+ try {
719
+ const plates = createRequire(import.meta.url)('./plates.cjs');
720
+ if (!plates.PLATE_IDS.includes(id)) return;
721
+ plates.stampPlateIfMissing(root, id);
722
+ } catch {
723
+ /* a missed stamp must not fail the stop */
724
+ }
725
+ }
726
+
727
+ /** A search stays stopped. The next move is the file on the lens, not the same search again. */
728
+ export function lensBeforeResearch(root, toolName, text) {
729
+ const spoken = String(text || '');
730
+ if (!researchCall(toolName, spoken)) return null;
731
+ const names = CARD_PLANES.filter((name) => intentWords(spoken).includes(name));
732
+ if (names.length === 1) stampPlane(root, names[0]);
733
+ return { gate: 'lens', decision: 'deny', reason: lensPage(root, names) };
734
+ }
735
+
736
+ function seenFields(plane, root) {
737
+ const files = Array.isArray(plane.files) ? plane.files.slice() : [];
738
+ let worn = plane.worn || '';
739
+ if (!files.length && Array.isArray(plane.unpathed) && plane.unpathed.includes('mill')) {
740
+ if (existsSync(join(root, 'scripts', 'foundry'))) files.push('scripts/foundry');
741
+ }
742
+ if (!files.length && Array.isArray(plane.clues) && plane.clues.length) {
743
+ const found = fileFromClues(plane.clues, root, plane.id);
744
+ if (found) files.push(found);
745
+ }
746
+ let skills = plane.skills || '';
747
+ if (!skills) skills = skillFor(plane.id, root);
748
+ if (!worn) {
749
+ for (const file of files) {
750
+ if (!file.startsWith('src/')) continue;
751
+ let rel = file.slice(4);
752
+ if (rel.endsWith('.ts')) rel = rel.replace(/\.ts$/, '.js');
753
+ else if (!/\.(cjs|mjs|js)$/.test(rel)) continue;
754
+ const next = `dist/${rel}`;
755
+ if (existsSync(join(root, next))) {
756
+ worn = next;
757
+ break;
758
+ }
759
+ }
760
+ }
761
+ return {
762
+ from: plane.id === 'ground' ? '' : 'ground',
763
+ digest: plane.digest || '',
764
+ plate: plane.plate || '',
765
+ entry: plane.entry || '',
766
+ exit: plane.exit || '',
767
+ files,
768
+ skills,
769
+ setup: plane.setup || '',
770
+ teardown: plane.teardown || '',
771
+ worn,
772
+ };
773
+ }
774
+
775
+ function kept(stored, seen, key) {
776
+ if (key === 'files') {
777
+ if (Array.isArray(seen.files) && seen.files.length) return seen.files.map(String);
778
+ return Array.isArray(stored.files) ? stored.files.map(String) : [];
779
+ }
780
+ if (seen[key]) return seen[key];
781
+ return stored[key] || '';
782
+ }
783
+
784
+ export function growPlane(id, platesDir, root) {
785
+ const seen = seenFields(assemblePlane(id, platesDir), sourceRoot(platesDir));
786
+ const views = loadViews(root);
787
+ const stored = views[id] && typeof views[id] === 'object' ? views[id] : {};
788
+ const grown = { id };
789
+ for (const key of VIEW_KEYS) grown[key] = kept(stored, seen, key);
790
+ if (id === 'ground') grown.from = stored.from || '';
791
+ views[id] = {};
792
+ for (const key of VIEW_KEYS) views[id][key] = grown[key];
793
+ saveViews(root, views);
794
+ const base = assemblePlane(id, platesDir);
795
+ return { ...base, ...grown };
796
+ }
797
+
470
798
  function repoRootFrom(platesDir) {
471
799
  return dirname(dirname(dirname(platesDir)));
472
800
  }
@@ -498,26 +826,39 @@ function fileHas(root, rel, mark) {
498
826
  }
499
827
 
500
828
 
501
- function examinePlane(plane, root) {
829
+ function factoryName(rel) {
830
+ return rel.startsWith('src/') || rel.startsWith('grok-bot/');
831
+ }
832
+
833
+ function wornHolds(plane, root) {
834
+ if (!plane.worn || !existsSync(join(root, plane.worn))) return false;
835
+ if (!plane.wornMark) return true;
836
+ return fileHas(root, plane.worn, plane.wornMark);
837
+ }
838
+
839
+ export function examinePlane(plane, root) {
502
840
  const files = Array.isArray(plane.files) ? plane.files : [];
503
841
  const checkable = files.length > 0 || plane.skills || plane.worn;
504
842
  if (!checkable) return drawing();
843
+ const held = wornHolds(plane, root);
844
+ if (plane.worn && !held) {
845
+ return drift(`the worn build is not ${files[0] || plane.worn}`);
846
+ }
505
847
  for (const rel of files) {
506
- if (!existsSync(join(root, rel))) return drift(`${rel} is not there`);
848
+ if (existsSync(join(root, rel))) continue;
849
+ if (held && factoryName(rel)) continue;
850
+ return drift(`${rel} is not there`);
507
851
  }
508
- if (plane.mark && !fileHas(root, files[0], plane.mark)) {
852
+ if (plane.mark && files[0] && existsSync(join(root, files[0])) && !fileHas(root, files[0], plane.mark)) {
509
853
  return drift(`${plane.mark} is not in ${files[0]}`);
510
854
  }
511
- if (plane.worn && !fileHas(root, plane.worn, plane.wornMark || plane.mark)) {
512
- return drift(`the worn build is not ${files[0] || plane.worn}`);
513
- }
514
- if (plane.skills && !existsSync(join(root, plane.skills))) {
855
+ if (plane.skills && !existsSync(join(root, plane.skills)) && !(held && factoryName(plane.skills))) {
515
856
  return drift(`${plane.skills} is not there`);
516
857
  }
517
- if (plane.entryIsMark && plane.entry && files[0] && !fileHas(root, files[0], plane.entry)) {
858
+ if (plane.entryIsMark && plane.entry && files[0] && existsSync(join(root, files[0])) && !fileHas(root, files[0], plane.entry)) {
518
859
  return drift(`${plane.entry} is not in ${files[0]}`);
519
860
  }
520
- if (plane.exitIsMark && plane.exit && files[0] && !fileHas(root, files[0], plane.exit)) {
861
+ if (plane.exitIsMark && plane.exit && files[0] && existsSync(join(root, files[0])) && !fileHas(root, files[0], plane.exit)) {
521
862
  return drift(`${plane.exit} is not in ${files[0]}`);
522
863
  }
523
864
  return holds();
@@ -535,8 +876,8 @@ function isGogglesWork(text) {
535
876
 
536
877
 
537
878
  export function cardStop(root, toolName, text, paths = []) {
538
- const raw = [String(text || ''), ...(paths || [])].join('\n');
539
- if (isGogglesWork(raw)) {
879
+ const spoken = String(text || '');
880
+ if (spoken.length <= 200 && isGogglesWork(spoken) && !(paths || []).some((item) => /goggles/i.test(String(item)))) {
540
881
  clearReading(root);
541
882
  clearCardLine(root);
542
883
  return null;
@@ -574,15 +915,21 @@ function intentWords(text) {
574
915
  return String(text || '').toLowerCase().split(/[^a-z0-9-]+/).filter(Boolean);
575
916
  }
576
917
 
918
+ function lookVerb(words) {
919
+ return words.includes('look') || words.includes('name');
920
+ }
921
+
577
922
  export function handCard(root, intent) {
578
923
  try {
579
- clearCardLine(root);
580
924
  const text = String(intent || '');
581
- if (isGogglesWork(text)) {
925
+ const words = intentWords(text);
926
+ if (isGogglesWork(text) && text.length <= 200) {
582
927
  clearReading(root);
928
+ clearCardLine(root);
583
929
  return null;
584
930
  }
585
- const words = intentWords(text);
931
+ if (!lookVerb(words)) return null;
932
+ clearCardLine(root);
586
933
  const looked = readGoggles(words);
587
934
  if (looked.text.startsWith('Actuality. Drift')) {
588
935
  saveReading(root, { drift: looked.text });
@@ -615,14 +962,23 @@ export function lensPath(root) {
615
962
 
616
963
 
617
964
  export function maintainLens(root) {
618
- const text = actualityLine();
965
+ const drift = actualityLine();
966
+ const text = drift || 'quiet (match)';
619
967
  mkdirSync(dirname(lensPath(root)), { recursive: true });
620
968
  writeFileSync(lensPath(root), `${text}\n`);
621
- return ok(text);
969
+ return ok(drift);
622
970
  }
623
971
 
624
972
 
625
- const isMain = Boolean(process.argv[1]) && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
973
+ function sameInvoked(argvPath, modulePath) {
974
+ try {
975
+ return realpathSync(argvPath) === realpathSync(modulePath);
976
+ } catch {
977
+ return resolve(argvPath) === resolve(modulePath);
978
+ }
979
+ }
980
+
981
+ const isMain = Boolean(process.argv[1]) && sameInvoked(process.argv[1], fileURLToPath(import.meta.url));
626
982
  if (isMain) {
627
983
  const answered = gogglesAnswer(process.argv.slice(2));
628
984
  const pane = process.stdout.isTTY && Array.isArray(answered.cards) && answered.cards.length > 0;
@@ -15,6 +15,9 @@
15
15
  ],
16
16
  "skills": "SKILLS.md"
17
17
  },
18
+ "synthesis": {
19
+ "file": "src/nucleus/synthesis.ts"
20
+ },
18
21
  "routing": {
19
22
  "files": ["src/nucleus/thin-dispatch.ts"],
20
23
  "worn": "dist/nucleus/thin-dispatch.js",
@@ -24,5 +27,25 @@
24
27
  },
25
28
  "house": {
26
29
  "files": ["grok-bot/lib/seat-doctor.cjs"]
30
+ },
31
+ "boot": {
32
+ "files": ["src/core/boot-orchestrator.ts"],
33
+ "skills": "src/skills/boot-orchestrator/SKILL.md"
34
+ },
35
+ "governance": {
36
+ "files": ["src/governance/governance-service.ts"]
37
+ },
38
+ "memory-recall": {
39
+ "files": ["src/integrations/hooks/plates.cjs"]
40
+ },
41
+ "orchestration": {
42
+ "files": ["src/mcps/orchestrator/server.ts"]
43
+ },
44
+ "processor": {
45
+ "files": ["src/processors/processor-manager.ts"],
46
+ "skills": "src/skills/processor-pipeline/SKILL.md"
47
+ },
48
+ "reporting": {
49
+ "files": ["src/reporting/framework-reporting-system.ts"]
27
50
  }
28
51
  }