total-recall-brain 3.47.0 → 3.47.1

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 (65) hide show
  1. package/bin/total-recall.mjs +5 -0
  2. package/package.json +1 -1
  3. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +4 -4
  4. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +3 -2
  5. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +30 -5
  6. package/scaffold/.agent/skills/total-recall/plugins/auth/skills/auth/SKILL.md +35 -0
  7. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/home.js +494 -79
  8. package/scaffold/.agent/skills/total-recall/plugins/automations/package-lock.json +4 -4
  9. package/scaffold/.agent/skills/total-recall/plugins/automations/package.json +3 -2
  10. package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +33 -8
  11. package/scaffold/.agent/skills/total-recall/plugins/automations/skills/automations/SKILL.md +29 -0
  12. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/dream-consolidation.md +17 -0
  13. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +4 -4
  14. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +3 -2
  15. package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +31 -4
  16. package/scaffold/.agent/skills/total-recall/plugins/code-quality/skills/code-quality/SKILL.md +27 -0
  17. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +4 -4
  18. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
  19. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +17 -3
  20. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +4 -4
  21. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +3 -2
  22. package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +24 -4
  23. package/scaffold/.agent/skills/total-recall/plugins/embeddings/skills/embeddings/SKILL.md +25 -0
  24. package/scaffold/.agent/skills/total-recall/plugins/git/package-lock.json +4 -4
  25. package/scaffold/.agent/skills/total-recall/plugins/git/package.json +3 -2
  26. package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +293 -58
  27. package/scaffold/.agent/skills/total-recall/plugins/git/skills/git/SKILL.md +36 -0
  28. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +4 -4
  29. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +3 -2
  30. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +47 -14
  31. package/scaffold/.agent/skills/total-recall/plugins/mesh/skills/mesh/SKILL.md +36 -0
  32. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +6 -3
  33. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +1 -1
  34. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +5 -2
  35. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +4 -2
  36. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +28 -9
  37. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +7 -1
  38. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/model-selection.mjs +59 -0
  39. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/remote-operation.mjs +3 -3
  40. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +9 -3
  41. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +50 -6
  42. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/session-controls.md +16 -0
  43. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +56 -7
  44. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +6 -6
  45. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +3 -2
  46. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +199 -36
  47. package/scaffold/.agent/skills/total-recall/plugins/openwiki/skills/openwiki/SKILL.md +32 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +34 -8
  49. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +6 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +15 -7
  51. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +4 -3
  52. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +4 -4
  53. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +1 -1
  54. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +22 -5
  55. package/scaffold/.agent/skills/total-recall/plugins/ssss/package.json +3 -2
  56. package/scaffold/.agent/skills/total-recall/plugins/ssss/plugin.json +169 -37
  57. package/scaffold/.agent/skills/total-recall/plugins/ssss/skills/ssss/SKILL.md +30 -0
  58. package/src/cli/plugin/create.mjs +2 -1
  59. package/src/cli/plugin/monetization.mjs +66 -32
  60. package/src/cli/plugin/search.mjs +25 -3
  61. package/src/cli/preflight.mjs +40 -6
  62. package/src/core/gate-mode.mjs +13 -24
  63. package/src/core/plugin-distribution.mjs +35 -1
  64. package/src/core/preflight-gate.mjs +28 -8
  65. package/src/core/preflight-receipt.mjs +11 -0
@@ -2,15 +2,29 @@
2
2
  "$schema": "https://github.com/gregiteen/total-recall/blob/main/metadata.plugin.schema.json",
3
3
  "id": "ssss",
4
4
  "name": "SSSS",
5
- "version": "0.2.0",
5
+ "version": "0.2.1",
6
6
  "description": "Canonical SSSS distribution, scaffolding and validation for Total Recall installations.",
7
7
  "license": "MIT",
8
- "use_cases": ["software-development", "state-conformance"],
9
- "distribution": { "tier": "core", "license": "open" },
8
+ "use_cases": [
9
+ "software-development",
10
+ "state-conformance"
11
+ ],
12
+ "distribution": {
13
+ "tier": "core",
14
+ "license": "open"
15
+ },
10
16
  "gate": {
11
- "tasks": ["Validate SSSS documents and registries", "Scaffold a standalone SSSS project", "Run the SSSS conformance suites", "Restore a missing SSSS skill or scaffold"],
17
+ "tasks": [
18
+ "Validate SSSS documents and registries",
19
+ "Scaffold a standalone SSSS project",
20
+ "Run the SSSS conformance suites",
21
+ "Restore a missing SSSS skill or scaffold"
22
+ ],
12
23
  "owned_commands": [
13
- { "pattern": "npx\\s+(--yes\\s+)?@gregiteen/ssss-cli\\s+new\\b", "use": "total-recall ssss scaffold --directory <folder> --json" }
24
+ {
25
+ "pattern": "npx\\s+(--yes\\s+)?@gregiteen/ssss-cli\\s+new\\b",
26
+ "use": "total-recall ssss scaffold --directory <folder> --json"
27
+ }
14
28
  ]
15
29
  },
