total-recall-brain 3.13.0 → 3.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +91 -108
  2. package/bin/antigravity.mjs +81 -52
  3. package/bin/total-recall.mjs +32 -57
  4. package/extension/background.js +1 -1
  5. package/extension/options/options.js +9 -3
  6. package/extension/sidepanel/sidepanel.js +0 -2
  7. package/frontend/index.html +4 -3
  8. package/frontend/package.json +1 -0
  9. package/frontend/src/App.tsx +109 -152
  10. package/frontend/src/api.ts +605 -23
  11. package/frontend/src/components/BrainSelector.tsx +211 -199
  12. package/frontend/src/components/DocumentEditorModal.tsx +57 -0
  13. package/frontend/src/components/DocumentTable.tsx +47 -0
  14. package/frontend/src/components/Graph3D.tsx +120 -71
  15. package/frontend/src/components/UsageChart.tsx +3 -3
  16. package/frontend/src/components/brand/BrandMark.tsx +95 -0
  17. package/frontend/src/index.css +286 -86
  18. package/frontend/src/pages/ApiKeysPage.tsx +1878 -215
  19. package/frontend/src/pages/ChatPage.tsx +103 -45
  20. package/frontend/src/pages/DesignDocsPage.tsx +5 -5
  21. package/frontend/src/pages/FilesPage.tsx +10 -6
  22. package/frontend/src/pages/GraphPage.tsx +5 -3
  23. package/frontend/src/pages/HelpPage.tsx +2 -2
  24. package/frontend/src/pages/InboxPage.tsx +82 -0
  25. package/frontend/src/pages/InstructionsPage.tsx +1 -1
  26. package/frontend/src/pages/IntegrationsPage.tsx +21 -10
  27. package/frontend/src/pages/LoginPage.tsx +95 -77
  28. package/frontend/src/pages/MemoryPage.tsx +31 -8
  29. package/frontend/src/pages/OnboardingPage.tsx +690 -0
  30. package/frontend/src/pages/OpenWikiPage.tsx +412 -323
  31. package/frontend/src/pages/SettingsPage.tsx +825 -922
  32. package/frontend/src/pages/SkillsPage.tsx +272 -147
  33. package/frontend/src/pages/UsagePage.tsx +23 -31
  34. package/frontend/src/pages/VaultPage.tsx +223 -0
  35. package/frontend/src/types.ts +13 -0
  36. package/package.json +4 -4
  37. package/scaffold/.agent/skills/total-recall/SKILL.md +43 -4
  38. package/scaffold/.agent/skills/total-recall/modules/README.md +10 -0
  39. package/scaffold/.agent/skills/total-recall/modules/agents/README.md +5 -0
  40. package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents → modules/agents}/agents.yml +8 -0
  41. package/scaffold/.agent/skills/total-recall/modules/skill-deploy/README.md +9 -0
  42. package/scaffold/.agent/skills/total-recall/openwiki/README.md +10 -0
  43. package/scaffold/.agent/skills/total-recall/openwiki/architecture.md +5 -0
  44. package/scaffold/.agent/skills/total-recall/openwiki/ide.md +10 -0
  45. package/scaffold/.agent/skills/total-recall/openwiki/memory.md +11 -0
  46. package/scaffold/.agent/skills/total-recall/openwiki/secrets.md +7 -0
  47. package/scaffold/.agent/skills/total-recall/openwiki/skills.md +7 -0
  48. package/src/cli/brain.mjs +63 -5
  49. package/src/cli/connect.mjs +11 -19
  50. package/src/cli/daemon.mjs +5 -4
  51. package/src/cli/deploy-ui.mjs +8 -3
  52. package/src/cli/deploy.mjs +84 -27
  53. package/src/cli/ingest/index.mjs +1 -1
  54. package/src/cli/init.mjs +63 -34
  55. package/src/cli/remember.mjs +3 -3
  56. package/src/cli/secret.mjs +812 -0
  57. package/src/cli/setup.mjs +34 -7
  58. package/src/cli/share.mjs +2 -2
  59. package/src/cli/skill.mjs +396 -35
  60. package/src/cli/sync.mjs +4 -4
  61. package/src/cli/task.mjs +287 -0
  62. package/src/core/clarity-rewriter.mjs +5 -5
  63. package/src/core/config.mjs +44 -2
  64. package/src/core/conflict-detector.mjs +5 -5
  65. package/src/core/crons.mjs +54 -0
  66. package/src/core/daemon-loop.mjs +87 -235
  67. package/src/core/dream.mjs +170 -10
  68. package/src/core/env-import.mjs +456 -0
  69. package/src/core/fact-seeker.mjs +14 -14
  70. package/src/core/import-rules.mjs +20 -7
  71. package/src/core/inference-engine.mjs +3 -3
  72. package/src/core/migrate.mjs +14 -8
  73. package/src/core/okf-adapter.mjs +2 -2
  74. package/src/core/operation-validator.mjs +39 -331
  75. package/src/core/optimizer.mjs +7 -0
  76. package/src/core/post-mortem.mjs +2 -2
  77. package/src/core/project-brain.mjs +349 -0
  78. package/src/core/provider-catalog.mjs +296 -0
  79. package/src/core/quick-capture.mjs +1 -1
  80. package/src/core/remote-vault-sync.mjs +194 -0
  81. package/src/core/research-queue.mjs +7 -11
  82. package/src/core/runtime.mjs +33 -9
  83. package/src/core/scheduler.mjs +59 -5
  84. package/src/core/schema.mjs +14 -0
  85. package/src/core/search.mjs +1 -1
  86. package/src/core/secrets-env-export.mjs +323 -0
  87. package/src/core/secrets-rotate.mjs +245 -0
  88. package/src/core/secrets-store.mjs +645 -0
  89. package/src/core/semantic-index.mjs +3 -3
  90. package/src/core/skills-registry.mjs +1062 -0
  91. package/src/core/source-adapters.mjs +4 -1
  92. package/src/core/ssss-host-extension.mjs +94 -0
  93. package/src/core/ssss-kernel-bridge.mjs +530 -0
  94. package/src/core/surface.mjs +14 -0
  95. package/src/core/task-envelope.mjs +298 -0
  96. package/src/core/task-executors.mjs +600 -0
  97. package/src/core/validated-write.mjs +124 -56
  98. package/src/core/vault.mjs +21 -34
  99. package/src/core/webauthn-store.mjs +272 -0
  100. package/src/server/api.mjs +26 -16
  101. package/src/server/auth.mjs +51 -0
  102. package/src/server/index.mjs +3 -3
  103. package/src/server/keys.mjs +30 -4
  104. package/src/server/rest.mjs +198 -25
  105. package/src/server/route-manifest.json +156 -0
  106. package/src/server/routes/_shared.mjs +40 -1
  107. package/src/server/routes/docs.mjs +260 -0
  108. package/src/server/routes/memory.mjs +145 -32
  109. package/src/server/routes/research.mjs +30 -0
  110. package/src/server/routes/secrets.mjs +470 -0
  111. package/src/server/routes/share.mjs +2 -2
  112. package/src/server/routes/skills.mjs +452 -54
  113. package/src/server/routes/sync.mjs +48 -0
  114. package/src/server/routes/webauthn.mjs +184 -0
  115. package/src/server/update-route-manifest.mjs +39 -0
  116. package/templates/onboarding-interview.md +32 -22
  117. package/templates/openwiki/README.md +10 -0
  118. package/templates/openwiki/architecture.md +5 -0
  119. package/templates/openwiki/ide.md +10 -0
  120. package/templates/openwiki/memory.md +11 -0
  121. package/templates/openwiki/secrets.md +7 -0
  122. package/templates/openwiki/skills.md +7 -0
  123. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/SKILL.md +0 -121
  124. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/antigravity.txt +0 -20
  125. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/claude.txt +0 -58
  126. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/codex.txt +0 -81
  127. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/gemini.txt +0 -44
  128. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/scripts/.gitkeep +0 -0
  129. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/subagents/.gitkeep +0 -0
  130. package/scaffold/.agent/skills/total-recall/skills/tr-research/SKILL.md +0 -338
  131. package/scaffold/.agent/skills/total-recall/skills/tr-skill/SKILL.md +0 -282
  132. package/scaffold/.agent/skills/total-recall/skills/tr-skill/assets/skill-template/SKILL.md +0 -20
  133. package/scaffold/.agent/skills/total-recall/skills/tr-skill/evals/evals.json +0 -20
  134. package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-add.sh +0 -14
  135. package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-delete.sh +0 -14
  136. package/scaffold/.agent/skills/total-recall/skills/tr-skill/learnings/.gitkeep +0 -0
  137. package/scaffold/.agent/skills/total-recall/skills/tr-skill/references/.gitkeep +0 -0
  138. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/.gitkeep +0 -0
  139. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/create-skill.sh +0 -65
  140. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/enforce-skill-optimization.mjs +0 -135
  141. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/skill-watcher.mjs +0 -64
  142. package/scaffold/.agent/skills/total-recall/skills/tr-skill/subagents/audit-all-skills.md +0 -62
  143. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/SKILL.md +0 -298
  144. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/evals/evals.json +0 -17
  145. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/CHANGELOG.md +0 -124
  146. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/ssss-spec.md +0 -1325
  147. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/scripts/validate-schema.mjs +0 -232
  148. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/subagents/audit-ssss-node.md +0 -9
  149. /package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents/evals → modules/skill-deploy/scripts}/.gitkeep +0 -0
  150. /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/find-skills.mjs +0 -0
  151. /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/install-skill.mjs +0 -0
  152. /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/scan-skill.mjs +0 -0
