total-recall-brain 3.1.0

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 (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/bin/antigravity.mjs +212 -0
  4. package/bin/init.mjs +36 -0
  5. package/bin/oci-sniper.sh +69 -0
  6. package/bin/total-recall.mjs +229 -0
  7. package/docs/ARCHITECTURE.md +251 -0
  8. package/docs/IDE_INTEGRATION.md +112 -0
  9. package/package.json +71 -0
  10. package/scaffold/.agent/skills/total-recall/SKILL.md +195 -0
  11. package/scaffold/.agent/skills/total-recall/evals/evals.json +20 -0
  12. package/scaffold/.agent/skills/total-recall/memory-vault/concepts/cli-help-reference.md +145 -0
  13. package/scaffold/.agent/skills/total-recall/memory-vault/invariants/operating-instructions.md +75 -0
  14. package/scaffold/.agent/skills/total-recall/memory-vault/preferences/always-websearch-gap.md +16 -0
  15. package/scaffold/.agent/skills/total-recall/memory-vault/preferences/topic-research-sop.md +17 -0
  16. package/scaffold/.agent/skills/total-recall/references/api-reference.md +187 -0
  17. package/scaffold/.agent/skills/total-recall/references/architecture-reference.md +81 -0
  18. package/scaffold/.agent/skills/total-recall/references/cli-reference.md +103 -0
  19. package/scaffold/.agent/skills/total-recall/references/ssss-reference.md +88 -0
  20. package/scaffold/.agent/skills/total-recall/references/troubleshooting.md +68 -0
  21. package/scaffold/.agent/skills/total-recall/scripts/sync-repo.mjs +83 -0
  22. package/scaffold/.agent/skills/total-recall/skills/cli-agents/SKILL.md +122 -0
  23. package/scaffold/.agent/skills/total-recall/skills/cli-agents/agents.yml +27 -0
  24. package/scaffold/.agent/skills/total-recall/skills/cli-agents/references/antigravity.txt +20 -0
  25. package/scaffold/.agent/skills/total-recall/skills/cli-agents/references/claude.txt +58 -0
  26. package/scaffold/.agent/skills/total-recall/skills/cli-agents/references/codex.txt +81 -0
  27. package/scaffold/.agent/skills/total-recall/skills/cli-agents/references/gemini.txt +44 -0
  28. package/scaffold/.agent/skills/total-recall/skills/research/SKILL.md +337 -0
  29. package/scaffold/.agent/skills/total-recall/skills/skill/SKILL.md +281 -0
  30. package/scaffold/.agent/skills/total-recall/skills/skill/assets/skill-template/SKILL.md +20 -0
  31. package/scaffold/.agent/skills/total-recall/skills/skill/evals/evals.json +20 -0
  32. package/scaffold/.agent/skills/total-recall/skills/skill/hooks/on-skill-add.sh +14 -0
  33. package/scaffold/.agent/skills/total-recall/skills/skill/hooks/on-skill-delete.sh +14 -0
  34. package/scaffold/.agent/skills/total-recall/skills/skill/learnings/.gitkeep +0 -0
  35. package/scaffold/.agent/skills/total-recall/skills/skill/references/.gitkeep +0 -0
  36. package/scaffold/.agent/skills/total-recall/skills/skill/scripts/.gitkeep +0 -0
  37. package/scaffold/.agent/skills/total-recall/skills/skill/scripts/create-skill.sh +65 -0
  38. package/scaffold/.agent/skills/total-recall/skills/skill/scripts/enforce-skill-optimization.mjs +135 -0
  39. package/scaffold/.agent/skills/total-recall/skills/skill/scripts/find-skills.mjs +124 -0
  40. package/scaffold/.agent/skills/total-recall/skills/skill/scripts/scan-skill.mjs +207 -0
  41. package/scaffold/.agent/skills/total-recall/skills/skill/scripts/skill-watcher.mjs +64 -0
  42. package/scaffold/.agent/skills/total-recall/skills/skill/subagents/audit-all-skills.md +62 -0
  43. package/scaffold/.agent/skills/total-recall/skills/ssss/SKILL.md +27 -0
  44. package/scaffold/.agent/skills/total-recall/skills/ssss/evals/evals.json +17 -0
  45. package/scaffold/.agent/skills/total-recall/skills/ssss/references/ssss-spec.md +117 -0
  46. package/scaffold/.agent/skills/total-recall/skills/ssss/scripts/validate-schema.mjs +54 -0
  47. package/scaffold/.agent/skills/total-recall/skills/ssss/subagents/audit-ssss-node.md +9 -0
  48. package/scaffold/.agent/skills/total-recall/subagents/total-recall-diagnostician.md +13 -0
  49. package/src/cli/agent-dir.mjs +105 -0
  50. package/src/cli/backup.mjs +313 -0
  51. package/src/cli/brain.mjs +224 -0
  52. package/src/cli/chat.mjs +74 -0
  53. package/src/cli/config.mjs +247 -0
  54. package/src/cli/connect.mjs +872 -0
  55. package/src/cli/daemon.mjs +228 -0
  56. package/src/cli/deploy-ui.mjs +1793 -0
  57. package/src/cli/deploy.mjs +1726 -0
  58. package/src/cli/doctor.mjs +254 -0
  59. package/src/cli/dream.mjs +80 -0
  60. package/src/cli/forget.mjs +92 -0
  61. package/src/cli/friction.mjs +21 -0
  62. package/src/cli/generate-pat.mjs +65 -0
  63. package/src/cli/hash-password.mjs +22 -0
  64. package/src/cli/help.mjs +153 -0
  65. package/src/cli/import-rules.mjs +102 -0
  66. package/src/cli/ingest/index.mjs +318 -0
  67. package/src/cli/ingest/parsers/chrome-bookmarks.mjs +238 -0
  68. package/src/cli/ingest/parsers/google-keep.mjs +147 -0
  69. package/src/cli/ingest/parsers/search-history.mjs +142 -0
  70. package/src/cli/ingest/parsers/youtube-history.mjs +181 -0
  71. package/src/cli/ingest/utils/dedup.mjs +69 -0
  72. package/src/cli/ingest/utils/takeout-walker.mjs +101 -0
  73. package/src/cli/ingest.mjs +102 -0
  74. package/src/cli/init.mjs +736 -0
  75. package/src/cli/integration-dispatcher.mjs +128 -0
  76. package/src/cli/lint.mjs +166 -0
  77. package/src/cli/map.mjs +194 -0
  78. package/src/cli/migrate.mjs +87 -0
  79. package/src/cli/rebuild.mjs +88 -0
  80. package/src/cli/recall.mjs +219 -0
  81. package/src/cli/relay.mjs +518 -0
  82. package/src/cli/remember.mjs +306 -0
  83. package/src/cli/research.mjs +381 -0
  84. package/src/cli/reset-password.mjs +75 -0
  85. package/src/cli/restore.mjs +135 -0
  86. package/src/cli/setup.mjs +616 -0
  87. package/src/cli/share.mjs +180 -0
  88. package/src/cli/skill.mjs +313 -0
  89. package/src/cli/snapshot.mjs +108 -0
  90. package/src/cli/start.mjs +63 -0
  91. package/src/cli/status.mjs +296 -0
  92. package/src/cli/sync.mjs +383 -0
  93. package/src/cli/uninstall.mjs +613 -0
  94. package/src/cli/upgrade.mjs +141 -0
  95. package/src/cli/wizard.html +3682 -0
  96. package/src/core/blackboard.mjs +54 -0
  97. package/src/core/clarity-rewriter.mjs +770 -0
  98. package/src/core/conclusion-writer.mjs +259 -0
  99. package/src/core/config.mjs +277 -0
  100. package/src/core/conflict-detector.mjs +536 -0
  101. package/src/core/crypto.mjs +107 -0
  102. package/src/core/daemon-control.mjs +158 -0
  103. package/src/core/daemon-loop.mjs +364 -0
  104. package/src/core/dream.mjs +347 -0
  105. package/src/core/drift-detector.mjs +143 -0
  106. package/src/core/embeddings.mjs +632 -0
  107. package/src/core/emergency-alerts.mjs +137 -0
  108. package/src/core/evolution.mjs +107 -0
  109. package/src/core/fact-seeker.mjs +1602 -0
  110. package/src/core/friction.mjs +95 -0
  111. package/src/core/import-rules.mjs +308 -0
  112. package/src/core/inference-engine.mjs +367 -0
  113. package/src/core/logger.mjs +75 -0
  114. package/src/core/memory-layers.mjs +96 -0
  115. package/src/core/migrate.mjs +116 -0
  116. package/src/core/notifications.mjs +77 -0
  117. package/src/core/operation-validator.mjs +272 -0
  118. package/src/core/optimizer.mjs +330 -0
  119. package/src/core/pattern_detector.mjs +113 -0
  120. package/src/core/post-mortem.mjs +413 -0
  121. package/src/core/protect-instructions.mjs +148 -0
  122. package/src/core/quick-capture.mjs +145 -0
  123. package/src/core/research-queue.mjs +431 -0
  124. package/src/core/research.mjs +461 -0
  125. package/src/core/runtime.mjs +515 -0
  126. package/src/core/sandbox.mjs +163 -0
  127. package/src/core/scheduler.mjs +569 -0
  128. package/src/core/schema.mjs +333 -0
  129. package/src/core/search.mjs +219 -0
  130. package/src/core/semantic-index.mjs +276 -0
  131. package/src/core/session-watcher.mjs +850 -0
  132. package/src/core/snapshot.mjs +133 -0
  133. package/src/core/source-adapters.mjs +1038 -0
  134. package/src/core/steering.mjs +137 -0
  135. package/src/core/surface.mjs +444 -0
  136. package/src/core/task_runner.mjs +125 -0
  137. package/src/core/tts.mjs +142 -0
  138. package/src/core/usage-tracker.mjs +349 -0
  139. package/src/core/vault-cache.mjs +76 -0
  140. package/src/core/vault.mjs +273 -0
  141. package/src/core/watchdog.mjs +236 -0
  142. package/src/server/api.mjs +944 -0
  143. package/src/server/auth.mjs +410 -0
  144. package/src/server/index.mjs +868 -0
  145. package/src/server/keys.mjs +181 -0
  146. package/src/server/rest.mjs +1652 -0
  147. package/src/server/route-manifest.json +77 -0
  148. package/src/server/routes/_shared.mjs +93 -0
  149. package/src/server/routes/keys.mjs +55 -0
  150. package/src/server/routes/memory.mjs +249 -0
  151. package/src/server/routes/sessions.mjs +336 -0
  152. package/src/server/routes/share.mjs +138 -0
  153. package/src/server/tools.mjs +618 -0
  154. package/templates/Caddyfile +37 -0
  155. package/templates/com.totalrecall.backup.plist +42 -0
  156. package/templates/com.totalrecall.daemon.plist +40 -0
  157. package/templates/com.totalrecall.duckdns.plist +23 -0
  158. package/templates/com.totalrecall.server.plist +46 -0
  159. package/templates/default-config/budget.yml +6 -0
  160. package/templates/default-config/runtime.yml +11 -0
  161. package/templates/default-config/security.yml +53 -0
  162. package/templates/default-config/voice.yml +34 -0
  163. package/templates/obsidian-queries/conflicts.md +26 -0
  164. package/templates/obsidian-queries/daily.md +17 -0
  165. package/templates/obsidian-queries/index.md +38 -0
  166. package/templates/obsidian-queries/skills.md +14 -0
  167. package/templates/onboarding-interview.md +72 -0
  168. package/templates/skill_engineering.md +40 -0
  169. package/templates/total-recall-daemon.service +21 -0
  170. package/templates/total-recall-server.service +20 -0
@@ -0,0 +1,180 @@
1
+ import crypto from 'node:crypto';
2
+ import { parseLayerFlag, resolveBrainDir, getBothBrains, defaultLayerForCategory } from './agent-dir.mjs';
3
+ import { writeNode, createMemoryNode } from '../core/vault.mjs';
4
+ import { addToQueue } from '../core/research-queue.mjs';
5
+ import { compileSurface } from '../core/surface.mjs';
6
+ import path from 'node:path';
7
+
8
+ function printHelp() {
9
+ console.log(`
10
+ total-recall share — Share a URL or text snippet to the brain
11
+
12
+ Usage: total-recall share [url] [options]
13
+
14
+ Arguments:
15
+ url URL to share (optional if --text is provided)
16
+
17
+ Options:
18
+ --text, -x <text> Text excerpt to remember
19
+ --action, -a <action> remember | research | auto (default: auto)
20
+ --tags, -t <comma-list> Comma-separated tags
21
+ --brain, -b <brainId> Target brain ID
22
+ --title <title> Custom title for the memory node
23
+ --global Save to global brain
24
+ --project Save to project brain
25
+
26
+ Auto-routing heuristic (when action is 'auto'):
27
+ - URL + no text → queues research project
28
+ - Text < 500 chars → saves as fact
29
+ - Text >= 500 chars → saves as concept
30
+ - URL + text → saves as fact/concept with URL citation
31
+
32
+ Examples:
33
+ npx total-recall share "https://example.com"
34
+ npx total-recall share "https://example.com" --text "Key insight from the article"
35
+ npx total-recall share --text "Important fact to remember" --tags "work,notes"
36
+ npx total-recall share "https://example.com" --action research
37
+ `);
38
+ }
39
+
40
+ /**
41
+ * Resolve the effective action from the auto-routing heuristic.
42
+ */
43
+ function resolveAction({ url, text, action }) {
44
+ if (action && action !== 'auto') {
45
+ if (action === 'remember') {
46
+ const category = text && text.length >= 500 ? 'concepts' : 'facts';
47
+ return { action: 'remember', category };
48
+ }
49
+ return { action, category: null };
50
+ }
51
+
52
+ if (url && !text) {
53
+ return { action: 'research', category: null };
54
+ }
55
+ if (text && text.length >= 500) {
56
+ return { action: 'remember', category: 'concepts' };
57
+ }
58
+ if (text) {
59
+ return { action: 'remember', category: 'facts' };
60
+ }
61
+ if (url) {
62
+ return { action: 'research', category: null };
63
+ }
64
+ return { action: 'remember', category: 'facts' };
65
+ }
66
+
67
+ export default async function share(args) {
68
+ const { layer: explicitLayer, remainingArgs } = parseLayerFlag(args);
69
+
70
+ // Parse positional URL and options
71
+ let url = null;
72
+ let text = null;
73
+ let action = 'auto';
74
+ let tags = [];
75
+ let brainId = null;
76
+ let title = null;
77
+
78
+ for (let i = 0; i < remainingArgs.length; i++) {
79
+ const arg = remainingArgs[i];
80
+ const val = remainingArgs[i + 1];
81
+
82
+ if (arg === '--help' || arg === '-h') {
83
+ printHelp();
84
+ return;
85
+ } else if (arg === '--text' || arg === '-x') {
86
+ if (val) { text = val; i++; }
87
+ } else if (arg === '--action' || arg === '-a') {
88
+ if (val) { action = val.toLowerCase(); i++; }
89
+ } else if (arg === '--tags' || arg === '-t') {
90
+ if (val) { tags = val.split(',').map(t => t.trim()); i++; }
91
+ } else if (arg === '--brain' || arg === '-b') {
92
+ if (val) { brainId = val; i++; }
93
+ } else if (arg === '--title') {
94
+ if (val) { title = val; i++; }
95
+ } else if (!arg.startsWith('-') && !url) {
96
+ // First positional arg is the URL
97
+ url = arg;
98
+ }
99
+ }
100
+
101
+ if (!url && !text) {
102
+ printHelp();
103
+ return;
104
+ }
105
+
106
+ const { action: resolvedAction, category } = resolveAction({ url, text, action });
107
+
108
+ if (resolvedAction === 'research') {
109
+ const topic = title || url || 'Untitled research';
110
+ const item = addToQueue({
111
+ topic,
112
+ priority: 'medium',
113
+ notes: text || '',
114
+ });
115
+ console.log(` ✅ Queued research: "${topic}" (id: ${item.id})`);
116
+ return;
117
+ }
118
+
119
+ // action === 'remember'
120
+ let layer = explicitLayer;
121
+ if (layer === 'auto') {
122
+ layer = defaultLayerForCategory(category || 'facts');
123
+ const project = getBothBrains().project;
124
+ if (layer === 'project' && !project) {
125
+ layer = 'global';
126
+ }
127
+ }
128
+
129
+ const resolvedBrainDir = resolveBrainDir(layer);
130
+ const vaultDir = path.join(resolvedBrainDir, 'memory-vault');
131
+ const skillsDir = path.join(resolvedBrainDir, '..', '..');
132
+ const derivedDir = path.join(resolvedBrainDir, 'memory-derived');
133
+ const instructionsFile = path.join(skillsDir, '..', 'INSTRUCTIONS.md');
134
+
135
+ const slugBase = category || 'facts';
136
+ const slug = `${slugBase}-${crypto.randomBytes(4).toString('hex')}`;
137
+ const nodeTitle = title || (text ? text.slice(0, 80) : url || 'Untitled share');
138
+
139
+ const node = createMemoryNode({
140
+ slug,
141
+ title: nodeTitle,
142
+ category: category || 'facts',
143
+ content: text || url || '',
144
+ });
145
+
146
+ node.source.type = 'share-cli';
147
+
148
+ if (tags.length > 0) {
149
+ node.tags = tags;
150
+ }
151
+
152
+ if (url) {
153
+ node.x_citations = [{
154
+ url,
155
+ title: title || url,
156
+ source: 'share-to-brain',
157
+ published: node.created,
158
+ relevance: 1.0,
159
+ accessed: node.created,
160
+ }];
161
+ }
162
+
163
+ writeNode(node, vaultDir);
164
+ const layerLabel = layer === 'project' ? '[project]' : '[global]';
165
+ console.log(` ✅ Saved as ${category || 'facts'} ${layerLabel}: ${category || 'facts'}/${slug}.md`);
166
+
167
+ // Recompile surfaces
168
+ console.log(' ⏳ Recompiling active memory surfaces and indexes...');
169
+ try {
170
+ const compileResult = await compileSurface({
171
+ vaultDir,
172
+ skillsDir,
173
+ derivedDir,
174
+ instructionsFile,
175
+ });
176
+ console.log(` ✅ Recompilation successful! Processed ${compileResult.nodesProcessed} SSSS nodes.`);
177
+ } catch (err) {
178
+ console.warn(` ⚠️ Memory saved, but surface recompilation failed: ${err.message}`);
179
+ }
180
+ }
@@ -0,0 +1,313 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { spawnSync } from 'node:child_process';
5
+
6
+ // Helper to resolve agent directories dynamically
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ const ROOT = path.resolve(__dirname, '../../');
9
+ const agentDir = path.join(ROOT, '.agent');
10
+ const skillsDir = path.join(agentDir, 'skills');
11
+
12
+ // Direct dynamic imports for scripts layer to preserve environment paths
13
+ const scriptsPath = path.join(agentDir, 'skills', 'total-recall', 'skills', 'skill', 'scripts');
14
+
15
+ function printHelp() {
16
+ console.log(`
17
+ total-recall skill — Sovereign AI Capability Registry & Manager
18
+
19
+ Usage: total-recall skill <command> [options]
20
+
21
+ Commands:
22
+ find <query> Search skills.sh registry sorted by absolute installs rating
23
+ install <package> Download/load a skill, run static security audit, and compile shims (alias: load, add)
24
+ create <name> Scaffold a new SSSS-compliant skill folder structure (alias: new)
25
+ edit <name> Open the local skill's SKILL.md file in your preferred editor
26
+ scan <name> Run security audit scan on an installed local skill directory
27
+ list List all locally active parent skills and nested sub-skills (alias: ls)
28
+ remove <name> Safely delete a skill package and hot-recompile shims (alias: rm)
29
+
30
+ Options:
31
+ --help, -h Show this help menu
32
+
33
+ Examples:
34
+ npx total-recall skill find git
35
+ npx total-recall skill load github/awesome-copilot@prd
36
+ npx total-recall skill create my-awesome-skill
37
+ npx total-recall skill edit my-awesome-skill
38
+ npx total-recall skill scan total-recall
39
+ `);
40
+ }
41
+
42
+ export default async function skillCli(args) {
43
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
44
+ printHelp();
45
+ return;
46
+ }
47
+
48
+ const command = args[0];
49
+
50
+ if (command === 'find') {
51
+ const query = args[1];
52
+ if (!query) {
53
+ console.error('❌ You must provide a search query.');
54
+ console.error('Usage: npx total-recall skill find <query>');
55
+ process.exit(1);
56
+ }
57
+
58
+ try {
59
+ const { searchAndSort } = await import('../../.agent/skills/total-recall/skills/skill/scripts/find-skills.mjs');
60
+ const results = searchAndSort(query);
61
+
62
+ if (results.length === 0) {
63
+ console.log('\nℹ️ No matching skills found on skills.sh.');
64
+ return;
65
+ }
66
+
67
+ console.log('\n✨ --- SKILLS.SH REGISTRY SEARCH RESULTS (Sorted by Installs) --- ✨\n');
68
+
69
+ const nameHeader = 'SKILL PACKAGE';
70
+ const installsHeader = 'INSTALLS';
71
+ const urlHeader = 'REGISTRY URL';
72
+
73
+ let maxNameLen = nameHeader.length;
74
+ let maxInstallsLen = installsHeader.length;
75
+
76
+ for (const r of results) {
77
+ if (r.name.length > maxNameLen) maxNameLen = r.name.length;
78
+ if (r.installsStr.length > maxInstallsLen) maxInstallsLen = r.installsStr.length;
79
+ }
80
+
81
+ const divider = '-'.repeat(maxNameLen + maxInstallsLen + 35);
82
+ console.log(divider);
83
+ console.log(
84
+ `${nameHeader.padEnd(maxNameLen)} | ${installsHeader.padEnd(maxInstallsLen)} | ${urlHeader}`
85
+ );
86
+ console.log(divider);
87
+
88
+ for (const r of results) {
89
+ console.log(
90
+ `${r.name.padEnd(maxNameLen)} | ${r.installsStr.padEnd(maxInstallsLen)} | ${r.url}`
91
+ );
92
+ }
93
+
94
+ console.log(divider);
95
+ console.log(`💡 Tip: To install any skill, run: npx total-recall skill install <package-name>\n`);
96
+ } catch (err) {
97
+ console.error('❌ Error executing search:', err.message);
98
+ process.exit(1);
99
+ }
100
+ }
101
+
102
+ else if (command === 'install' || command === 'add' || command === 'load') {
103
+ const pkg = args[1];
104
+ if (!pkg) {
105
+ console.error('❌ You must provide a skill package to load.');
106
+ console.error('Usage: npx total-recall skill load <owner/repo@skill>');
107
+ process.exit(1);
108
+ }
109
+
110
+ try {
111
+ const { installSkill } = await import('../../.agent/skills/total-recall/skills/skill/scripts/install-skill.mjs');
112
+ const res = installSkill(pkg);
113
+ if (!res.success) {
114
+ process.exit(1);
115
+ }
116
+ } catch (err) {
117
+ console.error('❌ Loading skill failed:', err.message);
118
+ process.exit(1);
119
+ }
120
+ }
121
+
122
+ else if (command === 'scan') {
123
+ const target = args[1];
124
+ if (!target) {
125
+ console.error('❌ You must specify a local skill directory to scan.');
126
+ console.error('Usage: npx total-recall skill scan <skill-name>');
127
+ process.exit(1);
128
+ }
129
+
130
+ const targetDir = path.resolve(skillsDir, target);
131
+
132
+ try {
133
+ const { runScan } = await import('../../.agent/skills/total-recall/skills/skill/scripts/scan-skill.mjs');
134
+ const result = runScan(targetDir);
135
+ process.exit(result.success ? 0 : 1);
136
+ } catch (err) {
137
+ console.error('❌ Audit execution failed:', err.message);
138
+ process.exit(1);
139
+ }
140
+ }
141
+
142
+ else if (command === 'list' || command === 'ls') {
143
+ if (!fs.existsSync(skillsDir)) {
144
+ console.log('No skills registry directory initialized yet.');
145
+ return;
146
+ }
147
+
148
+ const dirs = fs.readdirSync(skillsDir).filter(d =>
149
+ fs.statSync(path.join(skillsDir, d)).isDirectory()
150
+ );
151
+
152
+ if (dirs.length === 0) {
153
+ console.log('No installed skills found in the active brain.');
154
+ return;
155
+ }
156
+
157
+ console.log('\n📦 Installed Sovereign Skills:');
158
+ console.log('------------------------------------------------------------');
159
+ for (const d of dirs) {
160
+ const skillPath = path.join(skillsDir, d);
161
+ const subSkillsPath = path.join(skillPath, 'skills');
162
+ let subSkills = [];
163
+
164
+ if (fs.existsSync(subSkillsPath) && fs.statSync(subSkillsPath).isDirectory()) {
165
+ subSkills = fs.readdirSync(subSkillsPath).filter(sd =>
166
+ fs.statSync(path.join(subSkillsPath, sd)).isDirectory()
167
+ );
168
+ }
169
+
170
+ console.log(` • ${d.padEnd(20)} [Universal Skill Package]`);
171
+ if (subSkills.length > 0) {
172
+ console.log(` └ Sub-skills: ${subSkills.map(s => `🧩 ${s}`).join(', ')}`);
173
+ }
174
+ }
175
+ console.log('------------------------------------------------------------\n');
176
+ }
177
+
178
+ else if (command === 'remove' || command === 'rm') {
179
+ const target = args[1];
180
+ if (!target) {
181
+ console.error('❌ You must specify a skill name to remove.');
182
+ console.error('Usage: npx total-recall skill remove <skill-name>');
183
+ process.exit(1);
184
+ }
185
+
186
+ const targetDir = path.join(skillsDir, target);
187
+ if (!fs.existsSync(targetDir)) {
188
+ console.error(`❌ Skill "${target}" not found at ${targetDir}`);
189
+ process.exit(1);
190
+ }
191
+
192
+ console.log(`🗑️ Removing skill "${target}"...`);
193
+ fs.rmSync(targetDir, { recursive: true, force: true });
194
+ console.log('✅ Skill files deleted successfully.');
195
+
196
+ // Hot-recompile shims to strip the removed rules
197
+ console.log('🔄 Hot-recompiling active shims to reflect changes...');
198
+ const child = spawnSync('npx', ['total-recall', 'compile'], {
199
+ encoding: 'utf8',
200
+ cwd: ROOT
201
+ });
202
+
203
+ if (child.error || child.status !== 0) {
204
+ console.warn('⚠️ Shim hot-recompilation returned warning.');
205
+ } else {
206
+ console.log('🚀 Hot-recompilation successful. Skill is fully offline.');
207
+ }
208
+ }
209
+
210
+ else if (command === 'create' || command === 'new') {
211
+ const target = args[1];
212
+ if (!target) {
213
+ console.error('❌ You must specify a skill name to create.');
214
+ console.error('Usage: npx total-recall skill create <skill-name>');
215
+ process.exit(1);
216
+ }
217
+
218
+ if (!/^[a-z][a-z0-9-]{1,63}$/.test(target)) {
219
+ console.error('❌ Invalid skill name. Must be lowercase kebab-case (e.g. my-awesome-skill).');
220
+ process.exit(1);
221
+ }
222
+
223
+ const newSkillDir = path.join(skillsDir, target);
224
+ if (fs.existsSync(newSkillDir)) {
225
+ console.error(`❌ Skill directory already exists: ${newSkillDir}`);
226
+ process.exit(1);
227
+ }
228
+
229
+ console.log(`📁 Scaffolding skill folder structure for "${target}"...`);
230
+ fs.mkdirSync(newSkillDir, { recursive: true });
231
+
232
+ const folders = ['scripts', 'references', 'evals', 'subagents'];
233
+ for (const f of folders) {
234
+ fs.mkdirSync(path.join(newSkillDir, f), { recursive: true });
235
+ fs.writeFileSync(path.join(newSkillDir, f, '.gitkeep'), '', 'utf8');
236
+ }
237
+
238
+ // Write template SKILL.md
239
+ const skillMdContent = `---
240
+ type: skill
241
+ name: ${target}
242
+ description: "Use this skill when [TRIGGER CONDITION]. MANDATORY: You MUST read the full SKILL.md file before executing."
243
+ schema_version: 2
244
+ ---
245
+
246
+ # ${target.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')} Skill
247
+
248
+ Provide a high-level explanation of the skill's capabilities and context.
249
+
250
+ ## Core Directives
251
+
252
+ 1. Step-by-step procedural instruction
253
+ 2. Concrete action rule
254
+ 3. Pitfalls to avoid
255
+
256
+ ## References
257
+
258
+ * Core specifications or API documentation
259
+ `;
260
+ fs.writeFileSync(path.join(newSkillDir, 'SKILL.md'), skillMdContent, 'utf8');
261
+
262
+ // Write template evals.json
263
+ const evalsJsonContent = [
264
+ {
265
+ "name": "frontmatter-has-name",
266
+ "assertion": "SKILL.md YAML frontmatter contains a 'name' field",
267
+ "severity": "error"
268
+ },
269
+ {
270
+ "name": "description-is-trigger-optimized",
271
+ "assertion": "description field starts with 'Use this skill when'",
272
+ "severity": "warning"
273
+ },
274
+ {
275
+ "name": "no-empty-directories",
276
+ "assertion": "All required directories contain at least one non-.gitkeep file",
277
+ "severity": "error"
278
+ }
279
+ ];
280
+ fs.writeFileSync(path.join(newSkillDir, 'evals', 'evals.json'), JSON.stringify(evalsJsonContent, null, 2), 'utf8');
281
+
282
+ console.log(`✨ Skill "${target}" successfully created and SSSS-scaffolded!`);
283
+ console.log(`📍 Path: ${newSkillDir}`);
284
+ console.log(`💡 Next steps: edit the SKILL.md and add your automation scripts!`);
285
+ }
286
+
287
+ else if (command === 'edit') {
288
+ const target = args[1];
289
+ if (!target) {
290
+ console.error('❌ You must specify a skill name to edit.');
291
+ console.error('Usage: npx total-recall skill edit <skill-name>');
292
+ process.exit(1);
293
+ }
294
+
295
+ const skillPath = path.join(skillsDir, target, 'SKILL.md');
296
+ if (!fs.existsSync(skillPath)) {
297
+ console.error(`❌ Skill "${target}" not found at ${skillPath}`);
298
+ process.exit(1);
299
+ }
300
+
301
+ const editor = process.env.EDITOR || 'vi';
302
+ console.log(`📝 Opening skill "${target}" in ${editor}...`);
303
+ const { spawnSync: ss } = await import('node:child_process');
304
+ const child = ss(editor, [skillPath], { stdio: 'inherit' });
305
+ process.exit(child.status || 0);
306
+ }
307
+
308
+ else {
309
+ console.error(`❌ Unknown skill command: ${command}`);
310
+ printHelp();
311
+ process.exit(1);
312
+ }
313
+ }
@@ -0,0 +1,108 @@
1
+ import { createSnapshot, listSnapshots, rollbackVault } from '../core/snapshot.mjs';
2
+ import { resolveAgentDir, resolveBrainDir } from './agent-dir.mjs';
3
+ import { compileSurface } from '../core/surface.mjs';
4
+ import path from 'path';
5
+
6
+ /**
7
+ * total-recall snapshot [command]
8
+ *
9
+ * Commands:
10
+ * create [reason] Create a new point-in-time snapshot of the vault
11
+ * list List all available snapshots
12
+ * rollback <id> Restore the vault to a specific snapshot
13
+ */
14
+
15
+ function printHelp() {
16
+ console.log(`
17
+ total-recall snapshot — VFS Point-in-Time Recovery
18
+
19
+ Usage: total-recall snapshot <command> [options]
20
+
21
+ Commands:
22
+ create [reason] Create a fast local snapshot of the memory-vault
23
+ list List all available snapshots
24
+ rollback <id> Restore the vault to a specific snapshot ID
25
+
26
+ Options:
27
+ --help, -h Show this help
28
+ `);
29
+ }
30
+
31
+ export default async function snapshotCli(args) {
32
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
33
+ printHelp();
34
+ return;
35
+ }
36
+
37
+ const command = args[0];
38
+
39
+ if (command === 'create') {
40
+ const reason = args.slice(1).join(' ') || 'manual';
41
+ console.log('📸 Creating snapshot...');
42
+ const result = createSnapshot(reason);
43
+ if (result.success) {
44
+ console.log(`✅ Snapshot created successfully!`);
45
+ console.log(` ID: ${result.snapshot_id}`);
46
+ console.log(` Path: ${result.path}`);
47
+ console.log(` Reason: ${reason}`);
48
+ } else {
49
+ console.error(`❌ Failed to create snapshot: ${result.error}`);
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ else if (command === 'list') {
55
+ const snaps = listSnapshots();
56
+ if (snaps.length === 0) {
57
+ console.log('No snapshots found.');
58
+ return;
59
+ }
60
+ console.log('📸 Available Snapshots:');
61
+ console.log('------------------------------------------------------------');
62
+ for (const snap of snaps) {
63
+ const date = new Date(snap.created_at).toLocaleString();
64
+ console.log(`ID: ${snap.snapshot_id}`);
65
+ console.log(`Date: ${date}`);
66
+ console.log(`Reason: ${snap.reason}`);
67
+ console.log('------------------------------------------------------------');
68
+ }
69
+ }
70
+
71
+ else if (command === 'rollback') {
72
+ const id = args[1];
73
+ if (!id) {
74
+ console.error('❌ You must provide a snapshot ID to rollback to.');
75
+ console.error('Usage: total-recall snapshot rollback <id>');
76
+ process.exit(1);
77
+ }
78
+
79
+ console.log(`⚠️ Rolling back memory-vault to snapshot: ${id}`);
80
+ console.log(` (A pre-rollback safety snapshot will be created automatically)`);
81
+
82
+ const result = rollbackVault(id);
83
+ if (result.success) {
84
+ console.log(`✅ Rollback successful.`);
85
+ console.log(`🔄 Recompiling derived indexes from restored vault...`);
86
+
87
+ const agentDir = resolveAgentDir();
88
+ const brainDir = resolveBrainDir();
89
+ await compileSurface({
90
+ vaultDir: path.join(brainDir, 'memory-vault'),
91
+ skillsDir: path.join(agentDir, 'skills'),
92
+ derivedDir: path.join(brainDir, 'memory-derived'),
93
+ instructionsFile: path.join(agentDir, 'INSTRUCTIONS.md')
94
+ });
95
+
96
+ console.log(`✅ Vault restoration and projection rebuild complete.`);
97
+ } else {
98
+ console.error(`❌ Rollback failed: ${result.error}`);
99
+ process.exit(1);
100
+ }
101
+ }
102
+
103
+ else {
104
+ console.error(`❌ Unknown snapshot command: ${command}`);
105
+ printHelp();
106
+ process.exit(1);
107
+ }
108
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * total-recall start
3
+ *
4
+ * Start the brain server in the foreground.
5
+ * This is the same as running `node src/server/index.mjs` but works correctly
6
+ * regardless of install method (npx, global npm, local clone).
7
+ *
8
+ * Usage:
9
+ * npx total-recall start [--port 3000] [--host 127.0.0.1]
10
+ */
11
+
12
+ import { spawnSync } from 'node:child_process';
13
+ import path from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+ import { port as configPort, host as configHost, nodeEnv } from '../core/config.mjs';
16
+
17
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
+ const SERVER = path.join(__dirname, '..', 'server', 'index.mjs');
19
+
20
+ export default function start(args) {
21
+ const port = (() => {
22
+ const i = args.indexOf('--port');
23
+ return i !== -1 ? args[i + 1] : String(configPort);
24
+ })();
25
+ const host = (() => {
26
+ const i = args.indexOf('--host');
27
+ return i !== -1 ? args[i + 1] : configHost;
28
+ })();
29
+
30
+ if (args.includes('--help') || args.includes('-h')) {
31
+ console.log(`
32
+ total-recall start — Start the brain server
33
+
34
+ Usage: total-recall start [options]
35
+
36
+ Options:
37
+ --port <port> Port to listen on (default: 3000, or \$PORT)
38
+ --host <host> Host to bind to (default: 127.0.0.1, or \$HOST)
39
+ --help Show this help
40
+
41
+ The server reads config from ~/.agent/config/security.yml.
42
+ Use Cloudflare or Caddy in front for HTTPS when accessing remotely.
43
+ `);
44
+ return;
45
+ }
46
+
47
+ console.log(` ⚡ Starting Total Recall brain server on ${host}:${port}`);
48
+ console.log(` Press Ctrl+C to stop.\n`);
49
+
50
+ const result = spawnSync(process.execPath, [SERVER], {
51
+ stdio: 'inherit',
52
+ env: {
53
+ ...process.env,
54
+ PORT: port,
55
+ HOST: host,
56
+ NODE_ENV: nodeEnv,
57
+ },
58
+ });
59
+
60
+ if (result.status !== 0) {
61
+ process.exit(result.status ?? 1);
62
+ }
63
+ }