16
30
  "cli": {
@@ -19,65 +33,183 @@
19
33
  "handler": "cli.mjs",
20
34
  "subcommands": [
21
35
  {
22
- "name": "status", "description": "Inspect the installed engine, skill and full scaffold",
23
- "effect": "read", "output": "json",
24
- "args": [{"name":"scope","flag":"--scope","label":"Brain scope","type":"choice","options":["auto","project","global"]}]
36
+ "name": "status",
37
+ "description": "Inspect the installed engine, skill and full scaffold",
38
+ "effect": "read",
39
+ "output": "json",
40
+ "args": [
41
+ {
42
+ "name": "scope",
43
+ "flag": "--scope",
44
+ "label": "Brain scope",
45
+ "type": "choice",
46
+ "options": [
47
+ "auto",
48
+ "project",
49
+ "global"
50
+ ]
51
+ }
52
+ ]
25
53
  },
26
54
  {
27
- "name": "repair", "description": "Restore missing or previously repinned managed SSSS installation files",
28
- "effect": "write", "output": "json",
29
- "args": [{"name":"scope","flag":"--scope","label":"Brain scope","type":"choice","options":["auto","project","global"]}]
55
+ "name": "repair",
56
+ "description": "Restore missing or previously repinned managed SSSS installation files",
57
+ "effect": "write",
58
+ "output": "json",
59
+ "args": [
60
+ {
61
+ "name": "scope",
62
+ "flag": "--scope",
63
+ "label": "Brain scope",
64
+ "type": "choice",
65
+ "options": [
66
+ "auto",
67
+ "project",
68
+ "global"
69
+ ]
70
+ }
71
+ ]
30
72
  },
31
73
  {
32
- "name": "validate", "description": "Validate a document against the effective SSSS registry",
33
- "effect": "read", "output": "json",
74
+ "name": "validate",
75
+ "description": "Validate a document against the effective SSSS registry",
76
+ "effect": "read",
77
+ "output": "json",
34
78
  "args": [
35
- {"name":"file","flag":"--file","label":"Document path","type":"string","required":true},
36
- {"name":"extension","flag":"--extension","label":"Extension registry path","type":"string"}
79
+ {
80
+ "name": "file",
81
+ "flag": "--file",
82
+ "label": "Document path",
83
+ "type": "string",
84
+ "required": true
85
+ },
86
+ {
87
+ "name": "extension",
88
+ "flag": "--extension",
89
+ "label": "Extension registry path",
90
+ "type": "string"
91
+ }
37
92
  ]
38
93
  },
39
94
  {
40
- "name": "registry", "description": "Verify the effective registry against a lock file",
41
- "effect": "read", "output": "json",
95
+ "name": "registry",
96
+ "description": "Verify the effective registry against a lock file",
97
+ "effect": "read",
98
+ "output": "json",
42
99
  "args": [
43
- {"name":"lock","flag":"--lock","label":"Registry lock path","type":"string","required":true},
44
- {"name":"extension","flag":"--extension","label":"Extension registry path","type":"string"}
100
+ {
101
+ "name": "lock",
102
+ "flag": "--lock",
103
+ "label": "Registry lock path",
104
+ "type": "string",
105
+ "required": true
106
+ },
107
+ {
108
+ "name": "extension",
109
+ "flag": "--extension",
110
+ "label": "Extension registry path",
111
+ "type": "string"
112
+ }
45
113
  ]
46
114
  },
47
115
  {
48
- "name": "scaffold", "description": "Create a complete canonical SSSS project",
49
- "effect": "write", "output": "json",
116
+ "name": "scaffold",
117
+ "description": "Create a complete canonical SSSS project",
118
+ "effect": "write",
119
+ "output": "json",
50
120
  "args": [
51
- {"name":"directory","flag":"--directory","label":"New project directory","type":"string","required":true},
52
- {"name":"extension","flag":"--extension","label":"Extension registry name","type":"string"},
53
- {"name":"withTotalRecall","flag":"--with-total-recall","label":"Include Total Recall wiring","type":"boolean"}
121
+ {
122
+ "name": "directory",
123
+ "flag": "--directory",
124
+ "label": "New project directory",
125
+ "type": "string",
126
+ "required": true
127
+ },
128
+ {
129
+ "name": "extension",
130
+ "flag": "--extension",
131
+ "label": "Extension registry name",
132
+ "type": "string"
133
+ },
134
+ {
135
+ "name": "withTotalRecall",
136
+ "flag": "--with-total-recall",
137
+ "label": "Include Total Recall wiring",
138
+ "type": "boolean"
139
+ }
54
140
  ]
55
141
  },
56
142
  {
57
- "name": "conformance", "description": "Run the canonical reference engine conformance suite",
58
- "effect": "read", "output": "json"
143
+ "name": "conformance",
144
+ "description": "Run the canonical reference engine conformance suite",
145
+ "effect": "read",
146
+ "output": "json"
59
147
  }
60
148
  ]
61
149
  },
