total-recall-brain 3.40.6 → 3.41.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.
Files changed (170) hide show
  1. package/bin/total-recall-memory.mjs +0 -0
  2. package/bin/total-recall.mjs +9 -40
  3. package/docs/reference/cli-reference.md +65 -1
  4. package/frontend/dist/assets/{index-AGUes-VG.css → index-BG9NkVUy.css} +1 -1
  5. package/frontend/dist/assets/{index-Bt5iVPZj.js → index-BtuiZqwP.js} +20 -7
  6. package/frontend/dist/index.html +2 -2
  7. package/frontend/src/components/plugins/PluginPanel.tsx +31 -5
  8. package/frontend/src/components/plugins/card/CardEngine.tsx +15 -23
  9. package/frontend/src/components/plugins/card/CardScenes.tsx +52 -23
  10. package/frontend/src/components/plugins/card/card.css +2 -5
  11. package/frontend/src/components/plugins/card/contract.ts +13 -19
  12. package/frontend/src/components/plugins/card/format.ts +8 -1
  13. package/frontend/src/components/plugins/card/widgets.tsx +4 -35
  14. package/frontend/src/components/plugins/plugin-frame.ts +30 -3
  15. package/frontend/src/pages/PluginsPage.tsx +3 -2
  16. package/package.json +1 -1
  17. package/scaffold/.agent/skills/plugin/SKILL.md +110 -0
  18. package/scaffold/.agent/skills/plugin/evals/README.md +53 -0
  19. package/scaffold/.agent/skills/plugin/evals/cases/01-fabricated-seed.mjs +42 -0
  20. package/scaffold/.agent/skills/plugin/evals/cases/02-reassuring-default.mjs +39 -0
  21. package/scaffold/.agent/skills/plugin/evals/cases/03-declared-template.mjs +32 -0
  22. package/scaffold/.agent/skills/plugin/evals/cases/04-card-collects-credential.mjs +46 -0
  23. package/scaffold/.agent/skills/plugin/evals/cases/05-host-leak-in-scaffold.mjs +55 -0
  24. package/scaffold/.agent/skills/plugin/evals/cases/06-correct-plugin.mjs +92 -0
  25. package/scaffold/.agent/skills/plugin/evals/run.mjs +118 -0
  26. package/scaffold/.agent/skills/plugin/prompts/01-function.md +29 -0
  27. package/scaffold/.agent/skills/plugin/prompts/02-surfaces.md +43 -0
  28. package/scaffold/.agent/skills/plugin/prompts/03-scaffold.md +31 -0
  29. package/scaffold/.agent/skills/plugin/prompts/04-cli.md +49 -0
  30. package/scaffold/.agent/skills/plugin/prompts/05-credentials.md +41 -0
  31. package/scaffold/.agent/skills/plugin/prompts/06-card.md +46 -0
  32. package/scaffold/.agent/skills/plugin/prompts/07-scaffold.md +52 -0
  33. package/scaffold/.agent/skills/plugin/prompts/08-verify.md +43 -0
  34. package/scaffold/.agent/skills/plugin/references/anti-patterns.md +123 -0
  35. package/scaffold/.agent/skills/plugin/references/card.md +90 -0
  36. package/scaffold/.agent/skills/plugin/references/scaffold.md +120 -0
  37. package/scaffold/.agent/skills/plugin/references/surfaces.md +206 -0
  38. package/scaffold/.agent/skills/plugin/scripts/record.mjs +360 -0
  39. package/scaffold/.agent/skills/plugin/subagents/card-designer.md +60 -0
  40. package/scaffold/.agent/skills/plugin/subagents/plugin-reviewer.md +55 -0
  41. package/scaffold/.agent/skills/plugin/subagents/scaffold-extractor.md +71 -0
  42. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +71 -1441
  43. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +7 -5
  44. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +3 -44
  45. package/scaffold/.agent/skills/total-recall/plugins/auth/scripts/agent-login-pty.py +185 -0
  46. package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +20 -1
  47. package/scaffold/.agent/skills/total-recall/plugins/auth/src/host-secrets-store.mjs +15 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +6 -4
  49. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +1 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +0 -24
  51. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +4 -4
  52. package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +0 -21
  53. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +4 -4
  54. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +0 -32
  55. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +4 -4
  56. package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +11 -0
  57. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +5 -2
  58. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4 -4
  59. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +1 -28
  60. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +95 -13
  61. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +553 -340
  62. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +0 -36
  63. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/card.js +383 -400
  64. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +4 -4
  65. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +0 -1
  66. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +0 -28
  67. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +4 -4
  68. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +0 -32
  69. package/scaffold/.agent/skills/total-recall/references/auth.md +3 -1
  70. package/scaffold/.agent/skills/total-recall/references/memory.md +4 -0
  71. package/scaffold/.agent/skills/total-recall/references/openwiki.md +2 -0
  72. package/scaffold/.agent/skills/total-recall/references/operations.md +12 -0
  73. package/src/cli/brain.mjs +4 -2
  74. package/src/cli/context-stats.mjs +104 -0
  75. package/src/cli/context.mjs +8 -0
  76. package/src/cli/daemon.mjs +10 -1
  77. package/src/cli/export.mjs +3 -2
  78. package/src/cli/ingest-okf.mjs +12 -15
  79. package/src/cli/init.mjs +45 -85
  80. package/src/cli/lint.mjs +3 -2
  81. package/src/cli/plugin/copies.mjs +68 -0
  82. package/src/cli/plugin/create.mjs +22 -24
  83. package/src/cli/plugin/index.mjs +49 -3
  84. package/src/cli/plugin/search.mjs +19 -2
  85. package/src/cli/rebuild.mjs +2 -0
  86. package/src/cli/remember.mjs +23 -0
  87. package/src/cli/status.mjs +10 -3
  88. package/src/cli/update.mjs +3 -2
  89. package/src/core/action-predictor.mjs +161 -0
  90. package/src/core/app-deploy/runtime-cli/node.mjs.tmpl +20 -3
  91. package/src/core/app-deploy/source.mjs +16 -9
  92. package/src/core/automatic-brain-updates.mjs +6 -3
  93. package/src/core/brain-registry.mjs +19 -2
  94. package/src/core/brain-state.mjs +23 -0
  95. package/src/core/cli-commands.mjs +1 -0
  96. package/src/core/cli-maintenance.mjs +1 -1
  97. package/src/core/cli-semantic-index.mjs +1 -1
  98. package/src/core/command-surface.mjs +3 -3
  99. package/src/core/composed-command-resolution.mjs +21 -0
  100. package/src/core/config.mjs +84 -0
  101. package/src/core/confined-memory.mjs +93 -0
  102. package/src/core/context-telemetry.mjs +214 -0
  103. package/src/core/daemon-control.mjs +14 -43
  104. package/src/core/fast-recall.mjs +1 -1
  105. package/src/core/memory-brain.mjs +2 -0
  106. package/src/core/plugin-activation.mjs +38 -6
  107. package/src/core/plugin-bundle.mjs +54 -0
  108. package/src/core/plugin-catalog.mjs +1 -1
  109. package/src/core/plugin-context.mjs +1 -1
  110. package/src/core/plugin-contracts/card.mjs +69 -37
  111. package/src/core/plugin-loader.mjs +5 -6
  112. package/src/core/plugin-runner.mjs +13 -6
  113. package/src/core/plugin-runtime.mjs +5 -4
  114. package/src/core/plugin-setup.mjs +5 -3
  115. package/src/core/plugin-state-store.mjs +12 -1
  116. package/src/core/plugin-state.mjs +46 -5
  117. package/src/core/plugin-store.mjs +78 -3
  118. package/src/core/plugin-tasks.mjs +3 -1
  119. package/src/core/plugin-updates.mjs +117 -0
  120. package/src/core/plugin-vendor.mjs +10 -4
  121. package/src/core/project-brain.mjs +14 -41
  122. package/src/core/project-id.mjs +4 -1
  123. package/src/core/required-plugins.mjs +151 -13
  124. package/src/core/skills-registry.mjs +27 -3
  125. package/src/core/ssss-kernel-bridge.mjs +3 -1
  126. package/src/core/startup-health.mjs +66 -16
  127. package/src/core/surface.mjs +51 -44
  128. package/src/core/testing/plugin-fixture.mjs +69 -0
  129. package/src/core/vault-files.mjs +13 -0
  130. package/src/core/vault.mjs +4 -30
  131. package/src/server/index.mjs +13 -1
  132. package/src/server/invoke-decision-child.mjs +49 -0
  133. package/src/server/invoke-execution.mjs +87 -0
  134. package/src/server/invoke-policy.mjs +64 -0
  135. package/src/server/invoke-read-child.mjs +26 -0
  136. package/src/server/invoke-targets.mjs +49 -0
  137. package/src/server/invoke-write-child.mjs +19 -0
  138. package/src/server/routes/invoke.mjs +314 -0
  139. package/src/server/routes/plugins.mjs +1 -1
  140. package/scaffold/.agent/skills/plugins/SKILL.md +0 -57
  141. package/scaffold/.agent/skills/plugins/references/card-design.md +0 -55
  142. package/scaffold/.agent/skills/plugins/references/distribution.md +0 -23
  143. package/scaffold/.agent/skills/plugins/references/manifest-contract.md +0 -90
  144. package/scaffold/.agent/skills/plugins/references/optimized/index.md +0 -9
  145. package/scaffold/.agent/skills/plugins/references/optimized/manifest.json +0 -35
  146. package/scaffold/.agent/skills/plugins/references/optimized/original.md +0 -58
  147. package/scaffold/.agent/skills/plugins/references/optimized/section-01.md +0 -5
  148. package/scaffold/.agent/skills/plugins/references/optimized/section-02.md +0 -25
  149. package/scaffold/.agent/skills/plugins/references/optimized/section-03.md +0 -12
  150. package/scaffold/.agent/skills/plugins/references/optimized/section-04.md +0 -7
  151. package/scaffold/.agent/skills/plugins/references/optimized/section-05.md +0 -5
  152. package/scaffold/.agent/skills/total-recall/openwiki/README.md +0 -10
  153. package/scaffold/.agent/skills/total-recall/openwiki/architecture.md +0 -5
  154. package/scaffold/.agent/skills/total-recall/openwiki/ide.md +0 -10
  155. package/scaffold/.agent/skills/total-recall/openwiki/memory.md +0 -11
  156. package/scaffold/.agent/skills/total-recall/openwiki/secrets.md +0 -7
  157. package/scaffold/.agent/skills/total-recall/openwiki/skills.md +0 -7
  158. package/scaffold/openwiki/README.md +0 -10
  159. package/scaffold/openwiki/architecture.md +0 -5
  160. package/scaffold/openwiki/ide.md +0 -10
  161. package/scaffold/openwiki/memory.md +0 -11
  162. package/scaffold/openwiki/secrets.md +0 -7
  163. package/scaffold/openwiki/skills.md +0 -7
  164. package/src/core/okf-adapter.mjs +0 -559
  165. package/templates/openwiki/README.md +0 -10
  166. package/templates/openwiki/architecture.md +0 -5
  167. package/templates/openwiki/ide.md +0 -10
  168. package/templates/openwiki/memory.md +0 -11
  169. package/templates/openwiki/secrets.md +0 -7
  170. package/templates/openwiki/skills.md +0 -7
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tr-plugin-auth",
3
- "version": "1.0.4",
3
+ "version": "1.2.1",
4
4
  "description": "White-label Total Recall auth plugin: Passkey biometric FIDO2 and Google OAuth2 authentication.",
