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.
- package/LICENSE +21 -0
- package/README.md +173 -0
- package/bin/antigravity.mjs +212 -0
- package/bin/init.mjs +36 -0
- package/bin/oci-sniper.sh +69 -0
- package/bin/total-recall.mjs +229 -0
- package/docs/ARCHITECTURE.md +251 -0
- package/docs/IDE_INTEGRATION.md +112 -0
- package/package.json +71 -0
- package/scaffold/.agent/skills/total-recall/SKILL.md +195 -0
- package/scaffold/.agent/skills/total-recall/evals/evals.json +20 -0
- package/scaffold/.agent/skills/total-recall/memory-vault/concepts/cli-help-reference.md +145 -0
- package/scaffold/.agent/skills/total-recall/memory-vault/invariants/operating-instructions.md +75 -0
- package/scaffold/.agent/skills/total-recall/memory-vault/preferences/always-websearch-gap.md +16 -0
- package/scaffold/.agent/skills/total-recall/memory-vault/preferences/topic-research-sop.md +17 -0
- package/scaffold/.agent/skills/total-recall/references/api-reference.md +187 -0
- package/scaffold/.agent/skills/total-recall/references/architecture-reference.md +81 -0
- package/scaffold/.agent/skills/total-recall/references/cli-reference.md +103 -0
- package/scaffold/.agent/skills/total-recall/references/ssss-reference.md +88 -0
- package/scaffold/.agent/skills/total-recall/references/troubleshooting.md +68 -0
- package/scaffold/.agent/skills/total-recall/scripts/sync-repo.mjs +83 -0
- package/scaffold/.agent/skills/total-recall/skills/cli-agents/SKILL.md +122 -0
- package/scaffold/.agent/skills/total-recall/skills/cli-agents/agents.yml +27 -0
- package/scaffold/.agent/skills/total-recall/skills/cli-agents/references/antigravity.txt +20 -0
- package/scaffold/.agent/skills/total-recall/skills/cli-agents/references/claude.txt +58 -0
- package/scaffold/.agent/skills/total-recall/skills/cli-agents/references/codex.txt +81 -0
- package/scaffold/.agent/skills/total-recall/skills/cli-agents/references/gemini.txt +44 -0
- package/scaffold/.agent/skills/total-recall/skills/research/SKILL.md +337 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/SKILL.md +281 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/assets/skill-template/SKILL.md +20 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/evals/evals.json +20 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/hooks/on-skill-add.sh +14 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/hooks/on-skill-delete.sh +14 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/learnings/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/references/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/scripts/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/scripts/create-skill.sh +65 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/scripts/enforce-skill-optimization.mjs +135 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/scripts/find-skills.mjs +124 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/scripts/scan-skill.mjs +207 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/scripts/skill-watcher.mjs +64 -0
- package/scaffold/.agent/skills/total-recall/skills/skill/subagents/audit-all-skills.md +62 -0
- package/scaffold/.agent/skills/total-recall/skills/ssss/SKILL.md +27 -0
- package/scaffold/.agent/skills/total-recall/skills/ssss/evals/evals.json +17 -0
- package/scaffold/.agent/skills/total-recall/skills/ssss/references/ssss-spec.md +117 -0
- package/scaffold/.agent/skills/total-recall/skills/ssss/scripts/validate-schema.mjs +54 -0
- package/scaffold/.agent/skills/total-recall/skills/ssss/subagents/audit-ssss-node.md +9 -0
- package/scaffold/.agent/skills/total-recall/subagents/total-recall-diagnostician.md +13 -0
- package/src/cli/agent-dir.mjs +105 -0
- package/src/cli/backup.mjs +313 -0
- package/src/cli/brain.mjs +224 -0
- package/src/cli/chat.mjs +74 -0
- package/src/cli/config.mjs +247 -0
- package/src/cli/connect.mjs +872 -0
- package/src/cli/daemon.mjs +228 -0
- package/src/cli/deploy-ui.mjs +1793 -0
- package/src/cli/deploy.mjs +1726 -0
- package/src/cli/doctor.mjs +254 -0
- package/src/cli/dream.mjs +80 -0
- package/src/cli/forget.mjs +92 -0
- package/src/cli/friction.mjs +21 -0
- package/src/cli/generate-pat.mjs +65 -0
- package/src/cli/hash-password.mjs +22 -0
- package/src/cli/help.mjs +153 -0
- package/src/cli/import-rules.mjs +102 -0
- package/src/cli/ingest/index.mjs +318 -0
- package/src/cli/ingest/parsers/chrome-bookmarks.mjs +238 -0
- package/src/cli/ingest/parsers/google-keep.mjs +147 -0
- package/src/cli/ingest/parsers/search-history.mjs +142 -0
- package/src/cli/ingest/parsers/youtube-history.mjs +181 -0
- package/src/cli/ingest/utils/dedup.mjs +69 -0
- package/src/cli/ingest/utils/takeout-walker.mjs +101 -0
- package/src/cli/ingest.mjs +102 -0
- package/src/cli/init.mjs +736 -0
- package/src/cli/integration-dispatcher.mjs +128 -0
- package/src/cli/lint.mjs +166 -0
- package/src/cli/map.mjs +194 -0
- package/src/cli/migrate.mjs +87 -0
- package/src/cli/rebuild.mjs +88 -0
- package/src/cli/recall.mjs +219 -0
- package/src/cli/relay.mjs +518 -0
- package/src/cli/remember.mjs +306 -0
- package/src/cli/research.mjs +381 -0
- package/src/cli/reset-password.mjs +75 -0
- package/src/cli/restore.mjs +135 -0
- package/src/cli/setup.mjs +616 -0
- package/src/cli/share.mjs +180 -0
- package/src/cli/skill.mjs +313 -0
- package/src/cli/snapshot.mjs +108 -0
- package/src/cli/start.mjs +63 -0
- package/src/cli/status.mjs +296 -0
- package/src/cli/sync.mjs +383 -0
- package/src/cli/uninstall.mjs +613 -0
- package/src/cli/upgrade.mjs +141 -0
- package/src/cli/wizard.html +3682 -0
- package/src/core/blackboard.mjs +54 -0
- package/src/core/clarity-rewriter.mjs +770 -0
- package/src/core/conclusion-writer.mjs +259 -0
- package/src/core/config.mjs +277 -0
- package/src/core/conflict-detector.mjs +536 -0
- package/src/core/crypto.mjs +107 -0
- package/src/core/daemon-control.mjs +158 -0
- package/src/core/daemon-loop.mjs +364 -0
- package/src/core/dream.mjs +347 -0
- package/src/core/drift-detector.mjs +143 -0
- package/src/core/embeddings.mjs +632 -0
- package/src/core/emergency-alerts.mjs +137 -0
- package/src/core/evolution.mjs +107 -0
- package/src/core/fact-seeker.mjs +1602 -0
- package/src/core/friction.mjs +95 -0
- package/src/core/import-rules.mjs +308 -0
- package/src/core/inference-engine.mjs +367 -0
- package/src/core/logger.mjs +75 -0
- package/src/core/memory-layers.mjs +96 -0
- package/src/core/migrate.mjs +116 -0
- package/src/core/notifications.mjs +77 -0
- package/src/core/operation-validator.mjs +272 -0
- package/src/core/optimizer.mjs +330 -0
- package/src/core/pattern_detector.mjs +113 -0
- package/src/core/post-mortem.mjs +413 -0
- package/src/core/protect-instructions.mjs +148 -0
- package/src/core/quick-capture.mjs +145 -0
- package/src/core/research-queue.mjs +431 -0
- package/src/core/research.mjs +461 -0
- package/src/core/runtime.mjs +515 -0
- package/src/core/sandbox.mjs +163 -0
- package/src/core/scheduler.mjs +569 -0
- package/src/core/schema.mjs +333 -0
- package/src/core/search.mjs +219 -0
- package/src/core/semantic-index.mjs +276 -0
- package/src/core/session-watcher.mjs +850 -0
- package/src/core/snapshot.mjs +133 -0
- package/src/core/source-adapters.mjs +1038 -0
- package/src/core/steering.mjs +137 -0
- package/src/core/surface.mjs +444 -0
- package/src/core/task_runner.mjs +125 -0
- package/src/core/tts.mjs +142 -0
- package/src/core/usage-tracker.mjs +349 -0
- package/src/core/vault-cache.mjs +76 -0
- package/src/core/vault.mjs +273 -0
- package/src/core/watchdog.mjs +236 -0
- package/src/server/api.mjs +944 -0
- package/src/server/auth.mjs +410 -0
- package/src/server/index.mjs +868 -0
- package/src/server/keys.mjs +181 -0
- package/src/server/rest.mjs +1652 -0
- package/src/server/route-manifest.json +77 -0
- package/src/server/routes/_shared.mjs +93 -0
- package/src/server/routes/keys.mjs +55 -0
- package/src/server/routes/memory.mjs +249 -0
- package/src/server/routes/sessions.mjs +336 -0
- package/src/server/routes/share.mjs +138 -0
- package/src/server/tools.mjs +618 -0
- package/templates/Caddyfile +37 -0
- package/templates/com.totalrecall.backup.plist +42 -0
- package/templates/com.totalrecall.daemon.plist +40 -0
- package/templates/com.totalrecall.duckdns.plist +23 -0
- package/templates/com.totalrecall.server.plist +46 -0
- package/templates/default-config/budget.yml +6 -0
- package/templates/default-config/runtime.yml +11 -0
- package/templates/default-config/security.yml +53 -0
- package/templates/default-config/voice.yml +34 -0
- package/templates/obsidian-queries/conflicts.md +26 -0
- package/templates/obsidian-queries/daily.md +17 -0
- package/templates/obsidian-queries/index.md +38 -0
- package/templates/obsidian-queries/skills.md +14 -0
- package/templates/onboarding-interview.md +72 -0
- package/templates/skill_engineering.md +40 -0
- package/templates/total-recall-daemon.service +21 -0
- 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
|
+
}
|