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.
Files changed (186) hide show
  1. package/README.md +152 -41
  2. package/bin/total-recall.mjs +16 -76
  3. package/docs/reference/cli-reference.md +10 -10
  4. package/extension/background.js +6 -2
  5. package/extension/config.js +8 -0
  6. package/extension/lib/brain-client.js +29 -4
  7. package/extension/options/options.html +1 -0
  8. package/extension/sidepanel/sidepanel.html +1 -0
  9. package/frontend/dist/assets/{index-CvQRdT2j.css → index-AGUes-VG.css} +1 -1
  10. package/frontend/dist/assets/index-Bt5iVPZj.js +5618 -0
  11. package/frontend/dist/index.html +2 -2
  12. package/frontend/src/api/keys.ts +9 -2
  13. package/frontend/src/api/plugins.ts +4 -0
  14. package/frontend/src/components/plugins/card/CardEngine.tsx +2 -1
  15. package/frontend/src/components/plugins/card/card.css +1 -1
  16. package/frontend/src/pages/IntegrationsPage.tsx +315 -8
  17. package/frontend/src/pages/PluginsPage.tsx +532 -87
  18. package/package.json +6 -5
  19. package/scaffold/.agent/skills/meta-harness/references/optimized/original.md +1 -1
  20. package/scaffold/.agent/skills/meta-harness/references/optimized/section-09.md +1 -1
  21. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +10 -10
  22. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -6
  23. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +0 -1
  24. package/scaffold/.agent/skills/total-recall/plugins/auth/src/browser-session.mjs +309 -4
  25. package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +44 -34
  26. package/scaffold/.agent/skills/total-recall/plugins/auth/src/google-oauth.mjs +6 -4
  27. package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-catalog.mjs +1 -1
  28. package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-rotation-recipes.mjs +118 -4
  29. package/scaffold/.agent/skills/total-recall/plugins/auth/src/routes/auth.mjs +2 -2
  30. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +3 -3
  31. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +1 -1
  32. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-rotate.mjs +24 -6
  33. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +164 -123
  34. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +65 -0
  35. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +76 -0
  36. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +19 -0
  37. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +290 -0
  38. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +446 -0
  39. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +152 -0
  40. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +71 -0
  41. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +36 -0
  42. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +35 -0
  43. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +175 -0
  44. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +18 -0
  45. package/scaffold/.agent/skills/total-recall/plugins/code-quality/LICENSE +21 -0
  46. package/scaffold/.agent/skills/total-recall/plugins/code-quality/README.md +84 -0
  47. package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +79 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/check.mjs +6 -0
  49. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/report.mjs +6 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +27 -0
  51. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/snapshot.mjs +163 -0
  52. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +757 -0
  53. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/config.schema.json +86 -0
  54. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/detect.mjs +89 -0
  55. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +145 -0
  56. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +210 -0
  57. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/state.mjs +20 -0
  58. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +2869 -0
  59. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +28 -0
  60. package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +186 -0
  61. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +18 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +272 -0
  63. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +129 -0
  64. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/LICENSE +21 -0
  65. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/README.md +73 -0
  66. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/cli.mjs +375 -0
  67. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/echo-tool.mjs +23 -0
  68. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/group.mjs +262 -0
  69. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/project-id.mjs +74 -0
  70. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +2869 -0
  71. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +31 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +221 -0
  73. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/schemas/cli_command.schema.json +107 -0
  74. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/skills/composable-cli/SKILL.md +63 -0
  75. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +313 -0
  76. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compile.mjs +19 -0
  77. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compose.mjs +144 -0
  78. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/contract.mjs +110 -0
  79. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/library.mjs +23 -0
  80. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/registry.mjs +192 -0
  81. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/resolve.mjs +221 -0
  82. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/risk.mjs +126 -0
  83. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/ssss-documents.mjs +29 -0
  84. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/DESIGN.md +18 -0
  85. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +273 -0
  86. package/scaffold/.agent/skills/total-recall/plugins/embeddings/LICENSE +21 -0
  87. package/scaffold/.agent/skills/total-recall/plugins/embeddings/README.md +20 -0
  88. package/scaffold/.agent/skills/total-recall/plugins/embeddings/cli.mjs +310 -0
  89. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +2826 -0
  90. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +11 -0
  91. package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +62 -0
  92. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +297 -0
  93. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/LICENSE +21 -0
  94. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +43 -0
  95. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/cli.mjs +258 -0
  96. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4005 -0
  97. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +20 -0
  98. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +125 -0
  99. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +18 -0
  100. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +278 -0
  101. package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +9 -0
  102. package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +6 -0
  103. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +4721 -0
  104. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +3 -21
  105. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +14 -1
  106. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +10 -0
  107. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/headscale-client.mjs +5 -1
  108. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/leader-election.mjs +4 -13
  109. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-access-verify.mjs +22 -0
  110. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-files.mjs +18 -0
  111. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +2 -0
  112. package/scaffold/.agent/skills/total-recall/plugins/mesh/test-support/memory-root.mjs +14 -0
  113. package/scaffold/.agent/skills/total-recall/plugins/openwiki/LICENSE +21 -0
  114. package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +35 -0
  115. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +124 -0
  116. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +4097 -0
  117. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +12 -0
  118. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +165 -0
  119. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +127 -0
  120. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +18 -0
  121. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +279 -0
  122. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +539 -0
  123. package/scaffold/.agent/skills/total-recall/plugins/security/LICENSE +21 -0
  124. package/scaffold/.agent/skills/total-recall/plugins/security/README.md +67 -0
  125. package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +932 -0
  126. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3256 -0
  127. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +25 -0
  128. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +297 -0
  129. package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +4 -0
  130. package/scaffold/.agent/skills/total-recall/plugins/security/skills/security/SKILL.md +37 -0
  131. package/scaffold/.agent/skills/total-recall/plugins/security/src/android.mjs +21 -0
  132. package/scaffold/.agent/skills/total-recall/plugins/security/src/devices.mjs +94 -0
  133. package/scaffold/.agent/skills/total-recall/plugins/security/src/machines.mjs +164 -0
  134. package/scaffold/.agent/skills/total-recall/plugins/security/src/scanner.mjs +41 -0
  135. package/scaffold/.agent/skills/total-recall/plugins/security/src/windows.mjs +42 -0
  136. package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +18 -0
  137. package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +95 -0
  138. package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +98 -0
  139. package/scaffold/.agent/skills/total-recall/references/auth.md +1 -1
  140. package/src/cli/brief.mjs +33 -9
  141. package/src/cli/command.mjs +2 -7
  142. package/src/cli/config.mjs +1 -0
  143. package/src/cli/connect.mjs +5 -0
  144. package/src/cli/context.mjs +2 -1
  145. package/src/cli/daemon.mjs +36 -1
  146. package/src/cli/doctor.mjs +6 -0
  147. package/src/cli/harness.mjs +3 -0
  148. package/src/cli/help.mjs +26 -0
  149. package/src/cli/init.mjs +4 -4
  150. package/src/cli/memory-entry.mjs +1 -0
  151. package/src/cli/memory-init.mjs +2 -0
  152. package/src/cli/plugin/index.mjs +11 -0
  153. package/src/cli/plugin/install.mjs +2 -1
  154. package/src/cli/rebuild.mjs +3 -1
  155. package/src/cli/startup.mjs +2 -1
  156. package/src/cli/update.mjs +19 -0
  157. package/src/core/automatic-brain-updates.mjs +67 -0
  158. package/src/core/brain-updates.mjs +21 -2
  159. package/src/core/cli-commands.mjs +71 -0
  160. package/src/core/cli-maintenance.mjs +45 -0
  161. package/src/core/cli-quick-reference.mjs +20 -0
  162. package/src/core/cli-semantic-index.mjs +722 -0
  163. package/src/core/context-compiler.mjs +27 -1
  164. package/src/core/crons.mjs +2 -13
  165. package/src/core/daemon-control.mjs +6 -4
  166. package/src/core/daemon-loop.mjs +12 -8
  167. package/src/core/default-plugins.mjs +5 -0
  168. package/src/core/global-brain-updates.mjs +39 -0
  169. package/src/core/mesh-brain-updates.mjs +30 -0
  170. package/src/core/meta-harness.mjs +108 -0
  171. package/src/core/plugin-dependencies.mjs +2 -1
  172. package/src/core/plugin-loader.mjs +2 -1
  173. package/src/core/plugin-runner.mjs +12 -4
  174. package/src/core/plugin-setup.mjs +20 -0
  175. package/src/core/plugin-store.mjs +22 -4
  176. package/src/core/plugin-tasks.mjs +1 -1
  177. package/src/core/project-brain.mjs +2 -0
  178. package/src/core/project-status.mjs +64 -0
  179. package/src/core/required-plugins.mjs +46 -0
  180. package/src/core/scaffold-plugins.mjs +17 -3
  181. package/src/core/source-brain-updates.mjs +47 -0
  182. package/src/core/startup-health.mjs +5 -3
  183. package/src/core/surface.mjs +44 -10
  184. package/src/server/routes/extension.mjs +53 -8
  185. package/templates/onboarding-interview.md +1 -1
  186. 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 source = fs.realpathSync(path.join(root, '.agent', 'skills', 'total-recall', 'plugins', id));
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 sha = digest(from);
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.copyFileSync(from, to); }
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 dispatcher = fs.readFileSync(cliEntry, 'utf8').match(/^const COMMANDS = \{([\s\S]*?)^\};/m)?.[1] || '';
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
- report.shared_ready = report.server.status === 'ready' && report.brain.status === 'ready' && report.daemon.status === 'running' && report.ssss.status === 'available';
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;
@@ -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: 2500 };
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
- lines.push(`- **${p.id}** v${p.manifest.version || 'unknown'} · ${pluginActivation(p).state} · \`${cmd}\` · ${p.manifest.cli?.subcommands?.length || 0} operations: ${desc}`);
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 header = bootstrap ? `# 🔴 MANDATORY FIRST STEP: TOTAL RECALL CLI & TASK ROUTING
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: 'commands', text: buildCommandsSection(listSurfaceCommands(commandDirsFor(skillsDir))) });
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
- return fileContent.slice(0, beginIdx) + DIRECTIVES_BEGIN + '\n' + rulesBlock + '\n' + DIRECTIVES_END + fileContent.slice(endIdx + DIRECTIVES_END.length);
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.trimEnd() + '\n\n' + DIRECTIVES_BEGIN + '\n' + rulesBlock + '\n' + DIRECTIVES_END + '\n';
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}\n${DIRECTIVES_BEGIN}\n${rulesBlock}\n${DIRECTIVES_END}\n`;
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 (_req, res) => {
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
- // Never inject PATs into packaged extension source. Pair from the extension
31
- // options page so secrets stay in extension-local storage.
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: extDir, stdio: ['ignore', 'pipe', 'ignore'] });
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', path.dirname(extDir), 'extension'], { stdio: ['ignore', 'pipe', 'ignore'] });
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