total-recall-brain 3.41.4 → 3.41.6

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 (147) hide show
  1. package/bin/total-recall.mjs +10 -6
  2. package/docs/reference/cli-reference.md +18 -0
  3. package/frontend/dist/assets/index-4tGQfTa2.js +5605 -0
  4. package/frontend/dist/assets/index-DtwsDyFM.css +1 -0
  5. package/frontend/dist/index.html +2 -2
  6. package/frontend/package.json +11 -0
  7. package/frontend/src/App.tsx +47 -122
  8. package/frontend/src/components/plugins/plugin-frame.ts +1 -0
  9. package/frontend/src/index.css +152 -86
  10. package/frontend/src/pages/HelpPage.tsx +13 -10
  11. package/frontend/src/pages/MemoryPage.tsx +28 -30
  12. package/frontend/src/pages/NetworkPage.tsx +3 -3
  13. package/frontend/src/pages/PluginsPage.tsx +6 -5
  14. package/frontend/src/pages/RulesPage.tsx +16 -12
  15. package/package.json +4 -3
  16. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +3114 -36
  17. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +4 -1
  18. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +3114 -36
  19. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +4 -1
  20. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +3121 -33
  21. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +4 -1
  22. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +3122 -34
  23. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +4 -1
  24. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +3127 -49
  25. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +5 -2
  26. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +1 -1
  27. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +136 -7
  28. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +135 -26
  29. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +3121 -33
  30. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +5 -2
  31. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +228 -9
  32. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +24 -30
  33. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +29 -16
  34. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +73 -0
  35. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +182 -365
  36. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -0
  37. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +40 -14
  38. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +4 -4
  39. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/agy.mjs +187 -0
  40. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/claude.mjs +275 -0
  41. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cline.mjs +132 -0
  42. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/codex.mjs +187 -0
  43. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cursor.mjs +158 -0
  44. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/devin.mjs +157 -0
  45. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/dsh.mjs +183 -0
  46. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/grok.mjs +132 -0
  47. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +173 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +87 -0
  49. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/openclaw.mjs +173 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/pi.mjs +129 -0
  51. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate.mjs +12 -0
  52. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/json-reply.mjs +22 -0
  53. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +72 -70
  54. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/model-catalog.mjs +195 -0
  55. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/paths.mjs +11 -0
  56. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/pool.mjs +36 -0
  57. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +169 -0
  58. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +136 -346
  59. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +1 -1
  60. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/ssss-store.mjs +20 -0
  61. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/telemetry.mjs +43 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +156 -0
  63. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +117 -35
  64. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +51 -0
  65. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +398 -57
  66. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +304 -46
  67. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +367 -41
  68. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +5 -2
  69. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +33 -0
  70. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +2 -2
  71. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +159 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +197 -0
  73. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/lint.mjs +153 -0
  74. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +261 -0
  75. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +151 -4
  76. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +114 -0
  77. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +96 -0
  78. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +899 -662
  79. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/openwiki.mjs +8987 -0
  80. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/mermaid-diagrams/SKILL.md +44 -0
  81. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/write-connector/SKILL.md +56 -0
  82. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3097 -81
  83. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +4 -1
  84. package/src/cli/agent.mjs +12 -1
  85. package/src/cli/compact.mjs +108 -0
  86. package/src/cli/connect.mjs +56 -1
  87. package/src/cli/context.mjs +55 -23
  88. package/src/cli/doctor.mjs +5 -2
  89. package/src/cli/forget.mjs +2 -0
  90. package/src/cli/harness.mjs +62 -50
  91. package/src/cli/memory-entry.mjs +2 -2
  92. package/src/cli/preflight.mjs +152 -16
  93. package/src/cli/release.mjs +386 -0
  94. package/src/cli/remember.mjs +19 -1
  95. package/src/cli/rules.mjs +54 -12
  96. package/src/cli/startup.mjs +1 -1
  97. package/src/cli/status.mjs +12 -2
  98. package/src/core/claude-hooks.mjs +41 -3
  99. package/src/core/cli-commands.mjs +2 -0
  100. package/src/core/cli-semantic-index.mjs +3 -2
  101. package/src/core/conflict-detector.mjs +23 -12
  102. package/src/core/context-compactor.mjs +145 -0
  103. package/src/core/context-policy.mjs +20 -0
  104. package/src/core/duplicate-guard.mjs +350 -0
  105. package/src/core/meta-harness.mjs +31 -11
  106. package/src/core/preflight-gate.mjs +2 -2
  107. package/src/core/preflight-receipt.mjs +29 -1
  108. package/src/core/scaffold-plugins.mjs +7 -0
  109. package/src/core/stdio-flush.mjs +23 -0
  110. package/src/core/surface.mjs +33 -10
  111. package/src/server/invoke-durability.mjs +39 -0
  112. package/src/server/invoke-write-child.mjs +25 -0
  113. package/src/server/routes/invoke.mjs +7 -0
  114. package/src/server/routes/plugins.mjs +2 -2
  115. package/frontend/dist/assets/index-BIQmqDKP.css +0 -1
  116. package/frontend/dist/assets/index-CRzjQLu7.js +0 -5631
  117. package/frontend/src/components/DaemonLogsTab.tsx +0 -54
  118. package/frontend/src/components/ResearchAgendaTab.tsx +0 -712
  119. package/frontend/src/components/TaskQueueTab.tsx +0 -193
  120. package/frontend/src/components/VoiceInput.tsx +0 -112
  121. package/frontend/src/pages/ChatPage.tsx +0 -1059
  122. package/frontend/src/pages/CollabPage.tsx +0 -470
  123. package/frontend/src/pages/DesignDocsPage.tsx +0 -621
  124. package/frontend/src/pages/FilesPage.tsx +0 -393
  125. package/frontend/src/pages/HealthPage.tsx +0 -359
  126. package/frontend/src/pages/InboxPage.tsx +0 -83
  127. package/frontend/src/pages/InstructionsPage.tsx +0 -583
  128. package/frontend/src/pages/IntegrationsPage.tsx +0 -761
  129. package/frontend/src/pages/MeshPage.css +0 -58
  130. package/frontend/src/pages/MeshPage.tsx +0 -1219
  131. package/frontend/src/pages/OkfPage.tsx +0 -535
  132. package/frontend/src/pages/SandboxPage.tsx +0 -113
  133. package/frontend/src/pages/SkillsPage.tsx +0 -1994
  134. package/frontend/src/pages/TasksPage.tsx +0 -427
  135. package/frontend/src/pages/UsagePage.tsx +0 -702
  136. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +0 -65
  137. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +0 -76
  138. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +0 -19
  139. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +0 -266
  140. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +0 -446
  141. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +0 -152
  142. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +0 -71
  143. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +0 -36
  144. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +0 -35
  145. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +0 -175
  146. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/DESIGN.md +0 -32
  147. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +0 -575
