total-recall-brain 3.44.0 → 3.45.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 +6 -4
- package/docs/ARCHITECTURE.md +10 -7
- package/docs/reference/cli-reference.md +13 -8
- package/frontend/dist/assets/index-DACh1dhx.js +5613 -0
- package/frontend/dist/index.html +1 -1
- package/frontend/src/App.tsx +2 -2
- package/frontend/src/pages/PluginsPage.tsx +1 -1
- package/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +20 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-store.mjs +12 -1
- package/scaffold/.agent/skills/total-recall/plugins/automations/BRICK.md +7 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/README.md +66 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/adapters.mjs +231 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/capability/index.mjs +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/cli.mjs +121 -0
- package/{src/core/plugin-cron.mjs → scaffold/.agent/skills/total-recall/plugins/automations/cron.mjs} +1 -3
- package/scaffold/.agent/skills/total-recall/plugins/automations/inventory.mjs +88 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/package-lock.json +7661 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/package.json +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +167 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/scheduler.mjs +246 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/DESIGN.md +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/automations.js +2 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/card.js +273 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +57 -5
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +40 -14
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +15 -40
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/git/README.md +112 -0
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/cli.mjs +135 -23
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +161 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/context.mjs +94 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/phase3-spec-support.mjs +50 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/runtime.mjs +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/spec-support.mjs +51 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/_support.mjs +195 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/auth.mjs +201 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/bisect.mjs +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/cli-meta.mjs +159 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit-memory.mjs +30 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit.mjs +46 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/fleet.mjs +156 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/force-push.mjs +58 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/group.mjs +119 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/handoff.mjs +59 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/message.mjs +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/resolve.mjs +60 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/rules.mjs +81 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/secret-firewall.mjs +66 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/undo.mjs +132 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/why.mjs +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/ui/card.js +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +25 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +18 -6
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +124 -24
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +147 -14
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +17 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adoption.mjs +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +92 -18
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/control.mjs +145 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +42 -21
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch-request.mjs +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +55 -11
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +26 -4
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +29 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/included-usage.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/jobs.mjs +84 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +3 -12
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/maintenance.mjs +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/mcp-server.mjs +228 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/process-manager.mjs +25 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reasoning.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/remote-operation.mjs +5 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +34 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reviews.mjs +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/claude.mjs +319 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/codex.mjs +387 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/result.mjs +67 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/resume.mjs +373 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/run.mjs +223 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session-jobs.mjs +72 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +6 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/termination.mjs +16 -12
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +32 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/usage-queue.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/verify-ssss.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +285 -40
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +76 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +5 -5
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +47 -18
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh-diagnostics.mjs +44 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +120 -113
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/references/operations.md +1 -1
- package/src/cli/agent.mjs +1 -1
- package/src/cli/command.mjs +41 -2
- package/src/cli/dream.mjs +93 -69
- package/src/cli/harness.mjs +4 -4
- package/src/cli/help.mjs +1 -1
- package/src/cli/init.mjs +0 -25
- package/src/cli/lessons.mjs +51 -0
- package/src/cli/memory-plugin.mjs +1 -1
- package/src/cli/plugin/copies.mjs +64 -14
- package/src/cli/plugin/create.mjs +6 -7
- package/src/cli/plugin/index.mjs +11 -7
- package/src/cli/plugin/install.mjs +10 -15
- package/src/cli/plugin/remove.mjs +1 -1
- package/src/cli/plugin/search.mjs +2 -2
- package/src/cli/preflight.mjs +19 -6
- package/src/cli/proposals.mjs +1 -1
- package/src/cli/recall.mjs +66 -23
- package/src/cli/remember.mjs +3 -14
- package/src/cli/server.mjs +18 -0
- package/src/cli/startup.mjs +1 -0
- package/src/cli/task.mjs +9 -2
- package/src/cli/update.mjs +6 -2
- package/src/core/access-ledger.mjs +78 -0
- package/src/core/agent-manager.mjs +2 -2
- package/src/core/brain-locate.mjs +15 -0
- package/src/core/brain-updates.mjs +31 -11
- package/src/core/claude-hooks.mjs +3 -1
- package/src/core/cleanup-task.mjs +15 -0
- package/src/core/cli-commands.mjs +2 -0
- package/src/core/cli-maintenance.mjs +4 -1
- package/src/core/cli-semantic-index.mjs +23 -3
- package/src/core/context-compiler.mjs +20 -0
- package/src/core/daemon-loop.mjs +39 -62
- package/src/core/default-plugins.mjs +1 -1
- package/src/core/dream.mjs +33 -105
- package/src/core/ensure-dependencies.mjs +148 -0
- package/src/core/fast-recall.mjs +2 -2
- package/src/core/field-evaluation.mjs +23 -0
- package/src/core/gate-mode.mjs +1 -1
- package/src/core/harness-hooks.mjs +14 -2
- package/src/core/lesson-hooks.mjs +74 -0
- package/src/core/lesson-memory.mjs +24 -0
- package/src/core/lesson-sessions.mjs +52 -0
- package/src/core/lessons.mjs +96 -0
- package/src/core/meta-harness.mjs +2 -2
- package/src/core/optimizer.mjs +66 -165
- package/src/core/plugin-contracts/card.mjs +3 -0
- package/src/core/plugin-loader.mjs +12 -3
- package/src/core/plugin-runtime.mjs +40 -1
- package/src/core/plugin-store.mjs +35 -23
- package/src/core/post-mortem.mjs +52 -36
- package/src/core/preflight-gate.mjs +23 -2
- package/src/core/proposal-applier.mjs +31 -2
- package/src/core/repo-sync.mjs +4 -6
- package/src/core/required-plugins.mjs +26 -4
- package/src/core/rule-length.mjs +23 -0
- package/src/core/runtime.mjs +1 -1
- package/src/core/scheduler.mjs +35 -43
- package/src/core/schema.mjs +1 -2
- package/src/core/search.mjs +4 -3
- package/src/core/server-service.mjs +148 -0
- package/src/core/session-watcher.mjs +19 -1
- package/src/core/skill-gap.mjs +16 -0
- package/src/core/ssss-kernel-bridge.mjs +32 -4
- package/src/core/startup-health.mjs +6 -1
- package/src/core/surface.mjs +65 -54
- package/src/core/task-executors.mjs +54 -106
- package/src/core/validated-write.mjs +1 -0
- package/src/core/vector-field.mjs +134 -192
- package/src/server/invoke-core-child.mjs +24 -0
- package/src/server/invoke-core-operations.mjs +102 -0
- package/src/server/route-manifest.json +0 -56
- package/src/server/routes/brains.mjs +0 -18
- package/src/server/routes/context.mjs +0 -41
- package/src/server/routes/invoke.mjs +26 -1
- package/src/server/routes/ssss.mjs +0 -169
- package/src/server/routes/system.mjs +0 -15
- package/src/server/routes/tasks.mjs +0 -26
- package/src/server/routes/vault.mjs +2 -23
- package/frontend/dist/assets/index-DD4qONvH.js +0 -5607
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +0 -53
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +0 -129
- package/src/core/append-log.mjs +0 -131
- package/src/core/plugin-tasks.mjs +0 -173
- package/templates/onboarding-interview.md +0 -82
- /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/LICENSE +0 -0
- /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/ui/DESIGN.md +0 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
|
-
import { validatePluginManifest } from "../../core/plugin-loader.mjs";
|
|
4
|
+
import { validatePluginManifest, isCronExpression } from "../../core/plugin-loader.mjs";
|
|
5
5
|
import { installPlugin } from "../../core/plugin-store.mjs";
|
|
6
|
-
import { parseCron } from "../../core/plugin-cron.mjs";
|
|
7
6
|
|
|
8
7
|
function toTitleCase(kebab) {
|
|
9
8
|
return kebab
|
|
@@ -111,7 +110,7 @@ customElements.define('${id}-panel', ${panelClass});
|
|
|
111
110
|
}
|
|
112
111
|
|
|
113
112
|
export async function createPlugin(args = []) {
|
|
114
|
-
|
|
113
|
+
// Every plugin is installed in the shared folder; --global is accepted and changes nothing.
|
|
115
114
|
let withCli = args.includes("--with-cli");
|
|
116
115
|
const withGenerator = args.includes("--with-generator");
|
|
117
116
|
// A card face is bespoke owner code, authored by hand once the plugin's
|
|
@@ -212,7 +211,7 @@ export async function createPlugin(args = []) {
|
|
|
212
211
|
|
|
213
212
|
if (!id) {
|
|
214
213
|
console.error("❌ Error: Missing plugin id.");
|
|
215
|
-
console.error(" Usage: total-recall plugin create <id> [--from-skill <path>] [--capability] [--name <name>] [--description <desc>] [--category <cat>]
|
|
214
|
+
console.error(" Usage: total-recall plugin create <id> [--from-skill <path>] [--capability] [--name <name>] [--description <desc>] [--category <cat>]\n");
|
|
216
215
|
process.exit(1);
|
|
217
216
|
}
|
|
218
217
|
|
|
@@ -226,7 +225,7 @@ export async function createPlugin(args = []) {
|
|
|
226
225
|
if (!taskCommand || !/^[a-z][a-z0-9-]*$/.test(taskCommand) || !taskSchedule || !taskIntent) {
|
|
227
226
|
throw new Error('--with-task requires --task-command <subcommand>, --task-schedule "<five-field cron>", and --task-intent <description>');
|
|
228
227
|
}
|
|
229
|
-
|
|
228
|
+
if (!isCronExpression(taskSchedule)) throw new Error(`--task-schedule must be a five-field cron expression, got "${taskSchedule}"`);
|
|
230
229
|
} else if (taskCommand || taskSchedule || taskIntent) {
|
|
231
230
|
throw new Error('--task-* options require --with-task');
|
|
232
231
|
}
|
|
@@ -589,7 +588,7 @@ test('every file the manifest references exists', () => {
|
|
|
589
588
|
// ── Install: a link to the repo, so the repo is what runs ───────────────
|
|
590
589
|
let installed = false;
|
|
591
590
|
if (!skipInstall) {
|
|
592
|
-
await installPlugin(pluginDir, { link: true,
|
|
591
|
+
await installPlugin(pluginDir, { link: true, projectRoot: process.cwd() });
|
|
593
592
|
installed = true;
|
|
594
593
|
}
|
|
595
594
|
|
|
@@ -613,7 +612,7 @@ test('every file the manifest references exists', () => {
|
|
|
613
612
|
if (withCli) {
|
|
614
613
|
console.log(` CLI Handler: \x1b[32m${path.join(pluginDir, "cli.mjs")}\x1b[0m`);
|
|
615
614
|
}
|
|
616
|
-
console.log(` Installed: ${installed ? `linked into
|
|
615
|
+
console.log(` Installed: ${installed ? `linked into the shared plugin folder` : 'no (--no-install)'}`);
|
|
617
616
|
console.log(` GitHub: ${githubUrl || (createGithub ? 'creation failed' : 'not created (re-run with --github, or: gh repo create tr-plugin-' + id + ' --private --source ' + pluginDir + ')')}`);
|
|
618
617
|
console.log(`\nNext steps:`);
|
|
619
618
|
console.log(` Test: \x1b[32mcd ${pluginDir} && npm test\x1b[0m`);
|
package/src/cli/plugin/index.mjs
CHANGED
|
@@ -27,8 +27,11 @@ Commands:
|
|
|
27
27
|
list Installed plugins (project & global) with source and sharing
|
|
28
28
|
library [--plugin ID] Installed CLI operation library (--json for agents)
|
|
29
29
|
refresh Rebuild installed command inventories and instruction surfaces
|
|
30
|
-
copies [dir] [--apply] Audit repository copies of shared defaults. --apply
|
|
31
|
-
bundled matches
|
|
30
|
+
copies [dir] [--apply] Audit repository copies of shared defaults and shadow links. --apply
|
|
31
|
+
deletes bundled matches, unlinks shadows (on/off records are kept)
|
|
32
|
+
and removes install records of plugins installed nowhere.
|
|
33
|
+
Add --archive-stale to preserve older differing copies, and
|
|
34
|
+
--drop-repo-only to delete plugins that exist only in the repository.
|
|
32
35
|
replacements [--prune] [--keep N] [--project] [--json]
|
|
33
36
|
Copies kept by install --replace (default: shared folder). Reports
|
|
34
37
|
the space; --prune keeps the newest N per plugin (default 1) and
|
|
@@ -49,7 +52,8 @@ Commands:
|
|
|
49
52
|
<path> a local directory
|
|
50
53
|
--link, -l Symlink a local directory instead of copying
|
|
51
54
|
--replace Replace an install, retain previous source and restore on failure
|
|
52
|
-
|
|
55
|
+
Every plugin installs into the shared folder for all repositories;
|
|
56
|
+
a repository switches it with 'plugin on|off <id>'.
|
|
53
57
|
update [--check] [--json] Reinstall every plugin whose git repo, mesh peer or source
|
|
54
58
|
directory moved ahead (--check only reports; also runs hourly
|
|
55
59
|
in 'update --automatic')
|
|
@@ -57,7 +61,7 @@ Commands:
|
|
|
57
61
|
unshare <id> Stop offering it
|
|
58
62
|
publish <id> Publish plugin to the Store and lock release (--no-lock)
|
|
59
63
|
monetize <id> Configure plugin pricing, period, and payout recipient
|
|
60
|
-
remove <id> Uninstall
|
|
64
|
+
remove <id> Uninstall from the shared folder
|
|
61
65
|
license <id> [cmd] Check or activate plugin license (status, activate, checkout, verify)
|
|
62
66
|
info <id> Manifest, provenance, hash and task history
|
|
63
67
|
vendor <source> [dir] Add a plugin to this project as a brick (default bricks/<id>); the
|
|
@@ -80,10 +84,10 @@ Commands:
|
|
|
80
84
|
|
|
81
85
|
Examples:
|
|
82
86
|
npx total-recall plugin available --use-case software-development
|
|
83
|
-
npx total-recall plugin install git
|
|
84
|
-
npx total-recall plugin share git
|
|
87
|
+
npx total-recall plugin install git
|
|
88
|
+
npx total-recall plugin share git
|
|
85
89
|
npx total-recall plugin peers
|
|
86
|
-
npx total-recall plugin install peer:mac-mini/git
|
|
90
|
+
npx total-recall plugin install peer:mac-mini/git
|
|
87
91
|
npx total-recall plugin create reading-list --use-case research --with-cli
|
|
88
92
|
`);
|
|
89
93
|
}
|
|
@@ -2,18 +2,22 @@ import { installPlugin as install } from '../../core/plugin-distribution.mjs';
|
|
|
2
2
|
import { syncPluginCommands } from '../command.mjs';
|
|
3
3
|
|
|
4
4
|
export async function installPlugin(args = []) {
|
|
5
|
-
|
|
5
|
+
// Every plugin installs into the shared folder; `--global` is accepted and changes nothing.
|
|
6
6
|
const isLink = args.includes('--link') || args.includes('-l');
|
|
7
7
|
const source = args.find(a => !a.startsWith('-'));
|
|
8
8
|
|
|
9
|
+
if (args.includes('--project')) {
|
|
10
|
+
console.error("❌ Plugins are installed only in the shared folder. In a repository, switch one with 'total-recall plugin on|off <id>'.");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
9
13
|
if (!source) {
|
|
10
14
|
console.error('❌ Error: Missing plugin source.');
|
|
11
|
-
console.error(' Usage: total-recall plugin install <bundled-id | share-link | peer:<host>/<id> | git-url | path> [--link]
|
|
15
|
+
console.error(' Usage: total-recall plugin install <bundled-id | share-link | peer:<host>/<id> | git-url | path> [--link]\n');
|
|
12
16
|
process.exit(1);
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
try {
|
|
16
|
-
const result = await install(source, { projectRoot: process.cwd(),
|
|
20
|
+
const result = await install(source, { projectRoot: process.cwd(), link: isLink, replace:args.includes('--replace') });
|
|
17
21
|
const from = result.source.kind === 'peer' ? `peer ${result.source.peer_hostname}` : result.source.kind;
|
|
18
22
|
console.log(`\n✅ Installed '${result.plugin.name}' (id: ${result.plugin.id}, v${result.plugin.version}) from ${from}`);
|
|
19
23
|
console.log(` Location: ${result.plugin.dir}`);
|
|
@@ -24,7 +28,7 @@ export async function installPlugin(args = []) {
|
|
|
24
28
|
} else if (result.dependencies?.reason && !['no package.json', 'no dependencies declared'].includes(result.dependencies.reason)) {
|
|
25
29
|
console.log(` Dependencies: NOT installed (${result.dependencies.reason})`);
|
|
26
30
|
}
|
|
27
|
-
const cmds = syncPluginCommands(result.plugin.dir, { global:
|
|
31
|
+
const cmds = syncPluginCommands(result.plugin.dir, { global: true });
|
|
28
32
|
if (cmds.created.length) console.log(` Commands: ${cmds.created.map((c) => `total-recall ${c}`).join(', ')}`);
|
|
29
33
|
for (const c of cmds.skipped) console.log(` Skipped command '${c.name}': ${c.reason}`);
|
|
30
34
|
console.log('');
|
|
@@ -34,17 +38,8 @@ export async function installPlugin(args = []) {
|
|
|
34
38
|
return;
|
|
35
39
|
}
|
|
36
40
|
const { autoCompileSurfaces } = await import('../../core/auto-compile.mjs');
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
// resolves to the global plugins dir, so a plugin installed into the global
|
|
40
|
-
// brain still reports scope 'project'. Ask the resolver whether a project
|
|
41
|
-
// brain exists at all, and otherwise fall back to the global layer — the one
|
|
42
|
-
// that was really written. Assuming 'project' compiled nothing and printed
|
|
43
|
-
// "Auto-compile failed: No project brain found" after a successful install.
|
|
44
|
-
const { resolveBrainDir } = await import('../agent-dir.mjs');
|
|
45
|
-
let targetLayer = 'global';
|
|
46
|
-
try { resolveBrainDir('project'); targetLayer = 'project'; } catch { targetLayer = 'global'; }
|
|
47
|
-
if (!await autoCompileSurfaces({ layer: targetLayer, semantic: false, force: true })) process.exitCode = 1;
|
|
41
|
+
// The plugin landed in the shared folder, so the global layer is the one to compile.
|
|
42
|
+
if (!await autoCompileSurfaces({ layer: 'global', semantic: false, force: true })) process.exitCode = 1;
|
|
48
43
|
} catch (err) {
|
|
49
44
|
console.error(`❌ ${err.message}`);
|
|
50
45
|
process.exit(1);
|
|
@@ -8,7 +8,7 @@ export async function removePlugin(args = []) {
|
|
|
8
8
|
|
|
9
9
|
if (!id) {
|
|
10
10
|
console.error('❌ Error: Missing plugin ID to remove.');
|
|
11
|
-
console.error(' Usage: total-recall plugin remove <id
|
|
11
|
+
console.error(' Usage: total-recall plugin remove <id>\n');
|
|
12
12
|
process.exit(1);
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -22,7 +22,7 @@ function parseArgs(args) {
|
|
|
22
22
|
* `includes` makes a short query match inside unrelated words: searching "git"
|
|
23
23
|
* returned the signing plugin, whose description begins "Digital agreement…".
|
|
24
24
|
* A hit inside a word is only accepted at a word start so that real substrings
|
|
25
|
-
* still work ("
|
|
25
|
+
* still work ("qual" finds "code-quality") without matching "digital".
|
|
26
26
|
*/
|
|
27
27
|
function matches(p, query, useCase) {
|
|
28
28
|
if (useCase && !(p.use_cases || []).includes(useCase)) return false;
|
|
@@ -32,7 +32,7 @@ function matches(p, query, useCase) {
|
|
|
32
32
|
const words = query.split(/\s+/).filter(Boolean);
|
|
33
33
|
return words.every((word) => {
|
|
34
34
|
const escaped = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
35
|
-
// Anchor at a word start so "
|
|
35
|
+
// Anchor at a word start so "qual" matches "code-quality" but "git" does
|
|
36
36
|
// not match "digital".
|
|
37
37
|
const bounded = new RegExp(`(^|[^a-z0-9])${escaped}`, 'i');
|
|
38
38
|
return haystacks.some((h) => bounded.test(h) || h.startsWith(word));
|
package/src/cli/preflight.mjs
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { ensureDependencies } from '../core/ensure-dependencies.mjs';
|
|
4
5
|
import { detectProjectBrain } from '../core/brain-locate.mjs';
|
|
5
6
|
import { globalBrainDirFor, isBrainEnabled } from '../core/brain-registry.mjs';
|
|
6
7
|
import { GATE_MODES, gateConfigPath, projectBrainDir, readGateLayers, resolveGateMode, writeGateMode } from '../core/gate-mode.mjs';
|
|
7
8
|
import { installHarnessHooks, loadGateAdapters } from '../core/harness-hooks.mjs';
|
|
8
|
-
import { ackDirectory, capsuleVerb, decide, isAckCommand, isCheckCommand, isRouteCommand, isSubagentPayload, nativeToolOf, sessionOf, SWEEP_CONSULT_MS, whenGateFails } from '../core/preflight-gate.mjs';
|
|
9
|
+
import { ackDirectory, capsuleVerb, decide, gatePackageBroken, isAckCommand, isCheckCommand, isRouteCommand, isSubagentPayload, nativeToolOf, sessionOf, SWEEP_CONSULT_MS, whenGateFails } from '../core/preflight-gate.mjs';
|
|
9
10
|
import { ACK_COMMAND, acknowledge, checkAgentReceipt, checkAnyReceipt, findRoot, forgetCapsuleDeliveries, lastArrivalMs, lastCapsuleDelivery, lastConsultMs, lastRoutedMs, lastRouteWarnedMs, lastSessionAck, markArrival, markDelegated, markRouted, markRouteWarned, markSessionAck, noteCaller, recordDecision, sessionHasStanding, sessionKey, readDecisions, takeCaller } from '../core/preflight-receipt.mjs';
|
|
10
11
|
|
|
11
12
|
const USAGE = `Usage: total-recall preflight <command>
|
|
12
13
|
ack --task "<task>" [--action read,edit,test] [--json] Record a receipt after context ready:true and rules verify, and print the capsule.
|
|
13
14
|
Prints one line instead when this session received the same rules and skills within 2 hours
|
|
15
|
+
Restores missing or stale locked dependencies for edit/test actions
|
|
14
16
|
check [--json] Exit 1 unless a fresh receipt covers this agent, wherever it acknowledged.
|
|
15
17
|
Run by hand, it reports this directory's receipt
|
|
16
18
|
gate [--harness <id>] Hook entry: read a call on stdin, answer allow or deny (exit 2 on deny)
|
|
@@ -81,12 +83,23 @@ async function ack(args) {
|
|
|
81
83
|
const task = flag(args, '--task') || '';
|
|
82
84
|
const actions = flag(args, '--action') || 'read,edit,test';
|
|
83
85
|
const result = acknowledge({ root, task, actions, maxAgeMs: await configuredMaxAgeMs(), session: takeCaller(root, 'ack', { task }) });
|
|
86
|
+
const needsDependencies = actions.split(',').some(action => ['edit', 'test'].includes(action.trim()));
|
|
87
|
+
const dependencies = result.ok && needsDependencies ? await ensureDependencies(root, { reason: `preflight:${actions}` }) : null;
|
|
88
|
+
const ok = result.ok && dependencies?.ok !== false;
|
|
84
89
|
if (args.includes('--json')) {
|
|
85
|
-
console.log(JSON.stringify({ ok: result.
|
|
86
|
-
if (!
|
|
90
|
+
console.log(JSON.stringify({ ok, problems: [...result.problems, ...(dependencies?.ok === false ? [`dependencies: ${dependencies.error}`] : [])], receipt: result.file || null, capsule: result.capsule ?? null, unchanged: Boolean(result.unchanged), dependencies }));
|
|
91
|
+
if (!ok) process.exitCode = 1;
|
|
87
92
|
return;
|
|
88
93
|
}
|
|
89
94
|
if (!result.ok) return printFailure(result.problems);
|
|
95
|
+
if (dependencies) {
|
|
96
|
+
const detail = dependencies.ok ? (dependencies.command?.join(' ') || dependencies.trigger) : dependencies.error;
|
|
97
|
+
console.log(` dependencies: ${dependencies.status} — ${detail}`);
|
|
98
|
+
if (!dependencies.ok) {
|
|
99
|
+
console.error('✖ Dependency install failed. Repair dependencies before edit or test work. The instruction receipt is recorded.');
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
90
103
|
if (result.unchanged) {
|
|
91
104
|
const age = result.unchanged.ageMs < 60000 ? 'less than a minute ago' : `${Math.round(result.unchanged.ageMs / 60000)} minutes ago`;
|
|
92
105
|
console.log(`✔ tr-preflight recorded: rules and skills are unchanged since this session received the capsule ${age}, so there is nothing new to read. If your task is new, or you no longer have the capsule, run: total-recall context "<task>" --action ${actions}`);
|
|
@@ -398,6 +411,8 @@ function evaluate(call, options = {}) {
|
|
|
398
411
|
}
|
|
399
412
|
|
|
400
413
|
function judge(call, { harness, readablePaths, session = null, subagent = false, nativeTool = null } = {}) {
|
|
414
|
+
const broken = gatePackageBroken();
|
|
415
|
+
if (broken) return whenGateFails(call, new Error(broken));
|
|
401
416
|
const root = findRoot(call.cwd || process.cwd());
|
|
402
417
|
const limit = envMaxAgeMs();
|
|
403
418
|
const declared = readGateDeclarations();
|
|
@@ -584,12 +599,10 @@ async function mode(args) {
|
|
|
584
599
|
|
|
585
600
|
export default async function preflight(args = []) {
|
|
586
601
|
const [first = '--help'] = args;
|
|
587
|
-
if (first === '--help' || first === '-h' || first === 'help') return console.log(USAGE);
|
|
602
|
+
if (first === '--help' || first === '-h' || first === 'help' || args.includes('--help') || args.includes('-h')) return console.log(USAGE);
|
|
588
603
|
// Original `tr-preflight` forms.
|
|
589
604
|
if (first === '--ack' || first === 'ack') return ack(args);
|
|
590
605
|
if (first === '--check' || first === 'check') return check(args);
|
|
591
|
-
// Help never performs the subcommand: `install --help` once rewrote the harness settings (C-019).
|
|
592
|
-
if (args.includes('--help') || args.includes('-h')) return console.log(USAGE);
|
|
593
606
|
if (first === 'gate') return gate(args.slice(1));
|
|
594
607
|
if (first === 'remind') return remind(args);
|
|
595
608
|
if (first === 'session-start') return sessionStart();
|
package/src/cli/proposals.mjs
CHANGED
|
@@ -208,7 +208,7 @@ export default async function proposals(args) {
|
|
|
208
208
|
console.log(` ${String(days).padStart(4)}d [i${n.importance}] ${n.slug}`);
|
|
209
209
|
}
|
|
210
210
|
if (stale.length > 40) console.log(`\n … and ${stale.length - 40} more.`);
|
|
211
|
-
console.log('\n
|
|
211
|
+
console.log('\n This query is read-only. Request research explicitly for a node that needs verification.\n');
|
|
212
212
|
return;
|
|
213
213
|
}
|
|
214
214
|
|
package/src/cli/recall.mjs
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { resolveAgentDir, resolveBrainDir, parseLayerFlag, getBothBrains } from './agent-dir.mjs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
|
|
5
|
+
/** One brain search shared by recall and paired evaluation. Does not record access. */
|
|
6
|
+
export async function searchRecallBrain(query, { semantic = false, forceFast = false, field = false, ...options } = {}) {
|
|
7
|
+
let rankResults = null;
|
|
8
|
+
if (field) {
|
|
9
|
+
const { loadField, rankWithField } = await import('../core/vector-field.mjs');
|
|
10
|
+
const compiled = loadField(options.derivedDir);
|
|
11
|
+
if (!compiled) throw new Error('Vector field missing or corrupt. Run total-recall recall --compile-field first.');
|
|
12
|
+
rankResults = results => rankWithField(results, { field: compiled, derivedDir: options.derivedDir });
|
|
13
|
+
}
|
|
14
|
+
const { fastSearch } = await import('../core/fast-recall.mjs');
|
|
15
|
+
const fastResults = fastSearch(query, { ...options, fullText: !forceFast, rankResults });
|
|
16
|
+
if (!semantic || fastResults.some(result => (result.base_score ?? result.score) === 1) || forceFast) return fastResults;
|
|
17
|
+
const { semanticSearch } = await import('../core/search.mjs');
|
|
18
|
+
return semanticSearch(query, { ...options, rankResults });
|
|
19
|
+
}
|
|
3
20
|
|
|
4
21
|
function printHelp() {
|
|
5
22
|
console.log(`
|
|
@@ -20,8 +37,18 @@ function printHelp() {
|
|
|
20
37
|
--fast Force frontmatter-only search without semantic fallback
|
|
21
38
|
--local Search indexed body text without a provider (default)
|
|
22
39
|
--semantic Opt into semantic enrichment
|
|
40
|
+
--field Re-rank candidates with the compiled field (opt-in, local)
|
|
41
|
+
--compile-field Compile existing embeddings into the selected brain's field
|
|
42
|
+
--evaluate-field <file> Compare local recall with/without field on fixed JSON queries
|
|
23
43
|
--timings Print local stage timings on stderr
|
|
24
44
|
|
|
45
|
+
Field operations (support --project/--global, --top-k, --format):
|
|
46
|
+
total-recall recall --compile-field --project
|
|
47
|
+
total-recall recall --evaluate-field queries.json --project --format json
|
|
48
|
+
Query file: [{ "query": "search terms", "relevant": ["expected-slug"] }]
|
|
49
|
+
Overlap@k = shared slugs / k. Hit rate@k = queries with a relevant slug / queries.
|
|
50
|
+
Evaluation does not record access. It uses local recall and requires a compiled field.
|
|
51
|
+
|
|
25
52
|
Examples:
|
|
26
53
|
npx total-recall recall "Never run tsc directly"
|
|
27
54
|
npx total-recall recall "Express server port" --top-k 3
|
|
@@ -55,6 +82,7 @@ export default async function recall(args) {
|
|
|
55
82
|
let forceFast = false;
|
|
56
83
|
let semantic = false;
|
|
57
84
|
let timings = false;
|
|
85
|
+
let field = false;
|
|
58
86
|
|
|
59
87
|
for (let i = 1; i < remainingArgs.length; i++) {
|
|
60
88
|
const arg = remainingArgs[i];
|
|
@@ -111,8 +139,11 @@ export default async function recall(args) {
|
|
|
111
139
|
semantic = false;
|
|
112
140
|
} else if (arg === '--timings') {
|
|
113
141
|
timings = true;
|
|
142
|
+
} else if (arg === '--field') {
|
|
143
|
+
field = true;
|
|
114
144
|
}
|
|
115
145
|
}
|
|
146
|
+
top_k = Math.max(1, Math.min(20, top_k));
|
|
116
147
|
|
|
117
148
|
// Determine which brains to search
|
|
118
149
|
const brains = getBothBrains();
|
|
@@ -152,6 +183,26 @@ export default async function recall(args) {
|
|
|
152
183
|
}
|
|
153
184
|
|
|
154
185
|
try {
|
|
186
|
+
if (query === '--compile-field' || query === '--evaluate-field') {
|
|
187
|
+
const reports = [];
|
|
188
|
+
const cases = query === '--evaluate-field'
|
|
189
|
+
? JSON.parse(fs.readFileSync(remainingArgs[1], 'utf8')) : null;
|
|
190
|
+
for (const target of searchTargets) {
|
|
191
|
+
const options = { vaultDir: path.join(target.brainDir, 'memory-vault'),
|
|
192
|
+
derivedDir: path.join(target.brainDir, 'memory-derived'), top_k, includeSessions: false };
|
|
193
|
+
if (query === '--evaluate-field') {
|
|
194
|
+
const { evaluateField } = await import('../core/field-evaluation.mjs');
|
|
195
|
+
reports.push({ brain: target.label, ...await evaluateField(cases, {
|
|
196
|
+
topK: top_k, search: (q, enabled) => searchRecallBrain(q, { ...options, field: enabled }),
|
|
197
|
+
}) });
|
|
198
|
+
} else {
|
|
199
|
+
const { compileField } = await import('../core/vector-field.mjs');
|
|
200
|
+
reports.push({ brain: target.label, ...(await compileField(options)).meta });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
console.log(JSON.stringify(reports, null, 2));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
155
206
|
// Search all target brains and merge results
|
|
156
207
|
let allResults = [];
|
|
157
208
|
let isDegraded = false;
|
|
@@ -164,8 +215,7 @@ export default async function recall(args) {
|
|
|
164
215
|
|
|
165
216
|
try {
|
|
166
217
|
let results = [];
|
|
167
|
-
|
|
168
|
-
const fastResults = fastSearch(query, {
|
|
218
|
+
results = await searchRecallBrain(query, {
|
|
169
219
|
derivedDir,
|
|
170
220
|
vaultDir,
|
|
171
221
|
top_k,
|
|
@@ -174,28 +224,12 @@ export default async function recall(args) {
|
|
|
174
224
|
modality,
|
|
175
225
|
importance,
|
|
176
226
|
priority,
|
|
177
|
-
|
|
227
|
+
forceFast, semantic, field,
|
|
228
|
+
includeSessions: includeSessions && target.label !== 'global',
|
|
178
229
|
});
|
|
179
|
-
if (timings) console.error(JSON.stringify({ brain: target.label, ...
|
|
180
|
-
if (['missing', 'corrupt', 'metadata-only'].includes(
|
|
181
|
-
console.error(`Local full-text index for ${target.label}: ${
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (!semantic || fastResults.some(result => result.score === 1) || forceFast) {
|
|
185
|
-
results = fastResults;
|
|
186
|
-
} else {
|
|
187
|
-
const { semanticSearch } = await import('../core/search.mjs');
|
|
188
|
-
results = await semanticSearch(query, {
|
|
189
|
-
vaultDir,
|
|
190
|
-
derivedDir,
|
|
191
|
-
top_k,
|
|
192
|
-
includeSessions: includeSessions && target.label !== 'global', // sessions only in project/local
|
|
193
|
-
category,
|
|
194
|
-
tags,
|
|
195
|
-
modality,
|
|
196
|
-
importance,
|
|
197
|
-
priority
|
|
198
|
-
});
|
|
230
|
+
if (timings) console.error(JSON.stringify({ brain: target.label, ...results.stats }));
|
|
231
|
+
if (['missing', 'corrupt', 'metadata-only'].includes(results.stats?.index) && !forceFast) {
|
|
232
|
+
console.error(`Local full-text index for ${target.label}: ${results.stats.index}. Run total-recall compile to rebuild; available metadata results may be incomplete.`);
|
|
199
233
|
}
|
|
200
234
|
if (results.degradedTextSearch) {
|
|
201
235
|
isDegraded = true;
|
|
@@ -226,6 +260,15 @@ export default async function recall(args) {
|
|
|
226
260
|
allResults.sort((a, b) => (b.score || 0) - (a.score || 0));
|
|
227
261
|
allResults = allResults.slice(0, top_k);
|
|
228
262
|
|
|
263
|
+
// DECAY-01: a recalled memory is a used memory; record it in the brain it came from.
|
|
264
|
+
try {
|
|
265
|
+
const { recordAccess } = await import('../core/access-ledger.mjs');
|
|
266
|
+
for (const target of searchTargets) {
|
|
267
|
+
const slugs = allResults.filter(r => r._layer === target.label).map(r => r.slug || r.id).filter(Boolean);
|
|
268
|
+
recordAccess(path.join(target.brainDir, 'memory-derived'), slugs);
|
|
269
|
+
}
|
|
270
|
+
} catch { /* access accounting never breaks recall */ }
|
|
271
|
+
|
|
229
272
|
if (format === 'json') {
|
|
230
273
|
for (const error of searchErrors) console.error(`Search failed for ${error.label}: ${error.message.split('\n')[0]}`);
|
|
231
274
|
if (searchErrors.length && !allResults.length) process.exitCode = 1;
|
package/src/cli/remember.mjs
CHANGED
|
@@ -37,6 +37,7 @@ function printHelp() {
|
|
|
37
37
|
--object <string> SPO Object (default: brain)
|
|
38
38
|
--related <list> Comma-separated list of related slugs
|
|
39
39
|
--expires <duration> TTL for temporary rules (e.g. "7d", "2w", "30d", "6h", "3m")
|
|
40
|
+
--allow-long Permit a rule above 800 characters (context:policy also permits it)
|
|
40
41
|
--confined-fact Create a descriptive fact with generated identity; refresh local search without executable generators
|
|
41
42
|
|
|
42
43
|
Examples:
|
|
@@ -74,9 +75,6 @@ function parseDuration(str) {
|
|
|
74
75
|
return date;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
const RULE_SOFT_LIMIT = 300;
|
|
78
|
-
const RULE_HARD_LIMIT = 800;
|
|
79
|
-
|
|
80
78
|
export default async function remember(args) {
|
|
81
79
|
// Parse layer flag first
|
|
82
80
|
const { layer: explicitLayer, remainingArgs: layerArgs } = parseLayerFlag(args);
|
|
@@ -244,16 +242,6 @@ export default async function remember(args) {
|
|
|
244
242
|
}
|
|
245
243
|
}
|
|
246
244
|
|
|
247
|
-
// Rules load into every task capsule, so they must be brief.
|
|
248
|
-
if (['invariants', 'preferences', 'anti-patterns'].includes(category) && !tags.includes('context:policy')) {
|
|
249
|
-
const n = bodyContent.trim().length;
|
|
250
|
-
if (n > RULE_HARD_LIMIT && !allowLong) {
|
|
251
|
-
console.error(`❌ Rule is ${n} characters (limit ${RULE_HARD_LIMIT}). Rules must be brief: one or two sentences, no history, dates, quotes or incident detail (save those as a fact). Shorten it, or pass --allow-long.`);
|
|
252
|
-
process.exit(1);
|
|
253
|
-
}
|
|
254
|
-
if (n > RULE_SOFT_LIMIT) console.error(`⚠️ Rule is ${n} characters. Keep rules brief (under ${RULE_SOFT_LIMIT}): state the constraint only, move history and examples to a fact. Shorten with: total-recall edit <slug> "<shorter>"`);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
245
|
const resolvedAgentDir = resolveAgentDir(layer);
|
|
258
246
|
const resolvedBrainDir = resolveBrainDir(layer);
|
|
259
247
|
const skillsDir = path.join(resolvedAgentDir, 'skills');
|
|
@@ -344,7 +332,7 @@ export default async function remember(args) {
|
|
|
344
332
|
|
|
345
333
|
// Saving memory never optimizes or archives other canonical documents.
|
|
346
334
|
|
|
347
|
-
const vaultResult = await writeNodeValidatedAsync(node, vaultDir);
|
|
335
|
+
const vaultResult = await writeNodeValidatedAsync(node, vaultDir, { allowLong });
|
|
348
336
|
if (!vaultResult.success) {
|
|
349
337
|
console.error(` ❌ Validation failed: ${vaultResult.validation.errors.join('; ')}`);
|
|
350
338
|
if (vaultResult.repair) {
|
|
@@ -355,6 +343,7 @@ export default async function remember(args) {
|
|
|
355
343
|
}
|
|
356
344
|
process.exit(1);
|
|
357
345
|
}
|
|
346
|
+
for (const warning of vaultResult.validation?.warnings || []) console.error(` ⚠️ ${warning}`);
|
|
358
347
|
console.log(` ✅ Permanent SSSS memory node created ${layerLabel} in vault: memory-vault/${category}/${finalSlug}.md`);
|
|
359
348
|
|
|
360
349
|
// Automatically recompile surfaces and local indexes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ensureServerService } from '../core/server-service.mjs';
|
|
2
|
+
|
|
3
|
+
export default async function server(args = []) {
|
|
4
|
+
if (!args.length || args.includes('--help') || args.includes('-h')) {
|
|
5
|
+
console.log('Usage: total-recall server ensure [--dry-run]\nRepair this package\'s macOS LaunchAgent and verify its process. --dry-run prints the plist diff without changes. On systemd hosts, server-release owns the server.');
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (args[0] !== 'ensure' || args.slice(1).some(arg => arg !== '--dry-run')) {
|
|
9
|
+
console.error('Usage: total-recall server ensure [--dry-run]');
|
|
10
|
+
process.exitCode = 2;
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const result = await ensureServerService({ dryRun: args.includes('--dry-run') });
|
|
15
|
+
console.log(result.diff || result.reason || `Server verified: pid ${result.pid}, ${result.entry}`);
|
|
16
|
+
return result;
|
|
17
|
+
} catch (error) { console.error(error.message); process.exitCode = 1; }
|
|
18
|
+
}
|
package/src/cli/startup.mjs
CHANGED
|
@@ -36,6 +36,7 @@ export default async function startup(args = []) {
|
|
|
36
36
|
if (args.includes('--json')) console.log(JSON.stringify(report, null, 2));
|
|
37
37
|
else {
|
|
38
38
|
console.log(`Startup: required plugins ${report.required_plugins?.status || 'unknown'}; server ${report.server?.status || 'unknown'}; brain ${report.brain?.status || 'unknown'}; daemon ${report.daemon?.status || 'unknown'}; SSSS tooling ${report.ssss?.status || 'unknown'}; app ${report.app?.status || 'unknown'}`);
|
|
39
|
+
if (report.server?.reason) console.log(`Server: ${report.server.reason}`);
|
|
39
40
|
if (report.server?.stale) console.log(`Server: running ${report.server.stale.running} with ${report.server.stale.on_disk} on disk; it has not been replaced.`);
|
|
40
41
|
for (const error of report.required_plugins?.errors || []) console.log(`Required plugin: ${error}`);
|
|
41
42
|
const harnesses = await queryHarnessUsage().catch(() => []);
|
package/src/cli/task.mjs
CHANGED
|
@@ -34,10 +34,11 @@ function printHelp() {
|
|
|
34
34
|
show <slug> Show one task
|
|
35
35
|
cancel <slug> Cancel a task, optionally with --expected-status pending
|
|
36
36
|
executors List registered executor ids
|
|
37
|
+
idle Enqueue leader maintenance from an automations manifest task
|
|
37
38
|
|
|
38
39
|
add options:
|
|
39
40
|
--kind <k> memory | research | maintenance | system | custom (default: custom)
|
|
40
|
-
--executor <id> dream | custom | research |
|
|
41
|
+
--executor <id> dream | custom | research | legacy
|
|
41
42
|
--priority <p> high | normal | low | absolute | 0-100 (default: normal)
|
|
42
43
|
--cap <list> Comma-separated capabilities (default: vault:read)
|
|
43
44
|
Use vault:write to draft results into memory-inbox
|
|
@@ -58,7 +59,7 @@ function printHelp() {
|
|
|
58
59
|
|
|
59
60
|
Policy:
|
|
60
61
|
shell / net:post capabilities are denied by default.
|
|
61
|
-
|
|
62
|
+
Empty queues stay empty. Automations owns the idle schedule.
|
|
62
63
|
`);
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -168,6 +169,12 @@ export default async function taskCli(argv) {
|
|
|
168
169
|
const brainDir = resolveBrainDir(layer);
|
|
169
170
|
const queueDir = resolveQueueDir(brainDir);
|
|
170
171
|
|
|
172
|
+
if (opts.command === 'idle') {
|
|
173
|
+
const { enqueueScheduledIdleTasks } = await import('../core/scheduler.mjs');
|
|
174
|
+
console.log(JSON.stringify(await enqueueScheduledIdleTasks({ queueDir, vaultDir: path.join(brainDir, 'memory-vault') })));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
171
178
|
if (opts.command === 'executors') {
|
|
172
179
|
console.log('\n Registered executors:\n');
|
|
173
180
|
for (const id of listExecutorIds()) {
|
package/src/cli/update.mjs
CHANGED
|
@@ -50,6 +50,7 @@ export function parseArgs(args) {
|
|
|
50
50
|
else if (a === '--port' && args[i + 1]) opts.port = Number(args[++i]);
|
|
51
51
|
else if (a === '--no-build') opts.noBuild = true;
|
|
52
52
|
else if (a === '--all-brains') opts.allBrains = true;
|
|
53
|
+
else if (a === '--install-missing') opts.installMissing = true;
|
|
53
54
|
else if (a === '--json') opts.json = true;
|
|
54
55
|
else if (a === '--automatic') opts.automatic = true;
|
|
55
56
|
else if (a === '--status') opts.status = true;
|
|
@@ -70,9 +71,11 @@ function printHelp() {
|
|
|
70
71
|
total-recall update --apply --force Ignore throttle / disabled flag
|
|
71
72
|
total-recall update --repo <path> Include extra root (repeatable)
|
|
72
73
|
total-recall update --all-brains Plan SSSS + Total Recall updates, including plugins/nested packages
|
|
73
|
-
total-recall update --all-brains --apply [--json]
|
|
74
|
+
total-recall update --all-brains --apply [--install-missing] [--json]
|
|
74
75
|
Pin the same published versions, use npm/pnpm, replace shared dependency
|
|
75
76
|
links with independent local installs, and verify every resolved version.
|
|
77
|
+
A checkout with no node_modules keeps none: only its pin is updated.
|
|
78
|
+
--install-missing installs those trees too (never done automatically).
|
|
76
79
|
A plugin checkout linked into a brain but not registered as a project is
|
|
77
80
|
only installed here: its dev dependencies are skipped (it runs under the
|
|
78
81
|
shared install) and its runtime ones update with --omit=dev, without
|
|
@@ -127,7 +130,7 @@ export default async function update(args = []) {
|
|
|
127
130
|
|
|
128
131
|
if (opts.allBrains) {
|
|
129
132
|
const summary = await updateAllBrains({ brainDir, roots: opts.repos,
|
|
130
|
-
dryRun: !opts.apply || opts.dryRun, ensureDefaults: true });
|
|
133
|
+
dryRun: !opts.apply || opts.dryRun, ensureDefaults: true, installMissing: opts.installMissing === true });
|
|
131
134
|
if (summary.failed) process.exitCode = 1;
|
|
132
135
|
if (opts.apply && !opts.dryRun) {
|
|
133
136
|
const hooks = await installHarnessHooks({ log: (line) => { if (!opts.json) console.log(line); } });
|
|
@@ -137,6 +140,7 @@ export default async function update(args = []) {
|
|
|
137
140
|
if (opts.json) console.log(JSON.stringify(summary, null, 2));
|
|
138
141
|
else {
|
|
139
142
|
console.log(`\n Uniform brain updates: ${summary.consumer_count || 0} consumers`);
|
|
143
|
+
console.log(` Missing: ${summary.not_installed || 0}; current: ${summary.up_to_date || 0}; would update: ${summary.would_update || 0}; updated: ${summary.updated || 0}; failed: ${summary.failed || 0}`);
|
|
140
144
|
if (summary.reason) console.log(` ${summary.reason}`);
|
|
141
145
|
for (const row of summary.results) {
|
|
142
146
|
console.log(` ${row.root}: ${row.status}`);
|