total-recall-brain 3.44.0 → 3.46.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 (502) hide show
  1. package/bin/total-recall.mjs +10 -6
  2. package/docs/ARCHITECTURE.md +10 -7
  3. package/docs/reference/cli-reference.md +13 -8
  4. package/frontend/dist/assets/index-BRYE1jmv.css +1 -0
  5. package/frontend/dist/assets/index-D1goIPjC.js +5598 -0
  6. package/frontend/dist/index.html +2 -2
  7. package/frontend/dist/sw.js +5 -4
  8. package/frontend/src/App.tsx +185 -238
  9. package/frontend/src/api/auth-inventory.ts +62 -0
  10. package/frontend/src/api/memory.ts +13 -8
  11. package/frontend/src/api/plugin-credentials.ts +23 -0
  12. package/frontend/src/api/plugins.ts +37 -53
  13. package/frontend/src/api/workspace-drawers.ts +97 -0
  14. package/frontend/src/components/BrainSelector.tsx +9 -3
  15. package/frontend/src/components/plugins/PluginPanel.tsx +83 -42
  16. package/frontend/src/components/plugins/PluginSettings.tsx +10 -20
  17. package/frontend/src/components/plugins/PluginWorkspace.tsx +367 -0
  18. package/frontend/src/components/plugins/ScopedPluginCommands.tsx +142 -0
  19. package/frontend/src/components/plugins/plugin-frame.ts +44 -33
  20. package/frontend/src/components/plugins/scoped-plugin-operations.ts +28 -0
  21. package/frontend/src/components/workspace/DrawerChat.tsx +110 -0
  22. package/frontend/src/components/workspace/DrawerLayout.tsx +196 -0
  23. package/frontend/src/components/workspace/DrawerMarkup.tsx +49 -0
  24. package/frontend/src/components/workspace/PluginChunkLibrary.tsx +92 -0
  25. package/frontend/src/components/workspace/PluginHome.tsx +76 -0
  26. package/frontend/src/components/workspace/WorkspaceDrawers.tsx +96 -0
  27. package/frontend/src/components/workspace/WorkspacePage.tsx +95 -0
  28. package/frontend/src/components/workspace/usePluginDesign.ts +38 -0
  29. package/frontend/src/index.css +409 -136
  30. package/frontend/src/pages/HelpPage.tsx +15 -46
  31. package/frontend/src/pages/MemoryPage.tsx +19 -36
  32. package/frontend/src/pages/SettingsPage.tsx +1 -1
  33. package/metadata.plugin.schema.json +53 -227
  34. package/package.json +1 -1
  35. package/scaffold/.agent/skills/plugin/SKILL.md +13 -11
  36. package/scaffold/.agent/skills/plugin/evals/README.md +14 -21
  37. package/scaffold/.agent/skills/plugin/evals/cases/01-fabricated-seed.mjs +15 -18
  38. package/scaffold/.agent/skills/plugin/evals/cases/02-reassuring-default.mjs +19 -24
  39. package/scaffold/.agent/skills/plugin/evals/cases/03-declared-template.mjs +12 -20
  40. package/scaffold/.agent/skills/plugin/evals/cases/04-home-collects-credential.mjs +35 -0
  41. package/scaffold/.agent/skills/plugin/evals/cases/05-host-leak-in-scaffold.mjs +8 -28
  42. package/scaffold/.agent/skills/plugin/evals/cases/06-correct-plugin.mjs +85 -47
  43. package/scaffold/.agent/skills/plugin/evals/run.mjs +17 -18
  44. package/scaffold/.agent/skills/plugin/prompts/05-credentials.md +4 -4
  45. package/scaffold/.agent/skills/plugin/prompts/07-scaffold.md +2 -2
  46. package/scaffold/.agent/skills/plugin/prompts/08-verify.md +4 -4
  47. package/scaffold/.agent/skills/plugin/references/anti-patterns.md +2 -2
  48. package/scaffold/.agent/skills/plugin/references/generative-home.md +38 -0
  49. package/scaffold/.agent/skills/plugin/references/scaffold.md +5 -5
  50. package/scaffold/.agent/skills/plugin/references/surfaces.md +13 -4
  51. package/scaffold/.agent/skills/plugin/scripts/record.mjs +75 -153
  52. package/scaffold/.agent/skills/plugin/subagents/plugin-reviewer.md +4 -4
  53. package/scaffold/.agent/skills/total-recall/plugins/auth/DESIGN.md +76 -0
  54. package/scaffold/.agent/skills/total-recall/plugins/auth/README.md +3 -1
  55. package/scaffold/.agent/skills/total-recall/plugins/auth/cli.mjs +20 -15
  56. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +7 -7
  57. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +2 -1
  58. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +13 -51
  59. package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +20 -1
  60. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-store.mjs +12 -1
  61. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/access-overview.md +17 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/agent-login.md +20 -0
  63. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/baseline-audit.md +22 -0
  64. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/credential-repository-binding.md +17 -0
  65. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/credential-store-maintenance.md +17 -0
  66. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/dashboard-password.md +18 -0
  67. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/dashboard-sessions.md +18 -0
  68. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/endpoint-probe.md +23 -0
  69. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/google-sign-in.md +21 -0
  70. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/mesh-machines.md +16 -0
  71. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/passkey-enrollment.md +21 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/registered-brains.md +16 -0
  73. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/scoped-access-keys.md +17 -0
  74. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/secret-inventory.md +25 -0
  75. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/signing-key-rotation.md +22 -0
  76. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/home.js +375 -0
  77. package/scaffold/.agent/skills/total-recall/plugins/automations/BRICK.md +7 -0
  78. package/scaffold/.agent/skills/total-recall/plugins/automations/DESIGN.md +53 -0
  79. package/scaffold/.agent/skills/total-recall/plugins/automations/LICENSE +21 -0
  80. package/scaffold/.agent/skills/total-recall/plugins/automations/README.md +66 -0
  81. package/scaffold/.agent/skills/total-recall/plugins/automations/adapters.mjs +247 -0
  82. package/scaffold/.agent/skills/total-recall/plugins/automations/capability/index.mjs +16 -0
  83. package/scaffold/.agent/skills/total-recall/plugins/automations/cli.mjs +130 -0
  84. package/{src/core/plugin-cron.mjs → scaffold/.agent/skills/total-recall/plugins/automations/cron.mjs} +1 -3
  85. package/scaffold/.agent/skills/total-recall/plugins/automations/inventory.mjs +88 -0
  86. package/scaffold/.agent/skills/total-recall/plugins/automations/package-lock.json +7661 -0
  87. package/scaffold/.agent/skills/total-recall/plugins/automations/package.json +36 -0
  88. package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +151 -0
  89. package/scaffold/.agent/skills/total-recall/plugins/automations/scheduler.mjs +246 -0
  90. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/automations.js +2 -0
  91. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/control-ledger.md +42 -0
  92. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/local-inventory-triage.md +17 -0
  93. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/pending-control.md +39 -0
  94. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/run-evidence.md +41 -0
  95. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/schedule-vs-run-evidence.md +17 -0
  96. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/source-coverage.md +41 -0
  97. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/home.js +278 -0
  98. package/scaffold/.agent/skills/total-recall/plugins/code-quality/DESIGN.md +53 -0
  99. package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +23 -0
  100. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +5 -3
  101. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +61 -7
  102. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +40 -14
  103. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +15 -40
  104. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +7 -7
  105. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +2 -1
  106. package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +40 -40
  107. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/config.md +15 -0
  108. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/detect.md +15 -0
  109. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/gate.md +15 -0
  110. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/init.md +15 -0
  111. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/report.md +17 -0
  112. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/run.md +17 -0
  113. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/snapshot.md +15 -0
  114. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +55 -23
  115. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/home.mjs +214 -0
  116. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/DESIGN.md +146 -0
  117. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +7 -7
  118. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +2 -1
  119. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +51 -34
  120. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +1 -1
  121. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/create.md +35 -0
  122. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/history.md +35 -0
  123. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/library.md +49 -0
  124. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/list.md +42 -0
  125. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/promote.md +40 -0
  126. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/read.md +25 -0
  127. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/remove.md +26 -0
  128. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/rollback.md +26 -0
  129. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/show.md +53 -0
  130. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/status.md +40 -0
  131. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/test-path.md +38 -0
  132. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/test.md +46 -0
  133. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/update.md +32 -0
  134. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/home.js +533 -0
  135. package/scaffold/.agent/skills/total-recall/plugins/embeddings/DESIGN.md +110 -0
  136. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +7 -7
  137. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +2 -1
  138. package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +13 -10
  139. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/benchmark.md +49 -0
  140. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/coverage.md +53 -0
  141. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/models.md +43 -0
  142. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/pull.md +41 -0
  143. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/status.md +59 -0
  144. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/home.js +353 -0
  145. package/scaffold/.agent/skills/total-recall/plugins/git/DESIGN.md +31 -0
  146. package/scaffold/.agent/skills/total-recall/plugins/git/README.md +136 -0
  147. package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/cli.mjs +140 -24
  148. package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package-lock.json +9 -9
  149. package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package.json +4 -2
  150. package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +151 -0
  151. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/context.mjs +94 -0
  152. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/phase3-spec-support.mjs +50 -0
  153. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/runtime.mjs +40 -0
  154. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/spec-support.mjs +51 -0
  155. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/_support.mjs +195 -0
  156. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/auth.mjs +201 -0
  157. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/bisect.mjs +40 -0
  158. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/cli-meta.mjs +159 -0
  159. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit-memory.mjs +54 -0
  160. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit.mjs +46 -0
  161. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/fleet.mjs +174 -0
  162. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/force-push.mjs +58 -0
  163. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/group.mjs +119 -0
  164. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/handoff.mjs +59 -0
  165. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/message.mjs +22 -0
  166. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/resolve.mjs +60 -0
  167. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/rules.mjs +81 -0
  168. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/secret-firewall.mjs +92 -0
  169. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/undo.mjs +156 -0
  170. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/unmerged.mjs +182 -0
  171. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/why.mjs +16 -0
  172. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/change-comparison.md +27 -0
  173. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/commit-identity.md +22 -0
  174. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/create-worktree.md +25 -0
  175. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/registered-worktrees.md +22 -0
  176. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/remove-worktree.md +23 -0
  177. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/repository-ledger.md +25 -0
  178. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/unmerged-work.md +22 -0
  179. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/upstream-divergence.md +17 -0
  180. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/worktree-conflict-triage.md +17 -0
  181. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/worktree-terminal.md +22 -0
  182. package/scaffold/.agent/skills/total-recall/plugins/git/ui/home.js +273 -0
  183. package/scaffold/.agent/skills/total-recall/plugins/mesh/DESIGN.md +35 -0
  184. package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +4 -5
  185. package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +9 -3
  186. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +7 -7
  187. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +2 -1
  188. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +37 -39
  189. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +5 -3
  190. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/active-device-dispatch.md +22 -0
  191. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/control-probe.md +25 -0
  192. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/control-server.md +22 -0
  193. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/credential-readiness.md +23 -0
  194. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/elected-leader.md +22 -0
  195. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/enrollment-key.md +22 -0
  196. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/file-receipt.md +25 -0
  197. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/fleet-execution.md +23 -0
  198. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/fleet-presence.md +23 -0
  199. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/interactive-ssh.md +22 -0
  200. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/local-enrollment.md +23 -0
  201. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/mesh-policy.md +23 -0
  202. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/network-atlas.md +25 -0
  203. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/node-roster.md +26 -0
  204. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/node-selection-proof.md +17 -0
  205. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/owner-observations.md +23 -0
  206. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/peer-path.md +23 -0
  207. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/pinned-leader.md +24 -0
  208. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/reachability-evidence-ladder.md +17 -0
  209. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/recorded-login.md +23 -0
  210. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/remote-execution.md +22 -0
  211. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/secret-grants.md +26 -0
  212. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/{card.js → home.js} +93 -22
  213. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/DESIGN.md +86 -0
  214. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +44 -9
  215. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +1 -1
  216. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +18 -6
  217. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +128 -24
  218. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +7 -7
  219. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +3 -2
  220. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +188 -34
  221. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +17 -3
  222. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adoption.mjs +2 -2
  223. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +3 -3
  224. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +92 -18
  225. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/chat.mjs +80 -0
  226. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/control.mjs +145 -0
  227. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +42 -21
  228. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch-request.mjs +4 -2
  229. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -9
  230. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +55 -11
  231. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +26 -4
  232. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +29 -1
  233. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +1 -1
  234. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/included-usage.mjs +1 -1
  235. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/jobs.mjs +84 -0
  236. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +3 -12
  237. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/maintenance.mjs +17 -0
  238. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/mcp-server.mjs +228 -0
  239. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/process-manager.mjs +25 -2
  240. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reasoning.mjs +1 -1
  241. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/remote-operation.mjs +5 -1
  242. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +34 -3
  243. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reviews.mjs +25 -0
  244. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +1 -1
  245. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/claude.mjs +319 -0
  246. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/codex.mjs +387 -0
  247. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/result.mjs +67 -0
  248. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/resume.mjs +373 -0
  249. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/run.mjs +223 -0
  250. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session-jobs.mjs +72 -0
  251. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +6 -3
  252. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/termination.mjs +16 -12
  253. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +32 -1
  254. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/usage-queue.mjs +1 -1
  255. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/verify-ssss.mjs +3 -3
  256. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +285 -40
  257. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +76 -9
  258. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/adapter-docs-freshness.md +16 -0
  259. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/chat-handoff.md +26 -0
  260. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/fleet-maintenance.md +21 -0
  261. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/harness-inventory.md +21 -0
  262. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/included-access.md +18 -0
  263. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/included-usage-verification.md +15 -0
  264. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/independent-review.md +17 -0
  265. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/managed-execution.md +20 -0
  266. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/official-documentation.md +18 -0
  267. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/quota-recovery.md +21 -0
  268. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/review-routing-decision.md +16 -0
  269. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/route-work.md +18 -0
  270. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/routing-evidence.md +18 -0
  271. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/run-ledger.md +18 -0
  272. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/home.js +198 -0
  273. package/scaffold/.agent/skills/total-recall/plugins/openwiki/DESIGN.md +75 -0
  274. package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +2 -0
  275. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +64 -32
  276. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +7 -7
  277. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +2 -1
  278. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +10 -44
  279. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +5 -4
  280. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +3 -4
  281. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +23 -18
  282. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +8 -3
  283. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh-diagnostics.mjs +44 -0
  284. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +123 -113
  285. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +3 -5
  286. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +21 -47
  287. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +17 -6
  288. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/auto-refresh.md +18 -0
  289. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/bootstrap-pages.md +18 -0
  290. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/generate-wiki.md +21 -0
  291. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/generator-settings.md +18 -0
  292. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/host-probe.md +18 -0
  293. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/ingest-preview.md +18 -0
  294. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/link-hygiene.md +21 -0
  295. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/link-topology.md +19 -0
  296. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/passage-search.md +19 -0
  297. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/propagation-plan.md +18 -0
  298. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/provider-credentials.md +18 -0
  299. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/refresh-report.md +18 -0
  300. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/task-evidence.md +22 -0
  301. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/upstream-record.md +18 -0
  302. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/watch-upstream.md +18 -0
  303. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/wiki-atlas.md +19 -0
  304. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +261 -513
  305. package/scaffold/.agent/skills/total-recall/plugins/security/DESIGN.md +84 -0
  306. package/scaffold/.agent/skills/total-recall/plugins/security/README.md +4 -2
  307. package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +7 -19
  308. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +7 -7
  309. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +2 -3
  310. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +19 -52
  311. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/device-evidence.md +21 -0
  312. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/evidence-desk.md +18 -0
  313. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/guided-device-review.md +17 -0
  314. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/host-and-network.md +21 -0
  315. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/https-headers.md +17 -0
  316. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/mesh-coverage.md +18 -0
  317. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/private-security-tools.md +16 -0
  318. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/single-origin-header-check.md +17 -0
  319. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/source-review.md +21 -0
  320. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/ssh-permission-plan.md +17 -0
  321. package/scaffold/.agent/skills/total-recall/plugins/security/ui/home.js +283 -0
  322. package/scaffold/.agent/skills/total-recall/plugins/ssss/DESIGN.md +93 -0
  323. package/scaffold/.agent/skills/total-recall/plugins/ssss/package-lock.json +2 -2
  324. package/scaffold/.agent/skills/total-recall/plugins/ssss/package.json +2 -1
  325. package/scaffold/.agent/skills/total-recall/plugins/ssss/plugin.json +5 -3
  326. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/conformance.md +26 -0
  327. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/registry.md +37 -0
  328. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/repair.md +42 -0
  329. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/scaffold.md +33 -0
  330. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/status.md +43 -0
  331. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/validate.md +34 -0
  332. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/validation-diagnostics.md +17 -0
  333. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/workspace-registry-preflight.md +17 -0
  334. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/home.js +348 -0
  335. package/scaffold/.agent/skills/total-recall/references/operations.md +3 -1
  336. package/src/cli/agent.mjs +1 -1
  337. package/src/cli/api.mjs +3 -3
  338. package/src/cli/brief.mjs +3 -0
  339. package/src/cli/command.mjs +41 -2
  340. package/src/cli/config.mjs +16 -0
  341. package/src/cli/connect.mjs +2 -7
  342. package/src/cli/dream.mjs +93 -69
  343. package/src/cli/harness.mjs +4 -4
  344. package/src/cli/help.mjs +1 -1
  345. package/src/cli/init.mjs +0 -25
  346. package/src/cli/lessons.mjs +51 -0
  347. package/src/cli/memory-plugin.mjs +1 -1
  348. package/src/cli/plugin/chunks.mjs +145 -0
  349. package/src/cli/plugin/copies.mjs +64 -14
  350. package/src/cli/plugin/create.mjs +30 -59
  351. package/src/cli/plugin/index.mjs +31 -16
  352. package/src/cli/plugin/install.mjs +10 -15
  353. package/src/cli/plugin/remove.mjs +1 -1
  354. package/src/cli/plugin/search.mjs +2 -2
  355. package/src/cli/preflight.mjs +19 -6
  356. package/src/cli/proposals.mjs +1 -1
  357. package/src/cli/recall.mjs +66 -23
  358. package/src/cli/remember.mjs +3 -14
  359. package/src/cli/server.mjs +18 -0
  360. package/src/cli/setup.mjs +2 -9
  361. package/src/cli/start.mjs +1 -0
  362. package/src/cli/startup.mjs +19 -0
  363. package/src/cli/task.mjs +9 -2
  364. package/src/cli/update.mjs +6 -2
  365. package/src/core/access-ledger.mjs +78 -0
  366. package/src/core/agent-manager.mjs +2 -2
  367. package/src/core/app-deploy/design-tokens.mjs +14 -9
  368. package/src/core/app-deploy/ui-elements.mjs +1 -1
  369. package/src/core/brain-locate.mjs +15 -0
  370. package/src/core/brain-registry.mjs +5 -0
  371. package/src/core/brain-updates.mjs +35 -11
  372. package/src/core/claude-hooks.mjs +3 -1
  373. package/src/core/cleanup-task.mjs +15 -0
  374. package/src/core/cli-commands.mjs +2 -0
  375. package/src/core/cli-maintenance.mjs +4 -1
  376. package/src/core/cli-semantic-index.mjs +23 -3
  377. package/src/core/context-compiler.mjs +20 -0
  378. package/src/core/daemon-control.mjs +5 -1
  379. package/src/core/daemon-loop.mjs +39 -62
  380. package/src/core/dashboard-bundle.mjs +261 -0
  381. package/src/core/default-plugins.mjs +1 -1
  382. package/src/core/dream.mjs +33 -105
  383. package/src/core/ensure-dependencies.mjs +152 -0
  384. package/src/core/fast-recall.mjs +2 -2
  385. package/src/core/field-evaluation.mjs +23 -0
  386. package/src/core/gate-mode.mjs +1 -1
  387. package/src/core/harness-hooks.mjs +14 -2
  388. package/src/core/lesson-hooks.mjs +74 -0
  389. package/src/core/lesson-memory.mjs +24 -0
  390. package/src/core/lesson-sessions.mjs +52 -0
  391. package/src/core/lessons.mjs +96 -0
  392. package/src/core/memory-brain.mjs +30 -8
  393. package/src/core/meta-harness.mjs +2 -2
  394. package/src/core/optimizer.mjs +66 -165
  395. package/src/core/plugin-activation.mjs +63 -20
  396. package/src/core/plugin-contracts/{card.mjs → manifest.mjs} +26 -57
  397. package/src/core/plugin-contracts/startup-check.mjs +49 -0
  398. package/src/core/plugin-contracts/ui.mjs +9 -3
  399. package/src/core/plugin-distribution.mjs +6 -6
  400. package/src/core/plugin-lifecycle.mjs +58 -0
  401. package/src/core/plugin-loader.mjs +21 -8
  402. package/src/core/plugin-runner.mjs +21 -10
  403. package/src/core/plugin-runtime.mjs +40 -1
  404. package/src/core/plugin-startup-checks.mjs +44 -0
  405. package/src/core/plugin-state.mjs +19 -5
  406. package/src/core/plugin-store.mjs +64 -33
  407. package/src/core/post-mortem.mjs +52 -36
  408. package/src/core/preflight-gate.mjs +23 -2
  409. package/src/core/project-id.mjs +15 -5
  410. package/src/core/proposal-applier.mjs +31 -2
  411. package/src/core/repo-sync.mjs +4 -6
  412. package/src/core/required-plugins.mjs +57 -10
  413. package/src/core/rule-length.mjs +23 -0
  414. package/src/core/runtime.mjs +1 -1
  415. package/src/core/scheduler.mjs +35 -43
  416. package/src/core/schema.mjs +3 -2
  417. package/src/core/search.mjs +4 -3
  418. package/src/core/server-service.mjs +148 -0
  419. package/src/core/session-watcher.mjs +19 -1
  420. package/src/core/skill-gap.mjs +16 -0
  421. package/src/core/ssss-kernel-bridge.mjs +32 -4
  422. package/src/core/ssss-operation-service.mjs +1 -0
  423. package/src/core/startup-health.mjs +34 -4
  424. package/src/core/surface.mjs +65 -54
  425. package/src/core/task-executors.mjs +54 -106
  426. package/src/core/testing/plugin-fixture.mjs +22 -21
  427. package/src/core/throttled-fetch.mjs +25 -18
  428. package/src/core/ui-chunk-choice.mjs +77 -0
  429. package/src/core/ui-chunk.registry.json +23 -0
  430. package/src/core/ui-chunks.mjs +434 -0
  431. package/src/core/validated-write.mjs +1 -0
  432. package/src/core/vault-cache.mjs +58 -59
  433. package/src/core/vector-field.mjs +134 -192
  434. package/src/core/workspace-compositions.mjs +151 -0
  435. package/src/core/workspace-drawers.mjs +79 -0
  436. package/src/server/api.mjs +4 -27
  437. package/src/server/index.mjs +28 -21
  438. package/src/server/invoke-core-child.mjs +24 -0
  439. package/src/server/invoke-core-operations.mjs +102 -0
  440. package/src/server/memory-events.mjs +31 -0
  441. package/src/server/plugin-brain-targets.mjs +89 -0
  442. package/src/server/rest.mjs +2 -0
  443. package/src/server/route-inventory.mjs +3 -2
  444. package/src/server/route-manifest.json +47 -55
  445. package/src/server/routes/_shared.mjs +7 -14
  446. package/src/server/routes/brains.mjs +29 -62
  447. package/src/server/routes/context.mjs +0 -41
  448. package/src/server/routes/dashboard.mjs +26 -0
  449. package/src/server/routes/invoke.mjs +26 -1
  450. package/src/server/routes/plugins.mjs +154 -45
  451. package/src/server/routes/ssss.mjs +0 -169
  452. package/src/server/routes/system.mjs +0 -15
  453. package/src/server/routes/tasks.mjs +0 -26
  454. package/src/server/routes/ui-chunks.mjs +192 -0
  455. package/src/server/routes/vault.mjs +2 -23
  456. package/frontend/dist/assets/index-DD4qONvH.js +0 -5607
  457. package/frontend/dist/assets/index-DtwsDyFM.css +0 -1
  458. package/frontend/src/components/plugins/card/CardEngine.tsx +0 -108
  459. package/frontend/src/components/plugins/card/CardScenes.tsx +0 -103
  460. package/frontend/src/components/plugins/card/ExecutionPicker.tsx +0 -64
  461. package/frontend/src/components/plugins/card/args.ts +0 -28
  462. package/frontend/src/components/plugins/card/card.css +0 -32
  463. package/frontend/src/components/plugins/card/cli-line.ts +0 -13
  464. package/frontend/src/components/plugins/card/contract.ts +0 -91
  465. package/frontend/src/components/plugins/card/format.ts +0 -49
  466. package/frontend/src/components/plugins/card/runner-context.ts +0 -11
  467. package/frontend/src/components/plugins/card/runner.tsx +0 -43
  468. package/frontend/src/components/plugins/card/theme.ts +0 -35
  469. package/frontend/src/components/plugins/card/use-source.ts +0 -22
  470. package/frontend/src/components/plugins/card/widgets.tsx +0 -94
  471. package/frontend/src/components/plugins/workspace/WorkspaceHost.tsx +0 -23
  472. package/frontend/src/pages/PluginsPage.tsx +0 -1719
  473. package/scaffold/.agent/skills/plugin/evals/cases/04-card-collects-credential.mjs +0 -47
  474. package/scaffold/.agent/skills/plugin/prompts/06-card.md +0 -46
  475. package/scaffold/.agent/skills/plugin/references/card.md +0 -90
  476. package/scaffold/.agent/skills/plugin/subagents/card-designer.md +0 -60
  477. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/DESIGN.md +0 -67
  478. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +0 -1179
  479. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +0 -32
  480. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +0 -588
  481. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +0 -575
  482. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/DESIGN.md +0 -32
  483. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +0 -482
  484. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +0 -53
  485. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +0 -129
  486. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +0 -100
  487. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +0 -637
  488. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/DESIGN.md +0 -217
  489. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +0 -32
  490. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +0 -1276
  491. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +0 -45
  492. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +0 -1401
  493. package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +0 -4
  494. package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +0 -41
  495. package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +0 -396
  496. package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +0 -98
  497. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/DESIGN.md +0 -15
  498. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/card.js +0 -182
  499. package/src/core/append-log.mjs +0 -131
  500. package/src/core/plugin-tasks.mjs +0 -173
  501. package/templates/onboarding-interview.md +0 -82
  502. /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/LICENSE +0 -0