5
5
  "type": "module",
6
6
  "main": "cli.mjs",
@@ -16,18 +16,20 @@
16
16
  ],
17
17
  "license": "MIT",
18
18
  "dependencies": {
19
- "yaml": "^2.9.0",
19
+ "@simplewebauthn/server": "^13.3.2",
20
20
  "bcrypt": "^6.0.0",
21
- "jsonwebtoken": "^9.0.3",
21
+ "express": "^5.2.1",
22
22
  "express-rate-limit": "^8.5.1",
23
- "@simplewebauthn/server": "^13.3.2",
24
- "express": "^5.2.1"
23
+ "jsonwebtoken": "^9.0.3",
24
+ "yaml": "^2.9.0"
25
25
  },
26
26
  "files": [
27
27
  "cli.mjs",
28
28
  "plugin.json",
29
29
  "README.md",
30
30
  "src/",
31
+ "test/",
32
+ "scripts/",
31
33
  "!src/**/*.spec.mjs",
32
34
  "ui/"
33
35
  ],
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://github.com/gregiteen/total-recall/blob/main/metadata.plugin.schema.json",
3
3
  "id": "auth",
4
4
  "name": "Auth & Secrets",
5
- "version": "1.0.1",
5
+ "version": "1.2.1",
6
6
  "description": "Hardware WebAuthn biometric FIDO2 passkeys, OAuth session security, and access control.",