62
- "status": { "command": "status", "refresh_seconds": 60 },
63
- "runtime": { "modules": {
64
- "installation": "runtime/installation.cjs",
65
- "project-scaffold": "runtime/project-scaffold.mjs"
66
- } },
150
+ "status": {
151
+ "command": "status",
152
+ "refresh_seconds": 60
153
+ },
154
+ "runtime": {
155
+ "modules": {
156
+ "installation": "runtime/installation.cjs",
157
+ "project-scaffold": "runtime/project-scaffold.mjs"
158
+ }
159
+ },
67
160
  "capability": {
68
161
  "entry": "capability/index.mjs",
69
- "exports": ["canonicalToolchain", "scaffoldProject", "validateDocument", "verifyRegistry", "runConformance"]
162
+ "exports": [
163
+ "canonicalToolchain",
164
+ "scaffoldProject",
165
+ "validateDocument",
166
+ "verifyRegistry",
167
+ "runConformance"
168
+ ]
70
169
  },
71
170
  "branding": {
72
- "name": "SSSS", "color": "#b98439", "accent": "#f4c97a", "font": "ibm-plex",
73
- "density": "compact", "radius": "sm", "header": "ledger-rule", "logo": "ui/logo.svg"
171
+ "name": "SSSS",
172
+ "color": "#b98439",
173
+ "accent": "#f4c97a",
174
+ "font": "ibm-plex",
175
+ "density": "compact",
176
+ "radius": "sm",
177
+ "header": "ledger-rule",
178
+ "logo": "ui/logo.svg"
179
+ },
180
+ "auth": {
181
+ "plugin": "auth",
182
+ "methods": [
183
+ "passkey",
184
+ "google-oauth2"
185
+ ],
186
+ "session_verification": true
74
187
  },
75
- "auth": { "plugin": "auth", "methods": ["passkey", "google-oauth2"], "session_verification": true },
76
188
  "ui": {
77
189
  "design_tokens": "DESIGN.md",
78
190
  "home": "home",
79
191
  "elements": [
80
- {"id":"home","kind":"panel","tag":"tr-ssss-home","module":"ui/home.js","description":"SSSS installation ledger with document, registry, scaffold and conformance checks"}
192
+ {
193
+ "id": "home",
194
+ "kind": "panel",
195
+ "tag": "tr-ssss-home",
196
+ "module": "ui/home.js",
197
+ "description": "SSSS installation ledger with document, registry, scaffold and conformance checks"
198
+ }
81
199
  ]
82
- }
200
+ },
201
+ "reuse": {
202
+ "commands": [
203
+ "total-recall auth session",
204
+ "total-recall decision status"
205
+ ],
206
+ "owned": "SSSS canonical toolchain, schema/document validation, registry verification, project scaffolding, and conformance checks."
207
+ },
208
+ "skills": [
209
+ {
210
+ "id": "ssss",
211
+ "path": "./skills/ssss/SKILL.md",
212
+ "description": "Canonical SSSS distribution, scaffolding and validation for Total Recall installations."
213
+ }
214
+ ]
83
215
  }
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: ssss
3
+ description: "Canonical SSSS distribution, scaffolding and validation for Total Recall installations."
4
+ version: 0.2.0
5
+ repo_scoped: false
6
+ ---
7
+
8
+ # SSSS
9
+
10
+ Canonical SSSS distribution, scaffolding and validation for Total Recall installations.
11
+
12
+ ## When to Use
13
+
14
+ Use the `ssss` Total Recall plugin (`total-recall ssss`) instead of ad-hoc scripts or raw commands whenever working on ssss tasks.
15
+
16
+ ## Commands
17
+
18
+ ```bash
19
+ total-recall ssss --help
20
+ total-recall ssss status # Inspect the installed engine, skill and full scaffold
21
+ total-recall ssss repair # Restore missing or previously repinned managed SSSS installation files
22
+ total-recall ssss validate # Validate a document against the effective SSSS registry
23
+ total-recall ssss registry # Verify the effective registry against a lock file
24
+ total-recall ssss scaffold # Create a complete canonical SSSS project
25
+ total-recall ssss conformance # Run the canonical reference engine conformance suite
26
+ ```
27
+
28
+ ## Owned Raw Commands
29
+
30
+ This plugin owns: `npx\s+(--yes\s+)?@gregiteen/ssss-cli\s+new\b`.
@@ -274,7 +274,7 @@ export async function createPlugin(args = []) {
274
274
  };
