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,81 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
3
+ import { parseArgs } from 'node:util';
4
+ import { collectAudit, collectIdentity, isPassthrough } from '../../cli.mjs';
5
+ import { checkSecrets } from './secret-firewall.mjs';
6
+ import { failure, gitText, helpOnly, invocation, outgoingCommits, output, prospectiveCommit, repoId, stateStore } from './_support.mjs';
7
+
8
+ export const machineEmail = address => {
9
+ const domain = address.split('@')[1]?.toLowerCase();
10
+ return !domain || !domain.includes('.') || /\.(local|lan|home|internal|localdomain)$/.test(domain) || domain === 'localhost';
11
+ };
12
+
13
+ async function visibility(ctx) {
14
+ // No remote: nothing can be published, so the repository is local, not public.
15
+ if (!String(ctx.git(['remote']).stdout || '').trim()) return 'LOCAL';
16
+ const repository = repoId(ctx);
17
+ // Remote URLs can contain credentials. Cache only the URL fingerprint.
18
+ const remote = createHash('sha256').update(ctx.git(['config', '--get', 'remote.origin.url']).stdout?.trim() || '').digest('hex');
19
+ let store;
20
+ let cached;
21
+ try {
22
+ store = await stateStore(ctx, 'visibility');
23
+ cached = store.read({})[repository];
24
+ } catch { /* Live visibility can still prove the rule. */ }
25
+ const result = spawnSync(ctx.env?.GIT_PLUGIN_GH_BIN || 'gh', ['repo', 'view', '--json', 'visibility'], {
26
+ cwd: gitText(ctx, ['rev-parse', '--show-toplevel']).trim(), env: ctx.env, encoding: 'utf8', shell: false, timeout: 15000,
27
+ });
28
+ let value;
29
+ try { if (result.status === 0) value = JSON.parse(result.stdout).visibility?.toUpperCase(); } catch { /* refuse unknown */ }
30
+ if (['PUBLIC', 'PRIVATE', 'INTERNAL'].includes(value)) {
31
+ if (store) {
32
+ const cache = store.read({});
33
+ cache[repository] = { value, remote, checkedAt: new Date().toISOString() };
34
+ store.write(cache);
35
+ }
36
+ return value;
37
+ }
38
+ if (cached?.remote === remote && ['PUBLIC', 'PRIVATE', 'INTERNAL'].includes(cached.value) &&
39
+ Date.now() - Date.parse(cached.checkedAt) < 24 * 60 * 60 * 1000) return cached.value;
40
+ throw new Error('Cannot verify repository visibility for internal project records');
41
+ }
42
+
43
+ export async function checkRules(args, ctx) {
44
+ const call = invocation(args, ctx);
45
+ if (!['commit', 'push'].includes(call.command) || helpOnly(args)) return;
46
+ let prospective;
47
+ try {
48
+ const paths = new Set();
49
+ const checkIdentity = text => {
50
+ const emails = [...text.matchAll(/<([^<>\s]+)>/g)].map(m => m[1]);
51
+ if (!emails.length || emails.some(machineEmail)) throw new Error('Refusing a machine-derived identity');
52
+ };
53
+ if (call.command === 'commit') {
54
+ prospective = prospectiveCommit(call.ctx, call.args);
55
+ call.ctx = prospective.ctx;
56
+ for (const type of ['GIT_AUTHOR_IDENT', 'GIT_COMMITTER_IDENT']) checkIdentity(gitText(call.ctx, ['var', type]));
57
+ const authorAt = call.args.findIndex(a => a === '--author');
58
+ const author = authorAt >= 0 ? call.args[authorAt + 1] : call.args.find(a => a.startsWith('--author='))?.slice(9);
59
+ if (author) checkIdentity(author);
60
+ // --amend and -C/-c reuse identities from history unless reset-author is set.
61
+ if (!call.args.includes('--reset-author')) {
62
+ const reuseAt = call.args.findIndex(a => ['-C', '-c', '--reuse-message', '--reedit-message'].includes(a));
63
+ const reuse = reuseAt >= 0 ? call.args[reuseAt + 1] : call.args.find(a => /^--(reuse|reedit)-message=/.test(a))?.split('=').slice(1).join('=');
64
+ if (reuse || call.args.includes('--amend')) checkIdentity(gitText(call.ctx, ['show', '-s', '--format=%an <%ae>', reuse || 'HEAD']));
65
+ }
66
+ for (const file of gitText(call.ctx, ['diff', '--cached', '--name-only', '--diff-filter=ACMRT', '-z', '--']).split('\0').filter(Boolean)) paths.add(file);
67
+ } else {
68
+ for (const commit of outgoingCommits(call.ctx, call.args)) {
69
+ checkIdentity(gitText(call.ctx, ['show', '-s', '--format=%an <%ae>%n%cn <%ce>', commit]));
70
+ for (const file of gitText(call.ctx, ['ls-tree', '-r', '--name-only', '-z', commit]).split('\0').filter(Boolean)) paths.add(file);
71
+ }
72
+ }
73
+ if ([...paths].some(file => file === '.github/workflows' || file.startsWith('.github/workflows/'))) return { refuse: 'Workflow files are prohibited (.github/workflows)' };
74
+ if ([...paths].some(file => file.startsWith('docs/projects/')) && await visibility(call.ctx) === 'PUBLIC') {
75
+ return { refuse: 'Internal project records cannot enter a public repository (docs/projects)' };
76
+ }
77
+ } catch (error) { return { refuse: error.message }; }
78
+ finally { prospective?.cleanup(); }
79
+ }
80
+
81
+ export const guards = [{ name: 'rules', before: checkRules }];
@@ -0,0 +1,92 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { gitText, helpOnly, invocation, outgoingCommits, prospectiveCommit } from './_support.mjs';
3
+
4
+ /**
5
+ * Ask the auth plugin which stored values occur in the text. One call per check; values never
6
+ * enter this process, only key names come back (`auth secret scan --stdin`, exit 1 on a match).
7
+ */
8
+ export function scanText(ctx, text) {
9
+ const result = spawnSync(ctx.env?.GIT_PLUGIN_TR_BIN || 'total-recall', ['auth', 'secret', 'scan', '--stdin', '--json'], {
10
+ cwd: ctx.cwd, env: ctx.env, input: text, encoding: 'utf8', shell: false, timeout: 120000, maxBuffer: 16 * 1024 * 1024,
11
+ });
12
+ if (result.error) throw new Error(`auth secret scan could not start (${result.error.code || 'spawn failed'})`);
13
+ if (result.signal) throw new Error(`auth secret scan was stopped (${result.signal})`);
14
+ let reply = null;
15
+ try { reply = JSON.parse(result.stdout); } catch { /* named below; a parse error can quote the output */ }
16
+ if (![0, 1].includes(result.status) || !reply) throw new Error(scanFailure(result));
17
+ if (!Array.isArray(reply.keys)) throw new Error('auth secret scan returned no key list');
18
+ return reply.keys;
19
+ }
20
+
21
+ /**
22
+ * Name why the scan gave no result without forwarding the child's output,
23
+ * which can hold credential material. A generic "could not complete" hid that
24
+ * auth was simply turned off for the repository (2026-11-12).
25
+ */
26
+ function scanFailure(result) {
27
+ const stderr = String(result.stderr || '');
28
+ if (/Plugin auth is inactive/.test(stderr)) return 'the auth plugin is turned off for this repository; run total-recall plugin on auth';
29
+ if (/Unknown (plugin|command)|not installed/i.test(stderr)) return 'the auth plugin is not installed here or has no secret scan';
30
+ return `auth secret scan exited ${result.status} without a result`;
31
+ }
32
+
33
+ /**
34
+ * The publishable part of a patch: everything except removed lines. A removed line is content the
35
+ * commit takes out; scanning it blocked the very commit that redacts a secret already in history
36
+ * (a project tracker, 2026-11-12). Added lines, headers, binary patches and the commit message
37
+ * are still scanned, and every staged or outgoing blob is scanned whole.
38
+ */
39
+ export function publishedPatchText(patch) {
40
+ return String(patch).split('\n').filter((line) => !(line.startsWith('-') && !line.startsWith('---'))).join('\n');
41
+ }
42
+
43
+ export function checkSecrets(args, ctx) {
44
+ const call = invocation(args, ctx);
45
+ if (!['commit', 'push'].includes(call.command) || helpOnly(args)) return;
46
+ let prospective;
47
+ try {
48
+ if (call.command === 'commit') { prospective = prospectiveCommit(call.ctx, call.args); call.ctx = prospective.ctx; }
49
+ // Collect what would be published, then scan it in one auth call.
50
+ const chunks = [];
51
+ const add = value => chunks.push(Buffer.isBuffer(value) ? value.toString('utf8') : value);
52
+ if (call.command === 'commit') {
53
+ add(publishedPatchText(gitText(call.ctx, ['diff', '--cached', '--no-ext-diff', '--no-textconv', '--binary', '--'])));
54
+ const paths = gitText(call.ctx, ['diff', '--cached', '--name-only', '--diff-filter=ACMRT', '-z', '--']).split('\0').filter(Boolean);
55
+ for (const file of paths) {
56
+ const row = call.ctx.git(['show', `:${file}`], { encoding: null });
57
+ if (row.error || row.status !== 0) throw new Error('Cannot scan a staged object');
58
+ add(row.stdout);
59
+ }
60
+ } else {
61
+ const scanned = new Set();
62
+ for (const commit of outgoingCommits(call.ctx, call.args)) {
63
+ add(publishedPatchText(gitText(call.ctx, ['show', '--format=fuller', '--root', '--no-ext-diff', '--no-textconv', '--binary', commit, '--'])));
64
+ // Raw blobs of the files this commit changed: a binary patch does not contain the original bytes.
65
+ const changed = gitText(call.ctx, ['diff-tree', '-r', '-z', '--root', '--no-commit-id', commit]).split('\0').filter(Boolean);
66
+ for (const entry of changed) {
67
+ const m = entry.match(/^:\d+ \d+ [a-f0-9]+ ([a-f0-9]+) [ACMRT]/);
68
+ if (!m || /^0+$/.test(m[1]) || scanned.has(m[1])) continue;
69
+ scanned.add(m[1]);
70
+ const row = call.ctx.git(['cat-file', 'blob', m[1]], { encoding: null });
71
+ if (row.error || row.status !== 0) throw new Error('Cannot scan an outgoing object');
72
+ add(row.stdout);
73
+ }
74
+ }
75
+ }
76
+ // Batches stay under the auth scan input limit (64 MB) with room to spare.
77
+ const matches = new Set();
78
+ let batch = [], size = 0;
79
+ const flush = () => { if (batch.length) for (const key of scanText(call.ctx, batch.join('\n'))) matches.add(key); batch = []; size = 0; };
80
+ for (const chunk of chunks) {
81
+ if (size + chunk.length > 32 * 1024 * 1024) flush();
82
+ batch.push(chunk); size += chunk.length + 1;
83
+ }
84
+ flush();
85
+ if (matches.size) return { refuse: `Auth secret match: ${[...matches].sort().join(', ')}` };
86
+ } catch (error) {
87
+ // Our own messages only: git failures and scanFailure never carry file or secret content.
88
+ return { refuse: `Secret firewall could not complete: ${error.message}. Check auth store and repository access.` };
89
+ } finally { prospective?.cleanup(); }
90
+ }
91
+
92
+ export const guards = [{ name: 'secret-firewall', before: checkSecrets }];
@@ -0,0 +1,156 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdtempSync, rmSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { failure, gitText, helpOnly, invocation, output, repoId, stateStore } from './_support.mjs';
6
+
7
+ export function destructive(args) {
8
+ const [command, ...tail] = args;
9
+ if (helpOnly(tail)) return false;
10
+ const options = tail.slice(0, tail.indexOf('--') < 0 ? tail.length : tail.indexOf('--'));
11
+ return (command === 'reset' && options.some(a => a === '--hard' || a.startsWith('--hard='))) ||
12
+ (command === 'checkout' && tail.includes('--') && tail.indexOf('--') < tail.length - 1) ||
13
+ command === 'restore' ||
14
+ (command === 'clean' && !options.some(a => a === '--dry-run' || /^-[^-]*n/.test(a))) ||
15
+ (command === 'branch' && options.some(a => a === '--delete' && options.includes('--force') || /^-[^-]*D/.test(a))) ||
16
+ (command === 'stash' && ['drop', 'clear'].includes(tail[0])) ||
17
+ ['rebase', 'filter-branch'].includes(command) ||
18
+ (command === 'update-ref' && options.includes('-d'));
19
+ }
20
+
21
+ const backupPrefix = 'refs/git-plugin/undo/';
22
+ const objectEnv = ctx => ({ ...ctx.env,
23
+ GIT_AUTHOR_NAME: 'Git undo', GIT_AUTHOR_EMAIL: 'git-undo@example.invalid',
24
+ GIT_COMMITTER_NAME: 'Git undo', GIT_COMMITTER_EMAIL: 'git-undo@example.invalid' });
25
+
26
+ /** Use a private index. Do not stage files in the user's index. */
27
+ function untrackedSnapshot(ctx, includeIgnored) {
28
+ const files = gitText(ctx, ['ls-files', '--others', ...(includeIgnored ? [] : ['--exclude-standard']), '-z']).split('\0').filter(Boolean);
29
+ if (!files.length) return null;
30
+ const dir = mkdtempSync(path.join(os.tmpdir(), 'git-undo-index-'));
31
+ const env = { ...objectEnv(ctx), GIT_INDEX_FILE: path.join(dir, 'index') };
32
+ try {
33
+ gitText(ctx, ['read-tree', '--empty'], { env });
34
+ // Bounded argv also supports names that start with a dash.
35
+ for (let i = 0; i < files.length; i += 100) gitText(ctx, ['add', '--force', '--', ...files.slice(i, i + 100)], { env });
36
+ const tree = gitText(ctx, ['write-tree'], { env }).trim();
37
+ return gitText(ctx, ['commit-tree', tree, '-m', 'Git undo untracked files'], { env }).trim();
38
+ } finally { rmSync(dir, { recursive: true, force: true }); }
39
+ }
40
+
41
+ /**
42
+ * Tracked working-tree content as a commit on HEAD, through a private index. `git stash create`
43
+ * refuses while the index has unmerged paths, which blocked every conflict resolution
44
+ * (`checkout --theirs -- <file>`) with "Cannot save an undo snapshot". This keeps the content;
45
+ * an undo restores the files, not the merge state itself.
46
+ */
47
+ function trackedSnapshot(ctx) {
48
+ const dir = mkdtempSync(path.join(os.tmpdir(), 'git-undo-tracked-'));
49
+ const env = { ...objectEnv(ctx), GIT_INDEX_FILE: path.join(dir, 'index') };
50
+ try {
51
+ gitText(ctx, ['read-tree', 'HEAD'], { env });
52
+ gitText(ctx, ['add', '--update', '--', '.'], { env });
53
+ const tree = gitText(ctx, ['write-tree'], { env }).trim();
54
+ return gitText(ctx, ['commit-tree', tree, '-p', 'HEAD', '-m', 'Git undo working tree'], { env }).trim();
55
+ } finally { rmSync(dir, { recursive: true, force: true }); }
56
+ }
57
+
58
+ export async function snapshot(ctx, command, { includeIgnored = false } = {}) {
59
+ const store = await stateStore(ctx);
60
+ const repository = repoId(ctx);
61
+ const worktree = gitText(ctx, ['rev-parse', '--show-toplevel']).trim();
62
+ const originalCtx = ctx;
63
+ ctx = { ...ctx, git: (args, options) => originalCtx.git(['-C', worktree, ...args], options) };
64
+ const id = randomUUID();
65
+ const refs = gitText(ctx, ['for-each-ref', '--format=%(refname) %(objectname)']).trim().split('\n')
66
+ .filter(row => row && !row.startsWith(backupPrefix)).map(row => {
67
+ const [name, oid] = row.split(' '); return { name, oid };
68
+ });
69
+ const head = gitText(ctx, ['rev-parse', '--verify', 'HEAD']).trim();
70
+ const sym = ctx.git(['symbolic-ref', '-q', 'HEAD']);
71
+ const status = gitText(ctx, ['status', '--porcelain=v1', '-z']);
72
+ let stash = null, tracked = null;
73
+ if (status) {
74
+ const created = ctx.git(['stash', 'create'], { env: objectEnv(ctx) });
75
+ if (!created.error && created.status === 0) stash = String(created.stdout).trim() || null;
76
+ // Only a conflicted index makes stash create refuse for a reason a content snapshot answers.
77
+ else if (gitText(ctx, ['ls-files', '--unmerged']).trim()) tracked = trackedSnapshot(ctx);
78
+ else throw new Error('git stash create failed');
79
+ }
80
+ const untracked = untrackedSnapshot(ctx, includeIgnored);
81
+ const stashes = gitText(ctx, ['stash', 'list', '--format=%H%x00%gs']).trim().split('\n').filter(Boolean).map(row => {
82
+ const [oid, message] = row.split('\0'); return { oid, message };
83
+ });
84
+ const objects = [...new Set([head, stash, tracked, untracked, ...refs.map(r => r.oid), ...stashes.map(r => r.oid)].filter(Boolean))];
85
+ // Pin objects before the event is durable. Git GC must not remove a snapshot.
86
+ for (let i = 0; i < objects.length; i += 1) gitText(ctx, ['update-ref', `${backupPrefix}${id}/${i}`, objects[i]]);
87
+ const event = { id, type: 'git.snapshot', timestamp: new Date().toISOString(), repository, worktree,
88
+ command: [command[0]], refs, head, symbolicHead: sym.status === 0 ? sym.stdout.trim() : null,
89
+ stash, tracked, untracked, stashes, includeIgnored };
90
+ store.append(event);
91
+ return event;
92
+ }
93
+
94
+ async function before(args, ctx) {
95
+ const call = invocation(args, ctx);
96
+ if (helpOnly(args)) return;
97
+ if (!destructive([call.command, ...call.args])) return;
98
+ try {
99
+ await snapshot(call.ctx, [call.command, ...call.args], {
100
+ includeIgnored: call.command === 'clean' && call.args.some(a => a === '-X' || a === '-x' || /^-[^-]*[xX]/.test(a)),
101
+ });
102
+ } catch { return { refuse: 'Cannot save an undo snapshot. No destructive command was run.' }; }
103
+ }
104
+
105
+ async function restore(ctx, event) {
106
+ // Keep the current state recoverable if restoration fails or the user changes their mind.
107
+ await snapshot(ctx, ['undo', event.id], { includeIgnored: event.includeIgnored });
108
+ const current = gitText(ctx, ['for-each-ref', '--format=%(refname) %(objectname)']).trim().split('\n').filter(Boolean);
109
+ const desired = new Map(event.refs.map(row => [row.name, row.oid]));
110
+ for (const row of current) {
111
+ const [name, oid] = row.split(' ');
112
+ if (!name.startsWith(backupPrefix) && !desired.has(name)) gitText(ctx, ['update-ref', '-d', name, oid]);
113
+ }
114
+ for (const [name, oid] of desired) gitText(ctx, ['update-ref', name, oid]);
115
+ if (event.symbolicHead) gitText(ctx, ['symbolic-ref', 'HEAD', event.symbolicHead]);
116
+ else gitText(ctx, ['update-ref', '--no-deref', 'HEAD', event.head]);
117
+ gitText(ctx, ['reset', '--hard', event.head]);
118
+ if (event.stash) gitText(ctx, ['stash', 'apply', '--index', event.stash]);
119
+ for (const commit of [event.tracked, event.untracked].filter(Boolean)) {
120
+ const dir = mkdtempSync(path.join(os.tmpdir(), 'git-undo-restore-'));
121
+ const env = { ...ctx.env, GIT_INDEX_FILE: path.join(dir, 'index') };
122
+ try {
123
+ gitText(ctx, ['read-tree', commit], { env });
124
+ gitText(ctx, ['checkout-index', '--all', '--force'], { env });
125
+ } finally { rmSync(dir, { recursive: true, force: true }); }
126
+ }
127
+ // A ref alone cannot recover entries removed with stash clear.
128
+ gitText(ctx, ['update-ref', '-d', 'refs/stash']);
129
+ for (const row of [...event.stashes].reverse()) gitText(ctx, ['stash', 'store', '-m', row.message || 'Recovered stash', row.oid]);
130
+ const store = await stateStore(ctx);
131
+ store.append({ id: randomUUID(), type: 'git.undo', event: event.id, repository: event.repository, timestamp: new Date().toISOString() });
132
+ }
133
+
134
+ async function run(args, ctx) {
135
+ try {
136
+ if (args.length > 1 || args.some(a => a.startsWith('-') && a !== '--list')) throw new Error('Use undo [<event>] or undo --list');
137
+ const store = await stateStore(ctx);
138
+ const repository = repoId(ctx);
139
+ const worktree = gitText(ctx, ['rev-parse', '--show-toplevel']).trim();
140
+ const history = store.read([]);
141
+ const events = history.filter(e => e.type === 'git.snapshot' && e.repository === repository && e.worktree === worktree);
142
+ if (args[0] === '--list') {
143
+ output(ctx, { ok: true, events }, events.map(e => `${e.id} ${e.timestamp} ${e.command[0]}`).join('\n') || 'No undo snapshots.');
144
+ return 0;
145
+ }
146
+ const used = new Set(history.filter(e => e.type === 'git.undo').map(e => e.event));
147
+ const event = args[0] ? events.find(e => e.id === args[0]) : events.findLast(e => !used.has(e.id) && e.command[0] !== 'undo');
148
+ if (!event) throw new Error('No matching undo snapshot for this worktree');
149
+ await restore(ctx, event);
150
+ output(ctx, { ok: true, restored: event.id }, `Restored snapshot ${event.id}.`);
151
+ return 0;
152
+ } catch (error) { return failure(ctx, error); }
153
+ }
154
+
155
+ export const guards = [{ name: 'undo', before }];
156
+ export const operations = { undo: { summary: 'Restore refs, the index and files from an undo snapshot', usage: 'undo [<event>] [--list] [--json]', run } };
@@ -0,0 +1,182 @@
1
+ import { existsSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { failure, gitText, output } from './_support.mjs';
4
+ import { registeredPathState, registeredRepositories } from './fleet.mjs';
5
+
6
+ /**
7
+ * Work that never reached the default branch: local branches with commits the
8
+ * default branch does not contain, linked worktrees with uncommitted changes,
9
+ * and detached worktrees holding commits no branch contains (removing such a
10
+ * worktree loses them).
11
+ *
12
+ * Nothing reported this before. A week of drawer UI work sat on an unpushed
13
+ * branch with 256 uncommitted files in a worktree, and about 35 plugin
14
+ * worktrees sat beside it, while every status said the repositories were fine.
15
+ *
16
+ * A commit counts as landed when the default branch (local or origin) reaches
17
+ * it, or holds an equivalent patch (`git cherry`), so rebased and
18
+ * cherry-picked work is not reported. A squash merge is a different patch and
19
+ * still shows; delete the branch once it has landed. A branch the operator
20
+ * keeps on purpose (`git config --add totalrecall.keepBranch <name>`) is
21
+ * listed as kept and not counted.
22
+ */
23
+
24
+ const SUMMARY_LIMIT = 480;
25
+
26
+ function localCtx(ctx, directory) {
27
+ return { ...ctx, git: (args, options) => ctx.git(['-C', directory, ...args], options) };
28
+ }
29
+
30
+ const refExists = (local, ref) => local.git(['rev-parse', '--verify', '--quiet', `${ref}^{commit}`]).status === 0;
31
+
32
+ /** The default branch: origin's HEAD, else local main or master, else init.defaultBranch. */
33
+ export function defaultBranch(local) {
34
+ const originHead = local.git(['symbolic-ref', '--quiet', 'refs/remotes/origin/HEAD']);
35
+ const fromOrigin = originHead.status === 0 ? originHead.stdout.trim().replace(/^refs\/remotes\/origin\//, '') : null;
36
+ const configured = local.git(['config', '--get', 'init.defaultBranch']);
37
+ const candidates = [fromOrigin, 'main', 'master', configured.status === 0 ? configured.stdout.trim() : null].filter(Boolean);
38
+ for (const name of candidates) {
39
+ const localRef = `refs/heads/${name}`, remoteRef = `refs/remotes/origin/${name}`;
40
+ const refs = [localRef, remoteRef].filter(ref => refExists(local, ref));
41
+ if (refs.length) return { name, refs };
42
+ }
43
+ return null;
44
+ }
45
+
46
+ /** Non-merge commits on `tip` that no default ref reaches and whose patch the default branch lacks. */
47
+ function uniqueCommits(local, tip, base) {
48
+ const unreached = gitText(local, ['rev-list', '--no-merges', tip, '--not', ...base.refs, '--']).split('\n').filter(Boolean);
49
+ if (!unreached.length) return [];
50
+ const cherry = new Set(gitText(local, ['cherry', base.refs[0], tip]).split('\n')
51
+ .filter(line => line.startsWith('+ ')).map(line => line.slice(2).trim()));
52
+ return unreached.filter(commit => cherry.has(commit));
53
+ }
54
+
55
+ function parseWorktrees(text) {
56
+ return text.split('\0\0').filter(Boolean).map(record => Object.fromEntries(record.split('\0').filter(Boolean).map(row => {
57
+ const i = row.indexOf(' ');
58
+ return i < 0 ? [row, true] : [row.slice(0, i), row.slice(i + 1)];
59
+ })));
60
+ }
61
+
62
+ /** Inspect one repository. Never fetches: remote refs are as of the last fetch. */
63
+ export function inspectUnmerged(directory, ctx) {
64
+ const local = localCtx(ctx, directory);
65
+ const report = { path: directory, defaultBranch: null, outstanding: 0, branches: [], kept: [], worktrees: [], prunable: [] };
66
+ const state = registeredPathState(directory, ctx);
67
+ if (state === 'missing') return { ...report, error: 'Registered path does not exist' };
68
+ if (state === 'not-a-repository') return { ...report, notRepository: true };
69
+ const root = gitText(local, ['rev-parse', '--path-format=absolute', '--show-toplevel']).trim();
70
+ report.path = root;
71
+ const base = defaultBranch(local);
72
+ if (!base) return { ...report, error: 'No default branch (no origin/HEAD, main, master or init.defaultBranch)' };
73
+ report.defaultBranch = base.name;
74
+ const keep = new Set(local.git(['config', '--get-all', 'totalrecall.keepBranch']).stdout?.split('\n').map(s => s.trim()).filter(Boolean) || []);
75
+
76
+ const trees = parseWorktrees(gitText(local, ['worktree', 'list', '--porcelain', '-z']));
77
+ const checkedOut = new Map(trees.filter(t => typeof t.branch === 'string').map(t => [t.branch.replace(/^refs\/heads\//, ''), t.worktree]));
78
+
79
+ const rows = gitText(local, ['for-each-ref', '--format=%(refname:short)%00%(objectname)%00%(committerdate:iso-strict)%00%(upstream:short)%00%(subject)', 'refs/heads']).split('\n').filter(Boolean);
80
+ for (const row of rows) {
81
+ const [branch, tip, lastCommitAt, upstream, subject] = row.split('\0');
82
+ if (branch === base.name) continue;
83
+ const unique = uniqueCommits(local, tip, base);
84
+ if (!unique.length) continue;
85
+ const onRemote = Number(gitText(local, ['rev-list', '--count', tip, '--not', '--remotes', '--']).trim()) === 0;
86
+ const entry = { branch, commits: unique.length, lastCommitAt, subject, upstream: upstream || null, pushed: onRemote, worktree: checkedOut.get(branch) || null };
87
+ if (keep.has(branch)) report.kept.push(entry); else report.branches.push(entry);
88
+ }
89
+
90
+ trees.forEach((tree, index) => {
91
+ const primary = index === 0;
92
+ const branch = typeof tree.branch === 'string' ? tree.branch.replace(/^refs\/heads\//, '') : null;
93
+ if (tree.prunable || !existsSync(tree.worktree)) {
94
+ if (!primary) report.prunable.push({ path: tree.worktree, branch, reason: 'directory is gone; git worktree prune removes the record' });
95
+ return;
96
+ }
97
+ // The primary checkout on the default branch is ongoing work on that branch, which audit and fleet report.
98
+ if (primary && branch === base.name) return;
99
+ const tctx = localCtx(ctx, tree.worktree);
100
+ const status = gitText(tctx, ['status', '--porcelain=v1', '-z']);
101
+ const uncommitted = status.split('\0').filter(entry => /^[ MADRCU?!]{2} /.test(entry)).length;
102
+ const orphanCommits = branch ? 0 : Number(gitText(tctx, ['rev-list', '--count', 'HEAD', '--not', '--branches', '--tags', '--remotes', '--']).trim());
103
+ if (!uncommitted && !orphanCommits) return;
104
+ report.worktrees.push({ path: tree.worktree, branch, detached: !branch, uncommitted, orphanCommits, locked: Boolean(tree.locked), primary });
105
+ });
106
+ report.outstanding = report.branches.length + report.worktrees.length;
107
+ return report;
108
+ }
109
+
110
+ function describeRepo(repo, home) {
111
+ const rel = p => (home && p.startsWith(`${home}${path.sep}`) ? `~${p.slice(home.length)}` : p);
112
+ const parts = [
113
+ ...repo.branches.map(b => `branch ${b.branch} (+${b.commits}${b.pushed ? '' : ', not pushed'})`),
114
+ ...repo.worktrees.map(w => `worktree ${rel(w.path)} (${[w.uncommitted ? `${w.uncommitted} uncommitted` : '', w.orphanCommits ? `${w.orphanCommits} commits on no branch` : ''].filter(Boolean).join(', ')})`),
115
+ ];
116
+ return `${rel(repo.path)}: ${parts.join('; ')}`;
117
+ }
118
+
119
+ export function summarize(repositories, home = process.env.HOME) {
120
+ const outstanding = repositories.reduce((n, repo) => n + repo.outstanding, 0);
121
+ const failed = repositories.filter(repo => repo.error);
122
+ if (!outstanding) {
123
+ const count = repositories.filter(repo => !repo.notRepository).length;
124
+ const scope = repositories.length === 1 ? 'in this repository' : `in ${count} repositories`;
125
+ return `No branch or worktree holds work outside the default branch ${scope}${failed.length ? `; ${failed.length} could not be read` : ''}.`;
126
+ }
127
+ const head = `${outstanding} branch${outstanding === 1 ? '' : 'es'} or worktree${outstanding === 1 ? '' : 's'} hold work not in the default branch: `;
128
+ let text = head;
129
+ const withWork = repositories.filter(repo => repo.outstanding);
130
+ for (let i = 0; i < withWork.length; i++) {
131
+ const next = `${i ? ' | ' : ''}${describeRepo(withWork[i], home)}`;
132
+ if (text.length + next.length > SUMMARY_LIMIT) { text += ` | and ${withWork.length - i} more repositor${withWork.length - i === 1 ? 'y' : 'ies'}`; break; }
133
+ text += next;
134
+ }
135
+ return `${text}${failed.length ? `. ${failed.length} repositor${failed.length === 1 ? 'y' : 'ies'} could not be read` : ''}. Details: total-recall git unmerged${repositories.length > 1 ? ' --registered' : ''}.`;
136
+ }
137
+
138
+ function textReport(repositories, summary) {
139
+ const lines = [];
140
+ for (const repo of repositories) {
141
+ if (repo.error) { lines.push(`${repo.path}: ${repo.error}`); continue; }
142
+ if (repo.notRepository) continue;
143
+ if (!repo.outstanding && !repo.kept.length && !repo.prunable.length) continue;
144
+ lines.push(`${repo.path} (default ${repo.defaultBranch})`);
145
+ for (const b of repo.branches) lines.push(` branch ${b.branch}: ${b.commits} commit${b.commits === 1 ? '' : 's'} not in ${repo.defaultBranch}${b.pushed ? '' : ', not pushed'}; last ${b.lastCommitAt} "${b.subject}"${b.worktree ? `; checked out at ${b.worktree}` : ''}`);
146
+ for (const w of repo.worktrees) lines.push(` worktree ${w.path} (${w.branch || 'detached HEAD'}${w.locked ? ', locked' : ''}): ${w.uncommitted} uncommitted file${w.uncommitted === 1 ? '' : 's'}${w.orphanCommits ? `, ${w.orphanCommits} commit${w.orphanCommits === 1 ? '' : 's'} on no branch` : ''}`);
147
+ for (const b of repo.kept) lines.push(` kept ${b.branch}: ${b.commits} commit${b.commits === 1 ? '' : 's'} not in ${repo.defaultBranch} (totalrecall.keepBranch)`);
148
+ for (const p of repo.prunable) lines.push(` stale worktree record ${p.path}${p.branch ? ` (${p.branch})` : ''}: ${p.reason}`);
149
+ }
150
+ return [...lines, summary].join('\n');
151
+ }
152
+
153
+ async function run(args, ctx) {
154
+ try {
155
+ const unknown = args.filter(a => a !== '--registered');
156
+ if (unknown.length) throw new Error('Use unmerged [--registered] [--json]');
157
+ let directories;
158
+ if (args.includes('--registered')) directories = registeredRepositories(ctx);
159
+ else {
160
+ const top = ctx.git(['rev-parse', '--path-format=absolute', '--show-toplevel']);
161
+ if (top.status !== 0) throw new Error('Not inside a git repository; use --registered for every registered repository');
162
+ directories = [top.stdout.trim()];
163
+ }
164
+ const repositories = directories.map(directory => {
165
+ try { return inspectUnmerged(directory, ctx); }
166
+ catch (error) { return { path: directory, error: error.message, outstanding: 0, branches: [], kept: [], worktrees: [], prunable: [] }; }
167
+ });
168
+ const ok = repositories.every(repo => !repo.error);
169
+ const outstanding = repositories.reduce((n, repo) => n + repo.outstanding, 0);
170
+ const summary = summarize(repositories);
171
+ output(ctx, { ok, outstanding, summary, repositories }, textReport(repositories, summary));
172
+ return ok ? 0 : 1;
173
+ } catch (error) { return failure(ctx, error); }
174
+ }
175
+
176
+ export const operations = {
177
+ unmerged: {
178
+ summary: 'Branches and worktrees whose commits or changes are not in the default branch',
179
+ usage: 'unmerged [--registered] [--json] keep a branch on purpose: git config --add totalrecall.keepBranch <name>',
180
+ run,
181
+ },
182
+ };
@@ -0,0 +1,16 @@
1
+ import { git, commitContext, operation, output } from '../lib/context.mjs';
2
+
3
+ export const operations = {
4
+ why: operation('Show the recorded intent for one line', 'why <file>:<line> [--json]', async (args, ctx) => {
5
+ const match = args.length === 1 && /^(.*):([1-9]\d*)$/.exec(args[0]);
6
+ if (!match || !match[1]) throw new Error('Supply one file:line');
7
+ const [, file, line] = match;
8
+ const blame = git(ctx, ['blame', '--line-porcelain', '-L', `${line},${line}`, 'HEAD', '--', file]);
9
+ const sha = blame.match(/^([a-f0-9]{40,64})\s/m)?.[1];
10
+ if (!sha) throw new Error('Blame did not return a commit');
11
+ const context = await commitContext(ctx, sha);
12
+ const result = { ok: true, file, line: Number(line), blame, ...context, reason: context.recorded ? null : 'No commit record. Only commit trailers are available.' };
13
+ output(ctx, result, `${file}:${line} — ${sha}\n${context.message}\nSession: ${context.session || 'unknown'}\nTracker: ${context.trackerItems.join(', ') || 'unknown'}\nDecision: ${context.decisions.join('; ') || 'unknown'}\nRules: ${context.rules.join('; ') || 'unknown'}${result.reason ? `\n${result.reason}` : ''}`);
14
+ return 0;
15
+ }),
16
+ };
@@ -0,0 +1,27 @@
1
+ ---
2
+ type: ui-chunk:chunk
3
+ title: Git change comparison
4
+ description: Compare unstaged, staged, or base-relative changes using the owner diff operation.
5
+ timestamp: "2026-11-08T09:20:00Z"
6
+ plugin: git
7
+ id: change-comparison
8
+ intents: [review git diff, check staged files, compare branch to base, read diff stats, show a patch]
9
+ surfaces: [page, right]
10
+ operations: [diff]
11
+ data: ["diff:/ok", "diff:/cached", "diff:/diffStat", "diff:/timestamp"]
12
+ ---
13
+
14
+ <style>
15
+ .sentinel-diff { padding:var(--spacing-md); border-block:1px solid var(--color-border); background:var(--color-surface); }
16
+ .sentinel-diff h2 { margin-top:0; }
17
+ .sentinel-diff dt { color:var(--color-accent); }
18
+ .sentinel-diff dd { margin:0 0 .45em; }
19
+ </style>
20
+ <section class="sentinel-diff">
21
+ <h2>Choose a comparison</h2>
22
+ <dl>
23
+ <dt>Working tree</dt><dd>Read the current changes against the index.</dd>
24
+ <dt>Staged</dt><dd>Review the index against the selected base.</dd>
25
+ <dt>Patch</dt><dd>Use <code>--patch</code> for line changes. The Home displays file statistics.</dd>
26
+ </dl>
27
+ </section>
@@ -0,0 +1,22 @@
1
+ ---
2
+ type: ui-chunk:chunk
3
+ title: Commit identity audit
4
+ description: Check whether a commit identity is configured and count machine addresses recorded in local history.
5
+ timestamp: "2026-11-08T09:20:00Z"
6
+ plugin: git
7
+ id: commit-identity
8
+ intents: [check git author identity, verify commit email, audit machine addresses, git author history]
9
+ surfaces: [page]
10
+ operations: [identity]
11
+ data: ["identity:/ok", "identity:/machineCommits"]
12
+ ---
13
+
14
+ <style>
15
+ .sentinel-identity { padding:var(--spacing-md); border-inline-start:3px solid var(--color-accent); background:var(--color-surface); }
16
+ .sentinel-identity h2 { margin-top:0; }
17
+ .sentinel-identity p { color:var(--color-muted); }
18
+ </style>
19
+ <section class="sentinel-identity">
20
+ <h2>Check the configured author</h2>
21
+ <p>The identity audit checks Git’s current author configuration and counts commits with a machine-derived address. Review the count separately from the current configuration.</p>
22
+ </section>
@@ -0,0 +1,25 @@
1
+ ---
2
+ type: ui-chunk:chunk
3
+ title: Create a detached worktree
4
+ description: Create a linked Git worktree from an explicit base ref and verify its resulting revision.
5
+ timestamp: "2026-11-08T09:20:00Z"
6
+ plugin: git
7
+ id: create-worktree
8
+ intents: [create git worktree, isolate git checkout, add detached worktree, choose worktree base]
9
+ surfaces: [page]
10
+ operations: [worktree-create]
11
+ data: ["worktree-create:/ok", "worktree-create:/head", "worktree-create:/detached"]
12
+ ---
13
+
14
+ <style>
15
+ .sentinel-create { padding:var(--spacing-md); background:var(--color-surface); border-radius:var(--radius-md); }
16
+ .sentinel-create h2 { margin-top:0; }
17
+ .sentinel-create li::marker { color:var(--color-accent); }
18
+ </style>
19
+ <section class="sentinel-create">
20
+ <h2>Prepare an isolated checkout</h2>
21
+ <ol>
22
+ <li>Choose a destination and a base ref.</li>
23
+ <li>Confirm the destination before creation, then verify the detached revision.</li>
24
+ </ol>
25
+ </section>
@@ -0,0 +1,22 @@
1
+ ---
2
+ type: ui-chunk:chunk
3
+ title: Registered Git worktrees
4
+ description: List the repository's main and linked worktrees and inspect their checked-out revisions.
5
+ timestamp: "2026-11-08T09:20:00Z"
6
+ plugin: git
7
+ id: registered-worktrees
8
+ intents: [list git worktrees, inspect linked worktrees, find detached worktree, show active checkouts]
9
+ surfaces: [page, right]
10
+ operations: [worktree-list]
11
+ data: ["worktree-list:/ok", "worktree-list:/worktrees/0/head"]
12
+ ---
13
+
14
+ <style>
15
+ .sentinel-index { padding:var(--spacing-md); border:1px solid var(--color-border); background:var(--color-surface); }
16
+ .sentinel-index h2 { margin-top:0; }
17
+ .sentinel-index b { color:var(--color-accent); }
18
+ </style>
19
+ <section class="sentinel-index">
20
+ <h2>Read the worktree register</h2>
21
+ <p>Compare the revision and branch of each registered checkout. The Home labels <b>Main</b> and <b>Current</b> separately.</p>
22
+ </section>