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.
- package/bin/total-recall.mjs +10 -6
- package/docs/reference/cli-reference.md +18 -0
- package/frontend/dist/assets/index-4tGQfTa2.js +5605 -0
- package/frontend/dist/assets/index-DtwsDyFM.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/package.json +11 -0
- package/frontend/src/App.tsx +47 -122
- package/frontend/src/components/plugins/plugin-frame.ts +1 -0
- package/frontend/src/index.css +152 -86
- package/frontend/src/pages/HelpPage.tsx +13 -10
- package/frontend/src/pages/MemoryPage.tsx +28 -30
- package/frontend/src/pages/NetworkPage.tsx +3 -3
- package/frontend/src/pages/PluginsPage.tsx +6 -5
- package/frontend/src/pages/RulesPage.tsx +16 -12
- package/package.json +4 -3
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +3114 -36
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +4 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +3114 -36
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +4 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +3121 -33
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +4 -1
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +3122 -34
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +4 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +3127 -49
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +136 -7
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +135 -26
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +3121 -33
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +228 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +24 -30
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +29 -16
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +73 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +182 -365
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +40 -14
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/agy.mjs +187 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/claude.mjs +275 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cline.mjs +132 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/codex.mjs +187 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cursor.mjs +158 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/devin.mjs +157 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/dsh.mjs +183 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/grok.mjs +132 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +173 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +87 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/openclaw.mjs +173 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/pi.mjs +129 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate.mjs +12 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/json-reply.mjs +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +72 -70
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/model-catalog.mjs +195 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/paths.mjs +11 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/pool.mjs +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +169 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +136 -346
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/ssss-store.mjs +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/telemetry.mjs +43 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +156 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +117 -35
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +51 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +398 -57
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +304 -46
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +367 -41
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +33 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +159 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +197 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/lint.mjs +153 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +261 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +151 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +114 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +96 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +899 -662
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/openwiki.mjs +8987 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/mermaid-diagrams/SKILL.md +44 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/write-connector/SKILL.md +56 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3097 -81
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +4 -1
- package/src/cli/agent.mjs +12 -1
- package/src/cli/compact.mjs +108 -0
- package/src/cli/connect.mjs +56 -1
- package/src/cli/context.mjs +55 -23
- package/src/cli/doctor.mjs +5 -2
- package/src/cli/forget.mjs +2 -0
- package/src/cli/harness.mjs +62 -50
- package/src/cli/memory-entry.mjs +2 -2
- package/src/cli/preflight.mjs +152 -16
- package/src/cli/release.mjs +386 -0
- package/src/cli/remember.mjs +19 -1
- package/src/cli/rules.mjs +54 -12
- package/src/cli/startup.mjs +1 -1
- package/src/cli/status.mjs +12 -2
- package/src/core/claude-hooks.mjs +41 -3
- package/src/core/cli-commands.mjs +2 -0
- package/src/core/cli-semantic-index.mjs +3 -2
- package/src/core/conflict-detector.mjs +23 -12
- package/src/core/context-compactor.mjs +145 -0
- package/src/core/context-policy.mjs +20 -0
- package/src/core/duplicate-guard.mjs +350 -0
- package/src/core/meta-harness.mjs +31 -11
- package/src/core/preflight-gate.mjs +2 -2
- package/src/core/preflight-receipt.mjs +29 -1
- package/src/core/scaffold-plugins.mjs +7 -0
- package/src/core/stdio-flush.mjs +23 -0
- package/src/core/surface.mjs +33 -10
- package/src/server/invoke-durability.mjs +39 -0
- package/src/server/invoke-write-child.mjs +25 -0
- package/src/server/routes/invoke.mjs +7 -0
- package/src/server/routes/plugins.mjs +2 -2
- package/frontend/dist/assets/index-BIQmqDKP.css +0 -1
- package/frontend/dist/assets/index-CRzjQLu7.js +0 -5631
- package/frontend/src/components/DaemonLogsTab.tsx +0 -54
- package/frontend/src/components/ResearchAgendaTab.tsx +0 -712
- package/frontend/src/components/TaskQueueTab.tsx +0 -193
- package/frontend/src/components/VoiceInput.tsx +0 -112
- package/frontend/src/pages/ChatPage.tsx +0 -1059
- package/frontend/src/pages/CollabPage.tsx +0 -470
- package/frontend/src/pages/DesignDocsPage.tsx +0 -621
- package/frontend/src/pages/FilesPage.tsx +0 -393
- package/frontend/src/pages/HealthPage.tsx +0 -359
- package/frontend/src/pages/InboxPage.tsx +0 -83
- package/frontend/src/pages/InstructionsPage.tsx +0 -583
- package/frontend/src/pages/IntegrationsPage.tsx +0 -761
- package/frontend/src/pages/MeshPage.css +0 -58
- package/frontend/src/pages/MeshPage.tsx +0 -1219
- package/frontend/src/pages/OkfPage.tsx +0 -535
- package/frontend/src/pages/SandboxPage.tsx +0 -113
- package/frontend/src/pages/SkillsPage.tsx +0 -1994
- package/frontend/src/pages/TasksPage.tsx +0 -427
- package/frontend/src/pages/UsagePage.tsx +0 -702
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +0 -65
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +0 -76
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +0 -19
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +0 -266
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +0 -446
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +0 -152
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +0 -71
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +0 -36
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +0 -35
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +0 -175
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/DESIGN.md +0 -32
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +0 -575
package/src/cli/harness.mjs
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { checkReceipt, findRoot, ACK_COMMAND } from '../core/preflight-receipt.mjs';
|
|
4
|
+
|
|
5
|
+
/** Harness work belongs to the meta-harness plugin. These commands are a thin door to it. */
|
|
6
|
+
async function callPlugin(subcommand, args, options = {}) {
|
|
7
|
+
const { getPlugin } = await import('../core/plugin-loader.mjs');
|
|
8
|
+
const plugin = getPlugin('meta-harness', process.cwd());
|
|
9
|
+
if (!plugin?.valid) throw new Error('The meta-harness plugin is not installed. Run: total-recall plugin install meta-harness');
|
|
10
|
+
const { runPluginCommand } = await import('../core/plugin-runner.mjs');
|
|
11
|
+
const result = await runPluginCommand(plugin, { subcommand, args: [...args, '--json'], timeoutMs: 600000, ...options });
|
|
12
|
+
let data = null;
|
|
13
|
+
try { data = JSON.parse(result.output); } catch { /* reported below */ }
|
|
14
|
+
if (!data) throw new Error(`meta-harness ${subcommand} returned no result: ${String(result.output).trim().slice(0, 300)}`);
|
|
15
|
+
return { ok: result.ok, data };
|
|
16
|
+
}
|
|
4
17
|
|
|
5
18
|
function printHelp() {
|
|
6
19
|
console.log(`
|
|
@@ -10,10 +23,10 @@ Usage:
|
|
|
10
23
|
npx total-recall harness <command> [options]
|
|
11
24
|
|
|
12
25
|
Commands:
|
|
13
|
-
list
|
|
14
|
-
|
|
15
|
-
dispatch <id> "<task>" Headlessly invoke a specific agent harness
|
|
26
|
+
list, usage Harness readiness, versions and measured usage
|
|
27
|
+
dispatch <id> "<task>" Headlessly invoke a specific agent harness (read-only by default)
|
|
16
28
|
Options: --node <name> to execute remotely on a mesh node
|
|
29
|
+
--write to let the agent change files
|
|
17
30
|
council "<task>" Run concurrent multi-harness consensus deliberation
|
|
18
31
|
|
|
19
32
|
Available Harness IDs:
|
|
@@ -49,45 +62,32 @@ export async function run(argv) {
|
|
|
49
62
|
const asJson = args.includes('--json');
|
|
50
63
|
|
|
51
64
|
if (!command || command === 'list' || command === 'status' || command === 'usage') {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
let report;
|
|
66
|
+
try { ({ data: report } = await callPlugin('quota', [])); }
|
|
67
|
+
catch (err) { console.error(`\n❌ ${err.message}`); process.exit(1); }
|
|
68
|
+
if (asJson) { console.log(JSON.stringify(report, null, 2)); return; }
|
|
69
|
+
const pad = (value, width) => String(value ?? '—').padEnd(width).slice(0, width);
|
|
70
|
+
console.log(`\n🎮 Total Recall — Agent Harnesses (${report.node})\n`);
|
|
71
|
+
console.log(`${pad('Harness', 12)} ${pad('Version', 12)} ${pad('Status', 14)} ${pad('5h used', 9)} ${pad('Week used', 10)} Note`);
|
|
72
|
+
for (const [id, quota] of Object.entries(report.quotas || {})) {
|
|
73
|
+
if (id === 'system' || id === 'metered') continue;
|
|
74
|
+
console.log(`${pad(id, 12)} ${pad(quota.version, 12)} ${pad(quota.status, 14)} ${pad(quota.rolling5hUsedPercent, 9)} ${pad(quota.weeklyUsedPercent, 10)} ${quota.quotaNote || ''}`);
|
|
56
75
|
}
|
|
57
|
-
|
|
58
|
-
console.log(`\n🎮 Total Recall — Connected Agent Harnesses, Versions & Quotas\n`);
|
|
59
|
-
|
|
60
|
-
const pad = (s, l) => String(s || '').padEnd(l).slice(0, l);
|
|
61
|
-
console.log('┌──────────────┬──────────────────────────────┬──────────────┬──────────────┬──────────────────────────┬──────────────────────┬──────────────────────┬──────────────┐');
|
|
62
|
-
console.log(`│ ${pad('Harness ID', 12)} │ ${pad('Name', 28)} │ ${pad('Version', 12)} │ ${pad('Current?', 12)} │ ${pad('Auth Route', 24)} │ ${pad('5h Quota Rem. %', 20)} │ ${pad('Weekly Rem. %', 20)} │ ${pad('Status', 12)} │`);
|
|
63
|
-
console.log('├──────────────┼──────────────────────────────┼──────────────┼──────────────┼──────────────────────────┼──────────────────────┼──────────────────────┼──────────────┤');
|
|
64
|
-
|
|
65
|
-
for (const h of reports) {
|
|
66
|
-
const ver = h.version || '—';
|
|
67
|
-
const cur = h.isCurrent ? 'Current ✅ ' : (h.latestVersion ? `Update (${h.latestVersion.slice(0, 5)})` : 'Check ⚠️ ');
|
|
68
|
-
const auth = h.authRoute || '—';
|
|
69
|
-
const q5h = h.rolling5hRemainingPct || '—';
|
|
70
|
-
const qWeek = h.weeklyRemainingPct || '—';
|
|
71
|
-
let statusStr = '\x1b[31mNot Found ❌\x1b[0m';
|
|
72
|
-
if (h.available) {
|
|
73
|
-
if (h.status === 'ready') statusStr = '\x1b[32mReady ✅\x1b[0m ';
|
|
74
|
-
else if (h.status === 'update_available') statusStr = '\x1b[33mUpdate 🔄\x1b[0m ';
|
|
75
|
-
else statusStr = '\x1b[33mNo Auth ⚠️\x1b[0m ';
|
|
76
|
-
}
|
|
77
|
-
console.log(`│ \x1b[1m${pad(h.id, 12)}\x1b[0m │ ${pad(h.name, 28)} │ ${pad(ver, 12)} │ ${pad(cur, 12)} │ ${pad(auth, 24)} │ ${pad(q5h, 20)} │ ${pad(qWeek, 20)} │ ${statusStr} │`);
|
|
78
|
-
}
|
|
79
|
-
console.log('└──────────────┴──────────────────────────────┴──────────────┴──────────────┴──────────────────────────┴──────────────────────┴──────────────────────┴──────────────┘\n');
|
|
76
|
+
console.log('');
|
|
80
77
|
return;
|
|
81
78
|
}
|
|
82
79
|
|
|
83
80
|
if (command === 'dispatch') {
|
|
84
81
|
let targetNode = null;
|
|
82
|
+
let write = false;
|
|
85
83
|
const filteredRest = [];
|
|
86
84
|
for (let i = 0; i < rest.length; i++) {
|
|
87
85
|
if (rest[i] === '--node' || rest[i] === '-n') {
|
|
88
86
|
targetNode = rest[i + 1];
|
|
89
87
|
i++;
|
|
90
|
-
} else {
|
|
88
|
+
} else if (rest[i] === '--write') {
|
|
89
|
+
write = true;
|
|
90
|
+
} else if (rest[i] !== '--json') {
|
|
91
91
|
filteredRest.push(rest[i]);
|
|
92
92
|
}
|
|
93
93
|
}
|
|
@@ -100,13 +100,24 @@ export async function run(argv) {
|
|
|
100
100
|
process.exit(1);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
if (!targetNode) {
|
|
104
|
+
const root = findRoot(process.cwd());
|
|
105
|
+
const receipt = checkReceipt(root);
|
|
106
|
+
if (!receipt.ok) {
|
|
107
|
+
console.error(`\n❌ Dispatch refused by TR-PREFLIGHT gate: ${receipt.problems[0] || 'check failed'} (directory: ${root}).`);
|
|
108
|
+
console.error(` Read the complete context capsule, then run: ${ACK_COMMAND}\n`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
103
113
|
const nodeStr = targetNode ? ` on mesh node \x1b[1;33m${targetNode}\x1b[0m` : '';
|
|
104
114
|
console.log(`\n🚀 Dispatching task to \x1b[1;36m${harnessId}\x1b[0m${nodeStr}...`);
|
|
105
115
|
try {
|
|
106
|
-
const
|
|
107
|
-
console.log(
|
|
108
|
-
|
|
109
|
-
console.log(`\n
|
|
116
|
+
const { ok, data } = await callPlugin('dispatch', [], { stdinText: JSON.stringify({ agent: harnessId, cwd: process.cwd(), prompt: task, node: targetNode || 'local', permissions: { read: true, write }, allowUnknownAuth: true }) });
|
|
117
|
+
if (asJson) { console.log(JSON.stringify(data, null, 2)); if (!ok) process.exit(1); return; }
|
|
118
|
+
if (!ok) throw new Error(data.error || 'The agent failed');
|
|
119
|
+
console.log(`\n--- [Response from ${harnessId}${targetNode ? ' (' + targetNode + ')' : ''}] ---\n`);
|
|
120
|
+
console.log(data.response);
|
|
110
121
|
} catch (err) {
|
|
111
122
|
console.error(`\n❌ Dispatch failed: ${err.message}`);
|
|
112
123
|
process.exit(1);
|
|
@@ -115,7 +126,14 @@ export async function run(argv) {
|
|
|
115
126
|
}
|
|
116
127
|
|
|
117
128
|
if (command === 'council') {
|
|
118
|
-
const
|
|
129
|
+
const root = findRoot(process.cwd());
|
|
130
|
+
const receipt = checkReceipt(root);
|
|
131
|
+
if (!receipt.ok) {
|
|
132
|
+
console.error(`\n❌ Council deliberation refused by TR-PREFLIGHT gate: ${receipt.problems[0] || 'check failed'} (directory: ${root}).`);
|
|
133
|
+
console.error(` Read the complete context capsule, then run: ${ACK_COMMAND}\n`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
const task = rest.filter(arg => arg !== '--json').join(' ').trim();
|
|
119
137
|
if (!task) {
|
|
120
138
|
console.error('Error: Please specify task prompt for council deliberation.');
|
|
121
139
|
console.error('Usage: npx total-recall harness council "<task>"');
|
|
@@ -126,20 +144,14 @@ export async function run(argv) {
|
|
|
126
144
|
console.log(` Task: "${task}"\n`);
|
|
127
145
|
|
|
128
146
|
try {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
for (const
|
|
133
|
-
console.log(
|
|
134
|
-
console.log(
|
|
135
|
-
console.log(`================================================================`);
|
|
136
|
-
if (r.success) {
|
|
137
|
-
console.log(r.response);
|
|
138
|
-
} else {
|
|
139
|
-
console.log(`\x1b[31mError: ${r.error || r.stderr || 'Execution failed'}\x1b[0m`);
|
|
140
|
-
}
|
|
141
|
-
console.log('');
|
|
147
|
+
const { data } = await callPlugin('coordinate', [task, '--mode', 'council', '--execute']);
|
|
148
|
+
if (!data.votes) throw new Error(data.error || 'No votes returned');
|
|
149
|
+
if (asJson) { console.log(JSON.stringify(data, null, 2)); return; }
|
|
150
|
+
for (const vote of data.votes) {
|
|
151
|
+
console.log(`● \x1b[1;36m${vote.harness}\x1b[0m: ${vote.vote}${vote.error ? ` (${vote.error})` : ''}`);
|
|
152
|
+
if (vote.feedback) console.log(`${vote.feedback}\n`);
|
|
142
153
|
}
|
|
154
|
+
console.log(`Consensus: ${data.consensus}`);
|
|
143
155
|
} catch (err) {
|
|
144
156
|
console.error(`\n❌ Council deliberation failed: ${err.message}`);
|
|
145
157
|
process.exit(1);
|
package/src/cli/memory-entry.mjs
CHANGED
|
@@ -69,10 +69,10 @@ Installed capabilities:`);
|
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
const plugins = discoverPlugins(process.cwd()).filter(p => p.valid);
|
|
72
|
-
const plugin = plugins.find(p => p.manifest.cli?.handler && (p.manifest.cli.command || p.id) === command)
|
|
72
|
+
const plugin = plugins.find(p => p.manifest.cli?.handler && ((p.manifest.cli.command || p.id) === command || p.manifest.cli.alias === command))
|
|
73
73
|
|| plugins.find(p => p.manifest.commands?.some(entry => entry.name === command));
|
|
74
74
|
if (!plugin) throw new Error(`Unknown command '${command}'. Install its capability plugin or run total-recall --help.`);
|
|
75
|
-
const declared = plugin.manifest.cli?.handler && (plugin.manifest.cli.command || plugin.id) === command
|
|
75
|
+
const declared = plugin.manifest.cli?.handler && ((plugin.manifest.cli.command || plugin.id) === command || plugin.manifest.cli.alias === command)
|
|
76
76
|
? null : plugin.manifest.commands.find(entry => entry.name === command);
|
|
77
77
|
if (declared && rest.some(arg => ['--help', '-h'].includes(arg))) {
|
|
78
78
|
console.log(`Usage: total-recall ${command} [args] [--json]\n${declared.description || ''}`);
|
package/src/cli/preflight.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { decide, SWEEP_CONSULT_MS } from '../core/preflight-gate.mjs';
|
|
3
|
-
import { ACK_COMMAND, acknowledge, checkReceipt, findRoot, lastConsultMs } from '../core/preflight-receipt.mjs';
|
|
3
|
+
import { ACK_COMMAND, acknowledge, checkReceipt, findRoot, lastConsultMs, recordDecision, readDecisions } from '../core/preflight-receipt.mjs';
|
|
4
4
|
|
|
5
5
|
const USAGE = `Usage: total-recall preflight <command>
|
|
6
6
|
ack --task "<task>" [--action read,edit,test] [--json] Record a receipt after context ready:true and rules verify
|
|
7
7
|
check [--json] Exit 1 unless a fresh receipt exists for this directory
|
|
8
8
|
gate [--harness <id>] Hook entry: read a call on stdin, answer allow or deny (exit 2 on deny)
|
|
9
9
|
remind [--json] Prompt-hook entry: advisory text when the receipt is missing; always exits 0
|
|
10
|
-
install
|
|
10
|
+
install [--harness <id>] [--dry-run] Install the gate for a harness. The gate has no off switch.
|
|
11
|
+
verify | report Harness adapters, owned by the cli-agents plugin
|
|
11
12
|
Aliases: total-recall tr-preflight --ack | --check. A receipt lasts 30 minutes (config preflight_max_age_minutes).`;
|
|
12
13
|
|
|
13
14
|
/** An explicit override only. The configured window is read once, by `ack`, and recorded in the receipt. */
|
|
@@ -76,6 +77,15 @@ async function ack(args) {
|
|
|
76
77
|
console.log(` receipt: ${result.file}`);
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
async function loadGateAdapters() {
|
|
81
|
+
let adapters;
|
|
82
|
+
try { adapters = await (await import('../core/plugin-runtime.mjs')).loadPluginModule('meta-harness', 'gate-adapters'); } catch { adapters = null; }
|
|
83
|
+
if (!adapters) {
|
|
84
|
+
try { adapters = await (await import('../core/plugin-runtime.mjs')).loadPluginModule('cli-agents', 'gate-adapters'); } catch { adapters = null; }
|
|
85
|
+
}
|
|
86
|
+
return adapters;
|
|
87
|
+
}
|
|
88
|
+
|
|
79
89
|
/**
|
|
80
90
|
* Hook entry. It starts at exit 2 and only an explicit allow clears it, so any
|
|
81
91
|
* failure, including one nobody anticipated, denies: most harnesses let the
|
|
@@ -91,19 +101,15 @@ async function gate(args) {
|
|
|
91
101
|
if (!call || typeof call !== 'object' || Array.isArray(call)) return deny('gate-error', 'TR-PREFLIGHT gate input must be a JSON object.');
|
|
92
102
|
const harness = flag(args, '--harness');
|
|
93
103
|
if (harness) {
|
|
94
|
-
|
|
95
|
-
try { adapters = await (await import('../core/plugin-runtime.mjs')).loadPluginModule('meta-harness', 'gate-adapters'); } catch { adapters = null; }
|
|
96
|
-
if (!adapters) {
|
|
97
|
-
try { adapters = await (await import('../core/plugin-runtime.mjs')).loadPluginModule('cli-agents', 'gate-adapters'); } catch { adapters = null; }
|
|
98
|
-
}
|
|
104
|
+
const adapters = await loadGateAdapters();
|
|
99
105
|
const adapter = adapters?.get?.(harness);
|
|
100
106
|
if (!adapter) return deny('gate-error', `No gate adapter for harness "${harness}": adapters are owned by the meta-harness plugin and it does not provide one. The gate fails closed.`);
|
|
101
|
-
const out = await adapter.run({ stdin: call, decideWith: evaluate });
|
|
107
|
+
const out = await adapter.run({ stdin: call, decideWith: (c) => evaluate(c, { harness, readablePaths: adapter.readablePaths }) });
|
|
102
108
|
if (out?.stdout) process.stdout.write(out.stdout);
|
|
103
109
|
process.exitCode = Number.isInteger(out?.exitCode) ? out.exitCode : 2;
|
|
104
110
|
return;
|
|
105
111
|
}
|
|
106
|
-
const answer = evaluate(call);
|
|
112
|
+
const answer = evaluate(call, { harness: 'generic' });
|
|
107
113
|
process.stdout.write(`${JSON.stringify(answer)}\n`);
|
|
108
114
|
if (answer.decision === 'allow') process.exitCode = 0;
|
|
109
115
|
} catch (err) {
|
|
@@ -111,10 +117,128 @@ async function gate(args) {
|
|
|
111
117
|
}
|
|
112
118
|
}
|
|
113
119
|
|
|
114
|
-
function
|
|
120
|
+
async function install(args) {
|
|
121
|
+
const harness = flag(args, '--harness') || 'claude';
|
|
122
|
+
const dryRun = args.includes('--dry-run');
|
|
123
|
+
if (args.includes('--remove') || args.includes('--off')) {
|
|
124
|
+
console.error('✖ The preflight gate has no off switch. If it blocks work, fix the cause: `total-recall tr-preflight --ack` names it, and `total-recall rules` stays open to repair it.');
|
|
125
|
+
process.exitCode = 1;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const adapters = await loadGateAdapters();
|
|
129
|
+
if (!adapters) {
|
|
130
|
+
console.error('✖ Gate adapters are owned by the meta-harness (cli-agents) plugin and are not available.');
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const res = adapters.install(harness, { dryRun });
|
|
136
|
+
if (args.includes('--json')) {
|
|
137
|
+
console.log(JSON.stringify({ ok: true, harness, action: 'installed', ...res }));
|
|
138
|
+
} else {
|
|
139
|
+
console.log(`✔ Installed preflight gate for ${harness}`);
|
|
140
|
+
if (res.added?.length) console.log(` added: ${res.added.join(', ')}`);
|
|
141
|
+
if (res.removed?.length) console.log(` removed: ${res.removed.join(', ')}`);
|
|
142
|
+
console.log(` settings: ${res.settings}`);
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.error(`✖ Failed to install preflight gate for ${harness}: ${err.message}`);
|
|
146
|
+
process.exitCode = 1;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function verify(args) {
|
|
151
|
+
const harness = flag(args, '--harness') || 'claude';
|
|
152
|
+
const adapters = await loadGateAdapters();
|
|
153
|
+
if (!adapters) {
|
|
154
|
+
console.error('✖ Gate adapters are owned by the meta-harness (cli-agents) plugin and are not available.');
|
|
155
|
+
process.exitCode = 1;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
const res = adapters.verify(harness);
|
|
160
|
+
const ok = Boolean(res.installed && res.exercised);
|
|
161
|
+
if (args.includes('--json')) {
|
|
162
|
+
console.log(JSON.stringify({ ok, harness, ...res }));
|
|
163
|
+
} else {
|
|
164
|
+
if (ok) {
|
|
165
|
+
console.log(`✔ Preflight gate for ${harness} is verified (${res.level || 'enforced'})`);
|
|
166
|
+
} else {
|
|
167
|
+
console.error(`✖ Preflight gate for ${harness} verification failed`);
|
|
168
|
+
if (!res.installed) console.error(' hook is not installed in harness configuration');
|
|
169
|
+
if (!res.exercised) console.error(' synthetic deny exercise failed');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (!ok) process.exitCode = 1;
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error(`✖ Failed to verify preflight gate for ${harness}: ${err.message}`);
|
|
175
|
+
process.exitCode = 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function report(args) {
|
|
180
|
+
const adapters = await loadGateAdapters();
|
|
181
|
+
if (!adapters) {
|
|
182
|
+
console.error('✖ Gate adapters are owned by the meta-harness (cli-agents) plugin and are not available.');
|
|
183
|
+
process.exitCode = 1;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const all = adapters.list();
|
|
187
|
+
const results = all.map(a => ({
|
|
188
|
+
id: a.id,
|
|
189
|
+
level: a.level,
|
|
190
|
+
verification: adapters.verify(a.id)
|
|
191
|
+
}));
|
|
192
|
+
|
|
193
|
+
const decisions = readDecisions();
|
|
194
|
+
const summary = {
|
|
195
|
+
total: decisions.length,
|
|
196
|
+
allowed: decisions.filter(d => d.decision === 'allow').length,
|
|
197
|
+
denied: decisions.filter(d => d.decision === 'deny').length,
|
|
198
|
+
byHarness: {},
|
|
199
|
+
byRepo: {}
|
|
200
|
+
};
|
|
201
|
+
for (const d of decisions) {
|
|
202
|
+
const h = d.harness || 'generic';
|
|
203
|
+
summary.byHarness[h] ??= { total: 0, allowed: 0, denied: 0 };
|
|
204
|
+
summary.byHarness[h].total++;
|
|
205
|
+
if (d.decision === 'allow') summary.byHarness[h].allowed++;
|
|
206
|
+
else summary.byHarness[h].denied++;
|
|
207
|
+
|
|
208
|
+
const r = d.repo || 'unknown';
|
|
209
|
+
summary.byRepo[r] ??= { total: 0, allowed: 0, denied: 0 };
|
|
210
|
+
summary.byRepo[r].total++;
|
|
211
|
+
if (d.decision === 'allow') summary.byRepo[r].allowed++;
|
|
212
|
+
else summary.byRepo[r].denied++;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (args.includes('--json')) {
|
|
216
|
+
console.log(JSON.stringify({ adapters: results, compliance: summary }, null, 2));
|
|
217
|
+
} else {
|
|
218
|
+
for (const r of results) {
|
|
219
|
+
console.log(`- ${r.id}: ${r.level} (installed: ${r.verification.installed}, exercised: ${r.verification.exercised})`);
|
|
220
|
+
}
|
|
221
|
+
if (summary.total > 0) {
|
|
222
|
+
console.log(`\nCompliance summary (${summary.total} real gate decisions observed):`);
|
|
223
|
+
const pct = ((summary.allowed / summary.total) * 100).toFixed(1);
|
|
224
|
+
console.log(` Allowed: ${summary.allowed} | Denied: ${summary.denied} | Rate: ${pct}%`);
|
|
225
|
+
console.log(' By harness:');
|
|
226
|
+
for (const [hId, stats] of Object.entries(summary.byHarness)) {
|
|
227
|
+
const hPct = ((stats.allowed / stats.total) * 100).toFixed(1);
|
|
228
|
+
console.log(` ${hId.padEnd(14)}: ${stats.allowed}/${stats.total} allowed (${hPct}%)`);
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
console.log('\nCompliance summary:');
|
|
232
|
+
console.log(' No gate decisions recorded yet (live gate traffic has not been observed).');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function evaluate(call, { harness, readablePaths } = {}) {
|
|
115
238
|
const root = findRoot(call.cwd || process.cwd());
|
|
116
239
|
const limit = envMaxAgeMs();
|
|
117
|
-
|
|
240
|
+
const decision = decide(call, {
|
|
241
|
+
readablePaths,
|
|
118
242
|
check: () => checkReceipt(root, { maxAgeMs: limit }),
|
|
119
243
|
recentConsult: () => {
|
|
120
244
|
const now = Date.now();
|
|
@@ -123,6 +247,18 @@ function evaluate(call) {
|
|
|
123
247
|
return Boolean(receipt && now - Date.parse(receipt.at) < SWEEP_CONSULT_MS);
|
|
124
248
|
},
|
|
125
249
|
});
|
|
250
|
+
recordDecision({
|
|
251
|
+
harness: harness || call.harness || 'generic',
|
|
252
|
+
cwd: call.cwd || process.cwd(),
|
|
253
|
+
repo: root,
|
|
254
|
+
tool: call.tool || 'other',
|
|
255
|
+
command: call.command || null,
|
|
256
|
+
path: call.path || null,
|
|
257
|
+
decision: decision.decision,
|
|
258
|
+
rule: decision.rule,
|
|
259
|
+
reason: decision.reason || null
|
|
260
|
+
});
|
|
261
|
+
return decision;
|
|
126
262
|
}
|
|
127
263
|
|
|
128
264
|
/**
|
|
@@ -149,13 +285,13 @@ export default async function preflight(args = []) {
|
|
|
149
285
|
// Original `tr-preflight` forms.
|
|
150
286
|
if (first === '--ack' || first === 'ack') return ack(args);
|
|
151
287
|
if (first === '--check' || first === 'check') return check(args);
|
|
288
|
+
// Help never performs the subcommand: `install --help` once rewrote the harness settings (C-019).
|
|
289
|
+
if (args.includes('--help') || args.includes('-h')) return console.log(USAGE);
|
|
152
290
|
if (first === 'gate') return gate(args.slice(1));
|
|
153
291
|
if (first === 'remind') return remind(args);
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
292
|
+
if (first === 'install') return install(args.slice(1));
|
|
293
|
+
if (first === 'verify') return verify(args.slice(1));
|
|
294
|
+
if (first === 'report') return report(args.slice(1));
|
|
159
295
|
console.error(`Unknown preflight command: ${first}\n\n${USAGE}`);
|
|
160
296
|
process.exitCode = 1;
|
|
161
297
|
}
|