0xray 4.0.35 → 4.0.37

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 (173) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.xray/features.json +1 -1
  3. package/dist/CHANGELOG.md +25 -0
  4. package/dist/analytics/emerging-pattern-detector.d.ts +12 -1
  5. package/dist/analytics/emerging-pattern-detector.js +37 -12
  6. package/dist/analytics/pattern-learning-engine.js +1 -9
  7. package/dist/analytics/predictive-analytics.js +51 -33
  8. package/dist/analytics/prompt-pattern-analyzer.d.ts +1 -0
  9. package/dist/analytics/prompt-pattern-analyzer.js +39 -20
  10. package/dist/analytics/routing-performance-analyzer.d.ts +2 -0
  11. package/dist/analytics/routing-performance-analyzer.js +89 -37
  12. package/dist/analytics/simple-pattern-analyzer.d.ts +4 -0
  13. package/dist/analytics/simple-pattern-analyzer.js +66 -28
  14. package/dist/architect/architect-tools.d.ts +3 -0
  15. package/dist/architect/architect-tools.js +22 -0
  16. package/dist/cli/commands/grok-install.js +1 -1
  17. package/dist/cli/commands/hermes-install.js +11 -5
  18. package/dist/cli/commands/openclaw-install.js +13 -7
  19. package/dist/cli/commands/opencode-install.js +7 -4
  20. package/dist/cli/commands/suit-monitor.d.ts +30 -0
  21. package/dist/cli/commands/suit-monitor.js +128 -0
  22. package/dist/cli/index.js +52 -15
  23. package/dist/cli/suit-pulse.d.ts +29 -0
  24. package/dist/cli/suit-pulse.js +270 -0
  25. package/dist/delegation/agent-delegator.d.ts +4 -2
  26. package/dist/delegation/agent-delegator.js +46 -27
  27. package/dist/delegation/agent-expertise.js +2 -1
  28. package/dist/delegation/analytics/outcome-tracker.d.ts +6 -0
  29. package/dist/delegation/analytics/outcome-tracker.js +17 -3
  30. package/dist/delegation/ast-code-parser.d.ts +5 -0
  31. package/dist/delegation/ast-code-parser.js +53 -48
  32. package/dist/delegation/codebase-context-analyzer.d.ts +3 -7
  33. package/dist/delegation/codebase-context-analyzer.js +188 -159
  34. package/dist/delegation/session-coordinator.js +3 -2
  35. package/dist/enforcement/loaders/codex-loader.js +18 -14
  36. package/dist/enforcement/validators/architecture-validators.js +115 -9
  37. package/dist/governance/codex-policy.service.d.ts +12 -2
  38. package/dist/governance/codex-policy.service.js +91 -16
  39. package/dist/integrations/grok/hooks/session-start.js +6 -6
  40. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
  41. package/dist/integrations/hooks/goggles-mcp.mjs +10 -3
  42. package/dist/integrations/hooks/goggles-pipeline.mjs +221 -44
  43. package/dist/integrations/hooks/goggles-planes.json +172 -8
  44. package/dist/integrations/hooks/plates.cjs +138 -0
  45. package/dist/integrations/hooks/station-hook-runtime.cjs +63 -5
  46. package/dist/integrations/hooks/write-home.cjs +59 -0
  47. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  48. package/dist/mcps/architect-tools.server.d.ts +6 -0
  49. package/dist/mcps/architect-tools.server.js +19 -18
  50. package/dist/mcps/orchestrator/server.d.ts +4 -3
  51. package/dist/mcps/orchestrator/server.js +29 -17
  52. package/dist/memory-routing/lookup-cache.d.ts +8 -0
  53. package/dist/memory-routing/lookup-cache.js +72 -0
  54. package/dist/memory-routing/plates.d.ts +1 -1
  55. package/dist/memory-routing/plates.js +26 -0
  56. package/dist/memory-routing/provider-loader.js +3 -0
  57. package/dist/metrics/agent-metrics.d.ts +14 -5
  58. package/dist/metrics/agent-metrics.js +362 -224
  59. package/dist/nucleus/synthesis-consult-receipt.js +38 -16
  60. package/dist/orchestrator/enhanced-multi-agent-orchestrator.d.ts +5 -0
  61. package/dist/orchestrator/enhanced-multi-agent-orchestrator.js +9 -0
  62. package/dist/orchestrator/multi-agent-orchestration-coordinator.js +1 -2
  63. package/dist/orchestrator/orchestrator.js +1 -2
  64. package/dist/processors/implementations/agents-md-validation-processor.d.ts +0 -1
  65. package/dist/processors/implementations/agents-md-validation-processor.js +0 -11
  66. package/dist/processors/implementations/console-log-guard-processor.js +39 -50
  67. package/dist/processors/implementations/performance-budget-processor.d.ts +0 -5
  68. package/dist/processors/implementations/performance-budget-processor.js +29 -38
  69. package/dist/processors/implementations/storytelling-trigger-processor.d.ts +5 -0
  70. package/dist/processors/implementations/storytelling-trigger-processor.js +69 -16
  71. package/dist/processors/implementations/test-auto-creation-processor.js +39 -36
  72. package/dist/reporting/framework-reporting-system.d.ts +3 -0
  73. package/dist/reporting/framework-reporting-system.js +61 -8
  74. package/dist/reporting/log-parser.js +188 -105
  75. package/dist/reporting/metrics.js +42 -18
  76. package/dist/security/prompt-security-validator.js +8 -7
  77. package/dist/security/security-auditor.d.ts +4 -0
  78. package/dist/security/security-auditor.js +132 -57
  79. package/dist/security/security-hardener.d.ts +2 -0
  80. package/dist/security/security-hardener.js +31 -6
  81. package/dist/security/security-headers.d.ts +2 -0
  82. package/dist/security/security-headers.js +23 -21
  83. package/dist/security/security-scanner.js +18 -4
  84. package/dist/session/session-cleanup-manager.d.ts +2 -0
  85. package/dist/session/session-cleanup-manager.js +29 -8
  86. package/dist/session/session-monitor.d.ts +14 -4
  87. package/dist/session/session-monitor.js +208 -106
  88. package/dist/session/session-state-manager.d.ts +11 -0
  89. package/dist/session/session-state-manager.js +90 -8
  90. package/docs-site/docs/plates/activity-log.md +38 -0
  91. package/docs-site/docs/plates/cursor-compact.md +35 -0
  92. package/docs-site/docs/plates/grok-compact.md +35 -0
  93. package/docs-site/docs/plates/index.md +25 -3
  94. package/docs-site/docs/plates/inference-files.md +35 -0
  95. package/docs-site/docs/plates/kept-line.md +35 -0
  96. package/docs-site/docs/plates/lens-gate.md +35 -0
  97. package/docs-site/docs/plates/lens-page.md +35 -0
  98. package/docs-site/docs/plates/notes-page.md +35 -0
  99. package/docs-site/docs/plates/package-face.md +35 -0
  100. package/docs-site/docs/plates/payload-heat.md +35 -0
  101. package/docs-site/docs/plates/pickup-stamp.md +35 -0
  102. package/docs-site/docs/plates/pre-tool.md +35 -0
  103. package/docs-site/docs/plates/record-map.md +41 -0
  104. package/docs-site/docs/plates/reflection-page.md +35 -0
  105. package/docs-site/docs/plates/review.md +57 -0
  106. package/docs-site/docs/plates/session-capture.md +38 -0
  107. package/docs-site/docs/plates/site-manual.md +35 -0
  108. package/docs-site/docs/plates/stamp-plate.md +35 -0
  109. package/docs-site/docs/plates/station-card.md +35 -0
  110. package/docs-site/docs/plates/station-heat.md +35 -0
  111. package/docs-site/docs/plates/suit-organs.md +38 -0
  112. package/docs-site/docs/plates/suit-settings.md +35 -0
  113. package/docs-site/docs/plates/suit-wear.md +37 -0
  114. package/docs-site/docs/plates/trail-state.md +35 -0
  115. package/docs-site/docs/plates/work-fresh.md +35 -0
  116. package/docs-site/docs/plates/write-home.md +38 -0
  117. package/package.json +4 -2
  118. package/scripts/mjs/run-grok-hook.mjs +50 -0
  119. package/scripts/node/install-bridges.cjs +305 -81
  120. package/scripts/node/postinstall.cjs +14 -3
  121. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
  122. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  123. package/src/mcps/architect-tools.server.ts +25 -20
  124. package/src/mcps/orchestrator/server.ts +33 -19
  125. package/xray/features.json +1 -1
  126. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +0 -1
  127. package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +0 -1
  128. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +0 -1
  129. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +0 -1
  130. package/vendor/@0xray/repertoire/dist/index.d.ts.map +0 -1
  131. package/vendor/@0xray/repertoire/dist/index.js.map +0 -1
  132. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +0 -1
  133. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +0 -1
  134. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +0 -1
  135. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +0 -1
  136. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +0 -1
  137. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +0 -1
  138. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +0 -1
  139. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +0 -1
  140. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +0 -1
  141. package/vendor/@0xray/repertoire/dist/mcp/server.js.map +0 -1
  142. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +0 -1
  143. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +0 -1
  144. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +0 -1
  145. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +0 -1
  146. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +0 -1
  147. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +0 -1
  148. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +0 -1
  149. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +0 -1
  150. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +0 -1
  151. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +0 -1
  152. package/vendor/@0xray/repertoire/dist/paths.d.ts.map +0 -1
  153. package/vendor/@0xray/repertoire/dist/paths.js.map +0 -1
  154. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +0 -1
  155. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +0 -1
  156. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +0 -1
  157. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +0 -1
  158. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +0 -1
  159. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +0 -1
  160. package/vendor/@0xray/repertoire/dist/registry/confidence-decay.d.ts.map +0 -1
  161. package/vendor/@0xray/repertoire/dist/registry/confidence-decay.js.map +0 -1
  162. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +0 -1
  163. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +0 -1
  164. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +0 -1
  165. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +0 -1
  166. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +0 -1
  167. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +0 -1
  168. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +0 -1
  169. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +0 -1
  170. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +0 -1
  171. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +0 -1
  172. package/vendor/@0xray/repertoire/dist/types.d.ts.map +0 -1
  173. package/vendor/@0xray/repertoire/dist/types.js.map +0 -1