@@ -0,0 +1,183 @@
1
+ /**
2
+ * DeepSeek Harness (DSH) PreToolUse hook adapter.
3
+ * Migrates legacy tr-first.sh / tr-wrapper.sh into managed preflight gate.
4
+ */
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import os from 'node:os';
8
+
9
+ const ACK_COMMAND = 'total-recall tr-preflight --ack --task "<task>" --action read,edit,test';
10
+
11
+ export const dshAdapter = {
12
+ id: 'dsh',
13
+ level: 'enforced',
14
+ source: {
15
+ note: 'DeepSeek Harness hook configuration in ~/.dsh/hooks/hooks.json',
16
+ retrieved: '2026-11-04'
17
+ },
18
+ limits: [
19
+ 'Claude-compatible PreToolUse contract: exit code 2 blocks execution',
20
+ 'Repository sweep checks enforce Total Recall consult requirement'
21
+ ],
22
+
23
+ parse(input = {}) {
24
+ const event = input.hook_event_name || input.event || 'PreToolUse';
25
+ const toolName = input.tool_name || input.toolName || '';
26
+ const toolInput = input.tool_input || input.toolInput || {};
27
+ const cwd = input.cwd || process.cwd();
28
+
29
+ let tool = 'other';
30
+ let command;
31
+ let filePath;
32
+
33
+ if (toolName === 'Bash' || toolName === 'shell') {
34
+ tool = 'shell';
35
+ command = toolInput.command || toolInput.cmd || '';
36
+ } else if (toolName === 'Edit' || toolName === 'Write') {
37
+ tool = 'write';
38
+ filePath = toolInput.file_path || toolInput.path || '';
39
+ } else if (toolName === 'Read' || /^(Glob|Grep)$/.test(toolName)) {
40
+ tool = 'read';
41
+ filePath = toolInput.file_path || toolInput.path || '';
42
+ }
43
+
44
+ return {
45
+ event,
46
+ tool,
47
+ command,
48
+ path: filePath,
49
+ cwd,
50
+ raw: input
51
+ };
52
+ },
53
+
54
+ emit(decision, event, cwd = process.cwd()) {
55
+ if (decision?.decision === 'allow') {
56
+ return { stdout: '', exitCode: 0 };
57
+ }
58
+
59
+ const reason = decision?.reason || `TR-PREFLIGHT gate: action denied. Run: ${decision?.fix || ACK_COMMAND}`;
60
+ const payload = {
61
+ decision: 'deny',
62
+ permissionDecision: 'deny',
63
+ reason,
64
+ fix: decision?.fix || ACK_COMMAND
65
+ };
66
+ return { stdout: `${JSON.stringify(payload)}\n`, exitCode: 2 };
67
+ },
68
+
69
+ async run({ stdin, decideWith }) {
70
+ const parsed = this.parse(stdin);
71
+ const decision = decideWith({
72
+ tool: parsed.tool,
73
+ command: parsed.command,
74
+ path: parsed.path,
75
+ cwd: parsed.cwd
76
+ });
77
+ return this.emit(decision, parsed.event, parsed.cwd);
78
+ },
79
+
80
+ install({ home = os.homedir(), dryRun = false, remove = false, off = false } = {}) {
81
+ const isRemove = remove || off;
82
+ const dshDir = path.join(home, '.dsh', 'hooks');
83
+ const hooksPath = path.join(dshDir, 'hooks.json');
84
+ const legacyTrFirst = path.join(dshDir, 'tr-first.sh');
85
+ const added = [];
86
+ const removed = [];
87
+
88
+ let hooksConfig = { hooks: {} };
89
+ if (fs.existsSync(hooksPath)) {
90
+ try {
91
+ hooksConfig = JSON.parse(fs.readFileSync(hooksPath, 'utf8') || '{}');
92
+ } catch (err) {
93
+ throw new Error(`${hooksPath} is not valid JSON; fix it before installing hooks: ${err.message}`);
94
+ }
95
+ }
96
+ hooksConfig.hooks = hooksConfig.hooks || {};
97
+ hooksConfig.hooks.PreToolUse = hooksConfig.hooks.PreToolUse || [];
98
+
99
+ if (isRemove) {
100
+ const beforeLen = hooksConfig.hooks.PreToolUse.length;
101
+ hooksConfig.hooks.PreToolUse = hooksConfig.hooks.PreToolUse.filter(e =>
102
+ !String(e.command || '').includes('preflight') && !String(e.command || '').includes('tr-first')
103
+ );
104
+ if (hooksConfig.hooks.PreToolUse.length < beforeLen) {
105
+ removed.push('managed PreToolUse gate');
106
+ }
107
+ if (!dryRun && fs.existsSync(hooksPath)) {
108
+ fs.writeFileSync(hooksPath, `${JSON.stringify(hooksConfig, null, 2)}\n`);
109
+ }
110
+ return { settings: hooksPath, added: [], removed };
111
+ }
112
+
113
+ // Remove legacy unmanaged tr-first.sh scripts if present (F-011)
114
+ const beforeLen = hooksConfig.hooks.PreToolUse.length;
115
+ hooksConfig.hooks.PreToolUse = hooksConfig.hooks.PreToolUse.filter(e => {
116
+ const cmd = String(e.command || '');
117
+ return !cmd.includes('tr-first.sh') && !cmd.includes('tr-wrapper.sh');
118
+ });
119
+ if (hooksConfig.hooks.PreToolUse.length < beforeLen) {
120
+ removed.push('legacy tr-first.sh hook entry');
121
+ }
122
+
123
+ const gateCommand = 'total-recall preflight gate --harness dsh';
124
+ const hasPreTool = hooksConfig.hooks.PreToolUse.some(e =>
125
+ String(e.command || '').includes('preflight gate --harness dsh')
126
+ );
127
+
128
+ if (!hasPreTool) {
129
+ hooksConfig.hooks.PreToolUse.push({
130
+ matcher: '*',
131
+ command: gateCommand,
132
+ timeout: 10
133
+ });
134
+ added.push('managed PreToolUse gate');
135
+ }
136
+
137
+ if (!dryRun && (added.length || removed.length)) {
138
+ fs.mkdirSync(dshDir, { recursive: true });
139
+ if (fs.existsSync(hooksPath)) {
140
+ fs.copyFileSync(hooksPath, `${hooksPath}.bak-total-recall`);
141
+ }
142
+ fs.writeFileSync(hooksPath, `${JSON.stringify(hooksConfig, null, 2)}\n`);
143
+ }
144
+
145
+ return { settings: hooksPath, added, removed };
146
+ },
147
+
148
+ verify({ home = os.homedir() } = {}) {
149
+ const hooksPath = path.join(home, '.dsh', 'hooks', 'hooks.json');
150
+ let installed = false;
151
+ if (fs.existsSync(hooksPath)) {
152
+ try {
153
+ const config = JSON.parse(fs.readFileSync(hooksPath, 'utf8') || '{}');
154
+ const preHooks = config.hooks?.PreToolUse || [];
155
+ installed = preHooks.some(e => String(e.command || '').includes('preflight gate --harness dsh'));
156
+ } catch { /* unparseable */ }
157
+ }
158
+
159
+ const syntheticDeniedCall = {
160
+ hook_event_name: 'PreToolUse',
161
+ tool_name: 'Bash',
162
+ tool_input: { command: 'rm -rf /tmp/foo' },
163
+ cwd: '/nonexistent-directory-without-receipt'
164
+ };
165
+
166
+ const out = this.emit(
167
+ { decision: 'deny', reason: 'receipt missing', fix: ACK_COMMAND },
168
+ 'PreToolUse',
169
+ syntheticDeniedCall.cwd
170
+ );
171
+
172
+ const exercised = out.exitCode === 2 && out.stdout.includes('"decision":"deny"');
173
+
174
+ return {
175
+ installed,
176
+ exercised,
177
+ level: 'enforced',
178
+ native: out.stdout.trim()
179
+ };
180
+ }
181
+ };
182
+
183
+ export default dshAdapter;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * xAI Grok Build CLI hook adapter.
3
+ * Official doc: https://docs.x.ai/build/features/hooks
4
+ */
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import os from 'node:os';
8
+
9
+ const ACK_COMMAND = 'total-recall tr-preflight --ack --task "<task>" --action read,edit,test';
10
+
11
+ export const grokAdapter = {
12
+ id: 'grok',
13
+ level: 'enforced (unverified live)',
14
+ source: {
15
+ url: 'https://docs.x.ai/build/features/hooks',
16
+ retrieved: '2026-11-04',
17
+ note: 'Grok Build hooks in ~/.grok/hooks/ or .grok/hooks/'
18
+ },
19
+ limits: [
20
+ 'Timeouts, crashes and malformed outputs fail open in Grok Build',
21
+ 'Project-level hooks require trust in ~/.grok/trusted_folders.toml'
22
+ ],
23
+
24
+ parse(input = {}) {
25
+ const event = input.hookEventName || input.event || 'PreToolUse';
26
+ const toolName = input.toolName || input.tool_name || input.tool || '';
27
+ const toolInput = input.toolInput || input.tool_input || input.input || {};
28
+ const cwd = input.cwd || input.workspaceRoot || process.cwd();
29
+
30
+ let tool = 'other';
31
+ let command;
32
+ let filePath;
33
+
34
+ if (toolName === 'Bash' || toolName === 'exec' || toolName === 'shell') {
35
+ tool = 'shell';
36
+ command = toolInput.command || toolInput.cmd || '';
37
+ } else if (/^(Edit|Write)$/.test(toolName)) {
38
+ tool = 'write';
39
+ filePath = toolInput.file_path || toolInput.path || '';
40
+ } else if (/^(Read|Glob|Grep)$/.test(toolName)) {
41
+ tool = 'read';
42
+ filePath = toolInput.file_path || toolInput.path || '';
43
+ }
44
+
45
+ return {
46
+ event,
47
+ tool,
48
+ command,
49
+ path: filePath,
50
+ cwd,
51
+ raw: input
52
+ };
53
+ },
54
+
55
+ emit(decision, event, cwd = process.cwd()) {
56
+ if (decision?.decision === 'allow') {
57
+ const payload = { decision: 'allow' };
58
+ return { stdout: `${JSON.stringify(payload)}\n`, exitCode: 0 };
59
+ }
60
+
61
+ const reason = decision?.reason || `TR-PREFLIGHT gate: action denied. Run: ${decision?.fix || ACK_COMMAND}`;
62
+ const payload = {
63
+ decision: 'deny',
64
+ reason,
65
+ fix: decision?.fix || ACK_COMMAND
66
+ };
67
+ return { stdout: `${JSON.stringify(payload)}\n`, exitCode: 2 };
68
+ },
69
+
70
+ async run({ stdin, decideWith }) {
71
+ const parsed = this.parse(stdin);
72
+ const decision = decideWith({
73
+ tool: parsed.tool,
74
+ command: parsed.command,
75
+ path: parsed.path,
76
+ cwd: parsed.cwd
77
+ });
78
+ return this.emit(decision, parsed.event, parsed.cwd);
79
+ },
80
+
81
+ install({ home = os.homedir(), dryRun = false } = {}) {
82
+ const grokDir = path.join(home, '.grok', 'hooks');
83
+ const hooksPath = path.join(grokDir, 'total-recall-preflight.json');
84
+ const added = [];
85
+
86
+ const hookDef = {
87
+ name: 'total-recall-preflight',
88
+ description: 'Fail-closed preflight gate for Total Recall',
89
+ events: ['PreToolUse'],
90
+ matcher: '*',
91
+ command: 'total-recall preflight gate --harness grok',
92
+ timeout: 5
93
+ };
94
+
95
+ if (!dryRun) {
96
+ fs.mkdirSync(grokDir, { recursive: true });
97
+ fs.writeFileSync(hooksPath, `${JSON.stringify(hookDef, null, 2)}\n`);
98
+ added.push('managed Grok PreToolUse hook');
99
+ }
100
+
101
+ return { settings: hooksPath, added };
102
+ },
103
+
104
+ verify({ home = os.homedir() } = {}) {
105
+ const hooksPath = path.join(home, '.grok', 'hooks', 'total-recall-preflight.json');
106
+ const installed = fs.existsSync(hooksPath);
107
+
108
+ const syntheticDeniedCall = {
109
+ hookEventName: 'PreToolUse',
110
+ toolName: 'Bash',
111
+ toolInput: { command: 'rm -rf /tmp/foo' },
112
+ cwd: '/nonexistent-directory-without-receipt'
113
+ };
114
+
115
+ const out = this.emit(
116
+ { decision: 'deny', reason: 'receipt missing', fix: ACK_COMMAND },
117
+ 'PreToolUse',
118
+ syntheticDeniedCall.cwd
119
+ );
120
+
121
+ const exercised = out.exitCode === 2 && out.stdout.includes('"decision":"deny"');
122
+
123
+ return {
124
+ installed,
125
+ exercised,
126
+ level: 'enforced (unverified live)',
127
+ native: out.stdout.trim()
128
+ };
129
+ }
130
+ };
131
+
132
+ export default grokAdapter;
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Hermes Agent Runtime hook adapter.
3
+ * Official doc: https://hermes-agent.nousresearch.com/docs/user-guide/features/hooks
4
+ */
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import os from 'node:os';
8
+
9
+ const ACK_COMMAND = 'total-recall tr-preflight --ack --task "<task>" --action read,edit,test';
10
+
11
+ export const hermesAdapter = {
12
+ id: 'hermes',
13
+ level: 'enforced',
14
+ source: {
15
+ url: 'https://hermes-agent.nousresearch.com/docs/user-guide/features/hooks',
16
+ retrieved: '2026-11-04',
17
+ note: 'Hermes shell & plugin hooks in ~/.hermes/config.yaml'
18
+ },
19
+ limits: [
20
+ 'Hermes plugin callbacks fail closed on timeout',
21
+ 'Pre-tool hook returns {"action":"block","message":...} with exit 2'
22
+ ],
23
+
24
+ parse(input = {}) {
25
+ const event = input.event || input.hook_event_name || 'pre_tool_call';
26
+ const toolName = input.tool_name || input.tool || '';
27
+ const toolInput = input.tool_input || input.params || input.args || {};
28
+ const cwd = input.cwd || process.cwd();
29
+
30
+ let tool = 'other';
31
+ let command;
32
+ let filePath;
33
+
34
+ if (toolName === 'terminal' || toolName === 'Bash' || toolName === 'shell' || toolName === 'exec') {
35
+ tool = 'shell';
36
+ command = toolInput.command || toolInput.cmd || '';
37
+ } else if (toolName === 'write_file' || toolName === 'Edit' || toolName === 'Write') {
38
+ tool = 'write';
39
+ filePath = toolInput.path || toolInput.file_path || '';
40
+ } else if (toolName === 'read_file' || toolName === 'Read') {
41
+ tool = 'read';
42
+ filePath = toolInput.path || toolInput.file_path || '';
43
+ }
44
+
45
+ return {
46
+ event,
47
+ tool,
48
+ command,
49
+ path: filePath,
50
+ cwd,
51
+ raw: input
52
+ };
53
+ },
54
+
55
+ emit(decision, event, cwd = process.cwd()) {
56
+ if (decision?.decision === 'allow') {
57
+ const payload = { action: 'allow' };
58
+ return { stdout: `${JSON.stringify(payload)}\n`, exitCode: 0 };
59
+ }
60
+
61
+ const reason = decision?.reason || `TR-PREFLIGHT gate: action denied. Run: ${decision?.fix || ACK_COMMAND}`;
62
+ const payload = {
63
+ action: 'block',
64
+ message: reason,
65
+ fix: decision?.fix || ACK_COMMAND
66
+ };
67
+ return { stdout: `${JSON.stringify(payload)}\n`, exitCode: 2 };
68
+ },
69
+
70
+ async run({ stdin, decideWith }) {
71
+ const parsed = this.parse(stdin);
72
+ const decision = decideWith({
73
+ tool: parsed.tool,
74
+ command: parsed.command,
75
+ path: parsed.path,
76
+ cwd: parsed.cwd
77
+ });
78
+ return this.emit(decision, parsed.event, parsed.cwd);
79
+ },
80
+
81
+ install({ home = os.homedir(), dryRun = false, remove = false, off = false } = {}) {
82
+ const isRemove = remove || off;
83
+ const hermesDir = path.join(home, '.hermes');
84
+ const hooksPath = path.join(hermesDir, 'hooks.json');
85
+ const added = [];
86
+ const removed = [];
87
+
88
+ let hooksConfig = { hooks: {} };
89
+ if (fs.existsSync(hooksPath)) {
90
+ try {
91
+ hooksConfig = JSON.parse(fs.readFileSync(hooksPath, 'utf8') || '{}');
92
+ } catch (err) {
93
+ throw new Error(`${hooksPath} is not valid JSON; fix it before installing hooks: ${err.message}`);
94
+ }
95
+ }
96
+ hooksConfig.hooks = hooksConfig.hooks || {};
97
+ hooksConfig.hooks.pre_tool_call = hooksConfig.hooks.pre_tool_call || [];
98
+
99
+ if (isRemove) {
100
+ const beforeLen = hooksConfig.hooks.pre_tool_call.length;
101
+ hooksConfig.hooks.pre_tool_call = hooksConfig.hooks.pre_tool_call.filter(e =>
102
+ !String(e.command || '').includes('preflight')
103
+ );
104
+ if (hooksConfig.hooks.pre_tool_call.length < beforeLen) {
105
+ removed.push('managed pre_tool_call gate');
106
+ }
107
+ if (!dryRun && fs.existsSync(hooksPath)) {
108
+ fs.writeFileSync(hooksPath, `${JSON.stringify(hooksConfig, null, 2)}\n`);
109
+ }
110
+ return { settings: hooksPath, added: [], removed };
111
+ }
112
+
113
+ const gateCommand = 'total-recall preflight gate --harness hermes';
114
+ const hasPreTool = hooksConfig.hooks.pre_tool_call.some(e =>
115
+ String(e.command || '').includes('preflight gate --harness hermes')
116
+ );
117
+
118
+ if (!hasPreTool) {
119
+ hooksConfig.hooks.pre_tool_call.push({
120
+ matcher: '*',
121
+ command: gateCommand,
122
+ timeout: 10
123
+ });
124
+ added.push('managed pre_tool_call gate');
125
+ }
126
+
127
+ if (!dryRun && added.length) {
128
+ fs.mkdirSync(hermesDir, { recursive: true });
129
+ if (fs.existsSync(hooksPath)) {
130
+ fs.copyFileSync(hooksPath, `${hooksPath}.bak-total-recall`);
131
+ }
132
+ fs.writeFileSync(hooksPath, `${JSON.stringify(hooksConfig, null, 2)}\n`);
133
+ }
134
+
135
+ return { settings: hooksPath, added };
136
+ },
137
+
138
+ verify({ home = os.homedir() } = {}) {
139
+ const hooksPath = path.join(home, '.hermes', 'hooks.json');
140
+ let installed = false;
141
+ if (fs.existsSync(hooksPath)) {
142
+ try {
143
+ const config = JSON.parse(fs.readFileSync(hooksPath, 'utf8') || '{}');
144
+ const preHooks = config.hooks?.pre_tool_call || [];
145
+ installed = preHooks.some(e => String(e.command || '').includes('preflight gate --harness hermes'));
146
+ } catch { /* unparseable */ }
147
+ }
148
+
149
+ const syntheticDeniedCall = {
150
+ event: 'pre_tool_call',
151
+ tool_name: 'terminal',
152
+ tool_input: { command: 'rm -rf /tmp/foo' },
153
+ cwd: '/nonexistent-directory-without-receipt'
154
+ };
155
+
156
+ const out = this.emit(
157
+ { decision: 'deny', reason: 'receipt missing', fix: ACK_COMMAND },
158
+ 'pre_tool_call',
159
+ syntheticDeniedCall.cwd
160
+ );
161
+
162
+ const exercised = out.exitCode === 2 && out.stdout.includes('"action":"block"');
163
+
164
+ return {
165
+ installed,
166
+ exercised,
167
+ level: 'enforced',
168
+ native: out.stdout.trim()
169
+ };
170
+ }
171
+ };
172
+
173
+ export default hermesAdapter;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Gate Adapters registry for Meta Harness (tr-plugin-cli-agents).
3
+ * Provides harness-specific parsing, emission, installation and verification.
4
+ */
5
+ import { claudeAdapter } from './claude.mjs';
6
+ import { agyAdapter } from './agy.mjs';
7
+ import { codexAdapter } from './codex.mjs';
8
+ import { dshAdapter } from './dsh.mjs';
9
+ import { hermesAdapter } from './hermes.mjs';
10
+ import { openclawAdapter } from './openclaw.mjs';
11
+ import { cursorAdapter } from './cursor.mjs';
12
+ import { devinAdapter } from './devin.mjs';
13
+ import { grokAdapter } from './grok.mjs';
14
+ import { clineAdapter } from './cline.mjs';
15
+ import { piAdapter } from './pi.mjs';
16
+
17
+ const ADAPTERS = new Map([
18
+ ['claude', claudeAdapter],
19
+ ['claude-code', claudeAdapter],
20
+ ['agy', agyAdapter],
21
+ ['antigravity', agyAdapter],
22
+ ['codex', codexAdapter],
23
+ ['dsh', dshAdapter],
24
+ ['hermes', hermesAdapter],
25
+ ['openclaw', openclawAdapter],
26
+ ['cursor', cursorAdapter],
27
+ ['devin', devinAdapter],
28
+ ['windsurf', devinAdapter],
29
+ ['grok', grokAdapter],
30
+ ['cline', clineAdapter],
31
+ ['pi', piAdapter]
32
+ ]);
33
+
34
+ export function get(harnessId) {
35
+ if (!harnessId) return null;
36
+ return ADAPTERS.get(String(harnessId).toLowerCase()) || null;
37
+ }
38
+
39
+ export function list() {
40
+ const seen = new Set();
41
+ const result = [];
42
+ for (const adapter of ADAPTERS.values()) {
43
+ if (!seen.has(adapter.id)) {
44
+ seen.add(adapter.id);
45
+ result.push(adapter);
46
+ }
47
+ }
48
+ return result;
49
+ }
50
+
51
+ export function install(harnessId, opts = {}) {
52
+ const adapter = get(harnessId);
53
+ if (!adapter) throw new Error(`Unknown harness adapter: ${harnessId}`);
54
+ if (typeof adapter.install !== 'function') {
55
+ throw new Error(`Harness adapter ${harnessId} does not support installation`);
56
+ }
57
+ return adapter.install(opts);
58
+ }
59
+
60
+ export function verify(harnessId, opts = {}) {
61
+ const adapter = get(harnessId);
62
+ if (!adapter) throw new Error(`Unknown harness adapter: ${harnessId}`);
63
+ if (typeof adapter.verify !== 'function') {
64
+ throw new Error(`Harness adapter ${harnessId} does not support verification`);
65
+ }
66
+ return adapter.verify(opts);
67
+ }
68
+
69
+ export default {
70
+ get,
71
+ list,
72
+ install,
73
+ verify,
74
+ adapters: {
75
+ claude: claudeAdapter,
76
+ agy: agyAdapter,
77
+ codex: codexAdapter,
78
+ dsh: dshAdapter,
79
+ hermes: hermesAdapter,
80
+ openclaw: openclawAdapter,
81
+ cursor: cursorAdapter,
82
+ devin: devinAdapter,
83
+ grok: grokAdapter,
84
+ cline: clineAdapter,
85
+ pi: piAdapter
86
+ }
87
+ };