total-recall-brain 3.40.1 → 3.40.3
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 +413 -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 +368 -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
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Required native capability installation checks; never opens credential state. */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { readPluginsDir, PACKAGE_ROOT, resolvePluginFile } from './plugin-loader.mjs';
|
|
5
|
+
import { DEFAULT_PLUGIN_IDS } from './default-plugins.mjs';
|
|
6
|
+
import { preparePluginDependencies } from './plugin-dependencies.mjs';
|
|
7
|
+
|
|
8
|
+
export function inspectRequiredPlugins(brainDir) {
|
|
9
|
+
const plugins = readPluginsDir(path.join(brainDir, 'plugins'), 'required');
|
|
10
|
+
const errors = [];
|
|
11
|
+
const auth = plugins.find(plugin => plugin.id === 'auth');
|
|
12
|
+
if (!auth?.valid) errors.push('Required auth plugin is missing or invalid');
|
|
13
|
+
else {
|
|
14
|
+
if (auth.manifest.cli?.command !== 'auth') errors.push('Required auth namespace is missing');
|
|
15
|
+
for (const name of ['commands', 'secrets-store', 'runtime-credentials']) {
|
|
16
|
+
const file = auth.manifest.runtime?.modules?.[name];
|
|
17
|
+
if (!file) errors.push(`Required auth runtime module ${name} is missing`);
|
|
18
|
+
}
|
|
19
|
+
const files = [auth.manifest.cli?.handler, ...Object.values(auth.manifest.runtime?.modules || {})];
|
|
20
|
+
for (const file of files) {
|
|
21
|
+
try {
|
|
22
|
+
if (!file || !fs.statSync(resolvePluginFile(auth.dir, file)).isFile()) throw new Error();
|
|
23
|
+
} catch { errors.push(`Required auth file is missing or unsafe: ${file || 'CLI handler'}`); }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { status: errors.length ? 'failed' : 'ready', errors };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function ensureRequiredPlugins(brainDir, { sourceDir = path.join(PACKAGE_ROOT, 'scaffold', '.agent', 'skills', 'total-recall', 'plugins') } = {}) {
|
|
30
|
+
// Validate the complete package before changing any installation.
|
|
31
|
+
for (const id of DEFAULT_PLUGIN_IDS) {
|
|
32
|
+
if (!fs.existsSync(path.join(sourceDir, id, 'plugin.json'))) throw new Error(`Required bundled ${id} plugin is missing`);
|
|
33
|
+
}
|
|
34
|
+
for (const id of DEFAULT_PLUGIN_IDS) {
|
|
35
|
+
const target = path.join(brainDir, 'plugins', id);
|
|
36
|
+
if (!fs.existsSync(target)) {
|
|
37
|
+
const source = path.join(sourceDir, id);
|
|
38
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
39
|
+
fs.cpSync(source, target, { recursive: true, force: false, errorOnExist: true });
|
|
40
|
+
}
|
|
41
|
+
preparePluginDependencies(target);
|
|
42
|
+
}
|
|
43
|
+
const report = inspectRequiredPlugins(brainDir);
|
|
44
|
+
if (report.status !== 'ready') throw new Error(report.errors.join('; '));
|
|
45
|
+
return report;
|
|
46
|
+
}
|
|
@@ -10,6 +10,7 @@ import path from 'node:path';
|
|
|
10
10
|
import crypto from 'node:crypto';
|
|
11
11
|
import { spawnSync } from 'node:child_process';
|
|
12
12
|
import { isBrainState } from './brain-state.mjs';
|
|
13
|
+
import { discoverPlugins } from './plugin-loader.mjs';
|
|
13
14
|
|
|
14
15
|
export const TEST_PATTERNS = [/^test\//, /^tests\//, /(^|\/)[^/]+\.(spec|test)\.[cm]?[jt]sx?$/, /^vitest\.config\.[cm]?[jt]s$/];
|
|
15
16
|
|
|
@@ -35,6 +36,15 @@ export function releaseFiles(pluginDir, run = spawnSync) {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const digest = (file) => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
39
|
+
function releaseContent(file,rel) {
|
|
40
|
+
const raw=fs.readFileSync(file);
|
|
41
|
+
if(rel!=='package.json')return raw;
|
|
42
|
+
const pkg=JSON.parse(raw);
|
|
43
|
+
// Runtime bundles exclude tests and development installs; do not install a
|
|
44
|
+
// complete extra host package merely to satisfy a stripped test dependency.
|
|
45
|
+
delete pkg.devDependencies;
|
|
46
|
+
return Buffer.from(JSON.stringify(pkg,null,2)+'\n');
|
|
47
|
+
}
|
|
38
48
|
|
|
39
49
|
/** Compare (and with write:true, update) the scaffold copies and the pin. */
|
|
40
50
|
export function syncScaffoldPlugins({ root, ids, write = false, run = spawnSync }) {
|
|
@@ -43,7 +53,10 @@ export function syncScaffoldPlugins({ root, ids, write = false, run = spawnSync
|
|
|
43
53
|
const changes = [];
|
|
44
54
|
// Validate every owner before copying any source or replacing the release pin.
|
|
45
55
|
const plans = ids.map(id => {
|
|
46
|
-
const
|
|
56
|
+
const local = path.join(root, '.agent', 'skills', 'total-recall', 'plugins', id);
|
|
57
|
+
const selected = fs.existsSync(local) ? local : discoverPlugins(root).find(plugin => plugin.id === id && plugin.valid)?.dir;
|
|
58
|
+
if (!selected) throw new Error(`Required default plugin ${id} is not installed`);
|
|
59
|
+
const source = fs.realpathSync(selected);
|
|
47
60
|
const files = releaseFiles(source, run);
|
|
48
61
|
return { id, source, files };
|
|
49
62
|
});
|
|
@@ -52,11 +65,12 @@ export function syncScaffoldPlugins({ root, ids, write = false, run = spawnSync
|
|
|
52
65
|
for (const rel of files) {
|
|
53
66
|
const from = path.join(source, rel);
|
|
54
67
|
const to = path.join(target, rel);
|
|
55
|
-
const
|
|
68
|
+
const content=releaseContent(from,rel);
|
|
69
|
+
const sha = crypto.createHash('sha256').update(content).digest('hex');
|
|
56
70
|
lock[`${id}/${rel}`] = sha;
|
|
57
71
|
if (!fs.existsSync(to)) changes.push({ file: `${id}/${rel}`, change: 'added' });
|
|
58
72
|
else if (digest(to) !== sha) changes.push({ file: `${id}/${rel}`, change: 'updated' });
|
|
59
|
-
if (write) { fs.mkdirSync(path.dirname(to), { recursive: true }); fs.
|
|
73
|
+
if (write) { fs.mkdirSync(path.dirname(to), { recursive: true }); fs.writeFileSync(to,content); }
|
|
60
74
|
}
|
|
61
75
|
const keep = new Set(files);
|
|
62
76
|
const walk = (dir) => {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Fast-forward a clean source-linked CLI to the registry's exact release commit. */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {spawnSync} from 'node:child_process';
|
|
5
|
+
import {listUpdateRoots} from './package-auto-update.mjs';
|
|
6
|
+
import {PACKAGE_ROOT} from './plugin-loader.mjs';
|
|
7
|
+
import {comparePackageVersions} from './package-version.mjs';
|
|
8
|
+
|
|
9
|
+
export function updateSourceBrainPackages({brainDir,versions,roots,update=updateSourceBrainPackage}={}) {
|
|
10
|
+
const results=[];
|
|
11
|
+
for(const directory of new Set(roots || [PACKAGE_ROOT,...listUpdateRoots({brainDir}).map(item=>item.root)])) {
|
|
12
|
+
if(!fs.existsSync(path.join(directory,'.git')))continue;
|
|
13
|
+
let pkg;try{pkg=JSON.parse(fs.readFileSync(path.join(directory,'package.json'),'utf8'));}catch{continue;}
|
|
14
|
+
if(!Object.hasOwn(versions,pkg.name))continue;
|
|
15
|
+
const latest=versions[pkg.name],row={root:directory,package:pkg.name,installed:pkg.version,latest,status:'up_to_date'};results.push(row);
|
|
16
|
+
if(pkg.version===latest)continue;
|
|
17
|
+
if(comparePackageVersions(pkg.version,latest)===1){row.status='failed';row.error='Newer source preview; refusing implicit downgrade';continue;}
|
|
18
|
+
const result=update({directory,name:pkg.name,version:latest});
|
|
19
|
+
row.status=result.ok?'updated':'failed';
|
|
20
|
+
if(result.ok){row.installed=result.installed;row.commit=result.commit;}else row.error=result.error;
|
|
21
|
+
}
|
|
22
|
+
return {failed:results.filter(row=>row.status==='failed').length,results};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function updateSourceBrainPackage({directory,name,version,exec=spawnSync}={}) {
|
|
26
|
+
const run=(binary,args,timeout=30000)=>{
|
|
27
|
+
const result=exec(binary,args,{cwd:directory,encoding:'utf8',timeout,maxBuffer:8*1024*1024});
|
|
28
|
+
if(result.status!==0)throw new Error(result.error?.message || result.stderr?.trim().slice(-1000) || `${binary} failed`);
|
|
29
|
+
return result.stdout?.trim() || '';
|
|
30
|
+
};
|
|
31
|
+
const read=()=>JSON.parse(fs.readFileSync(path.join(directory,'package.json'),'utf8'));
|
|
32
|
+
try {
|
|
33
|
+
if(read().name!==name)throw new Error('Source package identity mismatch');
|
|
34
|
+
if(run('git',['status','--porcelain']))throw new Error('Source checkout has uncommitted changes');
|
|
35
|
+
const commit=JSON.parse(run('npm',['view',`${name}@${version}`,'gitHead','--json']));
|
|
36
|
+
if(!/^[a-f0-9]{40}$/.test(commit))throw new Error('Published package has no verified source commit');
|
|
37
|
+
const remote=run('git',['remote','get-url','origin']);
|
|
38
|
+
if(!remote.startsWith('https://'))throw new Error('Source update requires an HTTPS origin');
|
|
39
|
+
run('git',['fetch','origin',commit],120000);
|
|
40
|
+
const candidate=JSON.parse(run('git',['show',`${commit}:package.json`]));
|
|
41
|
+
if(candidate.name!==name || candidate.version!==version)throw new Error('Published source commit does not match the package version');
|
|
42
|
+
run('git',['merge','--ff-only',commit]);
|
|
43
|
+
run('npm',['ci','--no-audit','--no-fund'],600000);
|
|
44
|
+
if(read().version!==version || run('git',['rev-parse','HEAD'])!==commit)throw new Error('Source release readback failed');
|
|
45
|
+
return {ok:true,installed:version,commit};
|
|
46
|
+
}catch(error){return {ok:false,error:error.message};}
|
|
47
|
+
}
|
|
@@ -5,6 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { readProcessCommand } from './pid-lock.mjs';
|
|
8
|
+
import { CLI_COMMANDS } from './cli-commands.mjs';
|
|
8
9
|
|
|
9
10
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
10
11
|
export const cliEntry = path.join(packageRoot, 'bin/total-recall.mjs');
|
|
@@ -63,8 +64,7 @@ export function appVerdict(result) {
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
function projectCommand(cwd, name) {
|
|
66
|
-
const
|
|
67
|
-
const reserved = new Set([...dispatcher.matchAll(/^\s+'?([a-z][a-z0-9-]*)'?\s*:/gm)].map(m => m[1]));
|
|
67
|
+
const reserved = new Set(Object.keys(CLI_COMMANDS));
|
|
68
68
|
if (reserved.has(name) || !/^[a-z][a-z0-9-]{0,63}$/.test(name || '')) return false;
|
|
69
69
|
const file = path.join(cwd, '.agent', 'commands', name + '.mjs');
|
|
70
70
|
try { return fs.statSync(file).isFile() && fs.realpathSync(file).startsWith(fs.realpathSync(cwd) + path.sep) ? file : false; } catch { return false; }
|
|
@@ -167,7 +167,9 @@ export async function startupHealth({ cwd = process.cwd(), brainDir, brainUrl =
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
-
|
|
170
|
+
const { inspectRequiredPlugins } = await import('./required-plugins.mjs');
|
|
171
|
+
report.required_plugins = inspectRequiredPlugins(brainDir);
|
|
172
|
+
report.shared_ready = report.required_plugins.status === 'ready' && report.server.status === 'ready' && report.brain.status === 'ready' && report.daemon.status === 'running' && report.ssss.status === 'available';
|
|
171
173
|
report.ready_scope = appCheck ? 'shared-and-declared-current-repo-app' : 'shared-runtime-only';
|
|
172
174
|
report.ready = report.shared_ready && (!appCheck || report.app.status === 'ready');
|
|
173
175
|
return report;
|
package/src/core/surface.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import { selectRules, assembleContext, curatedRules } from './context-policy.mjs
|
|
|
20
20
|
import { discoverPlugins } from './plugin-loader.mjs';
|
|
21
21
|
import { refreshPluginCatalog } from './plugin-catalog.mjs';
|
|
22
22
|
import { pluginActivation } from './plugin-state.mjs';
|
|
23
|
+
import { CLI_QUICK_REFERENCE } from './cli-quick-reference.mjs';
|
|
23
24
|
|
|
24
25
|
// Long-lived processes (server, daemon, vault watcher) import this module once.
|
|
25
26
|
// If the rule builder is edited or upgraded after they start, their in-memory
|
|
@@ -343,7 +344,7 @@ export function readRuleBudgets(skillsDir) {
|
|
|
343
344
|
return out;
|
|
344
345
|
}
|
|
345
346
|
|
|
346
|
-
const DEFAULT_CONTEXT_BUDGET = { total_tokens: 4000, bootstrap_total_tokens:
|
|
347
|
+
const DEFAULT_CONTEXT_BUDGET = { total_tokens: 4000, bootstrap_total_tokens: 16000 };
|
|
347
348
|
|
|
348
349
|
/**
|
|
349
350
|
* Instruction-surface token budget from surface.yml files and the environment.
|
|
@@ -396,7 +397,9 @@ export function buildInstalledPluginsSummary(projectRoot = process.cwd()) {
|
|
|
396
397
|
for (const p of plugins) {
|
|
397
398
|
const cmd = p.manifest.cli?.command || p.id;
|
|
398
399
|
const desc = (p.manifest.description || 'Plugin capability').replace(/\s+/g, ' ').slice(0, 30).trimEnd();
|
|
399
|
-
|
|
400
|
+
const ops = (p.manifest.cli?.subcommands || []).map(sub => sub.name).filter(Boolean);
|
|
401
|
+
const shown = ops.join(' | ');
|
|
402
|
+
lines.push(`- **${p.id}** v${p.manifest.version || 'unknown'} · ${pluginActivation(p).state} · \`total-recall ${cmd}\` ${shown ? `[${shown}]` : '(no operations)'}: ${desc}`);
|
|
400
403
|
}
|
|
401
404
|
return lines.join('\n') + '\n';
|
|
402
405
|
} catch {
|
|
@@ -413,7 +416,8 @@ export async function buildRulesBlock(skillsDir, nodes = [], {
|
|
|
413
416
|
const rules = selectRules(curatedRules(nodes), { actions, projectRoot: root, bootstrap });
|
|
414
417
|
const contributions = [];
|
|
415
418
|
const counts = category => rules.filter(n => n.category === category).length;
|
|
416
|
-
const
|
|
419
|
+
const commands = buildCommandsSection(listSurfaceCommands(commandDirsFor(skillsDir)));
|
|
420
|
+
const header = bootstrap ? `${CLI_QUICK_REFERENCE}\n${buildInstalledPluginsSummary(root)}\n${commands}\n# 🔴 MANDATORY FIRST STEP: TOTAL RECALL CLI & TASK ROUTING
|
|
417
421
|
|
|
418
422
|
1. RUN TOTAL RECALL CLI HELP FIRST:
|
|
419
423
|
\`total-recall --help\`
|
|
@@ -427,16 +431,17 @@ Read the complete returned capsule and require ready:true. Unknown rule applicab
|
|
|
427
431
|
Total Recall is the agent's working memory, toolset, and single source of truth across all decisions. Refresh at every changed task, action or project, and after a memory/skill edit. Do not infer applicability from semantic similarity. CLI retrieval is local and works without the server. Skills have compact entrypoints; load only relevant references. Canonical vault writes use the validated CLI.
|
|
428
432
|
|
|
429
433
|
DO NOT SKIP THIS. Run Total Recall CLI help and context first before doing anything else.
|
|
434
|
+
|
|
430
435
|
` :
|
|
431
436
|
`## Active Rules: ${counts('invariants')} invariants, ${counts('preferences')} preferences, ${counts('anti-patterns')} corrections`;
|
|
432
437
|
contributions.push({ id: 'routing', text: header, required: true });
|
|
433
|
-
for (const node of rules) contributions.push({ id: `${node._layer || 'project'}:${node.slug}`, required: true,
|
|
434
|
-
text: node._directive ? `[${node.slug}] ${node._directive}` : `### ${node.title || node.slug} [${node.slug}]\n\n${node.body || node.content || ''}` });
|
|
435
438
|
const pluginsSummary = buildInstalledPluginsSummary(root);
|
|
436
|
-
if (pluginsSummary) {
|
|
439
|
+
if (pluginsSummary && !bootstrap) {
|
|
437
440
|
contributions.push({ id: 'installed-plugins', text: pluginsSummary, required: true });
|
|
438
441
|
}
|
|
439
|
-
contributions.push({ id: '
|
|
442
|
+
for (const node of rules) contributions.push({ id: `${node._layer || 'project'}:${node.slug}`, required: true,
|
|
443
|
+
text: node._directive ? `[${node.slug}] ${node._directive}` : `### ${node.title || node.slug} [${node.slug}]\n\n${node.body || node.content || ''}` });
|
|
444
|
+
if (!bootstrap) contributions.push({ id: 'commands', text: commands });
|
|
440
445
|
if (!bootstrap) {
|
|
441
446
|
if (skillsDir) {
|
|
442
447
|
contributions.push(...legacyRuleContributions(skillsDir));
|
|
@@ -460,7 +465,8 @@ function injectDirectives(fileContent, rulesBlock) {
|
|
|
460
465
|
const endIdx = fileContent.indexOf(DIRECTIVES_END);
|
|
461
466
|
|
|
462
467
|
if (beginIdx !== -1 && endIdx !== -1 && endIdx > beginIdx) {
|
|
463
|
-
|
|
468
|
+
const authored = fileContent.slice(0, beginIdx) + fileContent.slice(endIdx + DIRECTIVES_END.length);
|
|
469
|
+
return prependDirectives(authored, rulesBlock);
|
|
464
470
|
}
|
|
465
471
|
|
|
466
472
|
let content = fileContent.trimEnd();
|
|
@@ -469,7 +475,15 @@ function injectDirectives(fileContent, rulesBlock) {
|
|
|
469
475
|
content = baseline + '\n\n' + content;
|
|
470
476
|
}
|
|
471
477
|
|
|
472
|
-
return content
|
|
478
|
+
return prependDirectives(content, rulesBlock);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function prependDirectives(authored, rulesBlock) {
|
|
482
|
+
// YAML metadata must remain first for clients that parse .mdc frontmatter.
|
|
483
|
+
const frontmatter = authored.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);
|
|
484
|
+
const prefix = frontmatter?.[0] || '';
|
|
485
|
+
const body = authored.slice(prefix.length).trim();
|
|
486
|
+
return `${prefix}${DIRECTIVES_BEGIN}\n${rulesBlock}\n${DIRECTIVES_END}\n\n${body}\n`;
|
|
473
487
|
}
|
|
474
488
|
|
|
475
489
|
/**
|
|
@@ -482,7 +496,7 @@ async function writeShim(shimPath, skillsDir, nodes = [], { vaultDir, derivedDir
|
|
|
482
496
|
const mdcHeader = shimPath.endsWith('.mdc')
|
|
483
497
|
? '---\ndescription: "Total Recall — Auto-generated behavioral memory surface."\nglobs:\nalwaysApply: true\n---\n\n'
|
|
484
498
|
: '';
|
|
485
|
-
const fullContent = `${mdcHeader}${baseline}
|
|
499
|
+
const fullContent = prependDirectives(`${mdcHeader}${baseline}`, rulesBlock);
|
|
486
500
|
|
|
487
501
|
try {
|
|
488
502
|
const stat = fs.lstatSync(shimPath, { throwIfNoEntry: false });
|
|
@@ -778,6 +792,25 @@ export async function compileSurface({ vaultDir, skillsDir, derivedDir, instruct
|
|
|
778
792
|
semanticResult = { indexed: 0, skipped: nodes.length, unavailable: true, error: err.message };
|
|
779
793
|
}
|
|
780
794
|
|
|
795
|
+
// 3b. Build or update the CLI semantic index.
|
|
796
|
+
let cliIndexResult = { built: 0, skipped: 0, failed: 0 };
|
|
797
|
+
try {
|
|
798
|
+
const { buildCliSemanticIndex } = await import('./cli-semantic-index.mjs');
|
|
799
|
+
const projectRoot = skillsDir ? path.dirname(path.dirname(skillsDir)) : process.cwd();
|
|
800
|
+
cliIndexResult = await buildCliSemanticIndex({
|
|
801
|
+
derivedDir,
|
|
802
|
+
skillsDir,
|
|
803
|
+
projectRoot,
|
|
804
|
+
force,
|
|
805
|
+
semantic: semantic && process.env.TR_EMBEDDINGS_DISABLED !== '1',
|
|
806
|
+
});
|
|
807
|
+
if (cliIndexResult.built > 0) {
|
|
808
|
+
logger.info('surface', `CLI semantic index: ${cliIndexResult.built} indexed, ${cliIndexResult.skipped} skipped.`, { derivedDir });
|
|
809
|
+
}
|
|
810
|
+
} catch (err) {
|
|
811
|
+
logger.warn('surface', `CLI semantic index build non-fatal warning: ${err.message}`, { derivedDir });
|
|
812
|
+
}
|
|
813
|
+
|
|
781
814
|
// 4. Generate Obsidian Canvas
|
|
782
815
|
try {
|
|
783
816
|
generateCanvas(nodes, vaultDir);
|
|
@@ -841,6 +874,7 @@ function writeProjectionManifest(derivedDir, vaultHash) {
|
|
|
841
874
|
{ file: 'memory-layers.jsonl', disposable: true },
|
|
842
875
|
{ file: 'local-search.json', disposable: true },
|
|
843
876
|
{ file: 'embeddings.jsonl', disposable: true },
|
|
877
|
+
{ file: 'cli-semantic-index.json', disposable: true },
|
|
844
878
|
{ file: 'vault-hash.txt', disposable: true }
|
|
845
879
|
],
|
|
846
880
|
rebuild_command: 'npx total-recall compile'
|
|
@@ -8,18 +8,22 @@
|
|
|
8
8
|
import { Router } from 'express';
|
|
9
9
|
import fs from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
|
+
import os from 'node:os';
|
|
11
12
|
import { spawn } from 'node:child_process';
|
|
12
13
|
import { fileURLToPath } from 'node:url';
|
|
13
14
|
import { requireAuth, requireScope } from '../auth.mjs';
|
|
14
15
|
import { BRAIN_DIR, serverError } from './_shared.mjs';
|
|
16
|
+
import { issueKey } from '../keys.mjs';
|
|
15
17
|
|
|
16
18
|
const router = Router();
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* GET /api/extension/download
|
|
20
|
-
* Streams the Chrome extension directory as a .zip (or .tar.gz fallback)
|
|
22
|
+
* Streams the Chrome extension directory as a .zip (or .tar.gz fallback)
|
|
23
|
+
* with an automatically provisioned access token and brain URL baked into config.js/config.json.
|
|
21
24
|
*/
|
|
22
|
-
router.get('/api/extension/download', requireAuth, requireScope('config:read'), async (
|
|
25
|
+
router.get('/api/extension/download', requireAuth, requireScope('config:read'), async (req, res) => {
|
|
26
|
+
let tmpDir = null;
|
|
23
27
|
try {
|
|
24
28
|
// Extension lives at <package-root>/extension/
|
|
25
29
|
const extDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../extension');
|
|
@@ -27,31 +31,72 @@ router.get('/api/extension/download', requireAuth, requireScope('config:read'),
|
|
|
27
31
|
return res.status(404).json({ error: 'Chrome extension not found in this installation.' });
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
//
|
|
31
|
-
|
|
34
|
+
// Determine the brain URL from request headers/host
|
|
35
|
+
const proto = req.get('x-forwarded-proto') || req.protocol || 'http';
|
|
36
|
+
const host = req.get('x-forwarded-host') || req.get('host') || '127.0.0.1:3000';
|
|
37
|
+
const brainUrl = `${proto}://${host}`;
|
|
38
|
+
|
|
39
|
+
// Automatically issue a dedicated access token for this extension package
|
|
40
|
+
let token = '';
|
|
41
|
+
try {
|
|
42
|
+
const key = issueKey(`Chrome Extension (${host})`, { scopes: ['*'] });
|
|
43
|
+
token = (key && key.token) || '';
|
|
44
|
+
} catch {
|
|
45
|
+
token = '';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Create a temporary staging directory so git working copy remains untouched
|
|
49
|
+
tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'tr-ext-'));
|
|
50
|
+
await fs.promises.cp(extDir, tmpDir, { recursive: true });
|
|
51
|
+
|
|
52
|
+
// Bake pre-configured credentials directly into config.js and config.json inside the package
|
|
53
|
+
const configData = {
|
|
54
|
+
brainUrl,
|
|
55
|
+
pat: token,
|
|
56
|
+
generatedAt: new Date().toISOString(),
|
|
57
|
+
activeBrainId: req.headers['x-total-recall-brain'] || 'global',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const configJsContent = `// Automatically generated by Total Recall Brain\nself.TOTAL_RECALL_CONFIG = ${JSON.stringify(configData, null, 2)};\n`;
|
|
61
|
+
await fs.promises.writeFile(path.join(tmpDir, 'config.js'), configJsContent, 'utf8');
|
|
62
|
+
await fs.promises.writeFile(path.join(tmpDir, 'config.json'), JSON.stringify(configData, null, 2), 'utf8');
|
|
32
63
|
|
|
33
64
|
res.setHeader('Content-Type', 'application/zip');
|
|
34
65
|
res.setHeader('Content-Disposition', 'attachment; filename="total-recall-extension.zip"');
|
|
35
66
|
|
|
67
|
+
const cleanup = () => {
|
|
68
|
+
if (tmpDir) {
|
|
69
|
+
fs.promises.rm(tmpDir, { recursive: true, force: true }).catch(() => {});
|
|
70
|
+
tmpDir = null;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
res.on('finish', cleanup);
|
|
75
|
+
res.on('close', cleanup);
|
|
76
|
+
|
|
36
77
|
// Use zip if available, fall back to tar
|
|
37
78
|
// Unit specs live beside the extension sources but are not part of the product.
|
|
38
|
-
const zip = spawn('zip', ['-r', '-', '.', '-x', '*.spec.mjs'], { cwd:
|
|
79
|
+
const zip = spawn('zip', ['-r', '-', '.', '-x', '*.spec.mjs'], { cwd: tmpDir, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
39
80
|
zip.stdout.pipe(res);
|
|
40
81
|
zip.on('error', () => {
|
|
41
82
|
// zip not available — try tar
|
|
42
83
|
if (!res.headersSent) {
|
|
43
84
|
res.setHeader('Content-Type', 'application/gzip');
|
|
44
85
|
res.setHeader('Content-Disposition', 'attachment; filename="total-recall-extension.tar.gz"');
|
|
45
|
-
const tar = spawn('tar', ['czf', '-', '--exclude=*.spec.mjs', '-C',
|
|
86
|
+
const tar = spawn('tar', ['czf', '-', '--exclude=*.spec.mjs', '-C', tmpDir, '.'], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
46
87
|
tar.stdout.pipe(res);
|
|
47
|
-
tar.on('error', err => { if (!res.headersSent) serverError(res, err); });
|
|
48
|
-
tar.on('close', () => { if (!res.writableEnded) res.end(); });
|
|
88
|
+
tar.on('error', err => { cleanup(); if (!res.headersSent) serverError(res, err); });
|
|
89
|
+
tar.on('close', () => { cleanup(); if (!res.writableEnded) res.end(); });
|
|
49
90
|
}
|
|
50
91
|
});
|
|
51
92
|
zip.on('close', code => {
|
|
93
|
+
cleanup();
|
|
52
94
|
if (code !== 0 && !res.writableEnded) res.end();
|
|
53
95
|
});
|
|
54
96
|
} catch (err) {
|
|
97
|
+
if (tmpDir) {
|
|
98
|
+
fs.promises.rm(tmpDir, { recursive: true, force: true }).catch(() => {});
|
|
99
|
+
}
|
|
55
100
|
serverError(res, err);
|
|
56
101
|
}
|
|
57
102
|
});
|
|
@@ -37,7 +37,7 @@ npx total-recall remember preference "..." --tags "user,profile,onboarding" --im
|
|
|
37
37
|
|
|
38
38
|
6. **Background work** — "Want dream consolidation and optional daemon tasks running in the background, or on-demand only?"
|
|
39
39
|
|
|
40
|
-
7. **Secrets** — "API keys never go in the vault. Use `npx total-recall secret set`. Any keys you want to set up next?"
|
|
40
|
+
7. **Secrets** — "API keys never go in the vault. Use `npx total-recall auth secret set`. Any keys you want to set up next?"
|
|
41
41
|
|
|
42
42
|
8. **Notifications (optional)** — "Want Telegram pings when background tasks finish? If yes, BotFather token + chat ID."
|
|
43
43
|
|