@@ -3,7 +3,12 @@
3
3
  * A named plane returns its view. Field pipes fill what they can see.
4
4
  * The view keeps every field for the next look.
5
5
  * A held plane stays quiet. A leave is denied.
6
- * On this host a search is grep. The lens file stays open.
6
+ * On this host a search is grep, glob, or a shell that runs one.
7
+ * A shell that reads a project source file is a search.
8
+ * The lens file stays open. One later read may go on, then the next read stops.
9
+ * An opened source file that no lens lists is remembered.
10
+ * The next search stops until that file is on a lens and its plate is on disk.
11
+ * The hook does not author the plate.
7
12
  */
8
13
  import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs';
9
14
  import { createRequire } from 'node:module';
@@ -13,7 +18,7 @@ import { fileURLToPath } from 'node:url';
13
18
  const FIELD_ORDER = ['plate', 'entry', 'exit', 'files', 'skills', 'setup', 'teardown', 'worn'];
14
19
  const HERE = dirname(fileURLToPath(import.meta.url));
15
20
  const WORN_PLANES = ['dichotomy', 'syncopate', 'synthesis', 'digest', 'triage', 'loop'];
16
- const CARD_PLANES = ['ground', 'routing', 'house', 'boot', 'governance', 'memory-recall', 'orchestration', 'processor', 'reporting'];
21
+ const CARD_PLANES = ['ground', 'routing', 'house', 'boot', 'governance', 'memory-recall', 'orchestration', 'processor', 'reporting', 'stamp-plate', 'record-map', 'write-home', 'activity-log', 'session-capture', 'suit-wear', 'suit-organs', 'station-card', 'notes-page', 'reflection-page', 'site-manual', 'package-face', 'suit-settings', 'trail-state', 'inference-files', 'grok-compact', 'payload-heat', 'station-heat', 'pickup-stamp', 'cursor-compact', 'work-fresh', 'kept-line', 'lens-gate', 'pre-tool', 'lens-page'];
17
22
  const CARD_FLAVORS = ['digest', 'triage'];