@@ -0,0 +1,51 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ export function fixture() {
8
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-plugin-spec-'));
9
+ const bin = path.join(dir, 'bin');
10
+ fs.mkdirSync(bin);
11
+ const calls = path.join(dir, 'calls.jsonl');
12
+ const writeBin = (name, source) => {
13
+ const file = path.join(bin, name);
14
+ fs.writeFileSync(file, `#!${process.execPath}\n${source}`, { mode: 0o700 });
15
+ return file;
16
+ };
17
+ const tr = writeBin('tr-stub', `
18
+ import fs from 'node:fs';
19
+ const args = process.argv.slice(2);
20
+ fs.appendFileSync(process.env.SPEC_CALLS, JSON.stringify(args) + '\\n');
21
+ if (args[0] === 'auth' && args[2] === 'get') {
22
+ if (process.env.SPEC_AUTH_FAIL) { console.error('fixture-sensitive-diagnostic'); process.exitCode = 1; }
23
+ else console.log(JSON.stringify({ found: !process.env.SPEC_AUTH_MISSING, value: process.env.SPEC_TOKEN || 'fixture-token' }));
24
+ } else if (args[0] === 'auth' && args[2] === 'list') {
25
+ console.log(JSON.stringify({ keys: [{ key: 'github_token' }, { key: 'GITLAB_TOKEN' }] }));
26
+ } else if (args[0] === 'mesh' && args[1] === 'nodes') {
27
+ console.log(JSON.stringify({ nodes: [{ name: 'alpha' }, { name: 'beta' }] }));
28
+ } else if (args[0] === 'mesh' && args[1] === 'exec') {
29
+ if (process.env.SPEC_NODE_FAIL && args[2] === 'beta') process.exitCode = 1;
30
+ else console.log(JSON.stringify({ version: '2.52.0', hostname: args[2], versions_behind: 0 }));
31
+ } else if (args[0] !== 'alerts') process.exitCode = 1;
32
+ `);
33
+ const env = { ...process.env, HOME: dir, GIT_CONFIG_GLOBAL: path.join(dir, 'gitconfig'),
34
+ GIT_CONFIG_NOSYSTEM: '1', TR_PLUGIN_STATE_DIR: path.join(dir, 'state'),
35
+ TR_PACKAGE_ROOT: fileURLToPath(new URL('../../node_modules/total-recall-brain', import.meta.url)),
36
+ GIT_PLUGIN_TR_BIN: tr, SPEC_CALLS: calls, PATH: `${bin}${path.delimiter}${process.env.PATH}`,
37
+ GIT_AUTHOR_NAME: 'Fixture Person', GIT_AUTHOR_EMAIL: 'fixture@example.com',
38
+ GIT_COMMITTER_NAME: 'Fixture Person', GIT_COMMITTER_EMAIL: 'fixture@example.com',
39
+ };
40
+ for (const key of Object.keys(env)) if (/^GIT_CONFIG_(COUNT|KEY_\d+|VALUE_\d+)$/.test(key)) delete env[key];
41
+ return { dir, bin, env, writeBin,
42
+ calls: () => fs.existsSync(calls) ? fs.readFileSync(calls, 'utf8').trim().split('\n').filter(Boolean).map(JSON.parse) : [],
43
+ cleanup: () => fs.rmSync(dir, { recursive: true, force: true }),
44
+ };
45
+ }
46
+
47
+ export function realContext(f) {
48
+ return { cwd: f.dir, env: f.env, json: false,
49
+ git: (args, options = {}) => spawnSync('git', args, { cwd: f.dir, env: f.env, encoding: 'utf8', ...options }),
50
+ };
51
+ }
@@ -0,0 +1,195 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { copyFileSync, existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { pathToFileURL } from 'node:url';
7
+
8
+ const require = createRequire(import.meta.url);
9
+ export function helpOnly(args) {
10
+ const values = new Set(['-C', '-c', '-m', '-F', '-o', '--message', '--file', '--author', '--date',
11
+ '--reuse-message', '--reedit-message', '--fixup', '--squash', '--trailer', '--cleanup',
12
+ '--repo', '--receive-pack', '--exec', '--push-option', '--reason', '--git-dir', '--work-tree', '--namespace', '--config-env', '--pathspec-from-file']);
13
+ for (let i = 0; i < args.length; i += 1) {
14
+ if (args[i] === '--') return false;
15
+ if (values.has(args[i])) { i += 1; continue; }
16
+ if (args[i] === '--help' || args[i] === '-h') return true;
17
+ if (/^-[aneqvsiop]*[mFCc]$/.test(args[i])) i += 1;
18
+ }
19
+ return false;
20
+ }
21
+
22
+ /** Keep global git options attached to every inspection of the command. */
23
+ export function invocation(args, ctx) {
24
+ let i = 0;
25
+ while (i < args.length && args[i].startsWith('-')) {
26
+ if (['-C', '-c', '--git-dir', '--work-tree', '--namespace', '--config-env'].includes(args[i])) i += 2;
27
+ else i += 1;
28
+ }
29
+ const prefix = args.slice(0, i);
30
+ return { command: args[i], args: args.slice(i + 1), prefix,
31
+ ctx: { ...ctx, git: (tail, options) => ctx.git([...prefix, ...tail], options) } };
32
+ }
33
+
34
+ export function gitText(ctx, args, options) {
35
+ const row = ctx.git(args, options);
36
+ if (row.error || (row.status ?? row.code) !== 0) throw new Error(`Git ${args[0]} failed`);
37
+ return Buffer.isBuffer(row.stdout) ? row.stdout.toString('utf8') : String(row.stdout ?? '');
38
+ }
39
+
40
+ /** Do not forward child diagnostics: an auth failure can contain a credential. */
41
+ export function tr(ctx, args) {
42
+ const result = spawnSync(ctx.env?.GIT_PLUGIN_TR_BIN || 'total-recall', args, {
43
+ cwd: ctx.cwd, env: ctx.env, encoding: 'utf8', shell: false,
44
+ timeout: 30000, maxBuffer: 64 * 1024 * 1024,
45
+ });
46
+ if (result.error || result.status !== 0) throw new Error(`Total Recall ${args.slice(0, 3).join(' ')} failed`);
47
+ return result.stdout;
48
+ }
49
+ export function trJson(ctx, args) {
50
+ try { return JSON.parse(tr(ctx, args)); }
51
+ catch { throw new Error(`Total Recall ${args.slice(0, 3).join(' ')} returned no valid result`); }
52
+ }
53
+
54
+ export async function stateStore(ctx, name = 'events') {
55
+ const dir = ctx.env?.TR_PLUGIN_STATE_DIR;
56
+ if (!dir || !path.isAbsolute(dir)) throw new Error('TR_PLUGIN_STATE_DIR must be supplied by the Total Recall host');
57
+ const root = ctx.env?.TR_PACKAGE_ROOT;
58
+ let modulePath;
59
+ if (root) {
60
+ if (!path.isAbsolute(root) || JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8')).name !== 'total-recall-brain') {
61
+ throw new Error('Invalid Total Recall state host');
62
+ }
63
+ modulePath = path.join(root, 'src/core/plugin-state-store.mjs');
64
+ } else modulePath = require.resolve('total-recall-brain/src/core/plugin-state-store.mjs');
65
+ const { PluginStateStore } = await import(pathToFileURL(modulePath).href);
66
+ return new PluginStateStore(path.join(dir, `${name}.jsonl`), 'git');
67
+ }
68
+
69
+ export function repoId(ctx) {
70
+ return gitText(ctx, ['rev-parse', '--path-format=absolute', '--git-common-dir']).trim();
71
+ }
72
+
73
+ /** Inspect the index git would commit. Never change the user's index here. */
74
+ export function prospectiveCommit(ctx, args) {
75
+ const paths = [];
76
+ const pathFlags = [];
77
+ const valueOptions = new Set(['--message', '--file', '--author', '--date', '--reuse-message', '--reedit-message', '--fixup', '--squash', '--trailer', '--cleanup']);
78
+ let all = false, include = false, only = false;
79
+ for (let i = 0; i < args.length; i += 1) {
80
+ const arg = args[i];
81
+ if (arg === '--') { paths.push(...args.slice(i + 1)); break; }
82
+ if (['--interactive', '--patch'].includes(arg)) throw new Error('Stage interactive changes before commit so the guards can inspect them');
83
+ if (arg === '--all') { all = true; continue; }
84
+ if (arg === '--include') { include = true; continue; }
85
+ if (arg === '--only') { only = true; continue; }
86
+ if (arg === '--pathspec-from-file') { pathFlags.push(arg, args[++i]); continue; }
87
+ if (arg.startsWith('--pathspec-from-file=') || arg === '--pathspec-file-nul') { pathFlags.push(arg); continue; }
88
+ if (valueOptions.has(arg)) { i += 1; continue; }
89
+ if (arg.startsWith('--')) continue;
90
+ if (arg.startsWith('-')) {
91
+ for (let j = 1; j < arg.length; j += 1) {
92
+ const flag = arg[j];
93
+ if ('mFCc'.includes(flag)) { if (j === arg.length - 1) i += 1; break; }
94
+ if (flag === 'a') all = true;
95
+ if (flag === 'i') include = true;
96
+ if (flag === 'o') only = true;
97
+ if (flag === 'p') throw new Error('Stage interactive changes before commit so the guards can inspect them');
98
+ // -S/-u take an optional attached value. It is not a flag cluster.
99
+ if ('Su'.includes(flag)) break;
100
+ }
101
+ } else paths.push(arg);
102
+ }
103
+ if (!all && !only && !include && !paths.length && !pathFlags.length) return { ctx, cleanup() {} };
104
+ if (pathFlags.some(a => a === '--pathspec-from-file=-' || a === '-')) throw new Error('Stage stdin pathspecs before commit');
105
+ const dir = mkdtempSync(path.join(os.tmpdir(), 'git-prospective-index-'));
106
+ const env = { ...ctx.env, GIT_INDEX_FILE: path.join(dir, 'index') };
107
+ const local = { ...ctx, git: (a, options = {}) => ctx.git(a, { ...options, env }) };
108
+ const cleanup = () => rmSync(dir, { recursive: true, force: true });
109
+ try {
110
+ const index = gitText(ctx, ['rev-parse', '--path-format=absolute', '--git-path', 'index']).trim();
111
+ if ((paths.length || pathFlags.length || only) && !include) {
112
+ const head = ctx.git(['rev-parse', '--verify', 'HEAD']);
113
+ gitText(local, ['read-tree', ...(head.status === 0 ? ['HEAD'] : ['--empty'])]);
114
+ } else if (existsSync(index)) copyFileSync(index, env.GIT_INDEX_FILE);
115
+ else gitText(local, ['read-tree', '--empty']);
116
+ if (all) gitText(local, ['add', '-u']);
117
+ if (paths.length || pathFlags.length) gitText(local, ['add', '--all', ...pathFlags, '--', ...paths]);
118
+ return { ctx: local, cleanup };
119
+ } catch (error) { cleanup(); throw error; }
120
+ }
121
+
122
+ /** Scan every local branch and tag that the selected remote does not contain.
123
+ * A missing remote object is not an exclusion. This can over-scan, never under-scan.
124
+ */
125
+ /**
126
+ * The commits this push would publish: exactly the refs git itself would update
127
+ * (`git push --dry-run --porcelain` with the same arguments), minus what the remote already has.
128
+ * Local branches the push does not send are never counted.
129
+ */
130
+ export function outgoingCommits(ctx, args = []) {
131
+ const dry = ctx.git(['push', '--dry-run', '--porcelain', ...args.filter(a => a !== '--porcelain' && a !== '--dry-run')]);
132
+ if (dry.error || ![0, 1].includes(dry.status)) return allUnpushedCommits(ctx, args);
133
+ const tips = [];
134
+ let remote = null;
135
+ for (const line of String(dry.stdout || '').split('\n')) {
136
+ if (line.startsWith('To ')) { remote = line.slice(3).trim(); continue; }
137
+ const m = line.match(/^([ +\-*=!])\t([^:\t]*):([^\t]+)\t/);
138
+ if (!m || m[1] === '=' || m[1] === '-' || m[1] === '!' || !m[2]) continue;
139
+ const resolved = ctx.git(['rev-parse', '--verify', '--end-of-options', `${m[2]}^{commit}`]);
140
+ if (resolved.status === 0) tips.push(resolved.stdout.trim());
141
+ }
142
+ if (!tips.length) return [];
143
+ const advertised = remote ? ctx.git(['ls-remote', '--', remote]) : { status: 1, stdout: '' };
144
+ const excluded = [...new Set(String(advertised.stdout || '').split('\n').map(line => line.split(/\s+/)[0]).filter(oid => /^[a-f0-9]{40,64}$/.test(oid)))]
145
+ .filter(oid => ctx.git(['cat-file', '-e', `${oid}^{commit}`]).status === 0);
146
+ return gitText(ctx, ['rev-list', ...new Set(tips), ...(excluded.length ? ['--not', ...excluded] : []), '--']).trim().split('\n').filter(Boolean);
147
+ }
148
+
149
+ /** Every unpushed local commit: the conservative superset used when git cannot say what a push sends. */
150
+ function allUnpushedCommits(ctx, args = []) {
151
+ const takesValue = new Set(['--repo', '--receive-pack', '--exec', '--push-option', '-o']);
152
+ const positional = [];
153
+ let remoteOption;
154
+ for (let i = 0; i < args.length; i += 1) {
155
+ const a = args[i];
156
+ if (takesValue.has(a)) { if (a === '--repo') remoteOption = args[i + 1]; i += 1; }
157
+ else if (a.startsWith('--repo=')) remoteOption = a.slice(7);
158
+ else if (a === '--') positional.push(...args.slice(i + 1));
159
+ else if (!a.startsWith('-')) positional.push(a);
160
+ if (a === '--') break;
161
+ }
162
+ const branch = ctx.git(['symbolic-ref', '--short', '-q', 'HEAD']);
163
+ const config = key => {
164
+ const result = ctx.git(['config', '--get', key]);
165
+ return result.status === 0 ? result.stdout.trim() : '';
166
+ };
167
+ const remote = remoteOption || positional[0] ||
168
+ config(`branch.${branch.stdout?.trim()}.pushRemote`) || config('remote.pushDefault') ||
169
+ config(`branch.${branch.stdout?.trim()}.remote`) || 'origin';
170
+ const advertised = gitText(ctx, ['ls-remote', '--', remote]);
171
+ const excluded = [...new Set(advertised.split('\n').map(line => line.split(/\s+/)[0]).filter(oid => /^[a-f0-9]{40,64}$/.test(oid)))].filter(oid =>
172
+ ctx.git(['cat-file', '-e', `${oid}^{commit}`]).status === 0);
173
+ const tips = gitText(ctx, ['for-each-ref', '--format=%(objectname)', 'refs/heads', 'refs/tags']).trim().split('\n').filter(Boolean);
174
+ const head = ctx.git(['rev-parse', '--verify', 'HEAD']);
175
+ if (head.status === 0) tips.push(head.stdout.trim());
176
+ // Explicit source expressions may name commits outside branches and tags.
177
+ for (const spec of positional.slice(remoteOption ? 0 : 1)) {
178
+ const source = spec.replace(/^\+/, '').split(':')[0];
179
+ if (!source || source.includes('*')) continue;
180
+ const result = ctx.git(['rev-parse', '--verify', '--end-of-options', `${source}^{commit}`]);
181
+ if (result.status === 0) tips.push(result.stdout.trim());
182
+ }
183
+ if (!tips.length) return [];
184
+ return gitText(ctx, ['rev-list', ...new Set(tips), ...(excluded.length ? ['--not', ...excluded] : []), '--']).trim().split('\n').filter(Boolean);
185
+ }
186
+
187
+ export function output(ctx, value, text) {
188
+ if (ctx.json) console.log(JSON.stringify(value, null, 2));
189
+ else console.log(text);
190
+ }
191
+ export function failure(ctx, error) {
192
+ if (ctx.json) console.log(JSON.stringify({ ok: false, error: error.message }));
193
+ else console.error(error.message);
194
+ return 1;
195
+ }
@@ -0,0 +1,201 @@
1
+ import { readFileSync, existsSync, unlinkSync } from 'node:fs';
2
+ import { randomUUID } from 'node:crypto';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { emit, jsonResult, stateStore, tr } from '../lib/runtime.mjs';
6
+
7
+ export const HELPER = '!total-recall git credential-helper';
8
+ const DEFAULT_MAP = { 'github.com': 'github_token' };
9
+
10
+ function hostName(value) {
11
+ if (typeof value !== 'string' || !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::[0-9]+)?$/i.test(value)) {
12
+ throw new Error('Supply a host name with an optional port');
13
+ }
14
+ return value.toLowerCase();
15
+ }
16
+
17
+ async function mappings(ctx) {
18
+ if (!ctx.env.TR_PLUGIN_STATE_DIR) return { ...DEFAULT_MAP };
19
+ const store = await stateStore(ctx, 'auth.json');
20
+ const saved = store.read({ hosts: {} });
21
+ return { ...DEFAULT_MAP, ...saved.hosts };
22
+ }
23
+
24
+ /** Append config to the caller's environment. No credential enters argv or env. */
25
+ export async function authGitEnv(ctx) {
26
+ const env = { ...ctx.env };
27
+ let index = Number(env.GIT_CONFIG_COUNT || 0);
28
+ if (!Number.isSafeInteger(index) || index < 0 || index > 10000) throw new Error('Invalid GIT_CONFIG_COUNT');
29
+ for (const host of Object.keys(await mappings(ctx))) {
30
+ hostName(host);
31
+ for (const value of ['', HELPER]) {
32
+ env[`GIT_CONFIG_KEY_${index}`] = `credential.https://${host}.helper`;
33
+ env[`GIT_CONFIG_VALUE_${index}`] = value;
34
+ index += 1;
35
+ }
36
+ }
37
+ env.GIT_CONFIG_COUNT = String(index);
38
+ return env;
39
+ }
40
+
41
+ // The current dispatcher creates a fresh ctx for after(). Restore only our config keys.
42
+ const pending = new WeakMap();
43
+ export const guards = [{
44
+ name: 'auth-store',
45
+ async before(_args, ctx) {
46
+ try {
47
+ const next = await authGitEnv(ctx);
48
+ const previous = new Map();
49
+ for (const [key, value] of Object.entries(next)) {
50
+ if (key === 'GIT_CONFIG_COUNT' || /^GIT_CONFIG_(KEY|VALUE)_\d+$/.test(key)) {
51
+ previous.set(key, ctx.env[key]);
52
+ ctx.env[key] = value;
53
+ }
54
+ }
55
+ const stack = pending.get(ctx.env) || [];
56
+ stack.push(previous);
57
+ pending.set(ctx.env, stack);
58
+ } catch { return { refuse: 'Credential helper configuration is unavailable' }; }
59
+ },
60
+ after(_args, ctx) {
61
+ for (const [key, value] of pending.get(ctx.env)?.pop() || []) {
62
+ if (value === undefined) delete ctx.env[key]; else ctx.env[key] = value;
63
+ }
64
+ },
65
+ }];
66
+
67
+ async function credential(args, ctx) {
68
+ // Unsupported actions are silent, as the Git protocol requires.
69
+ if (args.length !== 1 || args[0] !== 'get') return 0;
70
+ try {
71
+ const input = readFileSync(0, 'utf8');
72
+ if (input.length > 65536) throw new Error('Invalid credential request');
73
+ const attributes = {};
74
+ for (const line of input.split(/\r?\n/)) {
75
+ if (!line) break;
76
+ const at = line.indexOf('=');
77
+ if (at > 0) attributes[line.slice(0, at)] = line.slice(at + 1);
78
+ }
79
+ if (attributes.protocol !== 'https' || !attributes.host) return 0;
80
+ const hosts = await mappings(ctx);
81
+ const host = hostName(attributes.host);
82
+ if (!Object.hasOwn(hosts, host)) return 0;
83
+ const key = hosts[host];
84
+ const result = jsonResult(tr(['auth', 'secret', 'get', key, '--json'], ctx), 'Auth store is unavailable');
85
+ if (!result.found || typeof result.value !== 'string' || !result.value || /[\r\n\0]/.test(result.value)) {
86
+ throw new Error('Auth key is unavailable');
87
+ }
88
+ if (ctx.env.GIT_PLUGIN_AUTH_PROBE_ID) {
89
+ const probes = await stateStore(ctx, 'auth-probes.jsonl');
90
+ probes.append({ probe: ctx.env.GIT_PLUGIN_AUTH_PROBE_ID, host, key, timestamp: new Date().toISOString() });
91
+ }
92
+ // This is the only credential output. Git reads this pipe.
93
+ process.stdout.write(`username=x-access-token\npassword=${result.value}\nquit=true\n\n`);
94
+ return 0;
95
+ } catch {
96
+ process.stdout.write('quit=true\n\n');
97
+ console.error('Git credential is unavailable from the auth store');
98
+ return 1;
99
+ }
100
+ }
101
+
102
+ async function remoteCheck(ctx) {
103
+ const remote = ctx.env.GIT_PLUGIN_AUTH_REMOTE || ctx.git(['remote', 'get-url', 'origin']).stdout?.trim();
104
+ let url;
105
+ try { url = new URL(remote); } catch { throw new Error('Set an HTTPS origin for the private repository to verify'); }
106
+ if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {
107
+ throw new Error('The verification remote must be HTTPS without credentials');
108
+ }
109
+ const hosts = await mappings(ctx);
110
+ const key = Object.hasOwn(hosts, url.host.toLowerCase()) ? hosts[url.host.toLowerCase()] : null;
111
+ if (!key) throw new Error('No auth key is mapped to the verification host');
112
+ const listed = jsonResult(tr(['auth', 'secret', 'list', '--json'], ctx), 'Cannot read auth key metadata');
113
+ if (!Array.isArray(listed.keys) || !listed.keys.some(row => row.key === key || row.name === key)) {
114
+ throw new Error('The mapped auth key is not present');
115
+ }
116
+ const probe = randomUUID();
117
+ const env = { ...await authGitEnv(ctx), GIT_TERMINAL_PROMPT: '0', GCM_INTERACTIVE: 'never', GIT_PLUGIN_AUTH_PROBE_ID: probe };
118
+ const live = ctx.git(['ls-remote', '--', url.href], { env, timeout: 30000, stdio: ['ignore', 'pipe', 'pipe'] });
119
+ const probes = await stateStore(ctx, 'auth-probes.jsonl');
120
+ const credentialUsed = probes.readTail(1000).some(row => row.probe === probe && row.host === url.host && row.key === key);
121
+ const remoteRead = live.status === 0 && !live.error && !live.signal;
122
+ return { host: url.host, key, key_present: true, remote_read: remoteRead, credential_used: credentialUsed, verified: remoteRead && credentialUsed };
123
+ }
124
+
125
+ async function setup(ctx) {
126
+ const config = 'credential.https://github.com.helper';
127
+ const previous = ctx.git(['config', '--global', '--get-all', config]);
128
+ if (![0, 1].includes(previous.status)) throw new Error('Cannot read global credential helpers');
129
+ const old = previous.status === 0 ? previous.stdout.trimEnd().split('\n') : [];
130
+ const set = args => {
131
+ if (ctx.git(['config', '--global', ...args]).status !== 0) throw new Error('Cannot write global credential helpers');
132
+ };
133
+ try {
134
+ set(['--replace-all', config, '']);
135
+ set(['--add', config, HELPER]);
136
+ const result = await remoteCheck(ctx);
137
+ if (!result.verified) throw new Error('Auth-store sign-in was not verified; plaintext credentials were kept. Use a private HTTPS origin');
138
+ if (result.host !== 'github.com') throw new Error('Setup must verify an HTTPS github.com private repository');
139
+ // Remove only gh helper values. Keep unrelated host and global helpers.
140
+ const helpers = ctx.git(['config', '--global', '--get-regexp', String.raw`^credential\..*helper$`]);
141
+ if (![0, 1].includes(helpers.status)) throw new Error('Cannot read legacy helper entries');
142
+ for (const line of (helpers.stdout || '').split('\n')) {
143
+ const match = line.match(/^(\S+)\s+(.+)$/);
144
+ if (match && /(?:^|[\s/!])gh\s+auth\s+git-credential(?:\s|$)/.test(match[2])) {
145
+ set(['--fixed-value', '--unset-all', match[1], match[2]]);
146
+ }
147
+ }
148
+ const file = path.join(ctx.env.HOME || os.homedir(), '.git-credentials');
149
+ if (existsSync(file)) {
150
+ console.error(`Removing verified plaintext credential file: ${file}`);
151
+ unlinkSync(file);
152
+ }
153
+ return { ...result, helper_wired: true, setup: true };
154
+ } catch (error) {
155
+ const cleared = ctx.git(['config', '--global', '--unset-all', config]);
156
+ let restored = [0, 5].includes(cleared.status);
157
+ for (const value of old) restored = ctx.git(['config', '--global', '--add', config, value]).status === 0 && restored;
158
+ if (!restored) throw new Error('Setup failed; global credential helper restoration failed');
159
+ throw error;
160
+ }
161
+ }
162
+
163
+ export const operations = {
164
+ 'credential-helper': {
165
+ summary: 'Read a Git credential from the auth store',
166
+ usage: 'credential-helper get|store|erase',
167
+ run: credential,
168
+ },
169
+ auth: {
170
+ summary: 'Verify sign-in or configure the auth-store helper',
171
+ usage: 'auth status | auth map <host> <KEY> | auth setup',
172
+ async run(args, ctx) {
173
+ try {
174
+ const [action = 'status', host, key] = args;
175
+ if (action === 'map' && args.length === 3) {
176
+ const normalized = hostName(host);
177
+ if (!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(key)) throw new Error('Supply an auth key name');
178
+ const store = await stateStore(ctx, 'auth.json');
179
+ const saved = store.read({ hosts: {} });
180
+ store.write({ ...saved, hosts: { ...saved.hosts, [normalized]: key } });
181
+ emit(ctx, { host: normalized, key }, `Mapped ${normalized} to ${key}`);
182
+ return 0;
183
+ }
184
+ if (args.length > 1 || !['status', 'setup'].includes(action)) throw new Error('Use auth status, auth map <host> <KEY>, or auth setup');
185
+ const result = action === 'setup' ? await setup(ctx) : await remoteCheck(ctx);
186
+ if (action === 'status') {
187
+ const row = ctx.git(['config', '--get-all', `credential.https://${result.host}.helper`]);
188
+ const helpers = row.status === 0 ? row.stdout.trimEnd().split('\n') : [];
189
+ result.helper_wired = helpers.at(-1) === HELPER && helpers.includes('');
190
+ }
191
+ emit(ctx, result, `${result.host}: auth key ${result.key} is present; live remote read ${result.remote_read ? 'passed' : 'failed'}; auth-store sign-in ${result.verified ? 'verified' : 'not verified (use a private HTTPS origin)'}`);
192
+ return result.verified ? 0 : 1;
193
+ } catch (error) {
194
+ // All errors here are fixed messages. Never forward subprocess output.
195
+ const reason = error.message.startsWith('Plugin state') || error.message.startsWith('SSSS') ? 'Plugin state is unavailable' : error.message;
196
+ emit(ctx, { ok: false, error: reason }, `Auth failed: ${reason}`);
197
+ return 1;
198
+ }
199
+ },
200
+ },
201
+ };
@@ -0,0 +1,40 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { git, tr, operation, output } from '../lib/context.mjs';
4
+
5
+ // Git treats 1..127 (except 125) as a bad revision. Abort on runner failures.
6
+ const RUNNER = `const {spawnSync}=require('node:child_process');
7
+ const r=spawnSync(process.env.GIT_PLUGIN_TR_BIN||'total-recall',['code-quality','check','--json'],{encoding:'utf8',shell:false,maxBuffer:16777216});
8
+ let data;try{data=JSON.parse(r.stdout)}catch{}
9
+ const valid=data&&typeof data.ok==='boolean';
10
+ process.exitCode=r.error||!valid||![0,1,125].includes(r.status)?128:r.status===125?125:r.status===0&&data.ok?0:1;`;
11
+
12
+ export const operations = {
13
+ 'bisect-agent': operation('Find the first bad commit with the repository quality gate', 'bisect-agent <good> <bad> [--json]', async (args, ctx) => {
14
+ if (args.length !== 2 || args.some(arg => arg.startsWith('-'))) throw new Error('Supply good and bad revisions');
15
+ if (git(ctx, ['status', '--porcelain=v1', '-z']).length) throw new Error('Bisect requires a clean tree');
16
+ const bisectStart = git(ctx, ['rev-parse', '--git-path', 'BISECT_START']).trim();
17
+ if (fs.existsSync(path.resolve(ctx.cwd, bisectStart))) throw new Error('A bisect session is already active');
18
+ const refs = args.map(ref => git(ctx, ['rev-parse', '--verify', '--end-of-options', `${ref}^{commit}`]).trim());
19
+ if (refs[0] === refs[1]) throw new Error('Good and bad revisions must differ');
20
+ git(ctx, ['merge-base', '--is-ancestor', ...refs]);
21
+ // Probe the contract before changing HEAD. A failing gate is a valid probe.
22
+ const probe = tr(ctx, ['code-quality', 'check', '--json']);
23
+ let report;
24
+ try { report = JSON.parse(probe.stdout); } catch { /* invalid below */ }
25
+ if (probe.error || ![0, 1].includes(probe.status) || typeof report?.ok !== 'boolean') throw new Error('Repository code-quality check is unavailable or returned an invalid report');
26
+ let started = false;
27
+ try {
28
+ git(ctx, ['bisect', 'start', refs[1], refs[0]]);
29
+ started = true;
30
+ const result = ctx.git(['bisect', 'run', process.execPath, '-e', RUNNER], { env: ctx.env, timeout: 30 * 60 * 1000 });
31
+ if ((result.status ?? result.code) !== 0 || result.error) throw new Error('Bisect did not identify one bad commit. The gate failed, aborted or skipped revisions.');
32
+ const firstBad = git(ctx, ['rev-parse', '--verify', 'refs/bisect/bad']).trim();
33
+ const message = git(ctx, ['show', '-s', '--format=%s', firstBad]).trim();
34
+ output(ctx, { ok: true, firstBad, message, gate: 'total-recall code-quality check --json' }, `First bad commit: ${firstBad}\n${message}`);
35
+ return 0;
36
+ } finally {
37
+ if (started) git(ctx, ['bisect', 'reset']);
38
+ }
39
+ }),
40
+ };
@@ -0,0 +1,159 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { accessSync, constants, realpathSync, statSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { emit, jsonResult, stateStore, tr } from '../lib/runtime.mjs';
6
+ import { authGitEnv } from './auth.mjs';
7
+
8
+ const UPSTREAM = 'https://github.com/git/git';
9
+ const DAY = 24 * 60 * 60 * 1000;
10
+ const compare = (a, b) => {
11
+ const left = a.split('.').map(Number), right = b.split('.').map(Number);
12
+ for (let i = 0; i < 3; i++) if (left[i] !== right[i]) return left[i] - right[i];
13
+ return 0;
14
+ };
15
+
16
+ export function executable(name, ctx) {
17
+ for (const dir of (ctx.env.PATH || '').split(path.delimiter)) {
18
+ const file = path.resolve(ctx.cwd, dir, name);
19
+ try { accessSync(file, constants.X_OK); if (statSync(file).isFile()) return file; } catch { /* try next PATH entry */ }
20
+ }
21
+ return null;
22
+ }
23
+
24
+ function tool(name, args, ctx, timeout = 15000) {
25
+ const file = executable(name, ctx);
26
+ if (!file) return { status: null };
27
+ return spawnSync(file, args, { cwd: ctx.cwd, env: ctx.env, encoding: 'utf8',
28
+ stdio: ['ignore', 'pipe', 'pipe'], timeout, maxBuffer: 16 * 1024 * 1024, shell: false });
29
+ }
30
+
31
+ function installSource(file, build, ctx) {
32
+ if (/[/\\](?:Cellar|Homebrew|linuxbrew)[/\\]/i.test(file)) return 'brew';
33
+ const brew = tool('brew', ['--prefix'], ctx);
34
+ if (brew.status === 0 && brew.stdout?.trim()) {
35
+ try { if (file.startsWith(`${realpathSync(brew.stdout.trim())}${path.sep}`)) return 'brew'; } catch { /* invalid prefix */ }
36
+ }
37
+ const apt = tool('dpkg-query', ['-S', file], ctx);
38
+ if (apt.status === 0 && /^git(?::[^:\s]+)?:\s/m.test(apt.stdout || '')) return 'apt';
39
+ if (file === '/usr/bin/git' && /Apple Git/i.test(build)) return 'apple-clt';
40
+ return 'other';
41
+ }
42
+
43
+ async function upstream(ctx, force) {
44
+ const store = await stateStore(ctx, 'upstream.json');
45
+ const cached = store.read(null);
46
+ const age = Date.now() - Date.parse(cached?.checked_at);
47
+ if (!force && age >= 0 && age < DAY && Array.isArray(cached?.versions) && cached.versions.length &&
48
+ cached.versions.every(v => /^\d+\.\d+\.\d+$/.test(v))) return { ...cached, cached: true, stale: false };
49
+ const result = ctx.git(['ls-remote', '--tags', '--refs', UPSTREAM], {
50
+ env: { ...await authGitEnv(ctx), GIT_TERMINAL_PROMPT: '0' }, timeout: 30000,
51
+ });
52
+ const versions = [...new Set((result.stdout || '').split('\n').flatMap(line => {
53
+ const match = line.match(/^[0-9a-f]+\s+refs\/tags\/v(\d+\.\d+\.\d+)$/);
54
+ return match ? [match[1]] : [];
55
+ }))].sort(compare);
56
+ if (result.status !== 0 || result.error || !versions.length) {
57
+ return { versions: [], checked_at: cached?.checked_at ?? null, cached: false, stale: true, error: 'Cannot read stable upstream Git releases' };
58
+ }
59
+ const value = { versions, checked_at: new Date().toISOString() };
60
+ store.write(value);
61
+ return { ...value, cached: false, stale: false };
62
+ }
63
+
64
+ export async function collectCli(ctx, { force = false } = {}) {
65
+ const command = executable('git', ctx);
66
+ if (!command) throw new Error('Git executable was not found in PATH');
67
+ const resolved = realpathSync(command);
68
+ const versionRow = ctx.git(['--version']);
69
+ const build = ctx.git(['version', '--build-options']);
70
+ const version = versionRow.status === 0 && versionRow.stdout?.match(/^git version (\d+\.\d+\.\d+)(?:\D.*)?\s*$/)?.[1];
71
+ if (!version || build.status !== 0) throw new Error('Cannot read the Git version and build options');
72
+ const releases = await upstream(ctx, force);
73
+ const latest = releases.versions.at(-1) ?? null;
74
+ const hostname = os.hostname();
75
+ const report = {
76
+ version, resolved_path: resolved, install_source: installSource(resolved, versionRow.stdout, ctx),
77
+ build_options: build.stdout.trimEnd(), latest_upstream: latest,
78
+ release_notes_url: latest ? `${UPSTREAM}/blob/master/Documentation/RelNotes/${latest}.adoc` : null,
79
+ versions_behind: latest ? releases.versions.filter(v => compare(v, version) > 0).length : null,
80
+ checked_at: new Date().toISOString(), hostname,
81
+ upstream_checked_at: releases.checked_at, upstream_cached: releases.cached, upstream_stale: releases.stale,
82
+ ...(releases.error ? { error: releases.error } : {}),
83
+ };
84
+ const id = Buffer.from(hostname).toString('hex');
85
+ const store = await stateStore(ctx, `cli-${id}.json`);
86
+ store.read(null);
87
+ store.write(report);
88
+ const events = await stateStore(ctx, 'cli-events.jsonl');
89
+ events.append({ type: 'git.cli_checked', timestamp: report.checked_at, ...report });
90
+ return report;
91
+ }
92
+
93
+ async function allNodes(ctx) {
94
+ const inventory = jsonResult(tr(['mesh', 'nodes', '--json'], ctx), 'Cannot read mesh nodes');
95
+ if (!Array.isArray(inventory.nodes) || !inventory.nodes.length) throw new Error('Mesh returned no nodes');
96
+ const nodes = [];
97
+ for (const node of inventory.nodes) {
98
+ const name = node.name || node.hostname || node.id;
99
+ if (typeof name !== 'string' || !name || name.startsWith('-')) {
100
+ nodes.push({ ok: false, error: 'Mesh node has no usable name' }); continue;
101
+ }
102
+ try {
103
+ const result = jsonResult(tr(['mesh', 'exec', name, 'total-recall git cli --json'], ctx), 'Node CLI check failed');
104
+ if (!result.version || !result.hostname || result.error || result.upstream_stale) throw new Error('Node CLI metadata is incomplete');
105
+ nodes.push({ node: name, ok: true, ...result });
106
+ } catch { nodes.push({ node: name, ok: false, error: 'Node CLI check failed' }); }
107
+ }
108
+ return { ok: nodes.every(node => node.ok), nodes };
109
+ }
110
+
111
+ async function update(ctx) {
112
+ const before = await collectCli(ctx, { force: true });
113
+ if (before.error) throw new Error(before.error);
114
+ if (before.versions_behind === 0) return { ok: true, updated: false, cli: before };
115
+ let result;
116
+ switch (before.install_source) {
117
+ case 'brew': result = tool('brew', ['upgrade', 'git'], ctx, 15 * 60 * 1000); break;
118
+ case 'apt': result = tool('apt-get', ['install', '--only-upgrade', '-y', 'git'], ctx, 15 * 60 * 1000); break;
119
+ case 'apple-clt':
120
+ if (!executable('brew', ctx)) throw new Error('Apple Command Line Tools Git needs Homebrew; no installer is available');
121
+ result = tool('brew', ['install', 'git'], ctx, 15 * 60 * 1000); break;
122
+ default: throw new Error('Git install source is other; no supported updater is available');
123
+ }
124
+ if (result.status !== 0 || result.error || result.signal) throw new Error('The Git installer failed; check installer permissions and configured sources');
125
+ const after = await collectCli(ctx);
126
+ if (after.error || after.versions_behind !== 0) throw new Error('Git is still behind upstream; the configured source or PATH has no current Git');
127
+ return { ok: true, updated: after.version !== before.version, previous_version: before.version, cli: after };
128
+ }
129
+
130
+ export const operations = {
131
+ cli: {
132
+ summary: 'Record Git CLI metadata or update Git through its installer',
133
+ usage: 'cli [--json] [--all-nodes] | cli update [--scheduled]',
134
+ async run(args, ctx) {
135
+ const json = ctx.json || args.includes('--json');
136
+ ctx = { ...ctx, json };
137
+ const rest = args.filter(a => a !== '--json');
138
+ const isUpdate = rest[0] === 'update';
139
+ try {
140
+ let report;
141
+ if (!rest.length) report = await collectCli(ctx);
142
+ else if (rest.length === 1 && rest[0] === '--all-nodes') report = await allNodes(ctx);
143
+ else if (isUpdate && (rest.length === 1 || (rest.length === 2 && rest[1] === '--scheduled'))) report = await update(ctx);
144
+ else throw new Error('Use cli [--all-nodes] or cli update [--scheduled]');
145
+ emit(ctx, report, JSON.stringify(report, null, 2));
146
+ return report.error || report.ok === false ? 1 : 0;
147
+ } catch (error) {
148
+ const reason = error.message;
149
+ emit(ctx, { ok: false, error: reason, checked_at: new Date().toISOString(), hostname: os.hostname() }, `Git CLI check failed: ${reason}`);
150
+ if (isUpdate) {
151
+ // Fixed message only. Installer and service output can contain private data.
152
+ const alert = tr(['alerts', 'send', '--title', 'Git update failed', '--message', 'Git remains out of date. Run total-recall git cli --json for details.'], ctx);
153
+ if (alert.status !== 0) console.error('The operator alert could not be delivered');
154
+ }
155
+ return 1;
156
+ }
157
+ },
158
+ },
159
+ };