7
7
  "author": "Total Recall contributors",
8
8
  "license": "MIT",
@@ -146,6 +146,7 @@
146
146
  ]
147
147
  },
148
148
  "ui": {
149
+ "design_tokens": "ui/DESIGN.md",
149
150
  "elements": [
150
151
  {
151
152
  "id": "card",
@@ -153,13 +154,6 @@
153
154
  "tag": "tr-auth-card",
154
155
  "module": "ui/card.js",
155
156
  "description": "Gatepass Card Face"
156
- },
157
- {
158
- "id": "panel",
159
- "kind": "panel",
160
- "tag": "tr-auth-panel",
161
- "module": "ui/panel.js",
162
- "description": "Auth Management Panel"
163
157
  }
164
158
  ]
165
159
  },
@@ -225,6 +219,7 @@
225
219
  "runtime": {
226
220
  "modules": {
227
221
  "secrets-store": "src/secrets-store.mjs",
222
+ "host-secrets-store": "src/host-secrets-store.mjs",
228
223
  "secrets-gateway": "src/secrets-gateway.mjs",
229
224
  "secrets-keychain": "src/secrets-keychain.mjs",
230
225
  "secrets-env-diff": "src/secrets-env-diff.mjs",
@@ -266,41 +261,5 @@
266
261
  "distribution": {
267
262
  "tier": "official",
268
263
  "license": "open"
269
- },
270
- "card_layout": {
271
- "template": "vault",
272
- "regions": [
273
- {
274
- "widget": "status",
275
- "title": "Status"
276
- },
277
- {
278
- "widget": "live",
279
- "title": "Live view"
280
- },
281
- {
282
- "widget": "list",
283
- "title": "Details",
284
- "source": {
285
- "command": "audit"
286
- }
287
- },
288
- {
289
- "widget": "actions",
290
- "title": "Commands"
291
- },
292
- {
293
- "widget": "settings",
294
- "title": "Settings"
295
- },
296
- {
297
- "widget": "credentials",
298
- "title": "Credentials"
299
- },
300
- {
301
- "widget": "activity",
302
- "title": "Recent activity"
303
- }
304
- ]
305
264
  }
306
265
  }
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env python3
2
+ """Sign in an agent CLI that authenticates on its own terminal.
3
+
4
+ Some agent CLIs (Antigravity / `agy`) print an OAuth consent URL, then block reading the
5
+ authorization code from their terminal. Driving that needs a pty, and Node has none built in and no
6
+ pty library in this plugin, so the pty lives here — as part of the plugin, not as a script beside it.
7
+
8
+ The code arrives through a file this process watches. That is a constraint, not a preference:
9
+ `legacy_tiocsti` is 0 on current kernels, so no outside process can type into another process's pty,
10
+ and a backgrounded command has no attachable stdin. `--code` and `--code-file` both funnel into the
11
+ same pty write.
12
+
13
+ Prints newline-delimited JSON events on stdout so the caller can relay progress, and exits 0 only
14
+ when the CLI's own login probe says the session exists.
15
+ """
16
+ import argparse
17
+ import json
18
+ import os
19
+ import pty
20
+ import re
21
+ import select
22
+ import subprocess
23
+ import sys
24
+ import time
25
+
26
+ ANSI = re.compile(r'\x1b\[[0-9;]*[a-zA-Z]')
27
+
28
+
29
+ def emit(event, **fields):
30
+ payload = {'event': event}
31
+ payload.update(fields)
32
+ sys.stdout.write(json.dumps(payload) + '\n')
33
+ sys.stdout.flush()
34
+
35
+
36
+ def resolve_binary(name):
37
+ for candidate in (
38
+ os.path.join(os.path.expanduser('~'), '.local', 'bin', name),
39
+ os.path.join('/usr/local/bin', name),
40
+ os.path.join('/usr/bin', name),
41
+ ):
42
+ if os.path.exists(candidate):
43
+ return candidate
44
+ return name
45
+
46
+
47
+ def probe(binary, args, signed_out_pattern):
48
+ """Ask the CLI itself whether a session exists. The transcript is not evidence."""
49
+ try:
50
+ result = subprocess.run([binary] + args, capture_output=True, text=True, timeout=60)
51
+ output = (result.stdout or '') + (result.stderr or '')
52
+ signed_out = bool(re.search(signed_out_pattern, output, re.I))
53
+ return {
54
+ 'signed_in': (not signed_out) and result.returncode == 0,
55
+ 'status': result.returncode,
56
+ 'output': output.strip()[:400],
57
+ }
58
+ except Exception as error: # noqa: BLE001 - report, never raise
59
+ return {'signed_in': False, 'status': None, 'output': str(error)[:400]}
60
+
61
+
62
+ def main():
63
+ parser = argparse.ArgumentParser()
64
+ parser.add_argument('--agent', required=True)
65
+ parser.add_argument('--binary', required=True)
66
+ parser.add_argument('--code', default='')
67
+ parser.add_argument('--code-file', default='')
68
+ parser.add_argument('--url-file', default='')
69
+ parser.add_argument('--timeout', type=int, default=900)
70
+ parser.add_argument('--probe-args', default='models')
71
+ parser.add_argument('--signed-out-pattern', default=r'please sign in')
72
+ parser.add_argument('--consent-host', default='accounts.google.com')
73
+ args = parser.parse_args()
74
+
75
+ binary = resolve_binary(args.binary)
76
+
77
+ before = probe(binary, args.probe_args.split(), args.signed_out_pattern)
78
+ if before['signed_in']:
79
+ emit('already_signed_in', probe=before)
80
+ emit('done', ok=True)
81
+ return 0
82
+
83
+ pid, fd = pty.fork()
84
+ if pid == 0:
85
+ os.environ['PATH'] = os.path.dirname(binary) + os.pathsep + os.environ.get('PATH', '')
86
+ os.execv(binary, [args.binary])
87
+
88
+ time.sleep(2)
89
+ os.write(fd, b'\r')
90
+
91
+ published_url = ''
92
+ delivered_code = args.code.strip()
93
+ buffer = b''
94
+ deadline = time.time() + args.timeout
95
+ next_probe_at = time.time()
96
+ success = False
97
+
98
+ while time.time() < deadline:
99
+ try:
100
+ ready, _, _ = select.select([fd], [], [], 1.0)
101
+ if ready:
102
+ chunk = os.read(fd, 8192)
103
+ if not chunk:
104
+ break
105
+ buffer += chunk
106
+ text = ANSI.sub('', buffer.decode('utf-8', errors='ignore'))
107
+
108
+ if not published_url:
109
+ parts, collecting = [], False
110
+ for line in text.splitlines():
111
+ line = line.strip()
112
+ if args.consent_host in line:
113
+ collecting = True
114
+ if collecting:
115
+ parts.append(line)
116
+ if 'state=' in line:
117
+ break
118
+ candidate = ''.join(parts).replace(' ', '')
119
+ if candidate:
120
+ published_url = candidate
121
+ if args.url_file:
122
+ with open(args.url_file, 'w') as handle:
123
+ handle.write(candidate + '\n')
124
+ emit('url', url=candidate)
125
+ except Exception:
126
+ break
127
+
128
+ # Deliver the code once the URL is out, from the argument or from the watched file.
129
+ if published_url and not delivered_code:
130
+ if args.code_file and os.path.exists(args.code_file):
131
+ try:
132
+ candidate = open(args.code_file).read().strip()
133
+ except Exception:
134
+ candidate = ''
135
+ if candidate:
136
+ delivered_code = candidate
137
+ os.write(fd, candidate.encode() + b'\r')
138
+ emit('code_delivered', length=len(candidate))
139
+ elif args.code:
140
+ delivered_code = args.code.strip()
141
+ os.write(fd, delivered_code.encode() + b'\r')
142
+ emit('code_delivered', length=len(delivered_code))
143
+
144
+ # Two ways to know it is done. The CLI's success line is one, but not every CLI prints a
145
+ # string we can rely on — Antigravity completed the sign-in without ever emitting the
146
+ # literal line. The authoritative signal is the CLI's own probe, so poll it too; otherwise
147
+ # this waits out the full timeout on a sign-in that already succeeded.
148
+ if published_url:
149
+ if re.search(r'authentication successful', ANSI.sub('', buffer.decode('utf-8', errors='ignore')), re.I):
150
+ success = True
151
+ break
152
+ if delivered_code:
153
+ if time.time() >= next_probe_at:
154
+ if probe(binary, args.probe_args.split(), args.signed_out_pattern)['signed_in']:
155
+ success = True
156
+ break
157
+ next_probe_at = time.time() + 5
158
+
159
+ if success:
160
+ after = probe(binary, args.probe_args.split(), args.signed_out_pattern)
161
+ emit('probe', probe=after)
162
+ ok = bool(after['signed_in'])
163
+ emit('done', ok=ok)
164
+ try:
165
+ os.write(fd, b'\x03')
166
+ except Exception:
167
+ pass
168
+ return 0 if ok else 2
169
+
170
+ emit('done', ok=False, reason='timed out waiting for the sign-in to complete')
171
+ # Restore the tty before exiting. Leaving it to the runtime produced
172
+ # 'tcsetattr: Inappropriate ioctl for device' on the way out.
173
+ try:
174
+ os.write(fd, b'\x03')
175
+ except Exception:
176
+ pass
177
+ try:
178
+ os.close(fd)
179
+ except Exception:
180
+ pass
181
+ return 2
182
+
183
+
184
+ if __name__ == '__main__':
185
+ sys.exit(main())
@@ -58,6 +58,7 @@ import {
58
58
  } from './secrets-remote-deploy.mjs';
59
59
  import { listProviders, getProvider } from './provider-catalog.mjs';
60
60
  import { resolveBrainDir, parseLayerFlag, detectProjectBrain } from './agent-dir.mjs';
61
+ import { hostSecretsBrainDir } from './host-secrets-store.mjs';
61
62
  import fs from 'node:fs';
62
63
  import { ensureProjectId } from './project-id.mjs';
63
64
  import path from 'node:path';
@@ -219,6 +220,8 @@ function printHelp() {
219
220
  --no-example Skip .env.example
220
221
  --no-global Only keys bound to this repo (skip unbound)
221
222
  --replace-all Overwrite entire .env (default: merge TR block only)
223
+ --rename STORE=ENV Write a store key under the env name an app expects
224
+ (repeatable, comma separated)
222
225
  --remote <name> Also push the SSOT projection to a configured remote
223
226
  target over SSH (see 'auth secret remote') instead of
224
227
  (or in addition to) a local file
@@ -344,6 +347,7 @@ function parseArgs(args) {
344
347
  noExample: false,
345
348
  noGlobal: false,
346
349
  replaceAll: false,
350
+ rename: {},
347
351
  exportEnv: false,
348
352
  exportAll: false,
349
353
  enqueue: false,
@@ -530,6 +534,18 @@ function parseArgs(args) {
530
534
  case '--replace-all':
531
535
  out.replaceAll = true;
532
536
  break;
537
+ case '--rename': {
538
+ // Write a store key under the variable name an app expects. An app
539
+ // reads the provider's plain variable name, but each app must own a
540
+ // distinct credential in the store, so prefix the store key per app
541
+ // (for example APP_A_PROVIDER_KEY, APP_B_PROVIDER_KEY, ...).
542
+ const spec = String(args[++i] || '');
543
+ for (const pair of spec.split(',')) {
544
+ const [from, to] = pair.split('=').map((v) => (v || '').trim());
545
+ if (from && to) out.rename[from] = to;
546
+ }
547
+ break;
548
+ }
533
549
  case '--from-clipboard':
534
550
  out.fromClipboard = true;
535
551
  break;
@@ -641,7 +657,8 @@ export default async function secretCli(argv) {
641
657
 
642
658
  let brainDir;
643
659
  try {
644
- brainDir = process.env.TR_SECRETS_BRAIN || process.env.TR_BRAIN || resolveBrainDir(layer);
660
+ if (layer === 'project') throw new Error('Secrets have one host store; --project does not apply.');
661
+ brainDir = hostSecretsBrainDir();
645
662
  } catch (err) {
646
663
  console.error(`❌ ${err.message}`);
647
664
  process.exit(1);
@@ -1180,6 +1197,7 @@ export default async function secretCli(argv) {
1180
1197
  const r = await deployEnvToRemote(brainDir, opts.key, {
1181
1198
  dryRun: opts.dryRun,
1182
1199
  keys: opts.keys.length ? opts.keys : undefined,
1200
+ rename: opts.rename,
1183
1201
  });
1184
1202
  if (r.dryRun) {
1185
1203
  console.log(`\n 📤 dry-run remote deploy → ${r.target} (${r.host})`);
@@ -1442,6 +1460,7 @@ export default async function secretCli(argv) {
1442
1460
  includeGlobal: !opts.noGlobal,
1443
1461
  replaceAll: !!opts.replaceAll,
1444
1462
  keys: opts.keys.length ? opts.keys : undefined,
1463
+ rename: opts.rename,
1445
1464
  };
1446
1465
 
1447
1466
  if (opts.remote) {
@@ -0,0 +1,15 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+
4
+ /**
5
+ * The host credential authority is independent of a caller's project brain.
6
+ * TR_SECRETS_BRAIN is the explicit operational override. The _TR_TEST_* roots
7
+ * preserve disposable test isolation without making AGENT_DIR/TR_BRAIN stores.
8
+ * This selector only resolves a path; it never opens or initializes a store.
9
+ */
10
+ export function hostSecretsBrainDir() {
11
+ if (process.env.TR_SECRETS_BRAIN) return process.env.TR_SECRETS_BRAIN;
12
+ if (process.env._TR_TEST_BRAIN_DIR) return process.env._TR_TEST_BRAIN_DIR;
13
+ const agentRoot = process.env._TR_TEST_AGENT_DIR || path.join(os.homedir(), '.agent');
14
+ return path.join(agentRoot, 'skills', 'total-recall');
15
+ }
@@ -146,19 +146,20 @@ export async function buildEnvProjection(brainDir, target = {}) {
146
146
  if (value == null || value === '') continue;
147
147
  const meta = byKey.get(key) || { key, repos: [], scope: 'global' };
148
148
  if (!secretMatchesTarget(meta, target)) continue;
149
- selected.push({ key, value: String(value), meta });
149
+ const outKey = (target.rename && target.rename[key]) || key;
150
+ selected.push({ key, outKey, value: String(value), meta });
150
151
  }
151
152
 
152
153
  selected.sort((a, b) => a.key.localeCompare(b.key));
153
154
 
154
- const lines = selected.map(({ key, value }) => {
155
+ const lines = selected.map(({ outKey, value }) => {
155
156
  // Quote if needed
156
157
  const needsQuote = /[\s#"'$\\]/.test(value);
157
158
  const v = needsQuote ? JSON.stringify(value) : value;
158
- return `${key}=${v}`;
159
+ return `${outKey}=${v}`;
159
160
  });
160
161
 
161
- const exampleLines = selected.map(({ key }) => `${key}=`);
162
+ const exampleLines = selected.map(({ outKey }) => `${outKey}=`);
162
163
 
163
164
  const body = `${envHeader()}${lines.join('\n')}${lines.length ? '\n' : ''}${END_MARK}\n`;
164
165
  const exampleBody = `# Example env — names only. Values live in Total Recall secrets store.
@@ -200,6 +201,7 @@ export async function exportEnvToProject(brainDir, projectPath, opts = {}) {
200
201
  projectSlug: projectSlugFromPath(abs),
201
202
  includeGlobal: opts.includeGlobal !== false,
202
203
  keys: opts.keys,
204
+ rename: opts.rename,
203
205
  };
204
206
 
205
207
  const projection = await buildEnvProjection(brainDir, target);
@@ -180,6 +180,7 @@ export async function deployEnvToRemote(brainDir, targetName, opts = {}) {
180
180
  keys: opts.keys?.length ? opts.keys : target.keys,
181
181
  projectPath,
182
182
  projectSlug: opts.projectSlug || projectSlugFromPath(projectPath),
183
+ rename: opts.rename,
183
184
  });
184
185
 
185
186
  const remoteFile = path.posix.join(target.remotePath, target.filename || '.env');
@@ -254,30 +254,6 @@
254
254
  "tier": "official",
255
255
  "license": "open"
256
256
  },
257
- "card_layout": {
258
- "template": "workbench",
259
- "regions": [
260
- {
261
- "widget": "live",
262
- "title": "Live view"
263
- },
264
- {
265
- "widget": "list",
266
- "title": "Details",
267
- "source": {
268
- "command": "status"
269
- }
270
- },
271
- {
272
- "widget": "actions",
273
- "title": "Commands"
274
- },
275
- {
276
- "widget": "activity",
277
- "title": "Recent activity"
278
- }
279
- ]
280
- },
281
257
  "tasks": [
282
258
  {
283
259
  "intent": "Keep official agent documentation and Total Recall integrations current",
@@ -12,7 +12,7 @@
12
12
  "@gregiteen/ssss-cli": "0.11.2"
13
13
  },
14
14
  "devDependencies": {
15
- "total-recall-brain": "3.40.4"
15
+ "total-recall-brain": "^3.40.7"
16
16
  }
17
17
  },
18
18
  "node_modules/@gregiteen/ssss-cli": {
@@ -2595,9 +2595,9 @@
2595
2595
  }
2596
2596
  },
2597
2597
  "node_modules/total-recall-brain": {
2598
- "version": "3.40.4",
2599
- "resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.40.4.tgz",
2600
- "integrity": "sha512-/9JfUWyzXcgHu3kV/rqsOvclMY25PpfW4jSrunsCrkt2MPDUDfGD+z22AoA/OG/jSHE4ZvD/A9QV4IYE2+O99w==",
2598
+ "version": "3.40.7",
2599
+ "resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.40.7.tgz",
2600
+ "integrity": "sha512-G+sEWXVucI5Zs8uV1ie8P+mV/6cncWX76ES72T5dnPB3sFAwlcG9ous2SYTUaYUOETGB2vHTSo3cV4XWkHQ0Ww==",
2601
2601
  "dev": true,
2602
2602
  "license": "MIT",
2603
2603
  "workspaces": [
@@ -161,26 +161,5 @@
161
161
  "distribution": {
162
162
  "tier": "official",
163
163
  "license": "open"
164
- },
165
- "card_layout": {
166
- "template": "workbench",
167
- "regions": [
168
- {
169
- "widget": "status",
170
- "title": "Status"
171
- },
172
- {
173
- "widget": "live",
174
- "title": "Live view"
175
- },
176
- {
177
- "widget": "actions",
178
- "title": "Commands"
179
- },
180
- {
181
- "widget": "activity",
182
- "title": "Recent activity"
183
- }
184
- ]
185
164
  }
186
165
  }
@@ -12,7 +12,7 @@
12
12
  "@gregiteen/ssss-cli": "0.11.2"
13
13
  },
14
14
  "devDependencies": {
15
- "total-recall-brain": "3.40.4"
15
+ "total-recall-brain": "^3.40.7"
16
16
  }
17
17
  },
18
18
  "node_modules/@gregiteen/ssss-cli": {
@@ -2595,9 +2595,9 @@
2595
2595
  }
2596
2596
  },
2597
2597
  "node_modules/total-recall-brain": {
2598
- "version": "3.40.4",
2599
- "resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.40.4.tgz",
2600
- "integrity": "sha512-/9JfUWyzXcgHu3kV/rqsOvclMY25PpfW4jSrunsCrkt2MPDUDfGD+z22AoA/OG/jSHE4ZvD/A9QV4IYE2+O99w==",
2598
+ "version": "3.40.7",
2599
+ "resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.40.7.tgz",
2600
+ "integrity": "sha512-G+sEWXVucI5Zs8uV1ie8P+mV/6cncWX76ES72T5dnPB3sFAwlcG9ous2SYTUaYUOETGB2vHTSo3cV4XWkHQ0Ww==",
2601
2601
  "dev": true,
2602
2602
  "license": "MIT",
2603
2603
  "workspaces": [
@@ -185,37 +185,5 @@
185
185
  "distribution": {
186
186
  "tier": "official",
187
187
  "license": "open"
188
- },
189
- "card_layout": {
190
- "template": "workbench",
191
- "regions": [
192
- {
193
- "widget": "status",
194
- "title": "Status"
195
- },
196
- {
197
- "widget": "live",
198
- "title": "Live view"
199
- },
200
- {
201
- "widget": "list",
202
- "title": "Details",
203
- "source": {
204
- "command": "list"
205
- }
206
- },
207
- {
208
- "widget": "actions",
209
- "title": "Commands"
210
- },
211
- {
212
- "widget": "credentials",
213
- "title": "Credentials"
214
- },
215
- {
216
- "widget": "activity",
217
- "title": "Recent activity"
218
- }
219
- ]
220
188
  }
221
189
  }
@@ -9,7 +9,7 @@
9
9
  "version": "0.1.0",
10
10
  "license": "MIT",
11
11
  "devDependencies": {
12
- "total-recall-brain": "3.40.4"
12
+ "total-recall-brain": "3.40.7"
13
13
  }
14
14
  },
15
15
  "node_modules/@hexagon/base64": {
@@ -2566,9 +2566,9 @@
2566
2566
  }
2567
2567
  },
2568
2568
  "node_modules/total-recall-brain": {
2569
- "version": "3.40.4",
2570
- "resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.40.4.tgz",
2571
- "integrity": "sha512-/9JfUWyzXcgHu3kV/rqsOvclMY25PpfW4jSrunsCrkt2MPDUDfGD+z22AoA/OG/jSHE4ZvD/A9QV4IYE2+O99w==",
2569
+ "version": "3.40.7",
2570
+ "resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.40.7.tgz",
2571
+ "integrity": "sha512-G+sEWXVucI5Zs8uV1ie8P+mV/6cncWX76ES72T5dnPB3sFAwlcG9ous2SYTUaYUOETGB2vHTSo3cV4XWkHQ0Ww==",
2572
2572
  "dev": true,
2573
2573
  "license": "MIT",
2574
2574
  "workspaces": [
@@ -55,6 +55,17 @@
55
55
  "handler": "./ui/card.js",
56
56
  "element": "tr-embeddings-card"
57
57
  },
58
+ "ui": {
59
+ "elements": [
60
+ {
61
+ "id": "card",
62
+ "kind": "card",
63
+ "tag": "tr-embeddings-card",
64
+ "module": "ui/card.js",
65
+ "description": "Embeddings card face"
66
+ }
67
+ ]
68
+ },
58
69
  "status": {
59
70
  "command": "status",
60
71
  "refresh_seconds": 60
@@ -1,8 +1,11 @@
1
1
  # Git Hygiene
2
2
 
3
- September 30 review: The audit reproduced two reporting bugs: trimming porcelain output corrupts its first leading status column; missing upstream/error can become zero unpushed commits. PIC-018 requires column-preserving parsing and explicit unknown/no-upstream/error states. These fields remain unreliable until repaired. See the [correction tracker](../../docs/projects/in-progress/PLUGIN_IMPLEMENTATION_CORRECTIONS/PLUGIN_IMPLEMENTATION_CORRECTIONS_PROJECT_TRACKER.md).
3
+ Reports the state of the git repository you run it in: current branch, staged / unstaged / untracked / conflicted files, commits not yet pushed to upstream, and stashes.
4
4
 
5
- Reports the state of the git repository you run it in: current branch, staged / unstaged / untracked files, commits not yet pushed to upstream, and stashes.
5
+ **Reporting accuracy, verified 2026-10-23.** Two defects reported in the September 30 review are fixed and covered by testing:
6
+
7
+ - **Porcelain columns are preserved.** `git status --porcelain` output is never trimmed, so a file whose name begins with a space parses correctly. Verified against a file literally named `" leading-space.txt"`, which reports as `A leading-space.txt`.
8
+ - **An unknown upstream is never reported as zero unpushed commits.** `upstreamStatus` distinguishes `tracking`, `none`, `gone`, `detached`, `unborn`, `unknown` and `error`; where no comparison exists, `unpushedCommits` is `null` rather than `0`. Verified in a repository with no remote configured.
6
9
 
7
10
  **Use cases:** software-development
8
11
 
@@ -9,7 +9,7 @@
9
9
  "version": "1.1.2",
10
10
  "license": "MIT",
11
11
  "devDependencies": {
12
- "total-recall-brain": "3.40.4",
12
+ "total-recall-brain": "3.40.7",
13
13
  "vitest": "^4.1.10"
14
14
  }
15
15
  },
@@ -3560,9 +3560,9 @@
3560
3560
  }
3561
3561
  },
3562
3562
  "node_modules/total-recall-brain": {
3563
- "version": "3.40.4",
3564
- "resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.40.4.tgz",
3565
- "integrity": "sha512-/9JfUWyzXcgHu3kV/rqsOvclMY25PpfW4jSrunsCrkt2MPDUDfGD+z22AoA/OG/jSHE4ZvD/A9QV4IYE2+O99w==",
3563
+ "version": "3.40.7",
3564
+ "resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.40.7.tgz",
3565
+ "integrity": "sha512-G+sEWXVucI5Zs8uV1ie8P+mV/6cncWX76ES72T5dnPB3sFAwlcG9ous2SYTUaYUOETGB2vHTSo3cV4XWkHQ0Ww==",
3566
3566
  "dev": true,
3567
3567
  "license": "MIT",
3568
3568
  "workspaces": [