18
23
  const SCOPE_ZOOM = ['ecosystem', 'part', 'one flow', 'one artifact'];
19
24
 
@@ -57,6 +62,25 @@ function organDeny(reason) {
57
62
  return { gate: 'goggles', decision: 'deny', reason };
58
63
  }
59
64
 
65
+ function plateIdsIn(spoken) {
66
+ const ids = [];
67
+ const re = /docs-site\/docs\/plates\/([a-z0-9-]+)\.md/ig;
68
+ let match;
69
+ while ((match = re.exec(String(spoken || '')))) ids.push(match[1]);
70
+ return ids;
71
+ }
72
+
73
+ function associatedPlates(plane) {
74
+ try {
75
+ const data = JSON.parse(readFileSync(planesFile(), 'utf8'));
76
+ const entry = data[plane];
77
+ const listed = entry && Array.isArray(entry.plates) ? entry.plates.map(String) : [];
78
+ return new Set([plane, ...listed]);
79
+ } catch {
80
+ return new Set([plane]);
81
+ }
82
+ }
83
+
60
84
  function planeWord(name, text) {
61
85
  return new RegExp(`(?:^|[^A-Za-z0-9-])${String(name).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?:[^A-Za-z0-9-]|$)`, 'i').test(String(text || ''));
62
86
  }
