total-recall-brain 3.41.0 → 3.41.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scaffold/.agent/skills/meta-harness/references/optimized/original.md +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +1456 -12
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +210 -27
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-catalog.mjs +10 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/rotation-capability.mjs +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +78 -6
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-merge.mjs +131 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +111 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-store.mjs +26 -5
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/{src/core/headscale-server.mjs → capability/control-server.mjs} +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/capability/headscale-api.mjs +192 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/capability/index.mjs +46 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +9 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +23 -22
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +6 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +242 -8
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +14 -9
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/server.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/credentials.mjs +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/headscale-client.mjs +34 -181
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-auth.mjs +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/presence.mjs +5 -5
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/screen-view.mjs +78 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/DESIGN.md +211 -12
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/card.js +323 -388
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +12 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +7 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +6 -6
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +34 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/bootstrap.mjs +99 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/architecture.md +5 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/ide.md +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/memory.md +11 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/secrets.md +7 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/skills.md +7 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/references/auth.md +2 -0
- package/src/cli/brain.mjs +68 -0
- package/src/cli/connect.mjs +10 -39
- package/src/core/surface.mjs +2 -2
|
@@ -14,8 +14,19 @@ Peers install it with `npx total-recall plugin install peer:<this-node>/openwiki
|
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
|
|
17
|
+
OpenWiki owns documentation bootstrap and templates. Total Recall core initializes portable memory without creating OpenWiki pages. After installing this plugin, create missing documentation explicitly:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
total-recall openwiki bootstrap --brain /absolute/path/to/brain \
|
|
21
|
+
--project-root /absolute/path/to/repository --json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`--brain` is required. `--project-root` adds repository documentation; omit it to seed only the brain's `openwiki/` directory. Existing files and page symlinks are preserved. A symlinked destination directory or a non-file page fails explicitly. `--dry-run` reports planned pages without writing. Bootstrap is a local Node-only operation and does not require credentials, a Total Recall server, ingestion or a provider request.
|
|
25
|
+
|
|
26
|
+
`total-recall openwiki hubs --json` lists repository pages. `total-recall openwiki ingest <directory> --project` explicitly imports documentation through the existing memory CLI. A successful bootstrap does not claim indexing or runtime readiness.
|
|
27
|
+
|
|
17
28
|
- **Identifier**: `openwiki`
|
|
18
|
-
- **Version**: 0.1.
|
|
29
|
+
- **Version**: 0.1.1
|
|
19
30
|
- **SSSS Category**: `openwiki-hubs`
|
|
20
31
|
- **CLI Command**: `npx total-recall openwiki`
|
|
21
32
|
- **UI**: `ui/panel.js` custom element with DESIGN.md tokens
|
|
@@ -6,11 +6,9 @@
|
|
|
6
6
|
// Scaffold: report honestly what exists. Add each real subcommand to
|
|
7
7
|
// IMPLEMENTED and to plugin.json "cli.subcommands" as it is built.
|
|
8
8
|
|
|
9
|
-
import { spawnSync } from "node:child_process";
|
|
10
9
|
import fs from "node:fs";
|
|
11
10
|
import path from "node:path";
|
|
12
|
-
|
|
13
|
-
const TR = process.env.TOTAL_RECALL_BIN || "total-recall";
|
|
11
|
+
import { runBootstrapCommand } from './src/bootstrap.mjs';
|
|
14
12
|
|
|
15
13
|
async function probeCommand(args) {
|
|
16
14
|
const json = args.includes("--json");
|
|
@@ -24,6 +22,7 @@ async function probeCommand(args) {
|
|
|
24
22
|
const result = { status, ok, latency_ms, endpoint: "/health" };
|
|
25
23
|
if (json) console.log(JSON.stringify(result));
|
|
26
24
|
else console.log(`Health probe: HTTP ${status} (${latency_ms}ms) from ${host}`);
|
|
25
|
+
if (!ok) process.exitCode = 1;
|
|
27
26
|
} catch (err) {
|
|
28
27
|
const latency_ms = Date.now() - start;
|
|
29
28
|
const result = { status: 503, ok: false, latency_ms, error: err.message };
|
|
@@ -84,6 +83,7 @@ const IMPLEMENTED = {};
|
|
|
84
83
|
IMPLEMENTED.probe = probeCommand;
|
|
85
84
|
IMPLEMENTED.hubs = hubsCommand;
|
|
86
85
|
IMPLEMENTED.ingest = ingestCommand;
|
|
86
|
+
IMPLEMENTED.bootstrap = runBootstrapCommand;
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
export async function run(argv = []) {
|
|
@@ -112,12 +112,15 @@ export async function run(argv = []) {
|
|
|
112
112
|
probe [--json] Check subsystem health
|
|
113
113
|
hubs [--json] List indexed documentation hubs
|
|
114
114
|
ingest <path> [options] Ingest Markdown files into knowledge graph
|
|
115
|
+
bootstrap --brain <path> [--project-root <path>] [--dry-run] [--json]
|
|
116
|
+
Create missing plugin-owned documentation pages
|
|
115
117
|
`);
|
|
116
118
|
return;
|
|
117
119
|
}
|
|
118
120
|
|
|
119
|
-
if (IMPLEMENTED
|
|
121
|
+
if (Object.hasOwn(IMPLEMENTED, sub)) return IMPLEMENTED[sub](args.slice(1));
|
|
120
122
|
|
|
123
|
+
if (json) console.log(JSON.stringify({ ok: false, error: `OpenWiki operation is not implemented: ${sub}` }));
|
|
121
124
|
console.error(`OpenWiki: "${sub}" is not implemented.`);
|
|
122
125
|
process.exitCode = 2;
|
|
123
126
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tr-plugin-openwiki",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "tr-plugin-openwiki",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.2",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"total-recall-brain": "
|
|
12
|
+
"total-recall-brain": "3.41.1"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"node_modules/@hexagon/base64": {
|
|
@@ -2556,9 +2556,9 @@
|
|
|
2556
2556
|
}
|
|
2557
2557
|
},
|
|
2558
2558
|
"node_modules/total-recall-brain": {
|
|
2559
|
-
"version": "3.
|
|
2560
|
-
"resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.
|
|
2561
|
-
"integrity": "sha512-
|
|
2559
|
+
"version": "3.41.1",
|
|
2560
|
+
"resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.41.1.tgz",
|
|
2561
|
+
"integrity": "sha512-QraXzcZpCTMssx1gaz8xjvSKf5KqirAaxzzER/ICBrWvawdNTLUDDMsVEYQUA/7thgwtL1QVIxU4dpfCGZwp+g==",
|
|
2562
2562
|
"dev": true,
|
|
2563
2563
|
"license": "MIT",
|
|
2564
2564
|
"workspaces": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tr-plugin-openwiki",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Multi-repo knowledge graph and documentation wiki engine for Total Recall",
|
|
6
6
|
"scripts": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"README.md",
|
|
14
14
|
"LICENSE",
|
|
15
15
|
"ui/",
|
|
16
|
-
"src/"
|
|
16
|
+
"src/",
|
|
17
|
+
"templates/"
|
|
17
18
|
]
|
|
18
19
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://github.com/gregiteen/total-recall/blob/main/metadata.plugin.schema.json",
|
|
3
3
|
"id": "openwiki",
|
|
4
4
|
"name": "OpenWiki",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.2",
|
|
6
6
|
"description": "Multi-repo knowledge graph and documentation wiki engine for Total Recall.",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"branding": {
|
|
@@ -83,6 +83,39 @@
|
|
|
83
83
|
"timeout_ms": 600000,
|
|
84
84
|
"effect": "write"
|
|
85
85
|
},
|
|
86
|
+
{
|
|
87
|
+
"name": "bootstrap",
|
|
88
|
+
"description": "Create missing OpenWiki documentation pages in explicit brain and project directories",
|
|
89
|
+
"effect": "write",
|
|
90
|
+
"output": "json",
|
|
91
|
+
"args": [
|
|
92
|
+
{
|
|
93
|
+
"name": "brain",
|
|
94
|
+
"flag": "--brain",
|
|
95
|
+
"label": "Brain directory",
|
|
96
|
+
"type": "string",
|
|
97
|
+
"required": true
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"name": "projectRoot",
|
|
101
|
+
"flag": "--project-root",
|
|
102
|
+
"label": "Project root",
|
|
103
|
+
"type": "string"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"name": "dryRun",
|
|
107
|
+
"flag": "--dry-run",
|
|
108
|
+
"label": "Preview without writing",
|
|
109
|
+
"type": "boolean"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"name": "json",
|
|
113
|
+
"flag": "--json",
|
|
114
|
+
"label": "JSON output",
|
|
115
|
+
"type": "boolean"
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
},
|
|
86
119
|
{
|
|
87
120
|
"name": "probe",
|
|
88
121
|
"description": "Run HTTP 200 health verification probe",
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/** Plugin-owned, create-only documentation bootstrap. No host runtime required. */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const templatesDir = fileURLToPath(new URL('../templates/openwiki/', import.meta.url));
|
|
7
|
+
|
|
8
|
+
function requireAbsolute(value, label) {
|
|
9
|
+
if (typeof value !== 'string' || !value || !path.isAbsolute(value)) throw new Error(`${label} must be an absolute directory path`);
|
|
10
|
+
return path.resolve(value);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function inspectDirectory(directory) {
|
|
14
|
+
const stat = fs.lstatSync(directory, { throwIfNoEntry: false });
|
|
15
|
+
if (stat && (stat.isSymbolicLink() || !stat.isDirectory())) throw new Error(`Unsafe documentation directory: ${directory}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function inspectTarget(root) {
|
|
19
|
+
inspectDirectory(root);
|
|
20
|
+
const directory = path.join(root, 'openwiki');
|
|
21
|
+
inspectDirectory(directory);
|
|
22
|
+
return directory;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Preserve every existing page; filesystem projections are not canonical memory. */
|
|
26
|
+
export function bootstrapOpenWiki({ brain, projectRoot = null, dryRun = false } = {}) {
|
|
27
|
+
const brainRoot = requireAbsolute(brain, '--brain');
|
|
28
|
+
const project = projectRoot === null ? null : requireAbsolute(projectRoot, '--project-root');
|
|
29
|
+
const roots = [...new Set([brainRoot, ...(project ? [project] : [])])];
|
|
30
|
+
const directories = roots.map(inspectTarget);
|
|
31
|
+
const templates = fs.readdirSync(templatesDir, { withFileTypes: true })
|
|
32
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
33
|
+
if (!templates.length || templates.some(entry => !entry.isFile() || !entry.name.endsWith('.md'))) {
|
|
34
|
+
throw new Error('OpenWiki documentation templates are missing or invalid');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Validate every destination before creating any directory or page.
|
|
38
|
+
for (const directory of directories) for (const entry of templates) {
|
|
39
|
+
const destination = path.join(directory, entry.name);
|
|
40
|
+
const stat = fs.lstatSync(destination, { throwIfNoEntry: false });
|
|
41
|
+
if (stat && !stat.isFile() && !stat.isSymbolicLink()) throw new Error(`Documentation page is not a file: ${destination}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const targets = directories.map(directory => {
|
|
45
|
+
const created = [], preserved = [], planned = [];
|
|
46
|
+
if (!dryRun) fs.mkdirSync(directory, { recursive: true });
|
|
47
|
+
for (const entry of templates) {
|
|
48
|
+
const destination = path.join(directory, entry.name);
|
|
49
|
+
if (fs.lstatSync(destination, { throwIfNoEntry: false })) {
|
|
50
|
+
preserved.push(entry.name);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (dryRun) { planned.push(entry.name); continue; }
|
|
54
|
+
try {
|
|
55
|
+
fs.copyFileSync(path.join(templatesDir, entry.name), destination, fs.constants.COPYFILE_EXCL);
|
|
56
|
+
created.push(entry.name);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
// A concurrently authored page belongs to its author, never the template.
|
|
59
|
+
if (err.code !== 'EEXIST') throw err;
|
|
60
|
+
preserved.push(entry.name);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { directory, created, preserved, planned };
|
|
64
|
+
});
|
|
65
|
+
return { ok: true, dry_run: dryRun, targets,
|
|
66
|
+
files_created: targets.reduce((sum, target) => sum + target.created.length, 0),
|
|
67
|
+
files_preserved: targets.reduce((sum, target) => sum + target.preserved.length, 0),
|
|
68
|
+
files_planned: targets.reduce((sum, target) => sum + target.planned.length, 0) };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function runBootstrapCommand(args = []) {
|
|
72
|
+
const json = args.includes('--json');
|
|
73
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
74
|
+
console.log('Usage: total-recall openwiki bootstrap --brain <absolute-directory> [--project-root <absolute-directory>] [--dry-run] [--json]');
|
|
75
|
+
return { ok: true, help: true };
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const options = {};
|
|
79
|
+
for (let index = 0; index < args.length; index++) {
|
|
80
|
+
const arg = args[index];
|
|
81
|
+
if (arg === '--brain' || arg === '--project-root') {
|
|
82
|
+
const value = args[++index];
|
|
83
|
+
if (!value || value.startsWith('--')) throw new Error(`Missing value for ${arg}`);
|
|
84
|
+
options[arg === '--brain' ? 'brain' : 'projectRoot'] = value;
|
|
85
|
+
} else if (arg === '--dry-run') options.dryRun = true;
|
|
86
|
+
else if (arg !== '--json') throw new Error(`Unknown bootstrap option: ${arg}`);
|
|
87
|
+
}
|
|
88
|
+
const result = bootstrapOpenWiki(options);
|
|
89
|
+
if (json) console.log(JSON.stringify(result));
|
|
90
|
+
else console.log(`OpenWiki: ${result.files_created} pages created, ${result.files_preserved} preserved${result.dry_run ? `, ${result.files_planned} planned (dry run)` : ''}.`);
|
|
91
|
+
return result;
|
|
92
|
+
} catch (err) {
|
|
93
|
+
const result = { ok: false, error: err.message };
|
|
94
|
+
if (json) console.log(JSON.stringify(result));
|
|
95
|
+
console.error(`OpenWiki bootstrap failed: ${err.message}`);
|
|
96
|
+
process.exitCode = 1;
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# OpenWiki
|
|
2
|
+
|
|
3
|
+
Portable knowledge documentation for this Total Recall brain.
|
|
4
|
+
|
|
5
|
+
| Layer | Purpose |
|
|
6
|
+
|-------|---------|
|
|
7
|
+
| **Memory vault** | SSSS nodes (rules, prefs, facts) — machine-enforceable |
|
|
8
|
+
| **OpenWiki** | Human/agent docs (architecture, how-tos, stack notes) |
|
|
9
|
+
|
|
10
|
+
OpenWiki documentation is provided by the OpenWiki plugin. Edit freely; do not put secrets here.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# IDE personalization
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
npx total-recall connect claude-code # CLAUDE.md
|
|
5
|
+
npx total-recall connect codex # AGENTS.md
|
|
6
|
+
npx total-recall connect cursor # .cursor/rules/
|
|
7
|
+
npx total-recall connect gemini # GEMINI.md
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Total Recall only writes inside managed inject markers. Your local rules stay intact.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Memory
|
|
2
|
+
|
|
3
|
+
Use the Total Recall CLI for durable memory:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx total-recall remember invariant "Never commit secrets." --importance 5
|
|
7
|
+
npx total-recall recall "secrets"
|
|
8
|
+
npx total-recall compile
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Categories: invariants, preferences, facts, decisions, patterns, anti-patterns, concepts, lore.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Secrets & API keys
|
|
2
|
+
|
|
3
|
+
Manage credentials through the auth plugin: `total-recall auth secret list|set|rotate|audit`.
|
|
4
|
+
|
|
5
|
+
**Never** put API keys in openwiki pages or compiled IDE instruction files.
|
|
6
|
+
|
|
7
|
+
Credential values belong in the encrypted auth store, never in documentation.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Skills
|
|
2
|
+
|
|
3
|
+
- **Total Recall skill** (only nested agent skill): `.agent/skills/total-recall/SKILL.md`
|
|
4
|
+
- **User skills**: `.agent/skills/<name>/SKILL.md` — project or global
|
|
5
|
+
- **TR modules** (not skills): `.agent/skills/total-recall/modules/` — internal docs/scripts
|
|
6
|
+
|
|
7
|
+
TR will track skill installs across repos and support adaptive deploy (see TR_CORE_FOCUS).
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"jsdom": "^26.1.0",
|
|
13
|
-
"total-recall-brain": "
|
|
13
|
+
"total-recall-brain": "3.41.1"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"node_modules/@asamuzakjp/css-color": {
|
|
@@ -2916,9 +2916,9 @@
|
|
|
2916
2916
|
}
|
|
2917
2917
|
},
|
|
2918
2918
|
"node_modules/total-recall-brain": {
|
|
2919
|
-
"version": "3.
|
|
2920
|
-
"resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.
|
|
2921
|
-
"integrity": "sha512-
|
|
2919
|
+
"version": "3.41.1",
|
|
2920
|
+
"resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.41.1.tgz",
|
|
2921
|
+
"integrity": "sha512-QraXzcZpCTMssx1gaz8xjvSKf5KqirAaxzzER/ICBrWvawdNTLUDDMsVEYQUA/7thgwtL1QVIxU4dpfCGZwp+g==",
|
|
2922
2922
|
"dev": true,
|
|
2923
2923
|
"license": "MIT",
|
|
2924
2924
|
"workspaces": [
|
|
@@ -8,6 +8,8 @@ There is one secrets store per host: the global auth store. The CLI never select
|
|
|
8
8
|
|
|
9
9
|
For provider key setup, verify against an authenticated provider operation before saving; format-only checks and timeouts are not verification. Require HTTPS except explicit loopback development, reject redirects containing credentials, and return safe errors. Rotation follows verify → store/project → deploy/verify → revoke, respecting the provider's supported recipe and user authorization. Manual provider steps remain manual. Exported environment files are deployment projections, not an alternate credential authority.
|
|
10
10
|
|
|
11
|
+
Every secret operation goes through `total-recall auth secret`; never read, compare or copy credential values with shell tools. Check a copied credential with `fingerprint --from-clipboard --expect-prefix <p> --not <KEY>` before `rotate <KEY> --from-clipboard`. After `remote deploy <target> --keys … [--path <repo>]`, confirm with `remote verify <target> --keys …` (fingerprint match, no values). A process manager can keep a captured environment that overrides the deployed file (pm2 does): move the keys the app uses into the store with `remote import-pm2 <target> --app <name> --keys … --apply`, then restart the app with a clean environment so it reads only the projection. Stripe webhook signing secrets are issued by Stripe; roll them in the dashboard, never mint them.
|
|
12
|
+
|
|
11
13
|
Current migration remains in progress until package/bootstrap tests, all-plugin bypass checks, owner suites and end-to-end UI/API checks pass. Manifest `auth.plugin` declarations alone do not certify any of these properties.
|
|
12
14
|
|
|
13
15
|
## Sessions, passwords and legacy data
|
package/src/cli/brain.mjs
CHANGED
|
@@ -16,6 +16,10 @@ function printHelp() { console.log(`
|
|
|
16
16
|
register <project-path> Track any project path (full brain + registry)
|
|
17
17
|
unregister <project-path> Remove a project directory path from the registry
|
|
18
18
|
ensure <project-path> Ensure a full project brain layout + register
|
|
19
|
+
import <vault-dir> --to <project-path> [--dry-run]
|
|
20
|
+
Import every memory node from another vault directory
|
|
21
|
+
(an archive or a retired brain) into that project's
|
|
22
|
+
brain through validated writes; existing slugs are kept
|
|
19
23
|
|
|
20
24
|
Any repo works — pass absolute or relative paths you choose. No product defaults.
|
|
21
25
|
|
|
@@ -27,6 +31,7 @@ function printHelp() { console.log(`
|
|
|
27
31
|
npx total-recall brain off hermes-agent
|
|
28
32
|
npx total-recall brain on global
|
|
29
33
|
npx total-recall brain unregister /path/to/project
|
|
34
|
+
npx total-recall brain import ~/Archive/old-brain/memory-vault --to ~/code/my-lib
|
|
30
35
|
|
|
31
36
|
Skill multi-repo sync: registered projects + TR_SYNC_REPOS + skill track/--repo
|
|
32
37
|
`);
|
|
@@ -58,6 +63,9 @@ export default async function brain(args) {
|
|
|
58
63
|
case 'ensure':
|
|
59
64
|
await handleEnsure(args.slice(1), globalBrainDir);
|
|
60
65
|
break;
|
|
66
|
+
case 'import':
|
|
67
|
+
await handleImport(args.slice(1));
|
|
68
|
+
break;
|
|
61
69
|
case 'on':
|
|
62
70
|
case 'off':
|
|
63
71
|
await handleToggle(subcommand.toLowerCase() === 'on', args.slice(1), globalBrainDir);
|
|
@@ -104,11 +112,14 @@ async function handleEnsure(targetArgs, globalBrainDir) {
|
|
|
104
112
|
tags: ['project-brain', 'full-brain'],
|
|
105
113
|
});
|
|
106
114
|
const health = inspectProjectBrain(abs);
|
|
115
|
+
const { ensureProjectGitignore } = await import('./init.mjs');
|
|
116
|
+
const ignored = ensureProjectGitignore(abs);
|
|
107
117
|
console.log(`\n ✅ Full project brain ready`);
|
|
108
118
|
console.log(` Repo: ${result.repoRoot}`);
|
|
109
119
|
console.log(` Brain: ${result.brainDir}`);
|
|
110
120
|
console.log(` complete=${health.complete} full_brain=${health.full_brain}`);
|
|
111
121
|
console.log(` directories created +${result.dirs_created}`);
|
|
122
|
+
if (ignored.length) console.log(` .gitignore +${ignored.length} brain-state exclusions`);
|
|
112
123
|
console.log('');
|
|
113
124
|
}
|
|
114
125
|
|
|
@@ -224,6 +235,11 @@ async function handleRegister(registryPath, targetArgs) {
|
|
|
224
235
|
register: true,
|
|
225
236
|
});
|
|
226
237
|
console.log(` ✅ Full project brain ready at ${path.join(absoluteRoot, '.agent', 'skills', 'total-recall')}`);
|
|
238
|
+
// A registered repo carries a brain whose config holds a live PAT; keep it
|
|
239
|
+
// out of git the same way `init --project` does.
|
|
240
|
+
const { ensureProjectGitignore } = await import('./init.mjs');
|
|
241
|
+
const ignored = ensureProjectGitignore(absoluteRoot);
|
|
242
|
+
if (ignored.length) console.log(` ✅ .gitignore +${ignored.length} brain-state exclusions`);
|
|
227
243
|
} catch (err) {
|
|
228
244
|
console.error(` ⚠️ Could not fully provision brain layout: ${err.message}`);
|
|
229
245
|
process.exitCode = 1;
|
|
@@ -300,6 +316,58 @@ async function handleUnregister(registryPath, targetArgs) {
|
|
|
300
316
|
console.log(` ✅ Successfully unregistered project: "${absoluteRoot}"`);
|
|
301
317
|
}
|
|
302
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Import memory nodes from a vault directory into a project's brain.
|
|
321
|
+
*
|
|
322
|
+
* The source is read, never written: an archived brain, or the vault of a brain
|
|
323
|
+
* being retired. Every node goes through the validated SSSS write path at the
|
|
324
|
+
* same vault-relative path, so the target's event log records each one. A slug
|
|
325
|
+
* the target already holds is left alone, which makes a rerun safe.
|
|
326
|
+
*/
|
|
327
|
+
async function handleImport(targetArgs) {
|
|
328
|
+
const toIdx = targetArgs.indexOf('--to');
|
|
329
|
+
const source = targetArgs.find((a, i) => !a.startsWith('-') && i !== toIdx + 1);
|
|
330
|
+
const target = toIdx !== -1 ? targetArgs[toIdx + 1] : null;
|
|
331
|
+
const dryRun = targetArgs.includes('--dry-run');
|
|
332
|
+
if (!source || !target) {
|
|
333
|
+
console.error(' ⚠️ Usage: npx total-recall brain import <vault-dir> --to <project-path> [--dry-run]');
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
const sourceDir = path.resolve(source);
|
|
337
|
+
const projectRoot = path.resolve(target);
|
|
338
|
+
if (!fs.existsSync(sourceDir) || !fs.statSync(sourceDir).isDirectory()) {
|
|
339
|
+
console.error(` ⚠️ Source vault not found: ${sourceDir}`);
|
|
340
|
+
process.exit(1);
|
|
341
|
+
}
|
|
342
|
+
const vaultDir = path.join(projectRoot, '.agent', 'skills', 'total-recall', 'memory-vault');
|
|
343
|
+
if (!fs.existsSync(vaultDir)) {
|
|
344
|
+
console.error(` ⚠️ No project brain at ${projectRoot}. Run: npx total-recall brain register ${projectRoot}`);
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
if (path.resolve(sourceDir) === path.resolve(vaultDir)) {
|
|
348
|
+
console.error(' ⚠️ Source and target are the same vault.');
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
const { loadNodes } = await import('../core/vault.mjs');
|
|
352
|
+
const { writeNodeValidatedAsync } = await import('../core/validated-write.mjs');
|
|
353
|
+
const existing = new Set(loadNodes(vaultDir).map(n => n.slug));
|
|
354
|
+
let imported = 0, skipped = 0;
|
|
355
|
+
const failed = [];
|
|
356
|
+
for (const node of loadNodes(sourceDir)) {
|
|
357
|
+
if (existing.has(node.slug)) { skipped++; continue; }
|
|
358
|
+
const rel = path.relative(sourceDir, node._filePath).split(path.sep).join('/');
|
|
359
|
+
if (dryRun) { imported++; continue; }
|
|
360
|
+
const { _filePath, _filepath, ...clean } = node;
|
|
361
|
+
const result = await writeNodeValidatedAsync(clean, vaultDir, { path: rel });
|
|
362
|
+
if (result.success) { imported++; existing.add(node.slug); }
|
|
363
|
+
else failed.push(`${rel}: ${result.validation?.errors?.join('; ') || result.error}`);
|
|
364
|
+
}
|
|
365
|
+
const verb = dryRun ? 'Would import' : 'Imported';
|
|
366
|
+
console.log(` ${failed.length ? '⚠️ ' : '✅'} ${verb} ${imported} node(s) into ${vaultDir}; ${skipped} already present`);
|
|
367
|
+
for (const line of failed) console.error(` ✗ ${line}`);
|
|
368
|
+
if (failed.length) process.exitCode = 1;
|
|
369
|
+
}
|
|
370
|
+
|
|
303
371
|
function countMdFiles(dir) {
|
|
304
372
|
if (!fs.existsSync(dir)) return 0;
|
|
305
373
|
let count = 0;
|
package/src/cli/connect.mjs
CHANGED
|
@@ -25,16 +25,20 @@ export const CLIENTS = {
|
|
|
25
25
|
},
|
|
26
26
|
hermes: {
|
|
27
27
|
label: 'Hermes Agent',
|
|
28
|
-
mode: '
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
mode: 'symlink',
|
|
29
|
+
// Hermes loads the AGENTS.md chain (git root to cwd) as context files.
|
|
30
|
+
// ~/.hermes/memories/MEMORY.md is the agent's own notes store, read only
|
|
31
|
+
// from HERMES_HOME; Total Recall never writes it.
|
|
32
|
+
target: 'AGENTS.md',
|
|
31
33
|
skillsProjection: { scope: 'project', dir: path.join('.hermes', 'skills') }
|
|
32
34
|
},
|
|
33
35
|
'hermes-agent': {
|
|
34
36
|
label: 'Hermes Agent',
|
|
35
|
-
mode: '
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
mode: 'symlink',
|
|
38
|
+
// Hermes loads the AGENTS.md chain (git root to cwd) as context files.
|
|
39
|
+
// ~/.hermes/memories/MEMORY.md is the agent's own notes store, read only
|
|
40
|
+
// from HERMES_HOME; Total Recall never writes it.
|
|
41
|
+
target: 'AGENTS.md',
|
|
38
42
|
skillsProjection: { scope: 'project', dir: path.join('.hermes', 'skills') }
|
|
39
43
|
},
|
|
40
44
|
dsh: {
|
|
@@ -884,39 +888,6 @@ export default async function connect(args) {
|
|
|
884
888
|
result.projection = { targetPath, action: 'symlinked' };
|
|
885
889
|
}
|
|
886
890
|
result.notes.push(` ~/.pi/agent/AGENTS.md → ${instructionsPath}`);
|
|
887
|
-
} else if (preset.mode === 'hermes') {
|
|
888
|
-
// Hermes: write compiled MEMORY.md to ~/.hermes/memories/MEMORY.md (max 2200 chars)
|
|
889
|
-
// and project-local .hermes/memories/MEMORY.md if inside a project workspace
|
|
890
|
-
const instructions = readInstructions(cwd, agentDir);
|
|
891
|
-
if (!instructions) throw new Error('No INSTRUCTIONS.md found. Run npx total-recall init or compile first.');
|
|
892
|
-
const homeDir = process.env.HOME || os.homedir();
|
|
893
|
-
const globalMemPath = path.join(homeDir, '.hermes', 'memories', 'MEMORY.md');
|
|
894
|
-
const capped = instructions.slice(0, 2200);
|
|
895
|
-
|
|
896
|
-
let wroteGlobal = false;
|
|
897
|
-
try {
|
|
898
|
-
fs.mkdirSync(path.dirname(globalMemPath), { recursive: true });
|
|
899
|
-
fs.writeFileSync(globalMemPath, capped, 'utf8');
|
|
900
|
-
wroteGlobal = true;
|
|
901
|
-
} catch {
|
|
902
|
-
// Gracefully continue if global directory is unwritable in sandbox
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
let localMemPath = null;
|
|
906
|
-
if (!isHome) {
|
|
907
|
-
localMemPath = path.join(cwd, '.hermes', 'memories', 'MEMORY.md');
|
|
908
|
-
fs.mkdirSync(path.dirname(localMemPath), { recursive: true });
|
|
909
|
-
fs.writeFileSync(localMemPath, capped, 'utf8');
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
result.projection = { targetPath: localMemPath || globalMemPath, action: 'written' };
|
|
913
|
-
if (wroteGlobal) {
|
|
914
|
-
result.notes.push(` Hermes MEMORY.md (${capped.length} chars) → ${globalMemPath}`);
|
|
915
|
-
}
|
|
916
|
-
if (localMemPath) {
|
|
917
|
-
result.notes.push(` Hermes local MEMORY.md → ${localMemPath}`);
|
|
918
|
-
}
|
|
919
|
-
result.notes.push(' Note: Hermes has a 2,200-char limit on MEMORY.md. Total Recall auto-caps this.');
|
|
920
891
|
} else if (preset.mode === 'dsh') {
|
|
921
892
|
// DeepSeek Harness: write global ~/.dsh/memory/MEMORY.md and symlink project AGENTS.md
|
|
922
893
|
const instructions = readInstructions(cwd, agentDir);
|
package/src/core/surface.mjs
CHANGED
|
@@ -567,8 +567,8 @@ const CLIENT_SHIMS = {
|
|
|
567
567
|
vscode: ['.github/copilot-instructions.md', '.vscode/copilot-instructions.md'],
|
|
568
568
|
githubCopilot: ['.github/copilot-instructions.md'],
|
|
569
569
|
pi: ['AGENTS.md'],
|
|
570
|
-
hermes: ['.
|
|
571
|
-
'hermes-agent': ['.
|
|
570
|
+
hermes: ['AGENTS.md'],
|
|
571
|
+
'hermes-agent': ['AGENTS.md'],
|
|
572
572
|
dsh: ['AGENTS.md'],
|
|
573
573
|
'deepseek-harness': ['AGENTS.md'],
|
|
574
574
|
openclaw: ['MEMORY.md', 'AGENTS.md']
|