total-recall-brain 3.28.2 → 3.30.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/bin/total-recall.mjs +24 -1
- package/docs/ARCHITECTURE.md +26 -18
- package/extension/background.js +259 -84
- package/extension/content-script.css +7 -13
- package/extension/content-script.js +252 -477
- package/extension/icons/icon-128.png +0 -0
- package/extension/icons/icon-16.png +0 -0
- package/extension/icons/icon-32.png +0 -0
- package/extension/icons/icon-48.png +0 -0
- package/extension/icons/mark.svg +6 -0
- package/extension/lib/brain-client.js +160 -73
- package/extension/lib/tokens.css +187 -0
- package/extension/lib/ui-helpers.js +229 -0
- package/extension/lib/ui-helpers.spec.mjs +94 -0
- package/extension/manifest.json +20 -5
- package/extension/options/options.css +64 -218
- package/extension/options/options.html +91 -43
- package/extension/options/options.js +208 -85
- package/extension/sidepanel/sidepanel.css +268 -970
- package/extension/sidepanel/sidepanel.html +200 -166
- package/extension/sidepanel/sidepanel.js +788 -942
- package/frontend/dist/assets/index-DGMNZi_1.js +5742 -0
- package/frontend/dist/index.html +1 -1
- package/frontend/src/api/plugins.ts +124 -92
- package/frontend/src/components/ResearchAgendaTab.spec.tsx +11 -0
- package/frontend/src/components/ResearchAgendaTab.tsx +19 -14
- package/frontend/src/pages/PluginsPage.spec.tsx +87 -16
- package/frontend/src/pages/PluginsPage.tsx +454 -1218
- package/package.json +2 -1
- package/plugins/code-quality/README.md +25 -0
- package/plugins/code-quality/cli.mjs +25 -0
- package/plugins/code-quality/plugin.json +17 -0
- package/plugins/git-sentinel/README.md +27 -0
- package/{scaffold/.agent/plugins → plugins}/git-sentinel/cli.mjs +8 -5
- package/plugins/git-sentinel/plugin.json +18 -0
- package/plugins/system-monitor/README.md +26 -0
- package/{scaffold/.agent/plugins → plugins}/system-monitor/cli.mjs +5 -4
- package/{scaffold/.agent/plugins → plugins}/system-monitor/generator.mjs +4 -5
- package/plugins/system-monitor/plugin.json +29 -0
- package/scaffold/.agent/skills/meta-harness/SKILL.md +4 -4
- package/scaffold/.agent/skills/plugins/SKILL.md +53 -0
- package/scaffold/.agent/skills/total-recall/scripts/brain-state.json +1 -0
- package/src/cli/agent.mjs +1 -1
- package/src/cli/app/index.mjs +493 -0
- package/src/cli/brain.mjs +33 -3
- package/src/cli/command.mjs +80 -41
- package/src/cli/harness.mjs +1 -1
- package/src/cli/init.mjs +3 -1
- package/src/cli/mesh.mjs +118 -12
- package/src/cli/plugin/create.mjs +144 -25
- package/src/cli/plugin/index.mjs +83 -53
- package/src/cli/plugin/install.mjs +10 -127
- package/src/cli/plugin/list.mjs +44 -44
- package/src/cli/plugin/remove.mjs +23 -30
- package/src/cli/plugin/scaffold-cmd.mjs +30 -0
- package/src/cli/plugin/search.mjs +108 -23
- package/src/cli/rebuild.mjs +37 -1
- package/src/cli/recall.mjs +17 -2
- package/src/cli/research.mjs +4 -6
- package/src/cli/share.mjs +2 -0
- package/src/core/app-deploy/apply.mjs +386 -0
- package/src/core/app-deploy/design-tokens.mjs +217 -0
- package/src/core/app-deploy/plan.mjs +249 -0
- package/src/core/app-deploy/resolve.mjs +295 -0
- package/src/core/app-deploy/source.mjs +337 -0
- package/src/core/app-deploy/standalone.mjs +248 -0
- package/src/core/app-deploy/upgrade.mjs +256 -0
- package/src/core/app-deploy/verify.mjs +213 -0
- package/src/core/bound-hosts.mjs +1 -1
- package/src/core/brain-registry.mjs +146 -0
- package/src/core/brain-state.json +1 -0
- package/src/core/daemon-loop.mjs +30 -26
- package/src/core/dream.mjs +5 -17
- package/src/core/fact-seeker.mjs +53 -368
- package/src/core/lan-discovery.mjs +3 -3
- package/src/core/mesh-access-sync.mjs +134 -0
- package/src/core/mesh-access.mjs +18 -3
- package/src/core/mesh-discover.mjs +173 -0
- package/src/core/mesh.mjs +131 -32
- package/src/core/optimizer.mjs +4 -41
- package/src/core/plugin-bundle.mjs +167 -0
- package/src/core/plugin-context.mjs +8 -13
- package/src/core/plugin-loader.mjs +272 -68
- package/src/core/plugin-peers.mjs +163 -0
- package/src/core/plugin-public.mjs +85 -0
- package/src/core/plugin-runner-child.mjs +24 -0
- package/src/core/plugin-runner.mjs +102 -0
- package/src/core/plugin-store.mjs +384 -0
- package/src/core/plugin-tasks.mjs +212 -0
- package/src/core/post-mortem.mjs +40 -9
- package/src/core/research-gate.mjs +182 -0
- package/src/core/research-queue.mjs +47 -7
- package/src/core/research-surface.mjs +211 -0
- package/src/core/research.mjs +25 -32
- package/src/core/scheduler.mjs +67 -153
- package/src/core/schema.mjs +129 -0
- package/src/core/search.mjs +12 -1
- package/src/core/secret-integration-research.mjs +12 -8
- package/src/core/secrets-store.mjs +32 -2
- package/src/core/session-watcher.mjs +6 -0
- package/src/core/surface.mjs +64 -3
- package/src/core/task-executors.mjs +8 -45
- package/src/core/vault-cache.mjs +70 -4
- package/src/server/api.mjs +21 -0
- package/src/server/rest.mjs +2 -0
- package/src/server/route-manifest.json +21 -5
- package/src/server/routes/collab.mjs +38 -11
- package/src/server/routes/extension.mjs +3 -2
- package/src/server/routes/memory.mjs +24 -2
- package/src/server/routes/mesh.mjs +7 -7
- package/src/server/routes/plugins-mesh.mjs +49 -0
- package/src/server/routes/plugins.mjs +91 -426
- package/src/server/routes/research.mjs +5 -2
- package/src/server/routes/share.mjs +2 -0
- package/src/server/tools.mjs +29 -2
- package/extension/icons/README.md +0 -27
- package/extension/lib/preconfigured.js +0 -5
- package/extension/popup/popup.css +0 -227
- package/extension/popup/popup.html +0 -50
- package/extension/popup/popup.js +0 -175
- package/frontend/dist/assets/index-U7o-qU99.js +0 -5742
- package/scaffold/.agent/plugins/.gitkeep +0 -0
- package/scaffold/.agent/plugins/code-quality/README.md +0 -16
- package/scaffold/.agent/plugins/code-quality/cli.mjs +0 -15
- package/scaffold/.agent/plugins/code-quality/plugin.json +0 -34
- package/scaffold/.agent/plugins/git-sentinel/README.md +0 -16
- package/scaffold/.agent/plugins/git-sentinel/plugin.json +0 -34
- package/scaffold/.agent/plugins/meta-harness/README.md +0 -16
- package/scaffold/.agent/plugins/meta-harness/cli.mjs +0 -34
- package/scaffold/.agent/plugins/meta-harness/plugin.json +0 -34
- package/scaffold/.agent/plugins/system-monitor/README.md +0 -16
- package/scaffold/.agent/plugins/system-monitor/plugin.json +0 -38
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Total Recall — App Capability Deployment CLI
|
|
3
|
+
*
|
|
4
|
+
* Implements `npx total-recall app <subcommand>`
|
|
5
|
+
* Supported subcommands:
|
|
6
|
+
* app plan <source> [--target <dir>] [--adapter <id>] [--json] [--pin <sha256>]
|
|
7
|
+
* app add <source> [--target <dir>] [--adapter <id>] [--dry-run] [--force] [--json]
|
|
8
|
+
* app create <dir> --plugin <source> [--adapter <id>] [--name <name>] [--force] [--json]
|
|
9
|
+
* app upgrade <id> --to <source> [--target <dir>] [--adapter <id>] [--dry-run] [--json]
|
|
10
|
+
* app verify [--target <dir>] [--json]
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { createDeploymentPlan } from '../../core/app-deploy/plan.mjs';
|
|
15
|
+
import {
|
|
16
|
+
applyDeploymentPlan,
|
|
17
|
+
AppApplyPreflightError,
|
|
18
|
+
AppApplyError
|
|
19
|
+
} from '../../core/app-deploy/apply.mjs';
|
|
20
|
+
import {
|
|
21
|
+
upgradeCapability,
|
|
22
|
+
AppUpgradeError
|
|
23
|
+
} from '../../core/app-deploy/upgrade.mjs';
|
|
24
|
+
import {
|
|
25
|
+
createStandaloneApp,
|
|
26
|
+
AppCreateError
|
|
27
|
+
} from '../../core/app-deploy/standalone.mjs';
|
|
28
|
+
import { verifyApplication } from '../../core/app-deploy/verify.mjs';
|
|
29
|
+
import {
|
|
30
|
+
IncompatibleAdapterError,
|
|
31
|
+
IncompatibleSsssVersionError,
|
|
32
|
+
OwnershipCollisionError
|
|
33
|
+
} from '../../core/app-deploy/resolve.mjs';
|
|
34
|
+
|
|
35
|
+
function printHelp() {
|
|
36
|
+
console.log(`
|
|
37
|
+
🚀 Total Recall — App Capability Deployment
|
|
38
|
+
|
|
39
|
+
Plan, compose, deploy, and verify modular capabilities into standalone and host applications.
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
npx total-recall app <command> [options]
|
|
43
|
+
|
|
44
|
+
Commands:
|
|
45
|
+
plan <source> Compute a read-only deterministic deployment plan
|
|
46
|
+
--target <dir> Target application directory (default: cwd)
|
|
47
|
+
--adapter <id> Target adapter (ssss-app, nextjs, react, flask)
|
|
48
|
+
--pin <sha256> Enforce exact source hash pin
|
|
49
|
+
--json Output plan as JSON
|
|
50
|
+
|
|
51
|
+
add <source> Apply capability plan to target application
|
|
52
|
+
--target <dir> Target application directory (default: cwd)
|
|
53
|
+
--adapter <id> Target adapter
|
|
54
|
+
--dry-run Preflight and stage only, no app mutations
|
|
55
|
+
--force Bypass identical checks
|
|
56
|
+
--json Output result as JSON
|
|
57
|
+
|
|
58
|
+
create <dir> Scaffold a new standalone application with capability
|
|
59
|
+
--plugin <source> Initial capability plugin source (required)
|
|
60
|
+
--adapter <id> Adapter (default: ssss-app)
|
|
61
|
+
--name <name> App name (default: dir basename)
|
|
62
|
+
--force Overwrite existing non-empty directory
|
|
63
|
+
--json Output result as JSON
|
|
64
|
+
|
|
65
|
+
upgrade <id> Upgrade an installed capability to a new version/pin
|
|
66
|
+
--to <source> New capability source or pinned version (required)
|
|
67
|
+
--target <dir> Target application directory (default: cwd)
|
|
68
|
+
--adapter <id> Adapter
|
|
69
|
+
--dry-run Preflight only
|
|
70
|
+
--json Output result as JSON
|
|
71
|
+
|
|
72
|
+
verify Verify deployed capabilities, file digests, and SSSS conformance
|
|
73
|
+
--target <dir> Target application directory (default: cwd)
|
|
74
|
+
--json Output verification report as JSON
|
|
75
|
+
|
|
76
|
+
Exit Codes:
|
|
77
|
+
0 Success
|
|
78
|
+
1 User/argument error or internal failure
|
|
79
|
+
2 Plan conflict detected or verification drift
|
|
80
|
+
3 Incompatible adapter or SSSS version
|
|
81
|
+
`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function run(argv = []) {
|
|
85
|
+
let args = Array.isArray(argv) ? argv : [];
|
|
86
|
+
if (args[0]?.endsWith('node')) {
|
|
87
|
+
args = args.slice(2);
|
|
88
|
+
}
|
|
89
|
+
if (args[0] === 'app') {
|
|
90
|
+
args = args.slice(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const sub = args[0];
|
|
94
|
+
const rest = args.slice(1);
|
|
95
|
+
|
|
96
|
+
if (!sub || sub === '--help' || sub === '-h') {
|
|
97
|
+
printHelp();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 1. APP PLAN
|
|
102
|
+
if (sub === 'plan') {
|
|
103
|
+
let source = null;
|
|
104
|
+
let target = process.cwd();
|
|
105
|
+
let adapter = 'ssss-app';
|
|
106
|
+
let isJson = false;
|
|
107
|
+
let pin = null;
|
|
108
|
+
|
|
109
|
+
for (let i = 0; i < rest.length; i++) {
|
|
110
|
+
const arg = rest[i];
|
|
111
|
+
if (arg === '--json') {
|
|
112
|
+
isJson = true;
|
|
113
|
+
} else if (arg === '--target' && rest[i + 1]) {
|
|
114
|
+
target = rest[i + 1];
|
|
115
|
+
i++;
|
|
116
|
+
} else if (arg === '--adapter' && rest[i + 1]) {
|
|
117
|
+
adapter = rest[i + 1];
|
|
118
|
+
i++;
|
|
119
|
+
} else if (arg === '--pin' && rest[i + 1]) {
|
|
120
|
+
pin = rest[i + 1];
|
|
121
|
+
i++;
|
|
122
|
+
} else if (!arg.startsWith('--') && !source) {
|
|
123
|
+
source = arg;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!source) {
|
|
128
|
+
if (isJson) {
|
|
129
|
+
console.error(JSON.stringify({ error: 'Missing capability source argument' }));
|
|
130
|
+
} else {
|
|
131
|
+
console.error('❌ Error: Missing capability source.');
|
|
132
|
+
console.error(' Usage: total-recall app plan <source> [--target <dir>] [--adapter <id>] [--json]\n');
|
|
133
|
+
}
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let plan;
|
|
138
|
+
try {
|
|
139
|
+
plan = await createDeploymentPlan(source, {
|
|
140
|
+
target,
|
|
141
|
+
adapter,
|
|
142
|
+
expectedHash: pin
|
|
143
|
+
});
|
|
144
|
+
} catch (err) {
|
|
145
|
+
if (err?.message?.startsWith('process.exit(')) {
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
if (isJson) {
|
|
149
|
+
console.error(JSON.stringify({ error: err.message, type: err.name }));
|
|
150
|
+
} else {
|
|
151
|
+
console.error(`\n❌ Plan Failed: ${err.message}\n`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (err instanceof IncompatibleAdapterError || err instanceof IncompatibleSsssVersionError) {
|
|
155
|
+
process.exit(3);
|
|
156
|
+
} else if (err instanceof OwnershipCollisionError) {
|
|
157
|
+
process.exit(2);
|
|
158
|
+
}
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (isJson) {
|
|
163
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
164
|
+
} else {
|
|
165
|
+
console.log(`\n📋 Capability Deployment Plan`);
|
|
166
|
+
console.log(` Plan Hash: \x1b[36m${plan.plan_hash}\x1b[0m`);
|
|
167
|
+
console.log(` Target App: ${plan.target_app.dir} (${plan.target_app.framework})`);
|
|
168
|
+
console.log(` Adapter: ${plan.adapter}`);
|
|
169
|
+
console.log(` Valid: ${plan.valid ? '\x1b[32m✔ YES\x1b[0m' : '\x1b[31m✖ NO (Conflicts detected)\x1b[0m'}\n`);
|
|
170
|
+
|
|
171
|
+
console.log(`📦 Capabilities (${plan.capabilities.length}):`);
|
|
172
|
+
for (const cap of plan.capabilities) {
|
|
173
|
+
console.log(` - \x1b[1m${cap.id}\x1b[0m v${cap.version} (hash: ${cap.source_sha256.slice(0, 12)}...)`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log(`\n📄 File Operations (${plan.file_operations.length}):`);
|
|
177
|
+
for (const op of plan.file_operations) {
|
|
178
|
+
const color = op.action === 'create' ? '\x1b[32m+' : op.action === 'conflict' ? '\x1b[31m!' : op.action === 'modify' ? '\x1b[33m~' : '\x1b[90m=';
|
|
179
|
+
console.log(` ${color} [${op.action.toUpperCase()}]\x1b[0m ${op.path} (${op.size} bytes)`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (plan.access_grants.length > 0) {
|
|
183
|
+
console.log(`\n🔑 Required Access Grants:`);
|
|
184
|
+
for (const g of plan.access_grants) {
|
|
185
|
+
console.log(` - ${g}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (plan.conflicts.length > 0) {
|
|
190
|
+
console.log(`\n⚠️ Conflicts Detected:`);
|
|
191
|
+
for (const c of plan.conflicts) {
|
|
192
|
+
console.log(` - \x1b[31m${c.path}\x1b[0m: file in target app conflicts with capability payload`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
console.log('');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
plan.cleanup?.();
|
|
199
|
+
|
|
200
|
+
if (!plan.valid) {
|
|
201
|
+
process.exit(2);
|
|
202
|
+
}
|
|
203
|
+
process.exit(0);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 2. APP ADD
|
|
207
|
+
else if (sub === 'add') {
|
|
208
|
+
let source = null;
|
|
209
|
+
let target = process.cwd();
|
|
210
|
+
let adapter = 'ssss-app';
|
|
211
|
+
let isJson = false;
|
|
212
|
+
let dryRun = false;
|
|
213
|
+
let force = false;
|
|
214
|
+
|
|
215
|
+
for (let i = 0; i < rest.length; i++) {
|
|
216
|
+
const arg = rest[i];
|
|
217
|
+
if (arg === '--json') {
|
|
218
|
+
isJson = true;
|
|
219
|
+
} else if (arg === '--dry-run') {
|
|
220
|
+
dryRun = true;
|
|
221
|
+
} else if (arg === '--force') {
|
|
222
|
+
force = true;
|
|
223
|
+
} else if (arg === '--target' && rest[i + 1]) {
|
|
224
|
+
target = rest[i + 1];
|
|
225
|
+
i++;
|
|
226
|
+
} else if (arg === '--adapter' && rest[i + 1]) {
|
|
227
|
+
adapter = rest[i + 1];
|
|
228
|
+
i++;
|
|
229
|
+
} else if (!arg.startsWith('--') && !source) {
|
|
230
|
+
source = arg;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!source) {
|
|
235
|
+
if (isJson) {
|
|
236
|
+
console.error(JSON.stringify({ error: 'Missing capability source argument' }));
|
|
237
|
+
} else {
|
|
238
|
+
console.error('❌ Error: Missing capability source.');
|
|
239
|
+
console.error(' Usage: total-recall app add <source> [--target <dir>] [--adapter <id>] [--dry-run] [--json]\n');
|
|
240
|
+
}
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let result;
|
|
245
|
+
try {
|
|
246
|
+
result = await applyDeploymentPlan(source, {
|
|
247
|
+
target,
|
|
248
|
+
adapter,
|
|
249
|
+
dryRun,
|
|
250
|
+
force
|
|
251
|
+
});
|
|
252
|
+
} catch (err) {
|
|
253
|
+
if (err?.message?.startsWith('process.exit(')) throw err;
|
|
254
|
+
if (isJson) {
|
|
255
|
+
console.error(JSON.stringify({ error: err.message, type: err.name }));
|
|
256
|
+
} else {
|
|
257
|
+
console.error(`\n❌ Add Failed: ${err.message}\n`);
|
|
258
|
+
}
|
|
259
|
+
if (err instanceof AppApplyPreflightError) {
|
|
260
|
+
process.exit(2);
|
|
261
|
+
}
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (isJson) {
|
|
266
|
+
console.log(JSON.stringify(result, null, 2));
|
|
267
|
+
} else {
|
|
268
|
+
if (dryRun) {
|
|
269
|
+
console.log(`\n✔ Dry-run successful! Preflight passed for plan \x1b[36m${result.plan_hash}\x1b[0m (${result.operations} operations).`);
|
|
270
|
+
} else if (result.already_applied) {
|
|
271
|
+
console.log(`\n✔ Capability already applied with matching plan \x1b[36m${result.plan_hash}\x1b[0m. No changes needed.`);
|
|
272
|
+
} else {
|
|
273
|
+
console.log(`\n✔ Successfully deployed capabilities [${result.capabilities.join(', ')}]!`);
|
|
274
|
+
console.log(` Applied files: ${result.applied_files}`);
|
|
275
|
+
console.log(` Plan hash: \x1b[36m${result.plan_hash}\x1b[0m\n`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
process.exit(0);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// 3. APP CREATE
|
|
282
|
+
else if (sub === 'create') {
|
|
283
|
+
let targetDir = null;
|
|
284
|
+
let plugin = null;
|
|
285
|
+
let adapter = 'ssss-app';
|
|
286
|
+
let name = null;
|
|
287
|
+
let force = false;
|
|
288
|
+
let isJson = false;
|
|
289
|
+
|
|
290
|
+
for (let i = 0; i < rest.length; i++) {
|
|
291
|
+
const arg = rest[i];
|
|
292
|
+
if (arg === '--json') {
|
|
293
|
+
isJson = true;
|
|
294
|
+
} else if (arg === '--force') {
|
|
295
|
+
force = true;
|
|
296
|
+
} else if (arg === '--plugin' && rest[i + 1]) {
|
|
297
|
+
plugin = rest[i + 1];
|
|
298
|
+
i++;
|
|
299
|
+
} else if (arg === '--adapter' && rest[i + 1]) {
|
|
300
|
+
adapter = rest[i + 1];
|
|
301
|
+
i++;
|
|
302
|
+
} else if (arg === '--name' && rest[i + 1]) {
|
|
303
|
+
name = rest[i + 1];
|
|
304
|
+
i++;
|
|
305
|
+
} else if (!arg.startsWith('--') && !targetDir) {
|
|
306
|
+
targetDir = arg;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (!targetDir || !plugin) {
|
|
311
|
+
if (isJson) {
|
|
312
|
+
console.error(JSON.stringify({ error: 'Missing target directory or --plugin source' }));
|
|
313
|
+
} else {
|
|
314
|
+
console.error('❌ Error: Missing target directory or --plugin source.');
|
|
315
|
+
console.error(' Usage: total-recall app create <dir> --plugin <source> [--adapter <id>] [--json]\n');
|
|
316
|
+
}
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let result;
|
|
321
|
+
try {
|
|
322
|
+
result = await createStandaloneApp(targetDir, {
|
|
323
|
+
plugin,
|
|
324
|
+
adapter,
|
|
325
|
+
name,
|
|
326
|
+
force
|
|
327
|
+
});
|
|
328
|
+
} catch (err) {
|
|
329
|
+
if (err?.message?.startsWith('process.exit(')) throw err;
|
|
330
|
+
if (isJson) {
|
|
331
|
+
console.error(JSON.stringify({ error: err.message, type: err.name }));
|
|
332
|
+
} else {
|
|
333
|
+
console.error(`\n❌ Create Failed: ${err.message}\n`);
|
|
334
|
+
}
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (isJson) {
|
|
339
|
+
console.log(JSON.stringify(result, null, 2));
|
|
340
|
+
} else {
|
|
341
|
+
console.log(`\n🎉 Standalone application '\x1b[1m${result.name}\x1b[0m' created successfully!`);
|
|
342
|
+
console.log(` Location: ${result.targetDir}`);
|
|
343
|
+
console.log(` Adapter: ${result.adapter}`);
|
|
344
|
+
console.log(` Capabilities: ${result.capabilities.join(', ')}\n`);
|
|
345
|
+
}
|
|
346
|
+
process.exit(0);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// 4. APP UPGRADE
|
|
350
|
+
else if (sub === 'upgrade') {
|
|
351
|
+
let capabilityId = null;
|
|
352
|
+
let toSource = null;
|
|
353
|
+
let target = process.cwd();
|
|
354
|
+
let adapter = 'ssss-app';
|
|
355
|
+
let dryRun = false;
|
|
356
|
+
let isJson = false;
|
|
357
|
+
|
|
358
|
+
for (let i = 0; i < rest.length; i++) {
|
|
359
|
+
const arg = rest[i];
|
|
360
|
+
if (arg === '--json') {
|
|
361
|
+
isJson = true;
|
|
362
|
+
} else if (arg === '--dry-run') {
|
|
363
|
+
dryRun = true;
|
|
364
|
+
} else if (arg === '--to' && rest[i + 1]) {
|
|
365
|
+
toSource = rest[i + 1];
|
|
366
|
+
i++;
|
|
367
|
+
} else if (arg === '--target' && rest[i + 1]) {
|
|
368
|
+
target = rest[i + 1];
|
|
369
|
+
i++;
|
|
370
|
+
} else if (arg === '--adapter' && rest[i + 1]) {
|
|
371
|
+
adapter = rest[i + 1];
|
|
372
|
+
i++;
|
|
373
|
+
} else if (!arg.startsWith('--') && !capabilityId) {
|
|
374
|
+
capabilityId = arg;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (!capabilityId || !toSource) {
|
|
379
|
+
if (isJson) {
|
|
380
|
+
console.error(JSON.stringify({ error: 'Missing capability ID or --to source' }));
|
|
381
|
+
} else {
|
|
382
|
+
console.error('❌ Error: Missing capability ID or --to source.');
|
|
383
|
+
console.error(' Usage: total-recall app upgrade <id> --to <source> [--target <dir>] [--json]\n');
|
|
384
|
+
}
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
let result;
|
|
389
|
+
try {
|
|
390
|
+
result = await upgradeCapability(capabilityId, {
|
|
391
|
+
to: toSource,
|
|
392
|
+
target,
|
|
393
|
+
adapter,
|
|
394
|
+
dryRun
|
|
395
|
+
});
|
|
396
|
+
} catch (err) {
|
|
397
|
+
if (err?.message?.startsWith('process.exit(')) throw err;
|
|
398
|
+
if (isJson) {
|
|
399
|
+
console.error(JSON.stringify({ error: err.message, type: err.name }));
|
|
400
|
+
} else {
|
|
401
|
+
console.error(`\n❌ Upgrade Failed: ${err.message}\n`);
|
|
402
|
+
}
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (isJson) {
|
|
407
|
+
console.log(JSON.stringify(result, null, 2));
|
|
408
|
+
} else {
|
|
409
|
+
if (dryRun) {
|
|
410
|
+
console.log(`\n✔ Dry-run upgrade plan computed: ${result.from_version} → \x1b[32m${result.to_version}\x1b[0m`);
|
|
411
|
+
console.log(` Removed obsolete files: ${result.removed_files.join(', ') || 'none'}`);
|
|
412
|
+
} else {
|
|
413
|
+
console.log(`\n🚀 Capability '\x1b[1m${result.capability_id}\x1b[0m' upgraded: v${result.from_version} → \x1b[32mv${result.to_version}\x1b[0m`);
|
|
414
|
+
console.log(` Plan Hash: ${result.plan_hash}`);
|
|
415
|
+
console.log(` Applied files: ${result.applied_files}\n`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
process.exit(0);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// 5. APP VERIFY
|
|
422
|
+
else if (sub === 'verify') {
|
|
423
|
+
let target = process.cwd();
|
|
424
|
+
let isJson = false;
|
|
425
|
+
|
|
426
|
+
for (let i = 0; i < rest.length; i++) {
|
|
427
|
+
const arg = rest[i];
|
|
428
|
+
if (arg === '--json') {
|
|
429
|
+
isJson = true;
|
|
430
|
+
} else if (arg === '--target' && rest[i + 1]) {
|
|
431
|
+
target = rest[i + 1];
|
|
432
|
+
i++;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
let report;
|
|
437
|
+
try {
|
|
438
|
+
report = await verifyApplication(target);
|
|
439
|
+
} catch (err) {
|
|
440
|
+
if (err?.message?.startsWith('process.exit(')) throw err;
|
|
441
|
+
if (isJson) {
|
|
442
|
+
console.error(JSON.stringify({ error: err.message }));
|
|
443
|
+
} else {
|
|
444
|
+
console.error(`\n❌ Verification Failed: ${err.message}\n`);
|
|
445
|
+
}
|
|
446
|
+
process.exit(1);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (isJson) {
|
|
450
|
+
console.log(JSON.stringify(report, null, 2));
|
|
451
|
+
} else {
|
|
452
|
+
console.log(`\n🔍 Application Verification Report`);
|
|
453
|
+
console.log(` Target: ${report.target}`);
|
|
454
|
+
console.log(` Status: ${report.valid ? '\x1b[32m✔ CONFORMANT (No drift)\x1b[0m' : '\x1b[31m✖ DRIFT DETECTED\x1b[0m'}`);
|
|
455
|
+
console.log(` Events: ${report.events_count} recorded\n`);
|
|
456
|
+
|
|
457
|
+
if (report.capabilities.length > 0) {
|
|
458
|
+
console.log(`📦 Capabilities (${report.capabilities.length}):`);
|
|
459
|
+
for (const cap of report.capabilities) {
|
|
460
|
+
const statusIcon = cap.status === 'installed' && cap.drift.length === 0 ? '✔' : '✖';
|
|
461
|
+
console.log(` ${statusIcon} \x1b[1m${cap.id}\x1b[0m v${cap.version} [${cap.status}] (${cap.files_intact}/${cap.files_checked} files verified)`);
|
|
462
|
+
for (const d of cap.drift) {
|
|
463
|
+
console.log(` \x1b[31m! Drift in ${d.path} (missing: ${Boolean(d.missing)})\x1b[0m`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (report.errors.length > 0) {
|
|
469
|
+
console.log(`\n⚠️ Errors:`);
|
|
470
|
+
for (const e of report.errors) {
|
|
471
|
+
console.log(` - \x1b[31m${e}\x1b[0m`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
console.log('');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (!report.valid) {
|
|
478
|
+
process.exit(2);
|
|
479
|
+
}
|
|
480
|
+
process.exit(0);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// UNKNOWN SUBCOMMAND
|
|
484
|
+
else {
|
|
485
|
+
console.error(`Unknown app subcommand: ${sub}`);
|
|
486
|
+
printHelp();
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export default async function (args = []) {
|
|
492
|
+
return run(args);
|
|
493
|
+
}
|
package/src/cli/brain.mjs
CHANGED
|
@@ -9,7 +9,9 @@ function printHelp() { console.log(`
|
|
|
9
9
|
Usage: total-recall brain <command> [options]
|
|
10
10
|
|
|
11
11
|
Commands:
|
|
12
|
-
list List global and all registered project brains (default)
|
|
12
|
+
list List global and all registered project brains, on/off (default)
|
|
13
|
+
on <brain> Switch a brain on (name, repo path, brain path, or "global")
|
|
14
|
+
off <brain> Switch a brain off — recall stops reading it
|
|
13
15
|
status Show active context resolution paths and loaded layers
|
|
14
16
|
register <project-path> Track any project path (full brain + registry)
|
|
15
17
|
unregister <project-path> Remove a project directory path from the registry
|
|
@@ -22,6 +24,8 @@ function printHelp() { console.log(`
|
|
|
22
24
|
npx total-recall brain register /path/to/any-app
|
|
23
25
|
npx total-recall brain ensure ~/code/my-lib
|
|
24
26
|
npx total-recall brain list
|
|
27
|
+
npx total-recall brain off hermes-agent
|
|
28
|
+
npx total-recall brain on global
|
|
25
29
|
npx total-recall brain unregister /path/to/project
|
|
26
30
|
|
|
27
31
|
Skill multi-repo sync: registered projects + TR_SYNC_REPOS + skill track/--repo
|
|
@@ -54,12 +58,35 @@ export default async function brain(args) {
|
|
|
54
58
|
case 'ensure':
|
|
55
59
|
await handleEnsure(args.slice(1), globalBrainDir);
|
|
56
60
|
break;
|
|
61
|
+
case 'on':
|
|
62
|
+
case 'off':
|
|
63
|
+
await handleToggle(subcommand.toLowerCase() === 'on', args.slice(1), globalBrainDir);
|
|
64
|
+
break;
|
|
57
65
|
default:
|
|
58
66
|
console.error(` ⚠️ Unknown brain command: "${subcommand}". Run total-recall brain --help for details.`);
|
|
59
67
|
process.exit(1);
|
|
60
68
|
}
|
|
61
69
|
}
|
|
62
70
|
|
|
71
|
+
async function handleToggle(enabled, targetArgs, globalBrainDir) {
|
|
72
|
+
const query = targetArgs.find((a) => !a.startsWith('-'));
|
|
73
|
+
const verb = enabled ? 'on' : 'off';
|
|
74
|
+
if (!query) {
|
|
75
|
+
console.error(` ⚠️ Usage: npx total-recall brain ${verb} <name | repo path | brain path | global>`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
const { listLocalBrains, findLocalBrain, setBrainEnabled } = await import('../core/brain-registry.mjs');
|
|
79
|
+
const brains = listLocalBrains({ globalBrainDir, activeProject: getBothBrains().project });
|
|
80
|
+
const target = findLocalBrain(brains, query);
|
|
81
|
+
if (!target) {
|
|
82
|
+
console.error(` ⚠️ No single brain matches "${query}". Known: ${brains.map((b) => b.name).join(', ')}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
setBrainEnabled(target.brainDir, enabled, { globalBrainDir });
|
|
86
|
+
console.log(` ✅ ${target.name} is now ${verb.toUpperCase()} (${target.brainDir})`);
|
|
87
|
+
if (!enabled) console.log(' recall skips it until: npx total-recall brain on ' + target.name);
|
|
88
|
+
}
|
|
89
|
+
|
|
63
90
|
async function handleEnsure(targetArgs, globalBrainDir) {
|
|
64
91
|
const targetPath = targetArgs[0];
|
|
65
92
|
if (!targetPath) {
|
|
@@ -90,6 +117,9 @@ async function handleEnsure(targetArgs, globalBrainDir) {
|
|
|
90
117
|
async function handleList(registryPath, globalBrainDir) {
|
|
91
118
|
const brains = getBothBrains();
|
|
92
119
|
const activeProjectDir = brains.project ? brains.project.brainDir : null;
|
|
120
|
+
const { isBrainEnabled, readBrainToggles } = await import('../core/brain-registry.mjs');
|
|
121
|
+
const toggles = readBrainToggles(globalBrainDir);
|
|
122
|
+
const state = (dir) => (isBrainEnabled(dir, { toggles }) ? ' [ON]' : ' ⏸ [OFF]');
|
|
93
123
|
|
|
94
124
|
console.log('\n 🧠 Active Total Recall Brain Registry:\n');
|
|
95
125
|
|
|
@@ -101,7 +131,7 @@ async function handleList(registryPath, globalBrainDir) {
|
|
|
101
131
|
}
|
|
102
132
|
const isGlobalActive = !activeProjectDir;
|
|
103
133
|
const activeGlobalMarker = isGlobalActive ? ' ⭐️ [ACTIVE]' : '';
|
|
104
|
-
console.log(` 🌐 Global Brain${activeGlobalMarker}`);
|
|
134
|
+
console.log(` 🌐 Global Brain${state(globalBrainDir)}${activeGlobalMarker}`);
|
|
105
135
|
console.log(` ├── Path: ${globalBrainDir}`);
|
|
106
136
|
console.log(` └── Nodes: ${globalCount} compiled SSSS memory nodes`);
|
|
107
137
|
console.log('');
|
|
@@ -124,7 +154,7 @@ async function handleList(registryPath, globalBrainDir) {
|
|
|
124
154
|
const projectVault = path.join(project.brainDir, 'memory-vault');
|
|
125
155
|
const nodeCount = exists ? countMdFiles(projectVault) : 0;
|
|
126
156
|
|
|
127
|
-
console.log(` ├── 📄 ${project.name}${marker}${existsLabel}`);
|
|
157
|
+
console.log(` ├── 📄 ${project.name}${state(project.brainDir)}${marker}${existsLabel}`);
|
|
128
158
|
console.log(` │ ├── Root: ${project.path}`);
|
|
129
159
|
console.log(` │ ├── Brain: ${project.brainDir}`);
|
|
130
160
|
console.log(` │ └── Nodes: ${nodeCount} memory nodes`);
|