total-recall-brain 3.40.1 → 3.40.2
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/README.md +152 -41
- package/bin/total-recall.mjs +16 -76
- package/docs/reference/cli-reference.md +10 -10
- package/extension/background.js +6 -2
- package/extension/config.js +8 -0
- package/extension/lib/brain-client.js +29 -4
- package/extension/options/options.html +1 -0
- package/extension/sidepanel/sidepanel.html +1 -0
- package/frontend/dist/assets/{index-CvQRdT2j.css → index-AGUes-VG.css} +1 -1
- package/frontend/dist/assets/index-Bt5iVPZj.js +5618 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/src/api/keys.ts +9 -2
- package/frontend/src/api/plugins.ts +4 -0
- package/frontend/src/components/plugins/card/CardEngine.tsx +2 -1
- package/frontend/src/components/plugins/card/card.css +1 -1
- package/frontend/src/pages/IntegrationsPage.tsx +315 -8
- package/frontend/src/pages/PluginsPage.tsx +532 -87
- package/package.json +6 -5
- package/scaffold/.agent/skills/meta-harness/references/optimized/original.md +1 -1
- package/scaffold/.agent/skills/meta-harness/references/optimized/section-09.md +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +10 -10
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -6
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +0 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/browser-session.mjs +309 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +44 -34
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/google-oauth.mjs +6 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-catalog.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-rotation-recipes.mjs +118 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/routes/auth.mjs +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-rotate.mjs +24 -6
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +164 -123
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +65 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +76 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +290 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +446 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +152 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +71 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +175 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/README.md +84 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +79 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/check.mjs +6 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/report.mjs +6 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +27 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/snapshot.mjs +163 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +757 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/config.schema.json +86 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/detect.mjs +89 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +145 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +210 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/state.mjs +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +2869 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +28 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +186 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +272 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +129 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/README.md +73 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/cli.mjs +375 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/echo-tool.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/group.mjs +262 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/project-id.mjs +74 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +2869 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +31 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +221 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/schemas/cli_command.schema.json +107 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/skills/composable-cli/SKILL.md +63 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +313 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compile.mjs +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compose.mjs +144 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/contract.mjs +110 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/library.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/registry.mjs +192 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/resolve.mjs +221 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/risk.mjs +126 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/ssss-documents.mjs +29 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +273 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/README.md +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/cli.mjs +310 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +2826 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +11 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +62 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +297 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +43 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/cli.mjs +258 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4005 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +125 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +278 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +9 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +6 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +4721 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +3 -21
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +14 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/headscale-client.mjs +5 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/leader-election.mjs +4 -13
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-access-verify.mjs +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-files.mjs +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +2 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/test-support/memory-root.mjs +14 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +124 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +4097 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +12 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +165 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +127 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +279 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +539 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/README.md +67 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +932 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3256 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +297 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +4 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/skills/security/SKILL.md +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/android.mjs +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/devices.mjs +94 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/machines.mjs +164 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/scanner.mjs +41 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/windows.mjs +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +95 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +98 -0
- package/scaffold/.agent/skills/total-recall/references/auth.md +1 -1
- package/src/cli/brief.mjs +33 -9
- package/src/cli/command.mjs +2 -7
- package/src/cli/config.mjs +1 -0
- package/src/cli/connect.mjs +5 -0
- package/src/cli/context.mjs +2 -1
- package/src/cli/daemon.mjs +36 -1
- package/src/cli/doctor.mjs +6 -0
- package/src/cli/harness.mjs +3 -0
- package/src/cli/help.mjs +26 -0
- package/src/cli/init.mjs +4 -4
- package/src/cli/memory-entry.mjs +1 -0
- package/src/cli/memory-init.mjs +2 -0
- package/src/cli/plugin/index.mjs +11 -0
- package/src/cli/plugin/install.mjs +2 -1
- package/src/cli/rebuild.mjs +3 -1
- package/src/cli/startup.mjs +2 -1
- package/src/cli/update.mjs +19 -0
- package/src/core/automatic-brain-updates.mjs +67 -0
- package/src/core/brain-updates.mjs +21 -2
- package/src/core/cli-commands.mjs +71 -0
- package/src/core/cli-maintenance.mjs +45 -0
- package/src/core/cli-quick-reference.mjs +20 -0
- package/src/core/cli-semantic-index.mjs +722 -0
- package/src/core/context-compiler.mjs +27 -1
- package/src/core/crons.mjs +2 -13
- package/src/core/daemon-control.mjs +6 -4
- package/src/core/daemon-loop.mjs +12 -8
- package/src/core/default-plugins.mjs +5 -0
- package/src/core/global-brain-updates.mjs +39 -0
- package/src/core/mesh-brain-updates.mjs +30 -0
- package/src/core/meta-harness.mjs +108 -0
- package/src/core/plugin-dependencies.mjs +2 -1
- package/src/core/plugin-loader.mjs +2 -1
- package/src/core/plugin-runner.mjs +12 -4
- package/src/core/plugin-setup.mjs +20 -0
- package/src/core/plugin-store.mjs +22 -4
- package/src/core/plugin-tasks.mjs +1 -1
- package/src/core/project-brain.mjs +2 -0
- package/src/core/project-status.mjs +64 -0
- package/src/core/required-plugins.mjs +46 -0
- package/src/core/scaffold-plugins.mjs +17 -3
- package/src/core/source-brain-updates.mjs +47 -0
- package/src/core/startup-health.mjs +5 -3
- package/src/core/surface.mjs +44 -10
- package/src/server/routes/extension.mjs +53 -8
- package/templates/onboarding-interview.md +1 -1
- package/frontend/dist/assets/index-SOVUlZqm.js +0 -5596
package/src/cli/connect.mjs
CHANGED
|
@@ -115,6 +115,11 @@ export const CLIENTS = {
|
|
|
115
115
|
// Grok discovers repository skills in <project>/.agents/skills.
|
|
116
116
|
skillsProjection: { scope: 'project', dir: path.join('.agents', 'skills') }
|
|
117
117
|
},
|
|
118
|
+
muse: {
|
|
119
|
+
label: 'Muse Code',
|
|
120
|
+
mode: 'symlink',
|
|
121
|
+
target: 'AGENTS.md'
|
|
122
|
+
},
|
|
118
123
|
replit: {
|
|
119
124
|
label: 'Replit Agent',
|
|
120
125
|
mode: 'file',
|
package/src/cli/context.mjs
CHANGED
|
@@ -41,8 +41,9 @@ export default async function context(args = []) {
|
|
|
41
41
|
const nodes = own ? mergeGlobalRuleNodes(getNodes(path.join(own, 'memory-vault')), global ? getNodes(path.join(global, 'memory-vault')) : []) :
|
|
42
42
|
getNodes(path.join(global, 'memory-vault')).map(n => ({ ...n, _layer: 'global' }));
|
|
43
43
|
const skillsDir = path.dirname(own || global);
|
|
44
|
+
const derivedDir = own ? path.join(own, 'memory-derived') : (global ? path.join(global, 'memory-derived') : null);
|
|
44
45
|
const result = await compileContext({ query, actions, nodes, includeKnowledge, budget: { total: budget }, projectRoot: process.cwd(),
|
|
45
|
-
contributions: legacyRuleContributions(skillsDir), versionInputs: surfaceInputsHash({ skillsDir }) });
|
|
46
|
+
derivedDir, contributions: legacyRuleContributions(skillsDir), versionInputs: surfaceInputsHash({ skillsDir }) });
|
|
46
47
|
if (debug && format === 'text') throw new Error('--debug requires --format json');
|
|
47
48
|
const response = capsuleResponse(result, { total: budget, debug, format });
|
|
48
49
|
if (!response.ready && !debug) {
|
package/src/cli/daemon.mjs
CHANGED
|
@@ -56,8 +56,10 @@ function printHelp() {
|
|
|
56
56
|
|
|
57
57
|
Commands:
|
|
58
58
|
start Start the background Active Intelligence Daemon
|
|
59
|
+
ensure Enable the managed daemon; --restart reloads current source
|
|
59
60
|
stop Stop the background daemon
|
|
60
61
|
status Show daemon status + recent log entries
|
|
62
|
+
logs Read daemon logs (--lines N, default 50)
|
|
61
63
|
|
|
62
64
|
The daemon runs a durable task loop that:
|
|
63
65
|
• Ingests IDE conversation logs (Claude Code, Codex, Gemini, etc.)
|
|
@@ -207,6 +209,39 @@ export default async function daemon(args) {
|
|
|
207
209
|
const useLaunchd = !useSystemd && hasLaunchd();
|
|
208
210
|
|
|
209
211
|
switch (subcommand) {
|
|
212
|
+
case 'ensure': {
|
|
213
|
+
if(!useSystemd)throw new Error('Managed daemon ensure currently requires systemd on the daemon host');
|
|
214
|
+
// A detached worker can hold the same brain lock while systemd reports a
|
|
215
|
+
// successful short-lived start. Stop that verified owner before takeover.
|
|
216
|
+
const managed=spawnSync('systemctl',['show','total-recall-daemon','--property=MainPID','--value'],{encoding:'utf8'});
|
|
217
|
+
if(managed.status!==0)throw new Error('Cannot inspect managed daemon');
|
|
218
|
+
const owner=readPid(),managedPid=Number(managed.stdout.trim());
|
|
219
|
+
if(owner && (owner!==managedPid || args.includes('--restart'))){
|
|
220
|
+
if(owner===managedPid)execFileSync('systemctl',['stop','total-recall-daemon'],{stdio:'inherit'});
|
|
221
|
+
else if(!await stopDaemon())throw new Error('Cannot stop verified detached daemon');
|
|
222
|
+
}
|
|
223
|
+
execFileSync('systemctl',['enable','--now','total-recall-daemon'],{stdio:'inherit'});
|
|
224
|
+
await new Promise(resolve=>setTimeout(resolve,3000));
|
|
225
|
+
const active=spawnSync('systemctl',['is-active','--quiet','total-recall-daemon']);
|
|
226
|
+
const current=spawnSync('systemctl',['show','total-recall-daemon','--property=MainPID','--value'],{encoding:'utf8'});
|
|
227
|
+
const pid=readPid();
|
|
228
|
+
if(active.status!==0 || !pid || pid!==Number(current.stdout?.trim()))throw new Error('Managed daemon did not retain ownership of the brain lock');
|
|
229
|
+
console.log(JSON.stringify({ok:true,manager:'systemd',pid,enabled:true}));
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case 'logs': {
|
|
233
|
+
const index = args.indexOf('--lines');
|
|
234
|
+
const lines = index < 0 ? 50 : Number(args[index + 1]);
|
|
235
|
+
if (!Number.isInteger(lines) || lines < 1 || lines > 10000) throw new Error('--lines must be an integer from 1 to 10000');
|
|
236
|
+
if (useSystemd) {
|
|
237
|
+
const result = spawnSync('journalctl', ['-u', 'total-recall-daemon.service', '-n', String(lines), '--no-pager'], { stdio: 'inherit' });
|
|
238
|
+
if (result.error || result.status !== 0) throw new Error('Could not read the daemon journal');
|
|
239
|
+
} else {
|
|
240
|
+
if (!fs.existsSync(LOG_FILE)) throw new Error('No daemon log exists');
|
|
241
|
+
console.log(fs.readFileSync(LOG_FILE, 'utf8').trimEnd().split('\n').slice(-lines).join('\n'));
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
210
245
|
case 'start':
|
|
211
246
|
if (useSystemd) systemdStart();
|
|
212
247
|
else if (useLaunchd) launchctlStart();
|
|
@@ -224,7 +259,7 @@ export default async function daemon(args) {
|
|
|
224
259
|
break;
|
|
225
260
|
default:
|
|
226
261
|
console.error(` Unknown daemon command: ${subcommand}`);
|
|
227
|
-
console.error(` Valid commands: start, stop, status`);
|
|
262
|
+
console.error(` Valid commands: start, ensure, stop, status, logs`);
|
|
228
263
|
process.exit(1);
|
|
229
264
|
}
|
|
230
265
|
}
|
package/src/cli/doctor.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import { fileURLToPath, URL } from 'node:url';
|
|
|
18
18
|
import { getGlobalBrainDir } from './agent-dir.mjs';
|
|
19
19
|
import { processIdentity, verifyListener } from '../core/startup-health.mjs';
|
|
20
20
|
import { loadKnownRepoRoots } from '../core/skills-registry.mjs';
|
|
21
|
+
import { inspectRequiredPlugins } from '../core/required-plugins.mjs';
|
|
21
22
|
|
|
22
23
|
// ─── Formatting & UI Helpers ────────────────────────────────────────────────
|
|
23
24
|
function log(msg) { console.log(` ${msg}`); }
|
|
@@ -91,6 +92,11 @@ export default async function doctor() {
|
|
|
91
92
|
|
|
92
93
|
let warnings = 0;
|
|
93
94
|
let failures = 0;
|
|
95
|
+
const { resolveBrainDir } = await import('./agent-dir.mjs');
|
|
96
|
+
for (const brain of new Set([getGlobalBrainDir(), resolveBrainDir()])) {
|
|
97
|
+
const required = inspectRequiredPlugins(brain);
|
|
98
|
+
if (required.status !== 'ready') { logFail(`${brain}: ${required.errors.join('; ')}`); failures++; }
|
|
99
|
+
}
|
|
94
100
|
|
|
95
101
|
// ── Step 1: System Hardware Check ──
|
|
96
102
|
hr();
|
package/src/cli/harness.mjs
CHANGED
|
@@ -23,6 +23,9 @@ Available Harness IDs:
|
|
|
23
23
|
grok xAI Grok Build CLI (Autonomous coding, inspection, shell execution)
|
|
24
24
|
gemini Google Gemini CLI (Fast utilities & completion chaining)
|
|
25
25
|
ollama Ollama Local LLM (Local neural reasoning, zero API cost)
|
|
26
|
+
hermes Hermes Agent Runtime (Autonomous supervisor & messaging gateway)
|
|
27
|
+
dsh DeepSeek Harness DSH (High-throughput reasoning engine)
|
|
28
|
+
openclaw OpenClaw Agent Bridge (Multi-channel messaging & tool execution)
|
|
26
29
|
|
|
27
30
|
Examples:
|
|
28
31
|
npx total-recall harness list
|
package/src/cli/help.mjs
CHANGED
|
@@ -122,6 +122,32 @@ export default async function run(argv) {
|
|
|
122
122
|
console.log('\n' + '─'.repeat(60) + '\n');
|
|
123
123
|
}
|
|
124
124
|
} else {
|
|
125
|
+
// Semantic CLI search fallback across all core, plugin, and composed verbs
|
|
126
|
+
try {
|
|
127
|
+
const { queryCliSemanticIndex } = await import('../core/cli-semantic-index.mjs');
|
|
128
|
+
const matches = await queryCliSemanticIndex(requestedTopic || topic, { limit: 6, minScore: 0.25 });
|
|
129
|
+
if (matches.length > 0) {
|
|
130
|
+
if (isJson) {
|
|
131
|
+
console.log(JSON.stringify({ topic: requestedTopic || topic, matches }, null, 2));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
console.log(`\n\x1b[34m=== Total Recall Semantic Help: "${requestedTopic || topic}" ===\x1b[0m\n`);
|
|
135
|
+
console.log(' Top matching CLI verbs:\n');
|
|
136
|
+
for (const m of matches) {
|
|
137
|
+
const badge = m.category === 'plugin'
|
|
138
|
+
? `\x1b[35m[plugin:${m.namespace}]\x1b[0m`
|
|
139
|
+
: (m.category === 'composed' ? `\x1b[36m[composed]\x1b[0m` : `\x1b[32m[core]\x1b[0m`);
|
|
140
|
+
console.log(` • \x1b[1m${m.verb}\x1b[0m ${badge}`);
|
|
141
|
+
console.log(` ${m.description}`);
|
|
142
|
+
console.log(` \x1b[2mUsage: ${m.usage}\x1b[0m\n`);
|
|
143
|
+
}
|
|
144
|
+
console.log('─'.repeat(60) + '\n');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
/* Fallback to default not found message */
|
|
149
|
+
}
|
|
150
|
+
|
|
125
151
|
console.error(` ❌ Error: No help section found for topic or command: "${topic}"`);
|
|
126
152
|
console.error(` Run 'npx total-recall help' to see the complete list of commands.`);
|
|
127
153
|
process.exit(1);
|
package/src/cli/init.mjs
CHANGED
|
@@ -244,10 +244,10 @@ export default async function init(args) {
|
|
|
244
244
|
logOk(`Base directory structure ready (${created} created, ${dirs.length - created} already existed)`);
|
|
245
245
|
|
|
246
246
|
// Auth is a required scaffold plugin. Existing installations are preserved.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
if (!opts.dryRun) {
|
|
248
|
+
const { ensureRequiredPlugins } = await import('../core/required-plugins.mjs');
|
|
249
|
+
ensureRequiredPlugins(brainDir);
|
|
250
|
+
}
|
|
251
251
|
|
|
252
252
|
// Every scaffold plugin's node_modules are wired in Step 2.5, after the skill
|
|
253
253
|
// tree has been copied into the brain (see the note there).
|
package/src/cli/memory-entry.mjs
CHANGED
|
@@ -26,6 +26,7 @@ const handlers = {
|
|
|
26
26
|
|
|
27
27
|
export async function main(args = []) {
|
|
28
28
|
const [command, ...rest] = args;
|
|
29
|
+
if (command === 'secret' || command === 'secrets') throw new Error('Credential commands belong to total-recall auth secret');
|
|
29
30
|
if (!command || ['help', '--help', '-h'].includes(command)) {
|
|
30
31
|
console.log(`Total Recall — portable memory and instructions
|
|
31
32
|
Usage: total-recall <command> [options]
|
package/src/cli/memory-init.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { getGlobalAgentDir } from './agent-dir.mjs';
|
|
6
6
|
import { registerProjectBrain, writeBrainIdentity, VAULT_CATEGORIES } from '../core/memory-brain.mjs';
|
|
7
|
+
import { ensureRequiredPlugins } from '../core/required-plugins.mjs';
|
|
7
8
|
|
|
8
9
|
export default async function init(args = []) {
|
|
9
10
|
if (args.includes('--help') || args.includes('-h')) {
|
|
@@ -24,6 +25,7 @@ export default async function init(args = []) {
|
|
|
24
25
|
for (const relative of ['config', 'plugins', 'memory-derived', ...VAULT_CATEGORIES.map(c => `memory-vault/${c}`)]) {
|
|
25
26
|
fs.mkdirSync(path.join(brainDir, relative), { recursive: true });
|
|
26
27
|
}
|
|
28
|
+
ensureRequiredPlugins(brainDir);
|
|
27
29
|
const skillFile = path.join(brainDir, 'SKILL.md');
|
|
28
30
|
if (!fs.existsSync(skillFile)) {
|
|
29
31
|
// Resolve the template by PATH, not `new URL(..., import.meta.url)`. Under a
|
package/src/cli/plugin/index.mjs
CHANGED
|
@@ -25,6 +25,7 @@ Usage:
|
|
|
25
25
|
Commands:
|
|
26
26
|
list Installed plugins (project & global) with source and sharing
|
|
27
27
|
library [--plugin ID] Installed CLI operation library (--json for agents)
|
|
28
|
+
refresh Rebuild installed command inventories and instruction surfaces
|
|
28
29
|
on <id> / off <id> Enable/disable host execution; compile catalogs and surfaces
|
|
29
30
|
available [query] Plugins bundled with this Total Recall package
|
|
30
31
|
peers [query] Plugins your mesh peers share (queried live)
|
|
@@ -38,6 +39,7 @@ Commands:
|
|
|
38
39
|
<git-url> a git repository with plugin.json
|
|
39
40
|
<path> a local directory
|
|
40
41
|
--link, -l Symlink a local directory instead of copying
|
|
42
|
+
--replace Replace an install, retain previous source and restore on failure
|
|
41
43
|
--global, -g Install for every project on this machine
|
|
42
44
|
share <id> Offer an installed plugin by direct link and mesh
|
|
43
45
|
unshare <id> Stop offering it
|
|
@@ -83,6 +85,15 @@ export async function run(argv = []) {
|
|
|
83
85
|
|
|
84
86
|
const command = args[0];
|
|
85
87
|
const rest = args.slice(1);
|
|
88
|
+
if (command === 'refresh') {
|
|
89
|
+
const { recompileAfterPluginMutation } = await import('../../core/plugin-store.mjs');
|
|
90
|
+
const result = await recompileAfterPluginMutation(process.cwd(), rest.includes('--global'));
|
|
91
|
+
const { setupInstalledPlugins } = await import('../../core/plugin-setup.mjs');
|
|
92
|
+
const setup = result.ok ? await setupInstalledPlugins() : {ok:false,results:[]};
|
|
93
|
+
console.log(JSON.stringify({...result,setup,ok:result.ok && setup.ok}));
|
|
94
|
+
if (!result.ok || !setup.ok) process.exitCode = 1;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
86
97
|
if (command === 'on' || command === 'off') {
|
|
87
98
|
const result = await setPluginActive(rest[0], command === 'on');
|
|
88
99
|
console.log(JSON.stringify(result));
|
|
@@ -13,11 +13,12 @@ export async function installPlugin(args = []) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
try {
|
|
16
|
-
const result = await install(source, { projectRoot: process.cwd(), global: isGlobal, link: isLink });
|
|
16
|
+
const result = await install(source, { projectRoot: process.cwd(), global: isGlobal, link: isLink, replace:args.includes('--replace') });
|
|
17
17
|
const from = result.source.kind === 'peer' ? `peer ${result.source.peer_hostname}` : result.source.kind;
|
|
18
18
|
console.log(`\n✅ Installed '${result.plugin.name}' (id: ${result.plugin.id}, v${result.plugin.version}) from ${from}`);
|
|
19
19
|
console.log(` Location: ${result.plugin.dir}`);
|
|
20
20
|
console.log(` sha256: ${result.sha256}`);
|
|
21
|
+
if(result.previousDirectory)console.log(` Previous source retained: ${result.previousDirectory}`);
|
|
21
22
|
if (result.dependencies?.installed) {
|
|
22
23
|
console.log(` Dependencies: installed ${result.dependencies.packages.length} package(s)`);
|
|
23
24
|
} else if (result.dependencies?.reason && !['no package.json', 'no dependencies declared'].includes(result.dependencies.reason)) {
|
package/src/cli/rebuild.mjs
CHANGED
|
@@ -72,13 +72,14 @@ export async function runRebuild(options = {}) {
|
|
|
72
72
|
if (fs.existsSync(derivedDir)) {
|
|
73
73
|
for (const entry of fs.readdirSync(derivedDir)) {
|
|
74
74
|
if (entry.startsWith('embeddings.db')) continue; // also skips -wal / -shm
|
|
75
|
+
if (entry === 'cli-semantic-index.json') continue;
|
|
75
76
|
fs.rmSync(path.join(derivedDir, entry), { recursive: true, force: true });
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
console.log('🏗️ Recompiling surface from canonical vault...');
|
|
80
81
|
try {
|
|
81
|
-
const stats = await compileSurface({ vaultDir, skillsDir, derivedDir, instructionsFile, force:
|
|
82
|
+
const stats = await compileSurface({ vaultDir, skillsDir, derivedDir, instructionsFile, force: Boolean(options.force) });
|
|
82
83
|
console.log(`✅ Processed ${stats.nodesProcessed} canonical memory nodes.`);
|
|
83
84
|
console.log(`✅ Injected memory into ${stats.skillsInjected} skill files.`);
|
|
84
85
|
console.log(`✅ Rebuilt graph-index.jsonl, memory-layers.jsonl, and skill-routes.jsonl.`);
|
|
@@ -163,6 +164,7 @@ export default async function cli(args) {
|
|
|
163
164
|
const { layer, remainingArgs } = parseLayerFlag(args);
|
|
164
165
|
const options = {
|
|
165
166
|
check: remainingArgs.includes('--check'),
|
|
167
|
+
force: remainingArgs.includes('--force'),
|
|
166
168
|
layer,
|
|
167
169
|
};
|
|
168
170
|
let code = await runRebuild(options);
|
package/src/cli/startup.mjs
CHANGED
|
@@ -35,7 +35,8 @@ export default async function startup(args = []) {
|
|
|
35
35
|
const report = await inspectStartup(options);
|
|
36
36
|
if (args.includes('--json')) console.log(JSON.stringify(report, null, 2));
|
|
37
37
|
else {
|
|
38
|
-
console.log(`Startup: server ${report.server?.status || 'unknown'}; brain ${report.brain?.status || 'unknown'}; daemon ${report.daemon?.status || 'unknown'}; SSSS tooling ${report.ssss?.status || 'unknown'}; app ${report.app?.status || 'unknown'}`);
|
|
38
|
+
console.log(`Startup: required plugins ${report.required_plugins?.status || 'unknown'}; server ${report.server?.status || 'unknown'}; brain ${report.brain?.status || 'unknown'}; daemon ${report.daemon?.status || 'unknown'}; SSSS tooling ${report.ssss?.status || 'unknown'}; app ${report.app?.status || 'unknown'}`);
|
|
39
|
+
for (const error of report.required_plugins?.errors || []) console.log(`Required plugin: ${error}`);
|
|
39
40
|
const harnesses = await queryHarnessUsage().catch(() => []);
|
|
40
41
|
const availH = harnesses.filter(h => h.available);
|
|
41
42
|
const readyH = availH.filter(h => h.authed);
|
package/src/cli/update.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { brainDir } from '../core/config.mjs';
|
|
19
19
|
import { installAutoPull } from '../core/autopull-install.mjs';
|
|
20
20
|
import { updateAllBrains } from '../core/brain-updates.mjs';
|
|
21
|
+
import { automaticUpdateStatus, runAutomaticBrainUpdates } from '../core/automatic-brain-updates.mjs';
|
|
21
22
|
|
|
22
23
|
/** @param {string[]} args */
|
|
23
24
|
export function parseArgs(args) {
|
|
@@ -32,6 +33,9 @@ export function parseArgs(args) {
|
|
|
32
33
|
noBuild: false,
|
|
33
34
|
allBrains: false,
|
|
34
35
|
json: false,
|
|
36
|
+
automatic: false,
|
|
37
|
+
status: false,
|
|
38
|
+
localOnly: false,
|
|
35
39
|
};
|
|
36
40
|
for (let i = 0; i < args.length; i++) {
|
|
37
41
|
const a = args[i];
|
|
@@ -45,6 +49,9 @@ export function parseArgs(args) {
|
|
|
45
49
|
else if (a === '--no-build') opts.noBuild = true;
|
|
46
50
|
else if (a === '--all-brains') opts.allBrains = true;
|
|
47
51
|
else if (a === '--json') opts.json = true;
|
|
52
|
+
else if (a === '--automatic') opts.automatic = true;
|
|
53
|
+
else if (a === '--status') opts.status = true;
|
|
54
|
+
else if (a === '--local-only') opts.localOnly = true;
|
|
48
55
|
}
|
|
49
56
|
return opts;
|
|
50
57
|
}
|
|
@@ -64,6 +71,10 @@ function printHelp() {
|
|
|
64
71
|
total-recall update --all-brains --apply [--json]
|
|
65
72
|
Pin the same published versions, use npm/pnpm, replace shared dependency
|
|
66
73
|
links with independent local installs, and verify every resolved version.
|
|
74
|
+
total-recall update --automatic [--json]
|
|
75
|
+
Daemon maintenance: update both packages, ensure defaults and refresh surfaces.
|
|
76
|
+
total-recall update --status [--json]
|
|
77
|
+
Read the actual last automatic maintenance report without starting work.
|
|
67
78
|
|
|
68
79
|
This brain's own checkout (git):
|
|
69
80
|
total-recall update --install-autopull [--port N] [--no-build] [--dry-run]
|
|
@@ -89,6 +100,14 @@ export default async function update(args = []) {
|
|
|
89
100
|
return;
|
|
90
101
|
}
|
|
91
102
|
|
|
103
|
+
if(opts.status){const status=automaticUpdateStatus(brainDir);console.log(JSON.stringify(status,null,2));return status;}
|
|
104
|
+
if(opts.automatic){
|
|
105
|
+
const report=await runAutomaticBrainUpdates({brainDir,force:opts.force,fleet:!opts.localOnly});
|
|
106
|
+
console.log(JSON.stringify(report,null,2));
|
|
107
|
+
if(report.failed)process.exitCode=1;
|
|
108
|
+
return report;
|
|
109
|
+
}
|
|
110
|
+
|
|
92
111
|
if (opts.allBrains) {
|
|
93
112
|
const summary = await updateAllBrains({ brainDir, roots: opts.repos,
|
|
94
113
|
dryRun: !opts.apply || opts.dryRun });
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** The existing TR daemon maintains both published brain packages, not only TR. */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { updateAllBrains } from './brain-updates.mjs';
|
|
5
|
+
import { isPackageAutoUpdateEnabled } from './package-auto-update.mjs';
|
|
6
|
+
import { maintainCliSurfaces } from './cli-maintenance.mjs';
|
|
7
|
+
import { runBounded, cliEntry } from './startup-health.mjs';
|
|
8
|
+
import { logger } from './logger.mjs';
|
|
9
|
+
import { readProcessCommand, shouldHonorPidLock } from './pid-lock.mjs';
|
|
10
|
+
import { updateGlobalBrainPackages } from './global-brain-updates.mjs';
|
|
11
|
+
import { updateMeshBrainPackages } from './mesh-brain-updates.mjs';
|
|
12
|
+
import { updateSourceBrainPackages } from './source-brain-updates.mjs';
|
|
13
|
+
|
|
14
|
+
export function automaticUpdateStatus(brainDir) {
|
|
15
|
+
let last;
|
|
16
|
+
try { last=JSON.parse(fs.readFileSync(path.join(brainDir,'memory-derived/automatic-brain-updates.json'),'utf8')); } catch {}
|
|
17
|
+
return {enabled:isPackageAutoUpdateEnabled(), intervalMs:60*60*1000, last:last || null};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Keep synchronous package-manager work outside the daemon's event loop. This
|
|
21
|
+
// timer runs even when the task queue is empty; it does not create a scheduler.
|
|
22
|
+
export function startAutomaticBrainUpdates({brainDir,intervalMs=60*60*1000,run=runBounded,onError=message=>logger.error('automatic-brain-updates',message)}={}) {
|
|
23
|
+
let busy=false,stopped=false;
|
|
24
|
+
const tick=async()=>{
|
|
25
|
+
if(busy || stopped)return;
|
|
26
|
+
busy=true;
|
|
27
|
+
try {
|
|
28
|
+
const result=await run(process.execPath,[cliEntry,'update','--automatic','--json'],{cwd:path.resolve(brainDir,'../../..'),timeoutMs:55*60*1000,maxBytes:8*1024*1024});
|
|
29
|
+
if(result.code!==0)onError(result.reason || result.output || 'Automatic update failed');
|
|
30
|
+
} catch(error){onError(error.message);} finally{busy=false;}
|
|
31
|
+
};
|
|
32
|
+
// Wake frequently enough to retry failures; successful checks remain hourly.
|
|
33
|
+
const timer=setInterval(tick,Math.min(intervalMs,5*60*1000));timer.unref?.();void tick();
|
|
34
|
+
return {tick,stop(){stopped=true;clearInterval(timer);}};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function runAutomaticBrainUpdates({brainDir,fleet=false,force=false,skipThrottle=false,intervalMs=60*60*1000,update=updateAllBrains,compile=maintainCliSurfaces,globalUpdate=updateGlobalBrainPackages,meshUpdate=updateMeshBrainPackages,sourceUpdate=updateSourceBrainPackages,now=Date.now}={}) {
|
|
38
|
+
if(!force && !isPackageAutoUpdateEnabled())return {skipped:true,reason:'disabled',results:[]};
|
|
39
|
+
const file=path.join(brainDir,'memory-derived/automatic-brain-updates.json');
|
|
40
|
+
let saved;try{saved=JSON.parse(fs.readFileSync(file,'utf8'));}catch{}
|
|
41
|
+
const delay=saved?.failed?Math.min(intervalMs,5*60*1000):intervalMs;
|
|
42
|
+
if(!force && !skipThrottle && saved?.checkedAt && now()>=saved.checkedAt && now()-saved.checkedAt<delay)return {skipped:true,reason:'throttled',results:[]};
|
|
43
|
+
fs.mkdirSync(path.dirname(file),{recursive:true});
|
|
44
|
+
const lock=`${file}.lock`;
|
|
45
|
+
let fd;
|
|
46
|
+
if(fs.existsSync(lock)){
|
|
47
|
+
const text=fs.readFileSync(lock,'utf8');
|
|
48
|
+
const verdict=Number(text)===process.pid ? {honor:true} : shouldHonorPidLock(Number(text),{isAlive:pid=>{try{process.kill(pid,0);return true;}catch{return false;}},readCommand:readProcessCommand,entryHint:'total-recall'});
|
|
49
|
+
if(!verdict.honor && fs.readFileSync(lock,'utf8')===text)fs.unlinkSync(lock);
|
|
50
|
+
}
|
|
51
|
+
try {fd=fs.openSync(lock,'wx',0o600);fs.writeFileSync(fd,String(process.pid));}
|
|
52
|
+
catch(error){if(error.code==='EEXIST')return {skipped:true,reason:'maintenance-already-running',failed:1,results:[]};throw error;}
|
|
53
|
+
try {
|
|
54
|
+
let report;
|
|
55
|
+
try {
|
|
56
|
+
const summary=await update({brainDir,dryRun:false,ensureDefaults:true});
|
|
57
|
+
const global=summary.versions?await globalUpdate({versions:summary.versions}):{failed:1,results:[],error:'Cannot maintain global packages without verified published versions'};
|
|
58
|
+
const sources=summary.versions?await sourceUpdate({brainDir,versions:summary.versions}):{failed:1,results:[]};
|
|
59
|
+
const mesh=fleet && summary.versions?await meshUpdate({versions:summary.versions,version:summary.versions['total-recall-brain']}):{failed:0,results:[],scope:'local-only'};
|
|
60
|
+
const changed=summary.updated>0 || global.results.some(row=>row.status==='updated') || sources.results.some(row=>row.status==='updated') || JSON.stringify(saved?.versions)!==JSON.stringify(summary.versions);
|
|
61
|
+
const surfaces=await compile({brainDir,force:changed});
|
|
62
|
+
report={...summary,global,sources,mesh,surfaces,failed:(summary.failed || 0)+global.failed+sources.failed+mesh.failed+(surfaces.ok?0:surfaces.results.filter(row=>row.status==='failed').length),checkedAt:now()};
|
|
63
|
+
}catch(error){report={failed:1,error:error.message,checkedAt:now(),results:[]};}
|
|
64
|
+
const temporary=`${file}.${process.pid}.tmp`;fs.writeFileSync(temporary,JSON.stringify(report,null,2));fs.renameSync(temporary,file);
|
|
65
|
+
return report;
|
|
66
|
+
} finally {fs.closeSync(fd);fs.unlinkSync(lock);}
|
|
67
|
+
}
|
|
@@ -8,11 +8,14 @@ import { createHash } from 'node:crypto';
|
|
|
8
8
|
import { listUpdateRoots, resolveRegistryFiles, readRegistryEntries, rootsFromEnv } from './package-auto-update.mjs';
|
|
9
9
|
import { discoverPlugins } from './plugin-loader.mjs';
|
|
10
10
|
import { comparePackageVersions } from './package-version.mjs';
|
|
11
|
+
import { inspectRequiredPlugins } from './required-plugins.mjs';
|
|
12
|
+
import { ensureRequiredPlugins } from './required-plugins.mjs';
|
|
13
|
+
import YAML from 'yaml';
|
|
11
14
|
|
|
12
15
|
export const BRAIN_PACKAGES = ['total-recall-brain', '@gregiteen/ssss-cli'];
|
|
13
16
|
const sections = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
|
|
14
17
|
const ignored = new Set(['node_modules', 'vendor', 'dist', 'build', 'coverage', 'venv',
|
|
15
|
-
'test', 'tests', '__tests__', 'fixtures', 'tmp', 'scratch']);
|
|
18
|
+
'test', 'tests', '__tests__', 'fixtures', 'tmp', 'scratch', 'scaffold']);
|
|
16
19
|
const canonical = key => key === '@ssss/cli' ? '@gregiteen/ssss-cli' : key;
|
|
17
20
|
const read = file => JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
18
21
|
const isLink = file => { try { return fs.lstatSync(file).isSymbolicLink(); } catch { return false; } };
|
|
@@ -68,7 +71,12 @@ export function brainInstallOwner(root) {
|
|
|
68
71
|
const file = path.join(dir, 'package.json');
|
|
69
72
|
if (fs.existsSync(file)) {
|
|
70
73
|
const pkg = read(file);
|
|
71
|
-
|
|
74
|
+
let patterns=Array.isArray(pkg.workspaces)?pkg.workspaces:pkg.workspaces?.packages || [];
|
|
75
|
+
const pnpm=path.join(dir,'pnpm-workspace.yaml');
|
|
76
|
+
if(fs.existsSync(pnpm))patterns=[...patterns,...(YAML.parse(fs.readFileSync(pnpm,'utf8'))?.packages || [])];
|
|
77
|
+
const relative=path.relative(dir,root).split(path.sep).join('/');
|
|
78
|
+
const belongs=patterns.some(pattern=>typeof pattern==='string' && !pattern.startsWith('!') && new RegExp('^'+pattern.split('/').map(part=>part==='**'?'.*':part.split('*').map(chunk=>chunk.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join('[^/]*')).join('/')+'$').test(relative));
|
|
79
|
+
if (dir === root || belongs) owner = dir;
|
|
72
80
|
}
|
|
73
81
|
if (dir === path.dirname(dir)) break;
|
|
74
82
|
}
|
|
@@ -90,6 +98,17 @@ export async function updateAllBrains(opts = {}) {
|
|
|
90
98
|
const exec = opts.exec || spawnSync;
|
|
91
99
|
const roots = listUpdateRoots(opts).map(row => row.root);
|
|
92
100
|
const errors = [];
|
|
101
|
+
const brains = roots.map(root => ({ root, brain: path.join(root, '.agent', 'skills', 'total-recall') }))
|
|
102
|
+
.filter(({ brain }) => fs.existsSync(brain));
|
|
103
|
+
if (opts.brainDir) brains.push({ root: opts.brainDir, brain: opts.brainDir });
|
|
104
|
+
for (const { root, brain } of brains) {
|
|
105
|
+
if (!dryRun && opts.ensureDefaults === true) {
|
|
106
|
+
try { ensureRequiredPlugins(brain); }
|
|
107
|
+
catch(error){errors.push({root,status:'failed',error:error.message});continue;}
|
|
108
|
+
}
|
|
109
|
+
const required = inspectRequiredPlugins(brain);
|
|
110
|
+
if (required.status !== 'ready') errors.push({ root, status: 'failed', error: required.errors.join('; ') });
|
|
111
|
+
}
|
|
93
112
|
const requested = [...(opts.roots || []), ...rootsFromEnv(),
|
|
94
113
|
...resolveRegistryFiles(opts).flatMap(file => readRegistryEntries(file).map(row => row.path)).filter(Boolean)];
|
|
95
114
|
for (const root of new Set(requested.map(root => path.resolve(root)))) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** Canonical built-in CLI routing inventory. */
|
|
2
|
+
export const CLI_COMMANDS = {
|
|
3
|
+
app: 'app/index.mjs',
|
|
4
|
+
init: 'init.mjs',
|
|
5
|
+
setup: 'setup.mjs',
|
|
6
|
+
start: 'start.mjs',
|
|
7
|
+
startup: 'startup.mjs',
|
|
8
|
+
deploy: 'deploy.mjs',
|
|
9
|
+
doctor: 'doctor.mjs',
|
|
10
|
+
backup: 'backup.mjs',
|
|
11
|
+
dream: 'dream.mjs',
|
|
12
|
+
lint: 'lint.mjs',
|
|
13
|
+
daemon: 'daemon.mjs',
|
|
14
|
+
restore: 'restore.mjs',
|
|
15
|
+
upgrade: 'upgrade.mjs',
|
|
16
|
+
update: 'update.mjs',
|
|
17
|
+
friction: 'friction.mjs',
|
|
18
|
+
chat: 'chat.mjs',
|
|
19
|
+
status: 'status.mjs',
|
|
20
|
+
remember: 'remember.mjs',
|
|
21
|
+
forget: 'forget.mjs',
|
|
22
|
+
edit: 'edit.mjs',
|
|
23
|
+
recall: 'recall.mjs',
|
|
24
|
+
context: 'context.mjs',
|
|
25
|
+
rules: 'rules.mjs',
|
|
26
|
+
search: 'recall.mjs',
|
|
27
|
+
research: 'research.mjs',
|
|
28
|
+
proposals: 'proposals.mjs',
|
|
29
|
+
proposal: 'proposals.mjs',
|
|
30
|
+
task: 'task.mjs',
|
|
31
|
+
share: 'share.mjs',
|
|
32
|
+
'generate-pat': 'generate-pat.mjs',
|
|
33
|
+
key: 'key.mjs',
|
|
34
|
+
keys: 'key.mjs',
|
|
35
|
+
pat: 'key.mjs',
|
|
36
|
+
'hash-password': 'hash-password.mjs',
|
|
37
|
+
'reset-password': 'reset-password.mjs',
|
|
38
|
+
compile: 'rebuild.mjs',
|
|
39
|
+
rebuild: 'rebuild.mjs',
|
|
40
|
+
snapshot: 'snapshot.mjs',
|
|
41
|
+
migrate: 'migrate.mjs',
|
|
42
|
+
backfill: 'backfill.mjs',
|
|
43
|
+
ingest: 'ingest.mjs',
|
|
44
|
+
import: 'import-rules.mjs',
|
|
45
|
+
connect: 'connect.mjs',
|
|
46
|
+
sync: 'sync.mjs',
|
|
47
|
+
relay: 'relay.mjs',
|
|
48
|
+
uninstall: 'uninstall.mjs',
|
|
49
|
+
map: 'map.mjs',
|
|
50
|
+
brain: 'brain.mjs',
|
|
51
|
+
config: 'config.mjs',
|
|
52
|
+
skill: 'skill.mjs',
|
|
53
|
+
'project-id': 'project-id.mjs',
|
|
54
|
+
api: 'api.mjs',
|
|
55
|
+
brief: 'brief.mjs',
|
|
56
|
+
group: 'group.mjs',
|
|
57
|
+
groups: 'group.mjs',
|
|
58
|
+
help: 'help.mjs',
|
|
59
|
+
collab: 'collab.mjs',
|
|
60
|
+
command: 'command.mjs',
|
|
61
|
+
export: 'export.mjs',
|
|
62
|
+
harness: 'harness.mjs',
|
|
63
|
+
watch: 'rebuild.mjs',
|
|
64
|
+
agent: 'agent.mjs',
|
|
65
|
+
agents: 'agent.mjs',
|
|
66
|
+
new: 'new.mjs',
|
|
67
|
+
plugin: 'plugin/index.mjs',
|
|
68
|
+
plugins: 'plugin/index.mjs',
|
|
69
|
+
scaffold: 'plugin/scaffold-cmd.mjs',
|
|
70
|
+
'scaffold-plugin': 'plugin/scaffold-cmd.mjs',
|
|
71
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Daemon-owned change detection for CLI inventories and every registered surface. */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
import { listLocalBrains } from './brain-registry.mjs';
|
|
6
|
+
import { buildPluginCatalog } from './plugin-catalog.mjs';
|
|
7
|
+
import { discoverPlugins, PACKAGE_ROOT } from './plugin-loader.mjs';
|
|
8
|
+
import { runBounded } from './startup-health.mjs';
|
|
9
|
+
import { logger } from './logger.mjs';
|
|
10
|
+
|
|
11
|
+
export function cliInputFingerprint(root) {
|
|
12
|
+
const hash=crypto.createHash('sha256');
|
|
13
|
+
const add=file=>{try{const stat=fs.statSync(file);hash.update(`${file}:${stat.mtimeMs}:${stat.size}\n`);}catch{hash.update(`missing:${file}\n`);}};
|
|
14
|
+
for(const file of ['bin/total-recall.mjs','src/core/cli-commands.mjs','src/core/cli-quick-reference.mjs'])add(path.join(PACKAGE_ROOT,file));
|
|
15
|
+
const walk=directory=>{for(const item of fs.readdirSync(directory,{withFileTypes:true})){const file=path.join(directory,item.name);if(item.isDirectory())walk(file);else if(item.name.endsWith('.mjs'))add(file);}};
|
|
16
|
+
walk(path.join(PACKAGE_ROOT,'src/cli'));
|
|
17
|
+
hash.update(buildPluginCatalog(root).fingerprint);
|
|
18
|
+
for(const plugin of discoverPlugins(root)){add(plugin.manifestPath);if(plugin.manifest.cli?.handler)add(path.join(plugin.dir,plugin.manifest.cli.handler));}
|
|
19
|
+
return hash.digest('hex');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function maintainCliSurfaces({brainDir,brains,force=false,run=runBounded,fingerprint=cliInputFingerprint}={}) {
|
|
23
|
+
const results=[];
|
|
24
|
+
for(const brain of brains || listLocalBrains({globalBrainDir:brainDir})) {
|
|
25
|
+
if(!brain.exists || brain.enabled===false)continue;
|
|
26
|
+
const root=brain.root || path.dirname(path.dirname(brain.brainDir));
|
|
27
|
+
const file=path.join(brain.brainDir,'memory-derived/cli-maintenance.json');
|
|
28
|
+
let previous;try{previous=JSON.parse(fs.readFileSync(file,'utf8'));}catch{}
|
|
29
|
+
const current=fingerprint(root);
|
|
30
|
+
if(!force && previous?.fingerprint===current){results.push({root,status:'current'});continue;}
|
|
31
|
+
const result=await run(process.execPath,[path.join(PACKAGE_ROOT,'bin/total-recall.mjs'),'plugin','refresh',...(brain.kind==='global'?['--global']:[])],{cwd:root,timeoutMs:600000,maxBytes:262144});
|
|
32
|
+
if(result.code!==0){results.push({root,status:'failed',error:result.reason || result.output?.slice(-1000) || 'CLI compilation failed'});continue;}
|
|
33
|
+
fs.mkdirSync(path.dirname(file),{recursive:true});
|
|
34
|
+
const temp=`${file}.${process.pid}.tmp`;fs.writeFileSync(temp,JSON.stringify({fingerprint:current,compiledAt:new Date().toISOString()}));fs.renameSync(temp,file);
|
|
35
|
+
results.push({root,status:'refreshed'});
|
|
36
|
+
}
|
|
37
|
+
return {ok:results.every(row=>row.status!=='failed'),results};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function startCliSurfaceMaintenance({brainDir,intervalMs=30000}={}) {
|
|
41
|
+
let busy=false;
|
|
42
|
+
const tick=async()=>{if(busy)return;busy=true;try{const result=await maintainCliSurfaces({brainDir});if(!result.ok)logger.error('cli-maintenance',JSON.stringify(result));}catch(error){logger.error('cli-maintenance',error.message);}finally{busy=false;}};
|
|
43
|
+
const timer=setInterval(tick,intervalMs);timer.unref?.();void tick();
|
|
44
|
+
return {stop(){clearInterval(timer);}};
|
|
45
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The command map every compiled instruction surface leads with, so an agent
|
|
3
|
+
* sees which CLI owns a job before it reaches for curl, sed or ssh. The plugin
|
|
4
|
+
* namespaces and their operations are appended live by
|
|
5
|
+
* `buildInstalledPluginsSummary`, so they cannot go stale.
|
|
6
|
+
* `cli-quick-reference.spec.mjs` fails when a listed command stops existing in
|
|
7
|
+
* `total-recall --help`.
|
|
8
|
+
*/
|
|
9
|
+
import { CLI_COMMANDS } from './cli-commands.mjs';
|
|
10
|
+
export const CLI_QUICK_REFERENCE_COMMANDS = Object.keys(CLI_COMMANDS);
|
|
11
|
+
|
|
12
|
+
export const CLI_QUICK_REFERENCE = `## TOTAL RECALL CLI — USE IT FOR EVERYTHING (run \`total-recall <command> --help\` before guessing)
|
|
13
|
+
- Built-in commands: ${CLI_QUICK_REFERENCE_COMMANDS.map(command => `\`${command}\``).join(' · ')}
|
|
14
|
+
- Memory: \`context "<task>" --action <a>\` · \`recall "<q>" --local\` · \`remember <category> "<text>" --global|--project\` · \`edit <slug>\` · \`compile\` · \`brief\` · \`status\` / \`doctor\`
|
|
15
|
+
- CREDENTIALS GO THROUGH THE AUTH PLUGIN ONLY (Total Recall has no native secrets store; never hand-roll curl/sed/ssh for keys): \`auth secret list|get <K>|set <K> --stdin|rotate <K> --stdin|--from-clipboard [--remote <t>]|rotate-auto <K>|rotation-due|shared|meta|catalog|audit|diff-env|export-env\`; remote targets: \`auth secret remote add|list|deploy <t> [--keys a,b] [--dry-run]\`. The store follows \`TR_SECRETS_BRAIN\`.
|
|
16
|
+
- Auth: \`auth status|audit|key list|issue|revoke|password set --stdin|passkey|google status\`
|
|
17
|
+
- Mesh nodes: \`mesh nodes|status|ping|exec <node> <cmd>|ssh <node> [cmd]|access <node>|leader\` — never ssh a machine into itself.
|
|
18
|
+
- Agents and work: \`agent spawn|list|logs|kill\` · \`harness dispatch|council\` · \`task <cmd>\` · \`plugin list|install|info|library\` · \`skill <cmd>\` · \`brain <cmd>\` · \`daemon <cmd>\` · \`update --all-brains --apply\`
|
|
19
|
+
- Every installed plugin namespace and its operations is listed next; run \`total-recall <namespace> --help\` for flags.
|
|
20
|
+
`;
|