@@ -73,9 +97,17 @@ export function organStop(held, action) {
73
97
  const text = String(action?.text || '');
74
98
  const paths = (action?.paths || []).map(String).filter(Boolean);
75
99
  const tool = String(action?.tool || '');
76
- if (/docs-site\/docs\/plates\/[a-z0-9-]+\.md/i.test([text, ...paths].join('\n'))) {
77
- return organDeny(`The reading is ${plane}. That drawing is not the plane.`);
100
+ const spoken = [text, ...paths].join('\n');
101
+ const drawn = plateIdsIn(spoken);
102
+ if (drawn.length) {
103
+ const allowed = associatedPlates(plane);
104
+ const foreign = drawn.filter((id) => !allowed.has(id));
105
+ if (foreign.length) {
106
+ return organDeny(`The reading is ${plane}. That drawing is not the plane.`);
107
+ }
78
108
  }
109
+ const otherFile = otherPlaneFile(plane, spoken);
110
+ if (otherFile) return organDeny(`The reading is ${plane}. The action is ${otherFile}.`);
79
111
  const stripped = withoutPaths(text);
80
112
  const others = acceptedPlanes().filter((name) => name !== plane && planeWord(name, stripped));
81
113
  const namesThis = planeWord(plane, stripped);
@@ -248,6 +280,7 @@ function cardView(plane, scope, flavor, root) {
248
280
  from: plane.id === 'ground' ? '' : (plane.from || 'ground'),
249
281
  digest: plane.digest || '',
250
282
  plate: plane.plate || '',
283
+ plates: Array.isArray(plane.plates) ? plane.plates : [],
251
284
  entry: plane.entry || '',
252
285
  exit: plane.exit || '',
253
286
  files: listed,
@@ -256,20 +289,23 @@ function cardView(plane, scope, flavor, root) {
256
289
  teardown: plane.teardown || '',
257
290
  worn: plane.worn || '',
258
291
  };
292
+ view.exam = examinePlane(plane, root).text;
259
293
  if (flavor === 'triage') {
260
294
  const filled = new Set(filledOf(plane));
261
295
  view.empty = FIELD_ORDER.filter((name) => !filled.has(name));
262
- view.exam = examinePlane(plane, root).text;
263
296
  }
264
297
  return view;
265
298
  }
266
299
 
267
- function formatCardText(view) {
300
+ function cardRows(view) {
268
301
  const rows = [
269
302
  ['Plane', view.plane],
270
303
  ['From', view.from],
271
304
  ['Digest', view.digest],
272
305
  ['Plate', view.plate],
306
+ ];
307
+ if (Array.isArray(view.plates) && view.plates.length) rows.push(['Plates', view.plates.join(', ')]);
308
+ rows.push(
273
309
  ['Entry', view.entry],
274
310
  ['Exit', view.exit],
275
311
  ['Files', view.files.join(', ')],
@@ -277,34 +313,31 @@ function formatCardText(view) {
277
313
  ['Setup', view.setup],
278
314
  ['Teardown', view.teardown],
279
315
  ['Worn', view.worn],
280
- ];
316
+ );
317
+ return rows;
318
+ }
319
+
320
+ function formatCardText(view) {
321
+ const rows = cardRows(view);
281
322
  let text = rows.map(([key, value]) => `${key}: ${value}`.trimEnd()).join('\n');
282
323
  if (view.flavor === 'triage') {
283
324
  const lines = [];
284
325
  if (view.empty.length) lines.push(`Empty: ${view.empty.join(', ')}`);
285
326
  lines.push(view.exam);
286
327
  text = `${text}\n${lines.join('\n')}`;
328
+ } else if (typeof view.exam === 'string' && view.exam.startsWith('Drift:')) {
329
+ text = `${text}\n${view.exam}`;
287
330
  }
288
331
  return text;
289
332
  }
290
333
 
291
334
  export function formatCardPane(view) {
292
- const rows = [
293
- ['Plane', view.plane],
294
- ['From', view.from],
295
- ['Digest', view.digest],
296
- ['Plate', view.plate],
297
- ['Entry', view.entry],
298
- ['Exit', view.exit],
299
- ['Files', view.files.join(', ')],
300
- ['Skills', view.skills],
301
- ['Setup', view.setup],
302
- ['Teardown', view.teardown],
303
- ['Worn', view.worn],
304
- ];
335
+ const rows = cardRows(view);
305
336
  if (view.flavor === 'triage') {
306
337
  if (Array.isArray(view.empty) && view.empty.length) rows.push(['Empty', view.empty.join(', ')]);
307
338
  if (view.exam) rows.push(['', view.exam]);
339
+ } else if (typeof view.exam === 'string' && view.exam.startsWith('Drift:')) {
340
+ rows.push(['', view.exam]);
308
341
  }
309
342
  const body = rows.map(([label, value]) => (label ? `${label.padEnd(8)} ${value}` : value).trimEnd());
310
343
  const title = `${view.flavor} · ${view.plane}`;
@@ -516,6 +549,7 @@ export function assemblePlane(id, platesDir) {
516
549
  files,
517
550
  unpathed: extra.unpathed || null,
518
551
  skills: extra.skills || '',
552
+ plates: Array.isArray(extra.plates) ? extra.plates.map(String) : [],
519
553
  setup: extra.setup || sectionOf(src.body, 'Setup') || (flow && flow.setup) || '',
520
554
  teardown: extra.teardown || sectionOf(src.body, 'Teardown') || (flow && flow.teardown) || '',
521
555
  worn: extra.worn || '',
@@ -686,6 +720,33 @@ export function suitHint(text) {
686
720
 
687
721
  const DEEP_SEARCH = /search_codebase|find_implementation|get_documentation/i;
688
722
  const HOLDS_ON_NAME = new Set(['dichotomy', 'syncopate', 'synthesis']);
723
+ const SHELL_SEARCH = /(?:^|&&|\|\||[;&|`(\n])\s*(?:(?:\/[\w.+/-]+\/)|(?:npx\s+))?(?:rg|grep|ag|ack|fd|find)\b|\bgit\s+grep\b/;
724
+
725
+ function shellSearches(text) {
726
+ return SHELL_SEARCH.test(String(text || ''));
727
+ }
728
+
729
+ function mappedPlaneFiles(name) {
730
+ try {
731
+ const data = JSON.parse(readFileSync(planesFile(), 'utf8'));
732
+ const entry = data[name];
733
+ if (!entry || typeof entry !== 'object') return [];
734
+ const candidates = [entry.file, entry.worn, entry.skills];
735
+ if (Array.isArray(entry.files)) candidates.push(...entry.files);
736
+ return candidates.filter((file) => typeof file === 'string' && file.includes('.') && file.includes('/'));
737
+ } catch {
738
+ return [];
739
+ }
740
+ }
741
+
742
+ function otherPlaneFile(held, spoken) {
743
+ const text = String(spoken || '');
744
+ for (const name of acceptedPlanes()) {
745
+ if (name === held) continue;
746
+ if (mappedPlaneFiles(name).some((file) => text.includes(file))) return name;
747
+ }
748
+ return '';
749
+ }
689
750
 
690
751
  function mapFiles() {
691
752
  try {
@@ -711,12 +772,118 @@ function opensLensFile(text) {
711
772
  return mapFiles().some((file) => spoken.includes(file));
712
773
  }
713
774
 
775
+ const SOURCE_FILE = /(?:^|[^A-Za-z0-9_])((?:src|scripts|docs-site|\.agents|grok-bot)\/[\w./-]*\.[A-Za-z0-9]+)(?=$|[^A-Za-z0-9_./-])/g;
776
+ const SOURCE_READER = /(?:^|[^A-Za-z0-9_])(?:python3|node|cat|head|tail|sed|less|more|bat|awk)(?=$|[^A-Za-z0-9_])/;
777
+ const STAMP_TARGET = /docs-site\/docs\/plates\/[a-z0-9-]+\.md|goggles-planes\.json|\.agents\/skills\/[a-z0-9-]+\/SKILL\.md|src\/memory-routing\/plates\.ts|src\/integrations\/hooks\/plates\.cjs|docs-site\/sidebars\.ts|docs-site\/docs\/plates\/index\.md/;
778
+
779
+ function isStampPath(file) {
780
+ return STAMP_TARGET.test(String(file || '').replace(/\\/g, '/'));
781
+ }
782
+
783
+ function openedSourcePaths(text) {
784
+ const spoken = `\n${String(text || '').replace(/\\/g, '/')}`;
785
+ const found = [];
786
+ for (const match of spoken.matchAll(SOURCE_FILE)) {
787
+ const file = match[1];
788
+ if (!file || isStampPath(file) || found.includes(file)) continue;
789
+ found.push(file);
790
+ }
791
+ return found;
792
+ }
793
+
794
+ function shellReadsSource(text) {
795
+ const spoken = String(text || '');
796
+ return SOURCE_READER.test(spoken) && openedSourcePaths(spoken).length > 0;
797
+ }
798
+
799
+ function isLensWrite(toolName) {
800
+ return /write|edit|replace/i.test(String(toolName || ''));
801
+ }
802
+
803
+ function debtFile(root) {
804
+ return join(root, '.xray', 'state', 'goggles-lens-debt.json');
805
+ }
806
+
807
+ function loadDebt(root) {
808
+ try {
809
+ const data = JSON.parse(readFileSync(debtFile(root), 'utf8'));
810
+ return Array.isArray(data.paths) ? data.paths.filter((file) => typeof file === 'string') : [];
811
+ } catch {
812
+ return [];
813
+ }
814
+ }
815
+
816
+ function saveDebt(root, paths) {
817
+ const unique = [];
818
+ for (const file of paths) {
819
+ if (!unique.includes(file)) unique.push(file);
820
+ }
821
+ const dest = debtFile(root);
822
+ if (!unique.length) {
823
+ try {
824
+ unlinkSync(dest);
825
+ } catch {
826
+ /* already clear */
827
+ }
828
+ return;
829
+ }
830
+ mkdirSync(dirname(dest), { recursive: true });
831
+ writeFileSync(dest, `${JSON.stringify({ paths: unique })}\n`);
832
+ }
833
+
834
+ function lensRecords() {
835
+ try {
836
+ const data = JSON.parse(readFileSync(planesFile(), 'utf8'));
837
+ const records = [];
838
+ if (!data || typeof data !== 'object') return records;
839
+ for (const [name, entry] of Object.entries(data)) {
840
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) continue;
841
+ if (!Array.isArray(entry.files)) continue;
842
+ records.push([name, entry.files.filter((file) => typeof file === 'string')]);
843
+ }
844
+ return records;
845
+ } catch {
846
+ return [];
847
+ }
848
+ }
849
+
850
+ function plateIsOnDisk(root, name) {
851
+ const beside = findPlatesDir(HERE);
852
+ if (beside && existsSync(join(beside, `${name}.md`))) return true;
853
+ return existsSync(join(root, 'docs-site', 'docs', 'plates', `${name}.md`));
854
+ }
855
+
856
+ function lensCovers(root, file) {
857
+ for (const [name, files] of lensRecords()) {
858
+ if (!files.includes(file)) continue;
859
+ if (name === 'ground' || plateIsOnDisk(root, name)) return true;
860
+ }
861
+ return false;
862
+ }
863
+
864
+ function unsettledDebt(root) {
865
+ const prior = loadDebt(root);
866
+ const kept = prior.filter((file) => !lensCovers(root, file));
867
+ if (kept.length !== prior.length) saveDebt(root, kept);
868
+ return kept;
869
+ }
870
+
871
+ function rememberOpened(root, text) {
872
+ const fresh = openedSourcePaths(text).filter((file) => !lensCovers(root, file));
873
+ if (!fresh.length) return;
874
+ saveDebt(root, [...loadDebt(root), ...fresh]);
875
+ }
876
+
877
+ function debtReason(paths) {
878
+ return `Opened ${paths.join(', ')} has no lens.`;
879
+ }
880
+
714
881
  function researchCall(toolName, text) {
715
882
  const tool = String(toolName || '');
716
883
  const spoken = String(text || '');
717
884
  if (/researcher|explorer|deep[- ]?research/i.test(tool) || DEEP_SEARCH.test(tool)) return true;
718
- if (/bash|shell/i.test(tool) && (/researcher|explorer|deep[- ]?research/i.test(spoken) || DEEP_SEARCH.test(spoken))) return true;
719
- if (/^grep$/i.test(tool)) return true;
885
+ if (/bash|shell/i.test(tool) && (/researcher|explorer|deep[- ]?research/i.test(spoken) || DEEP_SEARCH.test(spoken) || shellSearches(spoken) || shellReadsSource(spoken))) return true;
886
+ if (/^(grep|glob)$/i.test(tool)) return true;
720
887
  return /^read_file$/i.test(tool) && !opensLensFile(spoken);
721
888
  }
722
889
 
@@ -727,16 +894,17 @@ function passPath(root) {
727
894
  function loadReadPass(root) {
728
895
  try {
729
896
  const data = JSON.parse(readFileSync(passPath(root), 'utf8'));
730
- return Boolean(data && data.read === true);
897
+ if (!data || data.read !== true) return null;
898
+ return data.spent === true ? 'spent' : 'armed';
731
899
  } catch {
732
- return false;
900
+ return null;
733
901
  }
734
902
  }
735
903
 
736
- function saveReadPass(root) {
904
+ function saveReadPass(root, spent) {
737
905
  const file = passPath(root);
738
906
  mkdirSync(dirname(file), { recursive: true });
739
- writeFileSync(file, `${JSON.stringify({ read: true })}\n`);
907
+ writeFileSync(file, `${JSON.stringify({ read: true, spent: Boolean(spent) })}\n`);
740
908
  }
741
909
 
742
910
  /** One plane: the files the suit continues with. A look with no plane stops. Two planes stay quiet. */
@@ -771,19 +939,33 @@ function stampPlane(root, id) {
771
939
  }
772
940
  }
773
941
 
774
- /** A search stays stopped. The lens file stays open. One later read may go on. */
942
+ /** Name one plane and the tool continues. Name none, or more than one, and the search stops. */
775
943
  export function lensBeforeResearch(root, toolName, text) {
776
944
  const spoken = String(text || '');
777
- if (!researchCall(toolName, spoken)) return null;
945
+ const openDebt = unsettledDebt(root);
946
+ const research = researchCall(toolName, spoken);
947
+ if (openDebt.length && research) {
948
+ return { gate: 'lens', decision: 'deny', reason: debtReason(openDebt) };
949
+ }
950
+ if (openDebt.length && isLensWrite(toolName) && !isStampPath(spoken)) {
951
+ return { gate: 'lens', decision: 'deny', reason: debtReason(openDebt) };
952
+ }
953
+ if (!research) return null;
778
954
  const names = CARD_PLANES.filter((name) => intentWords(spoken).includes(name));
955
+ if (names.length === 1) {
956
+ stampPlane(root, names[0]);
957
+ rememberOpened(root, spoken);
958
+ return null;
959
+ }
779
960
  if (/^read_file$/i.test(String(toolName || ''))) {
780
- if (loadReadPass(root)) return null;
781
- if (names.length === 1) stampPlane(root, names[0]);
782
- saveReadPass(root);
783
- return { gate: 'lens', decision: 'deny', reason: lensPage(root, names) };
961
+ if (loadReadPass(root) === 'armed') {
962
+ saveReadPass(root, true);
963
+ return null;
964
+ }
965
+ if (loadReadPass(root) !== 'spent') saveReadPass(root, false);
966
+ return { gate: 'lens', decision: 'deny', reason: 'Name one plane.' };
784
967
  }
785
- if (names.length === 1) stampPlane(root, names[0]);
786
- return { gate: 'lens', decision: 'deny', reason: lensPage(root, names) };
968
+ return { gate: 'lens', decision: 'deny', reason: 'Name one plane.' };
787
969
  }
788
970
 
789
971
  function seenFields(plane, root) {
@@ -825,22 +1007,17 @@ function seenFields(plane, root) {
825
1007
  };
826
1008
  }
827
1009
 
828
- function kept(stored, seen, key) {
829
- if (key === 'files') {
830
- if (Array.isArray(seen.files) && seen.files.length) return seen.files.map(String);
831
- return Array.isArray(stored.files) ? stored.files.map(String) : [];
832
- }
833
- if (seen[key]) return seen[key];
834
- return stored[key] || '';
1010
+ function freshField(seen, key) {
1011
+ if (key === 'files') return Array.isArray(seen.files) ? seen.files.map(String) : [];
1012
+ return seen[key] || '';
835
1013
  }
836
1014
 
837
1015
  export function growPlane(id, platesDir, root) {
838
1016
  const seen = seenFields(assemblePlane(id, platesDir), sourceRoot(platesDir));
839
1017
  const views = loadViews(root);
840
- const stored = views[id] && typeof views[id] === 'object' ? views[id] : {};
841
1018
  const grown = { id };
842
- for (const key of VIEW_KEYS) grown[key] = kept(stored, seen, key);
843
- if (id === 'ground') grown.from = stored.from || '';
1019
+ for (const key of VIEW_KEYS) grown[key] = freshField(seen, key);
1020
+ if (id === 'ground') grown.from = '';
844
1021
  views[id] = {};
845
1022
  for (const key of VIEW_KEYS) views[id][key] = grown[key];
846
1023
  saveViews(root, views);
@@ -23,29 +23,193 @@
23
23
  "worn": "dist/nucleus/thin-dispatch.js",
24
24
  "mark": "export function scoreAndRoute",
25
25
  "wornMark": "function scoreAndRoute",
26
- "pick": "resolveThinDispatch"
26
+ "pick": "resolveThinDispatch",
27
+ "skills": ".agents/skills/routing/SKILL.md",
28
+ "plates": ["orchestration", "processor"]
27
29
  },
28
30
  "house": {
29
- "files": ["grok-bot/lib/seat-doctor.cjs"]
31
+ "files": ["grok-bot/lib/seat-doctor.cjs"],
32
+ "skills": ".agents/skills/house/SKILL.md",
33
+ "plates": ["suit-wear"]
30
34
  },
31
35
  "boot": {
32
36
  "files": ["src/core/boot-orchestrator.ts"],
33
- "skills": "src/skills/boot-orchestrator/SKILL.md"
37
+ "skills": "src/skills/boot-orchestrator/SKILL.md",
38
+ "plates": ["suit-wear"]
34
39
  },
35
40
  "governance": {
36
- "files": ["src/governance/governance-service.ts"]
41
+ "files": ["src/governance/governance-service.ts"],
42
+ "skills": ".agents/skills/governance/SKILL.md"
37
43
  },
38
44
  "memory-recall": {
39
- "files": ["src/integrations/hooks/plates.cjs"]
45
+ "files": ["src/integrations/hooks/plates.cjs"],
46
+ "skills": ".agents/skills/memory-recall/SKILL.md",
47
+ "plates": ["record-map"]
40
48
  },
41
49
  "orchestration": {
42
- "files": ["src/mcps/orchestrator/server.ts"]
50
+ "files": ["src/mcps/orchestrator/server.ts"],
51
+ "skills": ".agents/skills/orchestration/SKILL.md",
52
+ "plates": ["routing", "processor"]
43
53
  },
44
54
  "processor": {
45
55
  "files": ["src/processors/processor-manager.ts"],
46
- "skills": "src/skills/processor-pipeline/SKILL.md"
56
+ "skills": "src/skills/processor-pipeline/SKILL.md",
57
+ "plates": ["orchestration"]
47
58
  },
48
59
  "reporting": {
49
- "files": ["src/reporting/framework-reporting-system.ts"]
60
+ "files": ["src/reporting/framework-reporting-system.ts"],
61
+ "skills": ".agents/skills/reporting/SKILL.md",
62
+ "plates": ["activity-log", "suit-organs"]
63
+ },
64
+ "stamp-plate": {
65
+ "files": [".agents/skills/stamp-plate/SKILL.md"],
66
+ "mark": "Stamp a plate or a lens",
67
+ "skills": ".agents/skills/stamp-plate/SKILL.md",
68
+ "plates": ["review"]
69
+ },
70
+ "record-map": {
71
+ "files": ["docs-site/docs/plates/record-map.md"],
72
+ "skills": ".agents/skills/record-map/SKILL.md",
73
+ "plates": ["station-card", "notes-page", "reflection-page", "site-manual", "package-face", "write-home", "activity-log", "session-capture", "lens-page"]
74
+ },
75
+ "write-home": {
76
+ "files": ["src/integrations/hooks/write-home.cjs"],
77
+ "mark": "function activityLog",
78
+ "skills": ".agents/skills/write-home/SKILL.md",
79
+ "plates": ["activity-log", "session-capture", "inference-files", "trail-state", "suit-wear"]
80
+ },
81
+ "activity-log": {
82
+ "files": ["src/core/activity-logger.ts"],
83
+ "mark": "activity.log",
84
+ "skills": ".agents/skills/activity-log/SKILL.md",
85
+ "plates": ["write-home", "reporting", "suit-organs"]
86
+ },
87
+ "session-capture": {
88
+ "files": ["src/inference/session-capture.ts"],
89
+ "mark": "saveSessionInference",
90
+ "skills": ".agents/skills/session-capture/SKILL.md",
91
+ "plates": ["write-home", "inference-files", "suit-organs"]
92
+ },
93
+ "suit-wear": {
94
+ "files": ["src/core/features-config.ts"],
95
+ "mark": "log_path",
96
+ "skills": ".agents/skills/suit-wear/SKILL.md",
97
+ "plates": ["suit-settings", "write-home", "package-face"]
98
+ },
99
+ "suit-organs": {
100
+ "files": ["src/reporting/framework-reporting-system.ts"],
101
+ "mark": "activity.log",
102
+ "skills": ".agents/skills/suit-organs/SKILL.md",
103
+ "plates": ["activity-log", "session-capture", "inference-files", "trail-state"]
104
+ },
105
+ "station-card": {
106
+ "files": ["src/integrations/hooks/station-hook-runtime.cjs"],
107
+ "mark": "formatStationMarkdown",
108
+ "skills": ".agents/skills/station-card/SKILL.md",
109
+ "plates": ["notes-page", "record-map", "kept-line"]
110
+ },
111
+ "notes-page": {
112
+ "files": ["src/integrations/hooks/station-hook-runtime.cjs"],
113
+ "mark": "stampNotesPickup",
114
+ "skills": ".agents/skills/notes-page/SKILL.md",
115
+ "plates": ["station-card", "record-map"]
116
+ },
117
+ "reflection-page": {
118
+ "files": ["src/inference/inference-accumulator.ts"],
119
+ "mark": "loadReflectionInferences",
120
+ "skills": ".agents/skills/reflection-page/SKILL.md",
121
+ "plates": ["record-map"]
122
+ },
123
+ "site-manual": {
124
+ "files": ["docs-site/docs/introduction.md"],
125
+ "mark": "survives the context window",
126
+ "skills": ".agents/skills/site-manual/SKILL.md",
127
+ "plates": ["record-map"]
128
+ },
129
+ "package-face": {
130
+ "files": ["package.json"],
131
+ "mark": "\"name\": \"0xray\"",
132
+ "skills": ".agents/skills/package-face/SKILL.md",
133
+ "plates": ["record-map", "suit-wear"]
134
+ },
135
+ "suit-settings": {
136
+ "files": ["xray/features.json"],
137
+ "mark": "log_path",
138
+ "skills": ".agents/skills/suit-settings/SKILL.md",
139
+ "plates": ["suit-wear", "record-map"]
140
+ },
141
+ "trail-state": {
142
+ "files": ["src/integrations/hooks/station-hook-runtime.cjs"],
143
+ "mark": "session-boot.json",
144
+ "skills": ".agents/skills/trail-state/SKILL.md",
145
+ "plates": ["write-home", "suit-organs"]
146
+ },
147
+ "inference-files": {
148
+ "files": ["src/inference/inference-accumulator.ts"],
149
+ "mark": "loadSessionInferences",
150
+ "skills": ".agents/skills/inference-files/SKILL.md",
151
+ "plates": ["session-capture", "write-home", "suit-organs"]
152
+ },
153
+ "grok-compact": {
154
+ "files": ["src/integrations/grok/hooks/session-start.js"],
155
+ "mark": "pre_compact",
156
+ "skills": ".agents/skills/grok-compact/SKILL.md",
157
+ "plates": ["payload-heat", "cursor-compact", "work-fresh"]
158
+ },
159
+ "payload-heat": {
160
+ "files": ["src/integrations/grok/hooks/grok-hook-utils.js"],
161
+ "mark": "function buildSessionBootPayload",
162
+ "skills": ".agents/skills/payload-heat/SKILL.md",
163
+ "plates": ["station-heat", "pickup-stamp"]
164
+ },
165
+ "station-heat": {
166
+ "files": ["src/integrations/hooks/station-hook-runtime.cjs"],
167
+ "mark": "function applyStationHeat",
168
+ "skills": ".agents/skills/station-heat/SKILL.md",
169
+ "plates": ["pickup-stamp", "notes-page"]
170
+ },
171
+ "pickup-stamp": {
172
+ "files": ["src/integrations/hooks/station-hook-runtime.cjs"],
173
+ "mark": "One pickup line",
174
+ "skills": ".agents/skills/pickup-stamp/SKILL.md",
175
+ "plates": ["notes-page"]
176
+ },
177
+ "cursor-compact": {
178
+ "files": ["src/integrations/cursor/hooks/pre-compact.js"],
179
+ "mark": "cannot block",
180
+ "skills": ".agents/skills/cursor-compact/SKILL.md",
181
+ "plates": ["payload-heat", "station-heat"]
182
+ }
183
+ ,
184
+ "work-fresh": {
185
+ "files": ["src/nucleus/work-freshness.mjs"],
186
+ "mark": "export function refreshFreshness",
187
+ "skills": ".agents/skills/work-fresh/SKILL.md",
188
+ "plates": ["station-card", "grok-compact"]
189
+ },
190
+ "kept-line": {
191
+ "files": ["src/integrations/hooks/station-hook-runtime.cjs"],
192
+ "mark": "function withOneFreshLine",
193
+ "skills": ".agents/skills/kept-line/SKILL.md",
194
+ "plates": ["station-card", "work-fresh"]
195
+ },
196
+ "lens-gate": {
197
+ "files": ["src/integrations/hooks/goggles-pipeline.mjs"],
198
+ "mark": "export function lensBeforeResearch",
199
+ "skills": ".agents/skills/lens-gate/SKILL.md",
200
+ "plates": ["pre-tool"]
201
+ },
202
+ "pre-tool": {
203
+ "files": ["src/integrations/grok/hooks/pre-tool-use.js"],
204
+ "mark": "function extractFromEvent",
205
+ "skills": ".agents/skills/pre-tool/SKILL.md",
206
+ "plates": ["lens-gate"]
207
+ },
208
+ "lens-page": {
209
+ "files": ["src/integrations/hooks/goggles-pipeline.mjs"],
210
+ "mark": "export function formatCardPane",
211
+ "skills": ".agents/skills/lens-page/SKILL.md",
212
+ "plates": ["record-map", "lens-gate"]
50
213
  }
51
214
  }
215
+