package/src/cli/setup.mjs CHANGED
@@ -315,14 +315,31 @@ async function remoteSetup(ip, user, opts = {}) {
315
315
 
316
316
  // ─── GitHub fork ─────────────────────────────────────────────────────────────
317
317
 
318
- const UPSTREAM_OWNER = 'gregiteen';
319
- const UPSTREAM_REPO = 'total-recall';
318
+ // Published package upstream for optional fork-during-setup (env only — no third-party product repos)
319
+ function resolveUpstreamGithub() {
320
+ if (process.env.TR_UPSTREAM_OWNER && process.env.TR_UPSTREAM_REPO) {
321
+ return { owner: process.env.TR_UPSTREAM_OWNER, repo: process.env.TR_UPSTREAM_REPO };
322
+ }
323
+ const repoUrl =
324
+ process.env.TR_UPSTREAM_REPO_URL ||
325
+ process.env.npm_package_repository_url ||
326
+ '';
327
+ const m = String(repoUrl).match(/github\.com[/:]([^/]+)\/([^/.]+)/i);
328
+ if (m) return { owner: m[1], repo: m[2].replace(/\.git$/, '') };
329
+ return null;
330
+ }
320
331
 
321
332
  async function forkRepo(githubToken) {
322
- info('Forking gregiteen/total-recall to your GitHub account...');
333
+ const upstream = resolveUpstreamGithub();
334
+ if (!upstream) {
335
+ throw new Error(
336
+ 'No upstream configured for fork. Set TR_UPSTREAM_OWNER + TR_UPSTREAM_REPO (or package repository.url).',
337
+ );
338
+ }
339
+ info(`Forking ${upstream.owner}/${upstream.repo} to your GitHub account...`);
323
340
  const res = await httpRequest(
324
341
  'POST',
325
- `https://api.github.com/repos/${UPSTREAM_OWNER}/${UPSTREAM_REPO}/forks`,
342
+ `https://api.github.com/repos/${upstream.owner}/${upstream.repo}/forks`,
326
343
  {
327
344
  headers: {
328
345
  Authorization: `Bearer ${githubToken}`,
@@ -436,10 +453,20 @@ export default async function setup(args) {
436
453
  ok('Run `npx total-recall sync --push` anytime to back up your vault to GitHub.');
437
454
  } catch (e) {
438
455
  warn(`Could not fork: ${e.message}`);
439
- warn('You can fork manually at https://github.com/gregiteen/total-recall');
456
+ const u = resolveUpstreamGithub();
457
+ warn(
458
+ u
459
+ ? `You can fork manually at https://github.com/${u.owner}/${u.repo}`
460
+ : 'Set TR_UPSTREAM_OWNER/TR_UPSTREAM_REPO to enable auto-fork, or fork your package upstream manually.',
461
+ );
440
462
  }
441
463
  } else {
442
- info('[dry-run] would fork gregiteen/total-recall to your account');
464
+ const u = resolveUpstreamGithub();
465
+ info(
466
+ u
467
+ ? `[dry-run] would fork ${u.owner}/${u.repo} to your account`
468
+ : '[dry-run] would fork package upstream (set TR_UPSTREAM_* if needed)',
469
+ );
443
470
  }
444
471
  // Store GitHub token for sync --push
445
472
  const secretsPath = path.join(resolveBrainDir(), 'config', 'secrets.enc');
@@ -595,7 +622,7 @@ export default async function setup(args) {
595
622
  { label: 'Cursor', value: 'cursor' },
596
623
  { label: 'Aider', value: 'aider' },
597
624
  { label: 'Obsidian', value: 'obsidian' },
598
- { label: 'UltraChat', value: 'ultrachat' },
625
+ { label: 'HTTP host app', value: 'http-api' },
599
626
  ];
600
627
 
601
628
  const selectedIDEs = await chooseMultiple(
package/src/cli/share.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import crypto from 'node:crypto';
2
2
  import { parseLayerFlag, resolveBrainDir, getBothBrains, defaultLayerForCategory } from './agent-dir.mjs';
3
3
  import { createMemoryNode } from '../core/vault.mjs';
4
- import { writeNodeValidated } from '../core/validated-write.mjs';
4
+ import { writeNodeValidatedAsync } from '../core/validated-write.mjs';
5
5
  import { addToQueue } from '../core/research-queue.mjs';
6
6
  import { compileSurface } from '../core/surface.mjs';
7
7
  import path from 'node:path';
@@ -161,7 +161,7 @@ export default async function share(args) {
161
161
  }];
162
162
  }
163
163
 
164
- const vaultResult = writeNodeValidated(node, vaultDir);
164
+ const vaultResult = await writeNodeValidatedAsync(node, vaultDir);
165
165
  if (!vaultResult.success) {
166
166
  console.error(` ❌ Validation failed: ${vaultResult.validation.errors.join('; ')}`);
167
167
  process.exit(1);
package/src/cli/skill.mjs CHANGED
@@ -2,14 +2,36 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath, pathToFileURL } from 'node:url';
4
4
  import { spawnSync } from 'node:child_process';
5
- import { resolveAgentDir, parseLayerFlag } from './agent-dir.mjs';
5
+ import { resolveAgentDir, resolveBrainDir, parseLayerFlag } from './agent-dir.mjs';
6
+ import {
7
+ registerSkill,
8
+ listRegistered,
9
+ listInstalls,
10
+ deploySkill,
11
+ skillStatus,
12
+ syncLocalSkillsToRegistry,
13
+ unregisterSkill,
14
+ resolveRegistryPath,
15
+ syncAllSkillsTwoWay,
16
+ syncSkillTwoWay,
17
+ discoverAllSkills,
18
+ pushAllSkills,
19
+ pullAllSkills,
20
+ loadKnownRepoRoots,
21
+ parseSyncReposEnv,
22
+ trackRepo,
23
+ normalizeRepoPaths,
24
+ } from '../core/skills-registry.mjs';
6
25
 
7
26
  // Helper to resolve agent directories dynamically
8
27
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
28
  const ROOT = path.resolve(__dirname, '../../');
10
29
 
11
30
  export function resolveSkillScriptPath(scriptName, { agentDir = resolveAgentDir(), root = ROOT } = {}) {
31
+ // skill-deploy module (TR_CORE_FOCUS); keep legacy nested-skill paths for old brains.
12
32
  const candidates = [
33
+ path.join(agentDir, 'skills', 'total-recall', 'modules', 'skill-deploy', 'scripts', scriptName),
34
+ path.join(root, 'scaffold', '.agent', 'skills', 'total-recall', 'modules', 'skill-deploy', 'scripts', scriptName),
13
35
  path.join(agentDir, 'skills', 'total-recall', 'skills', 'tr-skill', 'scripts', scriptName),
14
36
  path.join(agentDir, 'skills', 'total-recall', 'skills', 'skill', 'scripts', scriptName),
15
37
  path.join(root, 'scaffold', '.agent', 'skills', 'total-recall', 'skills', 'tr-skill', 'scripts', scriptName),
@@ -28,33 +50,85 @@ async function importSkillScript(scriptName, options) {
28
50
 
29
51
  function printHelp() {
30
52
  console.log(`
31
- total-recall skill — Sovereign AI Capability Registry & Manager
53
+ total-recall skill — Skills registry, install, and cross-repo deploy
32
54
 
33
55
  Usage: total-recall skill <command> [options]
34
56
 
35
- Commands:
36
- find <query> Search skills.sh registry sorted by absolute installs rating
37
- install <package> Download/load a skill, run static security audit, and compile shims (alias: load, add)
38
- create <name> Scaffold a new SSSS-compliant skill folder structure (alias: new)
39
- edit <name> Open the local skill's SKILL.md file in your preferred editor
40
- scan <name> Run security audit scan on an installed local skill directory
41
- list List all locally active parent skills and nested sub-skills (alias: ls)
42
- remove <name> Safely delete a skill package and hot-recompile shims (alias: rm)
57
+ Install / local:
58
+ find <query> Search skills.sh registry (sorted by installs)
59
+ install <package> Download skill, security scan, compile, auto-register (alias: load, add)
60
+ create <name> Scaffold a new skill folder (alias: new)
61
+ edit <name> Open local SKILL.md in $EDITOR
62
+ scan <name> Security audit on installed skill dir
63
+ list List local brain skills with SKILL.md (alias: ls)
64
+ remove <name> Delete skill from brain skills dir (alias: rm)
65
+
66
+ Registry / deploy / multi-repo sync (TR_CORE_FOCUS):
67
+ register <path|name> Add skill to global skills-registry (from path or local name)
68
+ registry List catalog entries in skills-registry/index.yaml
69
+ deploy <id> Copy skill into a repo (.agent/skills/<id>/) and record install map
70
+ status [id] Registry + install map + drift (omit id = summary)
71
+ sync-registry Register all local brain skills into the catalog
72
+ unregister <id> Remove catalog entry (does not delete repo copies)
73
+ track <path> Track any repo (full project brain + registry) for skill sync
74
+ discover Scan tracked / --repo / cwd repos for skills
75
+ sync [id] Two-way sync skill(s) across those repos
76
+ push One-way: catalog/source → all installs
77
+ pull One-way: prefer installs → source → fan-out
78
+
79
+ deploy options:
80
+ --repo <path> Target repository root (default: cwd)
81
+ --adapt Rewrite description with openwiki + package.json stack signals
82
+ --force Replace existing destination
83
+
84
+ discover / sync options (any user repos — no hardcoded paths):
85
+ --repo <path> Include this repo (repeatable). Works with absolute or relative paths
86
+ --register Permanently track --repo paths (project registry + full brain)
87
+ --ensure-brains Ensure full TR brain layout on all known roots
88
+ --prefer newest|registry|install Sync winner policy (default: newest)
89
+ --dry-run Show actions without writing
90
+ --include-core Also sync total-recall core skill (off by default)
91
+ --skip-discover Do not re-scan repos before sync
92
+
93
+ How any repo is included:
94
+ 1. npx total-recall skill track /path/to/any-repo
95
+ (or: brain register /path && brain ensure /path)
96
+ 2. One-shot: skill discover --repo /path --register
97
+ 3. Env: TR_SYNC_REPOS="/path/a:/path/b"
98
+ 4. cwd is auto-included when it looks like a project
43
99
 
44
100
  Options:
45
101
  --global Target the global brain (~/.agent)
46
102
  --project Target the current repo's project brain
47
103
  --help, -h Show this help menu
48
-
104
+
49
105
  Examples:
50
- npx total-recall skill find git
51
- npx total-recall skill load github/awesome-copilot@prd
52
- npx total-recall skill create my-awesome-skill
53
- npx total-recall skill edit my-awesome-skill
54
- npx total-recall skill scan total-recall
106
+ npx total-recall skill track .
107
+ npx total-recall skill track /path/to/my-app
108
+ npx total-recall skill discover --repo /path/to/app1 --repo /path/to/app2 --register
109
+ npx total-recall skill sync --repo /path/to/app1 --repo /path/to/app2
110
+ npx total-recall skill deploy my-skill --repo /path/to/my-app --adapt
111
+ npx total-recall skill sync --prefer newest
55
112
  `);
56
113
  }
57
114
 
115
+ /** Collect all --repo <path> values from argv */
116
+ function parseRepoFlags(args) {
117
+ const repos = [];
118
+ for (let i = 0; i < args.length; i++) {
119
+ if (args[i] === '--repo' && args[i + 1]) {
120
+ repos.push(args[++i]);
121
+ }
122
+ }
123
+ return repos;
124
+ }
125
+
126
+ function parseFlagValue(args, flag) {
127
+ const i = args.indexOf(flag);
128
+ if (i === -1) return null;
129
+ return args[i + 1] || null;
130
+ }
131
+
58
132
  export default async function skillCli(args) {
59
133
  const { layer, remainingArgs } = parseLayerFlag(args);
60
134
  if (remainingArgs.length === 0 || remainingArgs.includes('--help') || remainingArgs.includes('-h')) {
@@ -63,8 +137,10 @@ export default async function skillCli(args) {
63
137
  }
64
138
 
65
139
  let agentDir;
140
+ let brainDir;
66
141
  try {
67
142
  agentDir = resolveAgentDir(layer);
143
+ brainDir = resolveBrainDir(layer);
68
144
  } catch (err) {
69
145
  console.error(`❌ ${err.message}`);
70
146
  process.exit(1);
@@ -133,11 +209,30 @@ export default async function skillCli(args) {
133
209
  }
134
210
 
135
211
  try {
136
- const { installSkill } = await importSkillScript('install-skill.mjs', { agentDir });
212
+ const { installSkill, inferSkillName, installedSkillCandidates } = await importSkillScript('install-skill.mjs', { agentDir });
137
213
  const res = installSkill(pkg, { cwd: process.cwd(), agentDir });
138
214
  if (!res.success) {
139
215
  process.exit(1);
140
216
  }
217
+ // Auto-register installed skill into global catalog when discoverable
218
+ try {
219
+ const candidates = installedSkillCandidates(pkg, { cwd: process.cwd(), agentDir }).filter((p) =>
220
+ fs.existsSync(path.join(p, 'SKILL.md')),
221
+ );
222
+ const name = inferSkillName(pkg);
223
+ const hit = candidates[0] || (name && fs.existsSync(path.join(skillsDir, name, 'SKILL.md'))
224
+ ? path.join(skillsDir, name)
225
+ : null);
226
+ if (hit) {
227
+ const entry = registerSkill(brainDir, hit, {
228
+ source: pkg,
229
+ source_type: 'registry',
230
+ });
231
+ console.log(`📇 Registered in catalog: ${entry.id} → ${resolveRegistryPath(brainDir)}`);
232
+ }
233
+ } catch (regErr) {
234
+ console.warn(`⚠️ Installed but registry update skipped: ${regErr.message}`);
235
+ }
141
236
  } catch (err) {
142
237
  console.error('❌ Loading skill failed:', err.message);
143
238
  process.exit(1);
@@ -170,32 +265,23 @@ export default async function skillCli(args) {
170
265
  return;
171
266
  }
172
267
 
173
- const dirs = fs.readdirSync(skillsDir).filter(d =>
174
- fs.statSync(path.join(skillsDir, d)).isDirectory()
175
- );
268
+ // Only list packages with SKILL.md (real agent skills). Skip modules/, etc.
269
+ const dirs = fs.readdirSync(skillsDir).filter((d) => {
270
+ const skillPath = path.join(skillsDir, d);
271
+ if (!fs.statSync(skillPath).isDirectory()) return false;
272
+ return fs.existsSync(path.join(skillPath, 'SKILL.md'));
273
+ });
176
274
 
177
275
  if (dirs.length === 0) {
178
276
  console.log('No installed skills found in the active brain.');
179
277
  return;
180
278
  }
181
279
 
182
- console.log('\n📦 Installed Sovereign Skills:');
280
+ console.log('\n📦 Installed Skills (SKILL.md present):');
183
281
  console.log('------------------------------------------------------------');
184
282
  for (const d of dirs) {
185
- const skillPath = path.join(skillsDir, d);
186
- const subSkillsPath = path.join(skillPath, 'skills');
187
- let subSkills = [];
188
-
189
- if (fs.existsSync(subSkillsPath) && fs.statSync(subSkillsPath).isDirectory()) {
190
- subSkills = fs.readdirSync(subSkillsPath).filter(sd =>
191
- fs.statSync(path.join(subSkillsPath, sd)).isDirectory()
192
- );
193
- }
194
-
195
- console.log(` • ${d.padEnd(20)} [Universal Skill Package]`);
196
- if (subSkills.length > 0) {
197
- console.log(` └ Sub-skills: ${subSkills.map(s => `🧩 ${s}`).join(', ')}`);
198
- }
283
+ const note = d === 'total-recall' ? ' [TR core skill]' : '';
284
+ console.log(` • ${d.padEnd(20)}${note}`);
199
285
  }
200
286
  console.log('------------------------------------------------------------\n');
201
287
  }
@@ -331,6 +417,281 @@ Provide a high-level explanation of the skill's capabilities and context.
331
417
  process.exit(child.status || 0);
332
418
  }
333
419
 
420
+ // ─── Registry / deploy ──────────────────────────────────────────────────────
421
+
422
+ else if (command === 'register') {
423
+ const target = remainingArgs[1];
424
+ if (!target) {
425
+ console.error('❌ Usage: total-recall skill register <path-or-local-name>');
426
+ process.exit(1);
427
+ }
428
+ try {
429
+ let skillPath = path.resolve(target);
430
+ if (!fs.existsSync(path.join(skillPath, 'SKILL.md'))) {
431
+ skillPath = path.join(skillsDir, target);
432
+ }
433
+ const entry = registerSkill(brainDir, skillPath, {
434
+ source: target,
435
+ source_type: fs.existsSync(path.resolve(target)) ? 'path' : 'local',
436
+ });
437
+ console.log(`\n ✅ Registered: ${entry.id}`);
438
+ console.log(` version=${entry.version} hash=${entry.content_hash}`);
439
+ console.log(` catalog: ${resolveRegistryPath(brainDir)}\n`);
440
+ } catch (err) {
441
+ console.error(`❌ ${err.message}`);
442
+ process.exit(1);
443
+ }
444
+ }
445
+
446
+ else if (command === 'registry') {
447
+ const entries = listRegistered(brainDir);
448
+ if (!entries.length) {
449
+ console.log(`\n Catalog empty. Register with: total-recall skill register <path>`);
450
+ console.log(` Path: ${resolveRegistryPath(brainDir)}\n`);
451
+ return;
452
+ }
453
+ console.log(`\n 📇 Skills catalog (${entries.length}) — ${resolveRegistryPath(brainDir)}\n`);
454
+ for (const e of entries) {
455
+ console.log(` • ${e.id.padEnd(24)} v${e.version} hash=${e.content_hash || '-'}`);
456
+ if (e.description) console.log(` ${String(e.description).slice(0, 90)}`);
457
+ }
458
+ console.log('');
459
+ }
460
+
461
+ else if (command === 'deploy') {
462
+ const skillId = remainingArgs[1];
463
+ if (!skillId) {
464
+ console.error('❌ Usage: total-recall skill deploy <id-or-path> [--repo <path>] [--adapt] [--force]');
465
+ process.exit(1);
466
+ }
467
+ const repo = parseFlagValue(remainingArgs, '--repo') || process.cwd();
468
+ const adapt = remainingArgs.includes('--adapt');
469
+ const force = remainingArgs.includes('--force');
470
+ try {
471
+ const result = deploySkill(brainDir, skillId, {
472
+ repo,
473
+ agentSkillsDir: skillsDir,
474
+ adapt,
475
+ force,
476
+ });
477
+ console.log(`\n ✅ Deployed: ${result.skillId}`);
478
+ console.log(` → ${result.destDir}`);
479
+ console.log(` hash=${result.install.content_hash} adapted=${result.adapt.adapted}`);
480
+ console.log(` Install map updated in ${resolveRegistryPath(brainDir)}\n`);
481
+ } catch (err) {
482
+ console.error(`❌ Deploy failed: ${err.message}`);
483
+ process.exit(1);
484
+ }
485
+ }
486
+
487
+ else if (command === 'status') {
488
+ const skillId = remainingArgs[1];
489
+ if (!skillId) {
490
+ const catalog = listRegistered(brainDir);
491
+ const installs = listInstalls(brainDir);
492
+ console.log(`\n Registry: ${resolveRegistryPath(brainDir)}`);
493
+ console.log(` Catalog skills: ${catalog.length}`);
494
+ console.log(` Install map entries: ${installs.length}`);
495
+ if (installs.length) {
496
+ console.log('\n Installs:');
497
+ for (const i of installs) {
498
+ console.log(` • ${i.skill_id} → ${i.path}`);
499
+ }
500
+ }
501
+ console.log('');
502
+ return;
503
+ }
504
+ try {
505
+ const st = skillStatus(brainDir, skillId);
506
+ console.log(`\n Skill: ${skillId}`);
507
+ console.log(` Registered: ${st.registered}`);
508
+ if (st.entry) {
509
+ console.log(` Version: ${st.entry.version} hash=${st.entry.content_hash}`);
510
+ console.log(` Source: ${st.entry.source_path || st.entry.source}`);
511
+ }
512
+ console.log(` Installs: ${st.install_count} drift=${st.any_drift ? 'YES' : 'no'}`);
513
+ for (const inst of st.installs) {
514
+ console.log(
515
+ ` • ${inst.path}\n exists=${inst.exists} live=${inst.live_hash || '-'} drift=${inst.drift}`,
516
+ );
517
+ }
518
+ console.log('');
519
+ } catch (err) {
520
+ console.error(`❌ ${err.message}`);
521
+ process.exit(1);
522
+ }
523
+ }
524
+
525
+ else if (command === 'sync-registry') {
526
+ const result = syncLocalSkillsToRegistry(brainDir, skillsDir);
527
+ console.log(`\n ✅ Synced ${result.registered.length} skill(s) into catalog`);
528
+ if (result.registered.length) {
529
+ console.log(` ${result.registered.join(', ')}`);
530
+ }
531
+ console.log(` ${resolveRegistryPath(brainDir)}\n`);
532
+ }
533
+
534
+ else if (command === 'unregister') {
535
+ const skillId = remainingArgs[1];
536
+ if (!skillId) {
537
+ console.error('❌ Usage: total-recall skill unregister <id>');
538
+ process.exit(1);
539
+ }
540
+ const result = unregisterSkill(brainDir, skillId);
541
+ if (!result.success) {
542
+ console.error(`❌ ${result.error}`);
543
+ process.exit(1);
544
+ }
545
+ console.log(` ✅ Unregistered catalog entry: ${skillId}`);
546
+ }
547
+
548
+ else if (command === 'track') {
549
+ const target = remainingArgs[1];
550
+ if (!target || target.startsWith('--')) {
551
+ console.error('❌ Usage: total-recall skill track <any-repo-path>');
552
+ console.error(' Example: total-recall skill track .');
553
+ console.error(' total-recall skill track /path/to/my-app');
554
+ process.exit(1);
555
+ }
556
+ try {
557
+ const result = trackRepo(brainDir, target);
558
+ console.log(`\n ✅ Tracking repo as full Total Recall project brain`);
559
+ console.log(` Repo: ${result.repoRoot}`);
560
+ console.log(` Brain: ${result.brainDir}`);
561
+ console.log(` Skills sync will include this path on discover/sync.\n`);
562
+ } catch (err) {
563
+ console.error(`❌ ${err.message}`);
564
+ process.exit(1);
565
+ }
566
+ }
567
+
568
+ else if (command === 'discover') {
569
+ const includeCore = remainingArgs.includes('--include-core');
570
+ const ensureBrains = remainingArgs.includes('--ensure-brains');
571
+ const registerExtra = remainingArgs.includes('--register');
572
+ const extraRepos = parseRepoFlags(remainingArgs);
573
+ const result = discoverAllSkills(brainDir, {
574
+ includeCore,
575
+ ensureBrains,
576
+ extraRepos,
577
+ registerExtra,
578
+ });
579
+ const roots = loadKnownRepoRoots(brainDir, { extraRepos });
580
+ const envRoots = parseSyncReposEnv();
581
+ console.log(`\n 🔍 Discover complete`);
582
+ console.log(` Repos scanned: ${result.repos}`);
583
+ if (roots.length) {
584
+ for (const r of roots) console.log(` • ${r}`);
585
+ }
586
+ console.log(` Skills found: ${result.discovered}`);
587
+ console.log(` Registered: ${result.registered.length}`);
588
+ console.log(` Install rows: +${result.installs_added}`);
589
+ if (result.registered.length) {
590
+ console.log(` IDs: ${result.registered.slice(0, 20).join(', ')}${result.registered.length > 20 ? '…' : ''}`);
591
+ }
592
+ if (envRoots.length) console.log(` TR_SYNC_REPOS: ${envRoots.join(', ')}`);
593
+ if (extraRepos.length) {
594
+ console.log(
595
+ ` --repo: ${normalizeRepoPaths(extraRepos).join(', ')}${registerExtra ? ' [tracked permanently]' : ' [one-shot]'}`,
596
+ );
597
+ }
598
+ if (!roots.length) {
599
+ console.log(` Tip: total-recall skill track . # any repo you want`);
600
+ }
601
+ console.log('');
602
+ }
603
+
604
+ else if (command === 'sync' || command === 'push' || command === 'pull') {
605
+ const skillId = remainingArgs[1] && !remainingArgs[1].startsWith('--') ? remainingArgs[1] : null;
606
+ const dryRun = remainingArgs.includes('--dry-run');
607
+ const includeCore = remainingArgs.includes('--include-core');
608
+ const skipDiscover = remainingArgs.includes('--skip-discover');
609
+ const ensureBrains = remainingArgs.includes('--ensure-brains');
610
+ const registerExtra = remainingArgs.includes('--register');
611
+ const extraRepos = parseRepoFlags(remainingArgs);
612
+ let prefer = parseFlagValue(remainingArgs, '--prefer') || 'newest';
613
+ if (command === 'push') prefer = 'registry';
614
+ if (command === 'pull') prefer = 'install';
615
+
616
+ const syncOpts = {
617
+ prefer,
618
+ dryRun,
619
+ includeCore,
620
+ skipDiscover,
621
+ ensureBrains,
622
+ extraRepos,
623
+ registerExtra,
624
+ };
625
+
626
+ console.log(`\n 🔄 Skill ${command} (prefer=${prefer}${dryRun ? ', dry-run' : ''})…\n`);
627
+
628
+ if (skillId) {
629
+ if (!skipDiscover) {
630
+ discoverAllSkills(brainDir, {
631
+ includeCore,
632
+ extraRepos,
633
+ registerExtra,
634
+ ensureBrains,
635
+ });
636
+ }
637
+ const r = syncSkillTwoWay(brainDir, skillId, {
638
+ prefer,
639
+ dryRun,
640
+ includeCore,
641
+ });
642
+ if (r.skipped) {
643
+ console.log(` ⏭ ${skillId}: ${r.reason}`);
644
+ } else if (r.in_sync) {
645
+ console.log(` ✅ ${skillId}: already in sync (${r.locations} copies, hash=${r.hash})`);
646
+ } else {
647
+ console.log(` 🏆 Winner: ${r.winner?.path} (${r.winner?.role})`);
648
+ for (const a of r.actions || []) {
649
+ console.log(` ${dryRun ? 'would copy' : 'copied'} → ${a.to}`);
650
+ }
651
+ if (!(r.actions || []).length) console.log(` ✅ ${skillId}: no copies needed`);
652
+ }
653
+ console.log('');
654
+ return;
655
+ }
656
+
657
+ const report =
658
+ command === 'push'
659
+ ? pushAllSkills(brainDir, syncOpts)
660
+ : command === 'pull'
661
+ ? pullAllSkills(brainDir, syncOpts)
662
+ : syncAllSkillsTwoWay(brainDir, syncOpts);
663
+
664
+ if (report.roots?.length) {
665
+ console.log(` Roots (${report.roots.length}):`);
666
+ for (const r of report.roots) console.log(` • ${r}`);
667
+ }
668
+ console.log(` Repos scanned: ${report.discovery?.repos ?? '?'}`);
669
+ console.log(` Discovered: ${report.discovery?.discovered ?? 0}`);
670
+ console.log(` Skills checked: ${report.skills}`);
671
+ console.log(` Already synced: ${report.already_in_sync}`);
672
+ console.log(` Updated: ${report.updated}`);
673
+ console.log(` File copies: ${report.copies}`);
674
+ if (report.results) {
675
+ for (const r of report.results) {
676
+ if (r.skipped && r.reason === 'core') continue;
677
+ if (r.in_sync) continue;
678
+ if (r.actions?.length) {
679
+ console.log(`\n • ${r.skillId} ← ${r.winner?.role || '?'} ${r.winner?.path || ''}`);
680
+ for (const a of r.actions) {
681
+ console.log(` ${dryRun ? 'would →' : '→'} ${a.to}`);
682
+ }
683
+ } else if (r.skipped) {
684
+ console.log(` ⏭ ${r.skillId}: ${r.reason}`);
685
+ }
686
+ }
687
+ }
688
+ console.log(
689
+ dryRun
690
+ ? '\n Dry-run only. Re-run without --dry-run to apply.\n'
691
+ : '\n ✅ Sync finished. Check: npx total-recall skill status\n',
692
+ );
693
+ }
694
+
334
695
  else {
335
696
  console.error(`❌ Unknown skill command: ${command}`);
336
697
  printHelp();
package/src/cli/sync.mjs CHANGED
@@ -312,13 +312,13 @@ function pushToFork() {
312
312
  const root = gitRun(['rev-parse', '--show-toplevel'], { silent: true, ignoreErrors: true });
313
313
  if (!root) throw new Error('Not inside a git repository.');
314
314
 
315
- // Warn if origin appears to be the upstream (not a personal fork)
315
+ // Optional: refuse push when origin is the published upstream (set TR_UPSTREAM_REPO yourself)
316
316
  const originUrl = gitRun(['remote', 'get-url', 'origin'], { silent: true, ignoreErrors: true });
317
- if (originUrl.includes('gregiteen/total-recall')) {
318
- console.error(' ⚠️ origin is the upstream repo, not your personal fork.');
317
+ if (process.env.TR_UPSTREAM_REPO && originUrl && originUrl.includes(process.env.TR_UPSTREAM_REPO)) {
318
+ console.error(' ⚠️ origin matches TR_UPSTREAM_REPO (upstream), not your personal fork.');
319
319
  console.error(' Set up your fork first:');
320
320
  console.error(' git remote rename origin upstream');
321
- console.error(' git remote add origin https://github.com/YOUR_USERNAME/total-recall.git');
321
+ console.error(' git remote add origin https://github.com/YOUR_USERNAME/<your-fork>.git');
322
322
  throw new Error('Configure your fork as origin before using sync --push.');
323
323
  }
324
324