275
275
  }
276
276
 
277
- const withSkill = args.includes("--with-skill") || args.includes("--capability") || Boolean(fromSkillPath);
277
+ const withSkill = !args.includes("--no-skill");
278
278
  const isCapability = args.includes("--capability") || Boolean(fromSkillPath);
279
279
 
280
280
  if (withCli || isCapability || withTask) {
@@ -386,6 +386,7 @@ export default run;
386
386
  name: ${id}
387
387
  description: "${pluginDesc}"
388
388
  version: 0.1.0
389
+ repo_scoped: false
389
390
  ---
390
391
 
391
392
  # ${pluginName}
@@ -59,43 +59,50 @@ export async function managePluginLicense(args = []) {
59
59
  const TR = process.env.TOTAL_RECALL_BIN || 'total-recall';
60
60
  const amount = mon.price_cents || 1000;
61
61
  const currency = (mon.currency || 'USD').toLowerCase();
62
- const successUrl = 'https://total-recall.ai/plugins/success';
63
- const cancelUrl = 'https://total-recall.ai/plugins/cancel';
64
- try {
65
- const script = TR.endsWith('.mjs');
66
- const stripeRes = spawnSync(script ? process.execPath : TR, [
67
- ...(script ? [TR] : []),
68
- 'stripe', 'checkout',
69
- '--amount', String(amount),
70
- '--currency', currency,
71
- '--name', `${plugin.manifest.name} License`,
72
- '--success-url', successUrl,
73
- '--cancel-url', cancelUrl,
74
- '--json'
75
- ], { encoding: 'utf8', timeout: 15000 });
76
- if (stripeRes.status === 0 && stripeRes.stdout) {
77
- const parsed = JSON.parse(stripeRes.stdout);
78
- const url = parsed.url || parsed.checkout_url || parsed.data?.url;
79
- if (url) {
80
- if (json) {
81
- console.log(JSON.stringify({ plugin: pluginId, checkout_url: url, amount, currency }));
82
- } else {
83
- console.log(`\n💳 Stripe Checkout Session Created:`);
84
- console.log(` Plugin: ${plugin.manifest.name}`);
85
- console.log(` Amount: $${(amount / 100).toFixed(2)} ${currency.toUpperCase()}`);
86
- console.log(` Checkout URL: ${url}\n`);
87
- }
88
- return;
62
+ const baseUrl = (process.env.TR_STORE_BASE_URL || 'http://127.0.0.1:3000').replace(/\/+$/, '');
63
+ const sIdx = args.indexOf('--success-url');
64
+ const cIdx = args.indexOf('--cancel-url');
65
+ const successUrl = (sIdx !== -1 && args[sIdx + 1]) || `${baseUrl}/plugins/success?plugin=${encodeURIComponent(pluginId)}`;
66
+ const cancelUrl = (cIdx !== -1 && args[cIdx + 1]) || `${baseUrl}/plugins/cancel?plugin=${encodeURIComponent(pluginId)}`;
67
+ const stripeArgs = [
68
+ 'stripe', 'checkout',
69
+ '--amount', String(amount),
70
+ '--currency', currency,
71
+ '--name', `${plugin.manifest.name} License`,
72
+ '--success-url', successUrl,
73
+ '--cancel-url', cancelUrl,
74
+ '--json'
75
+ ];
76
+ if (mon.recipient?.account_id) stripeArgs.push('--account', mon.recipient.account_id);
77
+ const script = TR.endsWith('.mjs');
78
+ const stripeRes = spawnSync(script ? process.execPath : TR, [
79
+ ...(script ? [TR] : []),
80
+ ...stripeArgs
81
+ ], { encoding: 'utf8', timeout: 15000 });
82
+ if (stripeRes.status === 0 && stripeRes.stdout) {
83
+ let parsed = {};
84
+ try { parsed = JSON.parse(stripeRes.stdout); } catch {}
85
+ const url = parsed.url || parsed.checkout_url || parsed.data?.url;
86
+ if (url) {
87
+ if (json) {
88
+ console.log(JSON.stringify({ plugin: pluginId, checkout_url: url, session_id: parsed.id || parsed.session_id || null, amount, currency }));
89
+ } else {
90
+ console.log(`\n💳 Stripe Checkout Session Created:`);
91
+ console.log(` Plugin: ${plugin.manifest.name}`);
92
+ console.log(` Amount: $${(amount / 100).toFixed(2)} ${currency.toUpperCase()}`);
93
+ console.log(` Checkout URL: ${url}\n`);
89
94
  }
95
+ return;
90
96
  }
91
- } catch {}
97
+ }
98
+ const errDetail = (stripeRes.stderr || stripeRes.stdout || stripeRes.error?.message || 'Stripe checkout failed').trim();
92
99
  if (json) {
93
- console.log(JSON.stringify({ plugin: pluginId, price_formatted: mon.priceFormatted, requires_purchase: true }));
100
+ console.log(JSON.stringify({ ok: false, plugin: pluginId, error: errDetail, price_formatted: mon.priceFormatted, requires_purchase: true }));
94
101
  } else {
95
- console.log(`\n💳 Commercial License Purchase:`);
96
- console.log(` Plugin: ${plugin.manifest.name} (${mon.priceFormatted})`);
97
- console.log(` Activate license after purchase: total-recall plugin license ${pluginId} activate --stdin\n`);
102
+ console.error(`❌ Stripe checkout failed for '${pluginId}': ${errDetail}`);
103
+ console.error(` Activate an existing license with: total-recall plugin license ${pluginId} activate --stdin`);
98
104
  }
105
+ process.exitCode = 1;
99
106
  return;
100
107
  }
101
108
 
@@ -105,6 +112,33 @@ export async function managePluginLicense(args = []) {
105
112
  else console.log(`ℹ️ Plugin '${pluginId}' does not require a commercial license (${mon.model}).`);
106
113
  return;
107
114
  }
115
+ const sessIdx = args.indexOf('--session-id');
116
+ const sessionId = sessIdx !== -1 ? args[sessIdx + 1] : null;
117
+ if (sessionId) {
118
+ const TR = process.env.TOTAL_RECALL_BIN || 'total-recall';
119
+ const script = TR.endsWith('.mjs');
120
+ const vRes = spawnSync(script ? process.execPath : TR, [
121
+ ...(script ? [TR] : []),
122
+ 'stripe', 'verify-entitlement',
123
+ '--session-id', sessionId,
124
+ '--consumer', pluginId,
125
+ '--amount', String(mon.price_cents || 1000),
126
+ '--currency', (mon.currency || 'USD').toLowerCase(),
127
+ '--json'
128
+ ], { encoding: 'utf8', timeout: 15000 });
129
+ let vParsed = null;
130
+ try { vParsed = vRes.stdout ? JSON.parse(vRes.stdout) : null; } catch {}
131
+ if (vRes.status !== 0 || !vParsed?.entitled) {
132
+ const msg = vParsed?.error || (vRes.stderr || vRes.stdout || 'Stripe entitlement verification failed').trim();
133
+ if (json) console.log(JSON.stringify({ verified: false, plugin: pluginId, session_id: sessionId, error: msg }));
134
+ else console.error(`❌ Stripe entitlement check failed for '${pluginId}': ${msg}`);
135
+ process.exitCode = 1;
136
+ return;
137
+ }
138
+ if (json) console.log(JSON.stringify({ verified: true, plugin: pluginId, session_id: sessionId, status: 'active', entitlement: vParsed }));
139
+ else console.log(`✅ Stripe entitlement verified active for '${pluginId}' (session ${sessionId}).`);
140
+ return;
141
+ }
108
142
  const secretKey = mon.license_key_secret;
109
143
  const currentKey = secretKey ? await getSecret(defaultBrainDir(), secretKey, { actor: 'plugin-license' }) : null;
110
144
  if (!currentKey?.found || !currentKey?.value) {
@@ -2,17 +2,18 @@ import { listAvailableBundled } from '../../core/plugin-store.mjs';
2
2
 
3
3
  function parseArgs(args) {
4
4
  const isJson = args.includes('--json');
5
+ const isStore = args.includes('--store');
5
6
  let useCase = null;
6
7
  const words = [];
7
8
  for (let i = 0; i < args.length; i++) {
8
- if (args[i] === '--json') continue;
9
+ if (args[i] === '--json' || args[i] === '--store') continue;
9
10
  if (args[i] === '--use-case' && args[i + 1]) {
10
11
  useCase = args[++i];
11
12
  continue;
12
13
  }
13
14
  words.push(args[i]);
14
15
  }
15
- return { isJson, useCase, query: words.join(' ').toLowerCase().trim() };
16
+ return { isJson, isStore, useCase, query: words.join(' ').toLowerCase().trim() };
16
17
  }
17
18
 
18
19
  /**
@@ -108,7 +109,28 @@ export async function listPeers(args = []) {
108
109
 
109
110
  /** `plugin search <query>` — bundled plugins plus whatever mesh peers share. */
110
111
  export async function searchPlugins(args = []) {
111
- const { isJson, useCase, query } = parseArgs(args);
112
+ const { isJson, isStore, useCase, query } = parseArgs(args);
113
+ if (isStore) {
114
+ const base = (process.env.TR_STORE_URL || 'http://127.0.0.1:3000').replace(/\/+$/, '');
115
+ const params = new URLSearchParams();
116
+ if (query) params.set('q', query);
117
+ if (useCase) params.set('use_case', useCase);
118
+ const res = await fetch(`${base}/api/store/plugins${params.toString() ? `?${params}` : ''}`);
119
+ if (!res.ok) throw new Error(`Store catalog unavailable (HTTP ${res.status})`);
120
+ const data = await res.json();
121
+ const storePlugins = data.plugins || [];
122
+ if (isJson) {
123
+ console.log(JSON.stringify({ store: storePlugins }, null, 2));
124
+ return;
125
+ }
126
+ console.log(`\n🛒 Store plugins matching "${query || '*'}"${useCase ? ` (use case: ${useCase})` : ''}\n`);
127
+ if (storePlugins.length === 0) {
128
+ console.log('No store plugins found.\n');
129
+ return;
130
+ }
131
+ for (const p of storePlugins) printPlugin(p, `store:${p.id}`);
132
+ return;
133
+ }
112
134
  const bundled = listAvailableBundled(process.cwd()).filter((p) => matches(p, query, useCase));
113
135
  const { listPeerPlugins } = await import('../../core/plugin-peers.mjs');
114
136
  const peers = await listPeerPlugins();
@@ -7,7 +7,7 @@ import { globalBrainDirFor, isBrainEnabled } from '../core/brain-registry.mjs';
7
7
  import { GATE_MODES, gateConfigPath, projectBrainDir, readGateLayers, resolveGateMode, writeGateMode } from '../core/gate-mode.mjs';
8
8
  import { installHarnessHooks, loadGateAdapters } from '../core/harness-hooks.mjs';
9
9
  import { ackDirectory, capsuleVerb, decide, gatePackageBroken, isAckCommand, isCheckCommand, isRouteCommand, isSubagentPayload, nativeToolOf, sessionOf, SWEEP_CONSULT_MS, whenGateFails } from '../core/preflight-gate.mjs';
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
+ import { ACK_COMMAND, acknowledge, checkAgentReceipt, checkAnyReceipt, findRoot, forgetCapsuleDeliveries, lastArrivalMs, lastCapsuleDelivery, lastConsultMs, lastRoutedMs, lastRouteSelection, lastRouteWarnedMs, lastSessionAck, markArrival, markDelegated, markRouted, markRouteSelection, markRouteWarned, markSessionAck, noteCaller, recordDecision, sessionHasStanding, sessionKey, readDecisions, takeCaller } from '../core/preflight-receipt.mjs';
11
11
 
12
12
  const USAGE = `Usage: total-recall preflight <command>
13
13
  ack --task "<task>" [--action read,edit,test] [--json] Record a receipt after context ready:true and rules verify, and print the capsule.
@@ -19,9 +19,9 @@ const USAGE = `Usage: total-recall preflight <command>
19
19
  remind [--json] Prompt-hook entry: advisory text when the receipt is missing; always exits 0
20
20
  session-start Session-hook entry: after compaction or clear, the next ack prints the capsule; always exits 0
21
21
  install [--harness <id>|--all] [--dry-run] Install the gate for a harness, or for every harness here. Install and update do this. No off switch.
22
- mode [off|warn|enforce|clear] [--plugin <id>|--router] [--repo] [--json]
23
- Show or set what happens when work goes around a plugin. Default is warn.
24
- The preflight receipt itself has no off switch.
22
+ mode [enforce] [--plugin <id>|--router] [--repo] [--json]
23
+ Show gate enforcement status. All plugin and routing gates are permanently
24
+ enforced; off, warn, and clear bypasses are disabled.
25
25
  coverage [--dir <folder>] [--json] List plugins as declared or missing; exit 1 on a gap. Default: the shared plugin folder
26
26
  verify | report Harness adapters, owned by the cli-agents plugin
27
27
  Aliases: total-recall tr-preflight --ack | --check. A receipt lasts 30 minutes (config preflight_max_age_minutes).
@@ -397,6 +397,33 @@ function projectBrainRoot(dir) {
397
397
  return brain && isBrainEnabled(brain.brainDir) ? brain.projectRoot : null;
398
398
  }
399
399
 
400
+ /** How long a route's selection binds when the receipt names no window: the default receipt lifetime. */
401
+ const ROUTE_SELECTION_MS = 30 * 60000;
402
+
403
+ /**
404
+ * Records which harness a router command selected, so the gate can deny writes from the others.
405
+ * Called after the router plugin's own command runs, with what it printed. Output that is not a
406
+ * route result records nothing. Bookkeeping only: it never throws.
407
+ * @returns {string|null|undefined} the selected harness, null when the route chose none, undefined when nothing was recorded
408
+ */
409
+ export function noteRouteSelection({ manifest, args = [], stdout = '', cwd = process.cwd() } = {}) {
410
+ try {
411
+ const router = manifest?.gate?.router;
412
+ if (!router?.pattern) return undefined;
413
+ const command = `${manifest.cli?.command || manifest.id} ${args.map(String).join(' ')}`;
414
+ if (!new RegExp(`(?:^|\\s)(?:${router.pattern})(\\s|$)`).test(command)) return undefined;
415
+ const text = String(stdout || '').trim();
416
+ let result;
417
+ try { result = JSON.parse(text); } catch { result = JSON.parse(text.split('\n').pop()); }
418
+ if (!result || typeof result !== 'object' || !('selected' in result)) return undefined;
419
+ const selected = typeof result.selected === 'string' && result.selected ? result.selected : null;
420
+ const root = findRoot(cwd);
421
+ markRouted(root);
422
+ markRouteSelection(root, selected);
423
+ return selected;
424
+ } catch { return undefined; }
425
+ }
426
+
400
427
  /** The gate's answer to a call. Nothing here may throw, because an answer is all that keeps its repair reachable. */
401
428
  function evaluate(call, options = {}) {
402
429
  try {
@@ -450,6 +477,13 @@ function judge(call, { harness, readablePaths, session = null, subagent = false,
450
477
  ownerHealthy: (plugin, use) => ownerRuns(plugin, use, root),
451
478
  // A subagent or a run the router started reports to whoever routed the work.
452
479
  dispatched: runByRouter || subagent || Boolean(session && !ownAck),
480
+ // Only a named harness can be told apart from the one the router chose.
481
+ harness: harness && harness !== 'generic' ? harness : null,
482
+ routerRun: runByRouter,
483
+ selection: (receipt) => {
484
+ const chosen = lastRouteSelection(root);
485
+ return chosen && Date.now() - chosen.at < (limit ?? receipt?.limitMs ?? ROUTE_SELECTION_MS) ? chosen : null;
486
+ },
453
487
  check,
454
488
  recentConsult: () => {
455
489
  const now = Date.now();
@@ -569,14 +603,14 @@ async function mode(args) {
569
603
  const repo = args.includes('--repo');
570
604
  const brain = repo ? projectBrainDir(root) : globalBrainDirFor();
571
605
  if (value !== undefined) {
572
- if (value !== 'clear' && !GATE_MODES.includes(value)) return fail(`Unknown mode "${value}". Choose ${GATE_MODES.join(', ')} or clear.`);
606
+ if (value !== 'enforce') return fail(`✖ Plugin and routing gates are permanently enforced and have no "${value}" bypass.`);
573
607
  const plugin = flag(args, '--plugin');
574
608
  const router = args.includes('--router');
575
609
  if (plugin && router) return fail('Choose --plugin or --router, not both.');
576
610
  if (plugin && !plugins.includes(plugin)) return fail(`No plugin here declares raw commands under "${plugin}". Declared: ${plugins.join(', ') || 'none'}.`);
577
611
  if (router && !declared.router) return fail('No plugin here declares a router.');
578
612
  if (repo && !fs.existsSync(brain)) return fail(`${root} has no project brain to hold this setting. Run total-recall init there, or leave off --repo.`);
579
- writeGateMode(brain, { mode: value === 'clear' ? null : value, plugin, router });
613
+ writeGateMode(brain, { mode: 'enforce', plugin, router });
580
614
  }
581
615
  const layers = readGateLayers({ globalBrainDir: globalBrainDirFor(), root });
582
616
  const { adapters } = await loadGateAdapters(root);
@@ -1,19 +1,15 @@
1
1
  /**
2
2
  * Modes for the gate rules that steer work toward a plugin: raw commands a plugin owns, and
3
- * routing. `off` does nothing, `warn` lets the call run and tells the agent, `enforce` blocks.
4
- * The preflight receipt is not one of these rules and has no mode.
3
+ * routing. Gates are permanently enforced (`enforce`): there is no `off` or `warn` bypass.
5
4
  *
6
- * Settings live in `config/gate.json` in a brain:
7
- * { "default": "warn", "router": "enforce", "plugins": { "git": "enforce" } }
8
- * The repository's brain wins over the machine's. This runs on every tool call, so it reads the
9
- * files directly, and an unreadable file or unknown value counts as unset: a bad settings file
10
- * must never be what blocks work.
5
+ * Settings live in `config/gate.json` in a brain. Any legacy `off` or `warn` values in
6
+ * `gate.json` are ignored so enforcement cannot be disabled on disk.
11
7
  */
12
8
  import fs from 'node:fs';
13
9
  import path from 'node:path';
14
10
 
15
- export const GATE_MODES = ['off', 'warn', 'enforce'];
16
- export const DEFAULT_GATE_MODE = 'warn';
11
+ export const GATE_MODES = ['enforce'];
12
+ export const DEFAULT_GATE_MODE = 'enforce';
17
13
 
18
14
  export const gateConfigPath = (brainDir) => path.join(brainDir, 'config', 'gate.json');
19
15
  export const projectBrainDir = (root) => path.join(root, '.agent', 'skills', 'total-recall');
@@ -30,39 +26,32 @@ export function readGateLayers({ globalBrainDir, root }) {
30
26
  return [readSettings(projectBrainDir(root)), readSettings(globalBrainDir)];
31
27
  }
32
28
 
33
- const known = (value) => (GATE_MODES.includes(value) ? value : undefined);
34
-
35
29
  /**
36
30
  * @param {object[]} layers from readGateLayers
37
31
  * @param {'router'|'owned'} kind
38
32
  * @param {string} [plugin] the owning plugin, for `owned`
39
33
  */
40
- export function resolveGateMode(layers, kind, plugin) {
41
- for (const layer of layers) {
42
- const specific = kind === 'router' ? known(layer.router) : known(layer.plugins?.[plugin]);
43
- const mode = specific ?? known(layer.default);
44
- if (mode) return mode;
45
- }
34
+ export function resolveGateMode(_layers, _kind, _plugin) {
46
35
  return DEFAULT_GATE_MODE;
47
36
  }
48
37
 
49
38
  /**
50
- * Change one setting and leave the others alone. A `mode` of null removes the setting, so
51
- * that level follows the next layer again.
39
+ * Persist `enforce` mode. `off`, `warn`, and clearing are refused.
52
40
  * @param {string} brainDir
53
41
  * @param {{ mode: string|null, plugin?: string, router?: boolean }} change
54
42
  */
55
43
  export function writeGateMode(brainDir, { mode, plugin, router }) {
44
+ if (mode !== 'enforce') {
45
+ throw new Error('Plugin and routing gates are permanently enforced and cannot be set to off, warn, or cleared.');
46
+ }
56
47
  const settings = readSettings(brainDir);
57
48
  if (router) {
58
- if (mode === null) delete settings.router; else settings.router = mode;
49
+ settings.router = 'enforce';
59
50
  } else if (plugin) {
60
51
  settings.plugins = settings.plugins && typeof settings.plugins === 'object' ? settings.plugins : {};
61
- if (mode === null) delete settings.plugins[plugin]; else settings.plugins[plugin] = mode;
62
- } else if (mode === null) {
63
- delete settings.default;
52
+ settings.plugins[plugin] = 'enforce';
64
53
  } else {
65
- settings.default = mode;
54
+ settings.default = 'enforce';
66
55
  }
67
56
  const file = gateConfigPath(brainDir);
68
57
  fs.mkdirSync(path.dirname(file), { recursive: true });