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,278 @@
1
+ /** Automations control ledger. Values come only from the inventory owner. */
2
+ const SCREENS = [
3
+ { id: 'scheduled', label: 'Scheduled' }, { id: 'nodes', label: 'Nodes' },
4
+ { id: 'audit', label: 'Audit' }, { id: 'run', label: 'Run' },
5
+ ];
6
+ const escape = (value) => String(value ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
7
+ const words = (value) => String(value ?? 'unknown').replaceAll('_', ' ').replaceAll('-', ' ');
8
+ const date = (value) => value && Number.isFinite(Date.parse(value)) ? new Date(value).toLocaleString() : '— not recorded';
9
+ const stateClass = (value) => ['error', 'failed', 'offline', 'unreachable', 'unauthorized'].includes(value) ? 'bad' : 'neutral';
10
+
11
+ /** Decode the host wrapper without turning command failures into inventory. */
12
+ export function decodeInventory(result) {
13
+ if (result?.success === false || (typeof result?.exitCode === 'number' && result.exitCode !== 0) || (typeof result?.code === 'number' && result.code !== 0)) {
14
+ throw new Error(result.error || 'Inventory command failed');
15
+ }
16
+ let data = result?.output !== undefined ? result.output : result;
17
+ if (typeof data === 'string') {
18
+ try { data = JSON.parse(data); } catch { throw new Error('Inventory returned invalid JSON'); }
19
+ }
20
+ if (!data || !Array.isArray(data.nodes) || data.nodes.some((node) => !node || !Array.isArray(node.automations))) {
21
+ throw new Error('Inventory response is incomplete or unsupported');
22
+ }
23
+ return data;
24
+ }
25
+
26
+ export class AutomationsElement extends HTMLElement {
27
+ constructor() {
28
+ super();
29
+ this.attachShadow({ mode: 'open' });
30
+ this.screen = 'scheduled';
31
+ this.filters = { node: '', source: '', trigger: '', state: '' };
32
+ this.inventory = null;
33
+ this.phase = 'unchecked';
34
+ this.error = '';
35
+ this.feedback = '';
36
+ this.selected = '';
37
+ this.request = 0;
38
+ this.attemptedAt = null;
39
+ this.pendingControl = null;
40
+ this.localOnly = true;
41
+ this.showInventoryJson = false;
42
+ this.boundClick = (event) => this.click(event);
43
+ this.boundChange = (event) => this.change(event);
44
+ this.boundKey = (event) => this.key(event);
45
+ this.boundScreen = (event) => {
46
+ if (event.detail?.pluginId === this.host?.pluginId) this.selectScreen(event.detail.screen);
47
+ };
48
+ }
49
+
50
+ get host() { return this._host; }
51
+ set host(value) {
52
+ this._host = value;
53
+ if (this.isConnected && value?.run) void this.refresh();
54
+ }
55
+ get screens() { return SCREENS; }
56
+
57
+ connectedCallback() {
58
+ this.shadowRoot.addEventListener('click', this.boundClick);
59
+ this.shadowRoot.addEventListener('change', this.boundChange);
60
+ this.shadowRoot.addEventListener('keydown', this.boundKey);
61
+ window.addEventListener('plugin-show-screen', this.boundScreen);
62
+ this.render();
63
+ if (this.host?.run) void this.refresh();
64
+ }
65
+
66
+ disconnectedCallback() {
67
+ this.request++;
68
+ this.controller?.abort();
69
+ this.shadowRoot.removeEventListener('click', this.boundClick);
70
+ this.shadowRoot.removeEventListener('change', this.boundChange);
71
+ this.shadowRoot.removeEventListener('keydown', this.boundKey);
72
+ window.removeEventListener('plugin-show-screen', this.boundScreen);
73
+ }
74
+
75
+ async invoke(command, args = []) {
76
+ if (!this.host?.run) throw new Error('Authenticated automation bridge unavailable');
77
+ return this.host.run(command, args);
78
+ }
79
+
80
+ async refresh() {
81
+ const request = ++this.request;
82
+ this.controller?.abort();
83
+ this.controller = new AbortController();
84
+ this.phase = 'loading'; this.inventory = null; this.error = ''; this.feedback = ''; this.pendingControl = null;
85
+ this.showInventoryJson = false;
86
+ this.attemptedAt = new Date().toISOString();
87
+ this.render();
88
+ try {
89
+ const data = decodeInventory(await this.invoke('list', this.localOnly ? ['--local'] : []));
90
+ if (request !== this.request || !this.isConnected) return;
91
+ this.inventory = data; this.phase = 'ready';
92
+ } catch (error) {
93
+ if (request !== this.request || !this.isConnected) return;
94
+ this.inventory = null; this.phase = 'error';
95
+ this.error = error.message || 'Automation inventory unavailable';
96
+ }
97
+ this.render();
98
+ }
99
+
100
+ records() {
101
+ return (this.inventory?.nodes || []).flatMap((node) => node.automations.map((item) => ({
102
+ ...item, _node: node, _key: `${node.node_id || node.ip || node.hostname}:${item.id || `${item.plugin_id}:${item.command}`}`,
103
+ })));
104
+ }
105
+ filteredRecords() {
106
+ return this.records().filter((item) =>
107
+ (!this.filters.node || this.nodeKey(item._node) === this.filters.node) &&
108
+ (!this.filters.source || item.source === this.filters.source) &&
109
+ (!this.filters.trigger || item.trigger?.kind === this.filters.trigger) &&
110
+ (!this.filters.state || item.state === this.filters.state));
111
+ }
112
+ nodeKey(node) { return String(node.node_id || node.ip || node.hostname || ''); }
113
+ selectedRecord() {
114
+ const records = this.filteredRecords();
115
+ return records.find((item) => item._key === this.selected) || records[0];
116
+ }
117
+ selectScreen(screen, focus = false) {
118
+ if (!SCREENS.some((item) => item.id === screen)) return;
119
+ this.screen = screen; this.feedback = ''; this.pendingControl = null; this.render();
120
+ this.host?.screenChanged?.(screen);
121
+ if (focus) this.shadowRoot.querySelector(`[data-screen="${screen}"]`)?.focus();
122
+ }
123
+ key(event) {
124
+ const tab = event.target.closest('[data-screen]');
125
+ if (!tab) return;
126
+ const index = SCREENS.findIndex((item) => item.id === tab.dataset.screen);
127
+ const next = event.key === 'ArrowRight' ? (index + 1) % SCREENS.length
128
+ : event.key === 'ArrowLeft' ? (index + SCREENS.length - 1) % SCREENS.length
129
+ : event.key === 'Home' ? 0 : event.key === 'End' ? SCREENS.length - 1 : null;
130
+ if (next === null) return;
131
+ event.preventDefault(); this.selectScreen(SCREENS[next].id, true);
132
+ }
133
+ change(event) {
134
+ const filter = event.target.dataset.filter;
135
+ if (!(filter in this.filters)) return;
136
+ this.filters[filter] = event.target.value; this.selected = ''; this.pendingControl = null; this.render();
137
+ this.shadowRoot.querySelector(`[data-filter="${filter}"]`)?.focus();
138
+ }
139
+ click(event) {
140
+ const button = event.target.closest('button');
141
+ if (!button || button.disabled) return;
142
+ if (button.dataset.screen) this.selectScreen(button.dataset.screen, true);
143
+ else if (button.dataset.record) {
144
+ this.selected = button.dataset.record; this.pendingControl = null; this.render();
145
+ const selected = [...this.shadowRoot.querySelectorAll('[data-record]')].find((item) => item.dataset.record === this.selected);
146
+ selected?.focus();
147
+ } else if (button.dataset.action === 'refresh') void this.refresh();
148
+ else if (button.dataset.action === 'scope') { this.localOnly = !this.localOnly; void this.refresh(); }
149
+ else if (button.dataset.action === 'inventory-json') this.toggleInventoryJson();
150
+ else if (button.dataset.action === 'confirm-control') void this.executeControl(this.pendingControl?.index, true);
151
+ else if (button.dataset.action === 'dismiss-control') { this.pendingControl = null; this.render(); }
152
+ else if (button.dataset.control) void this.executeControl(Number(button.dataset.control));
153
+ }
154
+
155
+ toggleInventoryJson() {
156
+ if (!this.inventory) return;
157
+ this.showInventoryJson = !this.showInventoryJson;
158
+ this.render();
159
+ if (this.showInventoryJson) {
160
+ const field = this.shadowRoot.querySelector('#inventory-json-value');
161
+ field.focus();
162
+ field.select();
163
+ } else {
164
+ this.shadowRoot.querySelector('[data-action="inventory-json"]').focus();
165
+ }
166
+ }
167
+
168
+ async executeControl(index, confirmed = false) {
169
+ const item = this.selectedRecord();
170
+ const action = item?.actions?.[index];
171
+ if (!action || typeof action.command !== 'string' || !Array.isArray(action.args) || !action.args.every((arg) => typeof arg === 'string')) return;
172
+ if (Array.isArray(this.host?.operations) && !this.host.operations.includes(action.command)) {
173
+ this.feedback = 'Action unavailable for this access level'; this.render(); return;
174
+ }
175
+ if (!['read', 'write', 'destructive', 'send'].includes(action.effect)) {
176
+ this.feedback = 'Action unavailable: source did not declare its effect'; this.render(); return;
177
+ }
178
+ if (action.effect !== 'read') {
179
+ if (!confirmed) { this.pendingControl = { key: item._key, index }; this.render(); return; }
180
+ if (this.pendingControl?.key !== item._key || this.pendingControl.index !== index) return;
181
+ }
182
+ this.pendingControl = null;
183
+ this.feedback = 'Submitting source action…'; this.render();
184
+ const request = this.request;
185
+ try {
186
+ const result = await this.invoke(action.command, action.args);
187
+ if (request !== this.request || !this.isConnected) return;
188
+ if (result?.success === false || result?.code > 0 || result?.exitCode > 0) throw new Error(result.error || 'Source rejected the action');
189
+ await this.refresh();
190
+ if (this.isConnected) this.feedback = 'Source action completed; inventory refreshed';
191
+ } catch (error) {
192
+ if (request !== this.request || !this.isConnected) return;
193
+ this.feedback = `Action failed: ${error.message}`;
194
+ }
195
+ if (this.isConnected) this.render();
196
+ }
197
+
198
+ renderFilters() {
199
+ const records = this.records();
200
+ const options = (values, selected) => '<option value="">All</option>' + [...new Set(values.filter(Boolean))].sort().map((value) => `<option value="${escape(value)}" ${selected === value ? 'selected' : ''}>${escape(words(value))}</option>`).join('');
201
+ return `<div class="filters">
202
+ <label>Node<select data-filter="node">${'<option value="">All</option>' + (this.inventory?.nodes || []).map((node) => `<option value="${escape(this.nodeKey(node))}" ${this.filters.node === this.nodeKey(node) ? 'selected' : ''}>${escape(node.hostname || node.ip || 'Unnamed node')}</option>`).join('')}</select></label>
203
+ <label>Source<select data-filter="source">${options(records.map((item) => item.source), this.filters.source)}</select></label>
204
+ <label>Trigger<select data-filter="trigger">${options(records.map((item) => item.trigger?.kind), this.filters.trigger)}</select></label>
205
+ <label>State<select data-filter="state">${options(records.map((item) => item.state), this.filters.state)}</select></label>
206
+ </div>`;
207
+ }
208
+
209
+ renderDetails(item, controls = false) {
210
+ if (!item) return '<section class="detail"><h3>No automation selected</h3><p>Select a record after discovery.</p></section>';
211
+ const knownBool = (value, yes, no) => value === true ? yes : value === false ? no : '— not reported';
212
+ const run = item.last_run;
213
+ return `<section class="detail"><p class="eyebrow">${escape(words(item.source || 'source not reported'))}</p><h3>${escape(item.name || item.intent || item.id || item.command)}</h3>
214
+ <dl>
215
+ <dt>Trigger</dt><dd>${escape(item.trigger?.summary || item.schedule || '— not reported')} <span class="subtle">${escape(item.trigger?.kind || '')}</span></dd>
216
+ <dt>Enabled</dt><dd>${escape(knownBool(item.enabled, 'Enabled', 'Disabled'))}</dd>
217
+ <dt>Runner</dt><dd>${escape(knownBool(item.runner_available, 'Available', 'Unavailable'))}</dd>
218
+ <dt>Node</dt><dd>${escape(item._node.hostname || item._node.ip || '— not reported')}</dd>
219
+ <dt>Placement</dt><dd>${escape(words(item.placement || 'not reported'))}</dd>
220
+ <dt>Origin</dt><dd class="mono">${escape(item.origin || item.id || '— not reported')}</dd>
221
+ <dt>Last handled slot</dt><dd>${escape(item.last_handled_slot || '— not recorded')}</dd>
222
+ <dt>Observed run</dt><dd>${run ? `${escape(words(run.state))} · ${escape(date(run.at))}${run.exit_code != null ? ` · exit ${escape(run.exit_code)}` : ''}` : '— no run outcome recorded'}</dd>
223
+ </dl>
224
+ ${controls ? this.renderControls(item) : '<p class="note">A handled slot is scheduling evidence. It does not establish a successful run.</p>'}
225
+ </section>`;
226
+ }
227
+ renderControls(item) {
228
+ const actions = Array.isArray(item.actions) ? item.actions : [];
229
+ const pending = this.pendingControl?.key === item._key ? actions[this.pendingControl.index] : null;
230
+ return `<div class="source-actions">${actions.length ? actions.map((action, index) => `<button data-control="${index}" ${this.feedback === 'Submitting source action…' ? 'disabled' : ''}>${escape(action.label || words(action.command))}<span class="effect">${escape(action.effect || 'effect not declared')}</span></button>`).join('') : '<p>No executable controls were advertised by this source.</p>'}</div>${pending ? `<section class="confirmation" role="group" aria-label="Confirm source action"><p>${escape(pending.label || words(pending.command))}: <strong>${escape(item.name || item.id)}</strong>?</p><div class="source-actions"><button data-action="confirm-control">Confirm ${escape(pending.label || words(pending.command))}</button><button data-action="dismiss-control">Keep unchanged</button></div></section>` : ''}<p class="note">Actions are delegated to the source owner. Available controls depend on its capabilities and your access.</p>`;
231
+ }
232
+ renderScheduled() {
233
+ const records = this.filteredRecords();
234
+ return `${this.renderFilters()}<div class="ledger"><section class="index"><h3>Automation index <span class="count">${records.length}</span></h3>${records.length ? `<ul>${records.map((item) => `<li><button data-record="${escape(item._key)}" aria-pressed="${this.selectedRecord()?._key === item._key}"><span><strong>${escape(item.name || item.intent || item.id || item.command)}</strong><small>${escape(item._node.hostname || item._node.ip)} · ${escape(words(item.source || 'source not reported'))}</small></span><span class="state ${stateClass(item.state)}">${escape(words(item.state))}</span></button></li>`).join('')}</ul>` : '<p>No records match these filters.</p>'}</section>${this.renderDetails(this.selectedRecord())}</div>`;
235
+ }
236
+ renderNodes() {
237
+ const nodes = this.inventory?.nodes || [];
238
+ return `<div class="node-grid">${nodes.map((node) => `<section class="node"><header><h3>${escape(node.hostname || node.ip || 'Unnamed node')}</h3><span class="state ${stateClass(node.status)}">${escape(words(node.status))}</span></header><p class="mono">${escape(node.ip || node.node_id || '— address not reported')}</p>${node.error ? `<p class="bad">${escape(node.error)}</p>` : ''}${Array.isArray(node.coverage) && node.coverage.length ? `<ul class="coverage">${node.coverage.map((coverage) => `<li><div><strong>${escape(words(coverage.source))}</strong><span class="state ${stateClass(coverage.status)}">${escape(words(coverage.status))}</span></div>${coverage.error ? `<p>${escape(coverage.error)}</p>` : ''}<small>${escape(date(coverage.checked_at))}</small></li>`).join('')}</ul>` : '<p>— source coverage was not reported. This node cannot establish complete discovery.</p>'}</section>`).join('') || '<p>No mesh nodes were reported.</p>'}</div>`;
239
+ }
240
+ renderAudit() {
241
+ const records = this.filteredRecords();
242
+ return `${this.renderFilters()}<p class="note">Observed outcomes only. Declarations and handled slots do not prove execution.</p><ul class="audit">${records.map((item) => `<li><div><strong>${escape(item.name || item.id || item.command)}</strong><small>${escape(item._node.hostname || item._node.ip)} · ${escape(words(item.source))}</small></div><div><span class="state ${stateClass(item.last_run?.state)}">${escape(item.last_run ? words(item.last_run.state) : 'No observed run')}</span><small>${escape(date(item.last_run?.at))}</small>${item.last_run?.exit_code != null ? `<small>Exit ${escape(item.last_run.exit_code)}</small>` : ''}</div></li>`).join('') || '<li>No records match these filters.</li>'}</ul>`;
243
+ }
244
+ renderRun() {
245
+ const records = this.filteredRecords();
246
+ return `${this.renderFilters()}<div class="ledger"><section class="index"><h3>Choose a source-owned action</h3><p>Only advertised controls are executable.</p><ul>${records.map((item) => `<li><button data-record="${escape(item._key)}" aria-pressed="${this.selectedRecord()?._key === item._key}"><span><strong>${escape(item.name || item.id || item.command)}</strong><small>${escape(item._node.hostname || item._node.ip)} · ${(item.actions || []).length} advertised controls</small></span></button></li>`).join('')}</ul></section>${this.renderDetails(this.selectedRecord(), true)}</div>`;
247
+ }
248
+ render() {
249
+ const focus = this.shadowRoot.activeElement;
250
+ const focusedAction = focus?.dataset.action;
251
+ const status = this.phase === 'loading' ? 'Checking sources' : this.phase === 'error' ? 'Inventory unavailable'
252
+ : this.phase === 'ready' ? (this.inventory.complete === true ? 'Discovery complete' : 'Partial discovery — inspect source coverage') : 'Not checked';
253
+ const content = this.phase !== 'ready' ? `<div class="empty"><h3>${escape(status)}</h3><p>${escape(this.phase === 'error' ? this.error : this.phase === 'loading' ? 'Reading automation sources and node availability…' : 'Refresh to inspect automation sources.')}</p></div>`
254
+ : this.screen === 'nodes' ? this.renderNodes() : this.screen === 'audit' ? this.renderAudit() : this.screen === 'run' ? this.renderRun() : this.renderScheduled();
255
+ const inventoryJson = this.showInventoryJson ? `
256
+ <label for="inventory-json-value">Inventory JSON</label>
257
+ <p id="inventory-json-help" class="note">Copy the selection with your browser’s Copy command or Ctrl+C / ⌘C.</p>
258
+ <textarea id="inventory-json-value" aria-describedby="inventory-json-help" rows="8" readonly>${escape(JSON.stringify(this.inventory, null, 2))}</textarea>` : '';
259
+ this.shadowRoot.innerHTML = `
260
+ <style>
261
+ .inventory-json { padding:0 16px 16px; }
262
+ .inventory-json .note { margin:4px 0 8px; }
263
+ .inventory-json textarea { width:100%; resize:vertical; padding:8px; border:1px solid var(--automation-border); border-radius:5px; background:var(--automation-surface); color:var(--automation-text); font:12px/1.5 ui-monospace,monospace; }
264
+ .inventory-json textarea:focus-visible { outline:3px solid var(--automation-focus); outline-offset:3px; }
265
+ :host { --automation-bg:var(--color-background,#181713); --automation-surface:var(--color-surface,#24221c); --automation-text:var(--color-text,#f4efe5); --automation-muted:var(--color-muted,#c5bca9); --automation-border:var(--color-border,#726652); --automation-accent:var(--color-primary,#f2bd63); --automation-error:var(--color-error,#ffb5a9); --automation-focus:var(--color-focus,#b8d9ff); display:block; color:var(--automation-text); font:var(--typography-body-font-size,14px)/1.45 var(--typography-body-font-family,system-ui,sans-serif); min-width:0; color-scheme:dark; }
266
+ *,*::before,*::after { box-sizing:border-box; } button,select { font:inherit; color:inherit; min-height:44px; min-width:44px; } button { cursor:pointer; } button:disabled { cursor:wait; opacity:.6; } button:focus-visible,select:focus-visible { outline:3px solid var(--automation-focus); outline-offset:3px; } .workspace { border:1px solid var(--automation-border); border-radius:12px; background:var(--automation-bg); overflow:hidden; } .masthead { padding:20px; display:flex; gap:16px; align-items:center; flex-wrap:wrap; } .brand { display:flex; gap:12px; align-items:center; flex:1; min-width:0; } svg { width:40px; height:40px; color:var(--automation-accent); flex:none; } h2,h3,p { margin:0; } h2 { font-size:20px; letter-spacing:-.03em; } h3 { font-size:15px; } .eyebrow { text-transform:uppercase; letter-spacing:.11em; font-size:10px; color:var(--automation-muted); margin-bottom:4px; } .subtle,small,.note { color:var(--automation-muted); } small { display:block; font-size:11px; margin-top:3px; overflow-wrap:anywhere; } .refresh { border:1px solid var(--automation-accent); background:var(--automation-accent); color:var(--automation-bg); border-radius:7px; padding:8px 14px; font-weight:700; } .status-line { padding:12px 20px; background:var(--automation-surface); border-block:1px solid var(--automation-border); display:flex; justify-content:space-between; gap:12px; flex-wrap:wrap; } .status-line strong { font-size:12px; font-weight:600; } .tabs { display:flex; padding:8px 12px 0; gap:4px; border-bottom:1px solid var(--automation-border); } .tabs button { flex:1; background:transparent; border:0; border-bottom:3px solid transparent; padding:10px 4px; color:var(--automation-muted); } .tabs button[aria-selected=true] { border-color:var(--automation-accent); color:var(--automation-text); font-weight:700; } .body { padding:16px; min-height:200px; animation:reveal .16s ease-out; } .filters { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:10px; margin-bottom:16px; } label { font-size:11px; color:var(--automation-muted); display:grid; gap:4px; } select { width:100%; max-width:100%; background:var(--automation-surface); border:1px solid var(--automation-border); border-radius:5px; padding:6px; } .ledger { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:16px; } .index,.detail,.node { min-width:0; } .index h3 { margin-bottom:8px; } ul { list-style:none; padding:0; margin:0; } .index ul { max-height:380px; overflow:auto; } .index li+li { margin-top:6px; } .index button { width:100%; display:flex; justify-content:space-between; gap:12px; align-items:center; text-align:left; padding:12px; background:transparent; border:1px solid var(--automation-border); border-radius:7px; } .index button[aria-pressed=true] { border-color:var(--automation-accent); background:var(--automation-surface); } strong { overflow-wrap:anywhere; } .state { display:inline-block; text-transform:none; font:11px/1.4 ui-monospace,monospace; border:1px solid var(--automation-border); border-radius:4px; padding:3px 6px; overflow-wrap:anywhere; } .bad { color:var(--automation-error); } .detail { border-left:2px solid var(--automation-accent); padding:0 0 0 16px; } .detail h3 { font-size:19px; margin-bottom:12px; overflow-wrap:anywhere; } dl { display:grid; grid-template-columns:minmax(80px,.7fr) minmax(0,1fr); gap:9px 12px; margin:0; } dt { color:var(--automation-muted); font-size:11px; } dd { margin:0; overflow-wrap:anywhere; font-size:12px; } .mono { font-family:ui-monospace,monospace; overflow-wrap:anywhere; font-size:11px; } .note { font-size:11px; margin-top:16px; } .count { font:11px ui-monospace,monospace; margin-left:4px; color:var(--automation-muted); } .node-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr)); gap:16px; } .node { border:1px solid var(--automation-border); border-radius:8px; padding:14px; } .node header { display:flex; justify-content:space-between; align-items:start; gap:12px; margin-bottom:4px; } .coverage { margin-top:12px; } .coverage li { border-top:1px solid var(--automation-border); padding:10px 0; } .coverage li>div { display:flex; justify-content:space-between; align-items:center; gap:12px; } .coverage strong { font-size:12px; } .coverage p { font-size:12px; margin-top:6px; } .audit li { display:flex; justify-content:space-between; gap:16px; padding:12px 0; border-bottom:1px solid var(--automation-border); } .audit li>div { min-width:0; } .audit li>div:last-child { text-align:right; } .audit { margin-top:12px; } .source-actions { display:flex; flex-wrap:wrap; gap:8px; margin-top:16px; } .source-actions button { border:1px solid var(--automation-accent); border-radius:5px; background:var(--automation-surface); padding:8px 12px; } .effect { display:block; font:10px ui-monospace,monospace; color:var(--automation-muted); } .footer { display:flex; align-items:center; justify-content:space-between; padding:8px 16px; gap:12px; border-top:1px solid var(--automation-border); } .footer button { background:transparent; border:0; color:var(--automation-muted); padding:8px; font-size:12px; } .feedback { font-size:12px; overflow-wrap:anywhere; } .empty { padding:26px 12px; } .empty p { color:var(--automation-muted); margin-top:8px; } @keyframes reveal { from { opacity:.65; } to { opacity:1; } } @media(max-width:600px) { .masthead { padding:16px; gap:10px; } .filters { grid-template-columns:repeat(2,minmax(0,1fr)); } .ledger { grid-template-columns:minmax(0,1fr); } .detail { border-left:0; border-top:2px solid var(--automation-accent); padding:16px 0 0; } .index ul { max-height:260px; } .status-line { padding:10px 16px; } .body { padding:12px; } h2 { font-size:18px; } } @media(prefers-reduced-motion:reduce) { .body { animation:none; } }
267
+ </style>
268
+ <div class="workspace"><header class="masthead"><div class="brand"><svg viewBox="0 0 40 40" fill="none" aria-hidden="true"><path d="M4 20h9m0 0 7-11h12m-19 11 7 11h12M13 20h19" stroke="currentColor" stroke-width="2"/><circle cx="5" cy="20" r="3" fill="currentColor"/><path d="M30 6h6v6h-6zM30 17h6v6h-6zM30 28h6v6h-6z" stroke="currentColor" stroke-width="2"/></svg><div><p class="eyebrow">Triggers · sources · outcomes</p><h2>Automations</h2></div></div><button class="refresh" data-action="refresh" ${this.phase === 'loading' ? 'disabled' : ''}>Refresh</button></header>
269
+ <div class="status-line"><div role="status" aria-live="polite"><strong class="${this.phase === 'error' ? 'bad' : ''}">${escape(status)} · ${this.localOnly ? 'This node' : 'Mesh'}</strong><small>${this.phase === 'ready' ? 'Checked' : 'Last attempt'} ${escape(date(this.phase === 'ready' ? this.inventory.checked_at : this.attemptedAt))}</small></div><button data-action="scope" ${this.phase === 'loading' ? 'disabled' : ''}>${this.localOnly ? 'Scan mesh' : 'Use this node'}</button></div>
270
+ <nav class="tabs" role="tablist" aria-label="Automation views">${SCREENS.map((screen) => `<button id="tab-${screen.id}" role="tab" aria-controls="panel-${screen.id}" aria-selected="${this.screen === screen.id}" tabindex="${this.screen === screen.id ? '0' : '-1'}" data-screen="${screen.id}">${screen.label}</button>`).join('')}</nav>
271
+ <div class="body" id="panel-${this.screen}" role="tabpanel" aria-labelledby="tab-${this.screen}" tabindex="0" aria-busy="${this.phase === 'loading'}">${content}</div>
272
+ <footer class="footer"><span class="feedback" role="status">${escape(this.feedback || (this.phase === 'ready' ? 'Coverage determines completeness.' : 'No current inventory.'))}</span><button type="button" data-action="inventory-json" aria-expanded="${this.showInventoryJson}" aria-controls="inventory-json" ${this.inventory ? '' : 'disabled'}>${this.showInventoryJson ? 'Hide inventory JSON' : 'Select inventory JSON'}</button></footer>
273
+ <div class="inventory-json" id="inventory-json" ${this.showInventoryJson ? '' : 'hidden'}>${inventoryJson}</div></div>`;
274
+ if (focusedAction) this.shadowRoot.querySelector(`[data-action="${focusedAction}"]`)?.focus();
275
+ }
276
+ }
277
+
278
+ if (!customElements.get('tr-automations')) customElements.define('tr-automations', AutomationsElement);
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: "Code Quality Gates"
3
+ plugin: code-quality
4
+ tokens:
5
+ color-primary: "#eab308"
6
+ color-primary-strong: "#facc15"
7
+ color-background: "#0f172a"
8
+ color-surface: "#1e293b"
9
+ color-border: "#334155"
10
+ color-text: "#f8fafc"
11
+ color-muted: "#94a3b8"
12
+ color-accent: "#38bdf8"
13
+ color-success: "#10b981"
14
+ color-warning: "#f59e0b"
15
+ color-danger: "#ef4444"
16
+ color-on-primary: "#0f172a"
17
+ font-body: "'Space Grotesk', system-ui, sans-serif"
18
+ font-mono: "'JetBrains Mono', 'Fira Code', monospace"
19
+ radius-md: "8px"
20
+ space-4: "16px"
21
+ components:
22
+ report:
23
+ backgroundColor: "{tokens.color-surface}"
24
+ textColor: "{tokens.color-text}"
25
+ rounded: "{tokens.radius-md}"
26
+ padding: "{tokens.space-4}"
27
+ workflow:
28
+ backgroundColor: "{tokens.color-background}"
29
+ textColor: "{tokens.color-text}"
30
+ rounded: "{tokens.radius-md}"
31
+ padding: "{tokens.space-4}"
32
+ ---
33
+
34
+ # Code Quality Gates — design
35
+
36
+ The plugin's own visual language, for the Home panel, the SSSS UI chunks and any
37
+ host that themes them. Values follow `branding.color` in `plugin.json` and the
38
+ card palette in `ui/DESIGN.md`.
39
+
40
+ ## Meaning of colour
41
+
42
+ - `color-success`: a report with status `clean` and nothing else.
43
+ - `color-warning`: `partial`, `stale`, `unknown-freshness`, `zero-scope`, and skipped gates.
44
+ - `color-danger`: `failed`, `unparsed-failure`, `infrastructure`, `unreadable`, `missing`, and failing gates.
45
+ - `color-primary`: the one action that runs gates.
46
+
47
+ No colour ever signals "passing" without a `clean` report view. Absent data is
48
+ shown as absent, in `color-muted`.
49
+
50
+ ## Type
51
+
52
+ `font-mono` for gate ids, file paths, revisions and commands; `font-body` for
53
+ everything else.
@@ -2,6 +2,7 @@
2
2
  * `total-recall code-quality <verb>` for the repository you run it from.
3
3
  *
4
4
  * report [view] [arg] read the latest gate report; never starts a check
5
+ * run --tier fast|full run the configured gates once
5
6
  * snapshot --node <name> put the exact source under test on the test node (git file list only)
6
7
  * gate list|add|remove edit the repo's gate list
7
8
  * config get|set|…, detect [--apply], init
@@ -74,6 +75,28 @@ export async function run(argv = []) {
74
75
  return;
75
76
  }
76
77
 
78
+ if (verb === 'run') {
79
+ const usage = 'Usage: total-recall code-quality run [--tier fast|full] [--json]';
80
+ if (rest.includes('--help') || rest.includes('-h')) { console.log(usage); return; }
81
+ const json = rest.includes('--json');
82
+ const options = rest.filter((arg) => arg !== '--json');
83
+ const tier = options.length ? options[1] : 'fast';
84
+ const emitFailure = (exitCode, error) => {
85
+ if (json) console.log(JSON.stringify({ ok: false, exitCode, error }));
86
+ else console.error(error);
87
+ process.exitCode = exitCode;
88
+ };
89
+ if ((options.length && (options.length !== 2 || options[0] !== '--tier')) || !['fast', 'full'].includes(tier)) {
90
+ return emitFailure(2, usage);
91
+ }
92
+ try {
93
+ const { exitCode } = runCore('check.mjs', ['--tier', tier], { quiet: json });
94
+ if (json) console.log(JSON.stringify({ ok: exitCode === 0, exitCode, tier }));
95
+ if (exitCode) process.exitCode = exitCode;
96
+ } catch (error) { emitFailure(error.exitCode || 1, error.message); }
97
+ return;
98
+ }
99
+
77
100
  const moduleUrl = skillConfigModule();
78
101
  if (!moduleUrl) {
79
102
  return fail('Run this through the total-recall CLI: config verbs need a Total Recall install with skill config support.', 2);
@@ -15,13 +15,15 @@ export function findSkillDir(start = process.cwd()) {
15
15
  return stateDir(start);
16
16
  }
17
17
 
18
- export function runCore(script, args) {
18
+ export function runCore(script, args, { quiet = false } = {}) {
19
19
  if (!fs.existsSync(stateDir())) {
20
- const error = new Error('No code-quality state in this repository (.agent/skills/code-quality). Create it: total-recall code-quality init');
20
+ const error = new Error('No code-quality state in this repository (.agent/skills/code-quality). Create configuration: total-recall code-quality detect --apply');
21
21
  error.exitCode = 2;
22
22
  throw error;
23
23
  }
24
- const result = spawnSync(process.execPath, [path.join(CORE_DIR, script), ...args], { stdio: 'inherit' });
24
+ const result = spawnSync(process.execPath, [path.join(CORE_DIR, script), ...args], {
25
+ stdio: quiet ? ['ignore', 'ignore', 'inherit'] : 'inherit',
26
+ });
25
27
  if (result.error) throw result.error;
26
28
  return { exitCode: result.status ?? 1, data: { state_dir: stateDir(), script } };
27
29
  }
@@ -80,7 +80,7 @@ function loadConfig() {
80
80
  if (!existsSync(CONFIG_PATH)) {
81
81
  fail(
82
82
  `No config.json at ${CONFIG_PATH}\n` +
83
- `Run: total-recall skill config code-quality init`
83
+ `Run: total-recall code-quality detect --apply`
84
84
  );
85
85
  }
86
86
  let cfg;
@@ -204,12 +204,14 @@ function acquireLock(lockPath, label) {
204
204
  const ageMs = held?.startedAt ? Date.now() - Date.parse(held.startedAt) : Infinity;
205
205
  const expired = Number.isFinite(ageMs) && ageMs > STALE_AFTER_MS;
206
206
 
207
- if (STEAL_LOCK && state === 'alive') {
207
+ const protectedHolder = state === 'alive' || state === 'alive-foreign' ||
208
+ (state === 'unverifiable' && !expired);
209
+ if (STEAL_LOCK && protectedHolder) {
208
210
  // The machine-wide lock is shared by every repo on this host. A verified
209
211
  // live holder is another repo's (or this repo's) real run: stealing its
210
212
  // lock, or killing it, silently destroys that run's result.
211
213
  fail(
212
- `--steal-lock refused: the ${label} lock is held by a verified running check ` +
214
+ `--steal-lock refused: the ${label} lock has a protected holder (state=${state}) ` +
213
215
  `(pid ${held.pid}, ${held.repoRoot ?? 'unknown repo'}, started ${held.startedAt ?? 'unknown'}).\n` +
214
216
  ` Wait for it to finish, or stop that process yourself if you own it.`,
215
217
  3
@@ -678,6 +680,51 @@ function runCheck(check) {
678
680
 
679
681
  // ─── Main ─────────────────────────────────────────────────────────────────────
680
682
 
683
+ /**
684
+ * Restore the repository's locked dependencies before any gate runs. Idle repositories have
685
+ * their node_modules removed by the scheduled disk cleanup, and a gate run against a missing
686
+ * tree reports import failures as if the code were broken. Installs exactly what the lockfile
687
+ * pins, only when the installed tree is missing or older than the lockfile.
688
+ */
689
+ const LOCKFILES = [
690
+ { file: 'package-lock.json', manager: 'npm', marker: path.join('node_modules', '.package-lock.json'), args: ['ci', '--no-audit', '--no-fund'] },
691
+ { file: 'pnpm-lock.yaml', manager: 'pnpm', marker: path.join('node_modules', '.modules.yaml'), args: ['install', '--frozen-lockfile'] },
692
+ { file: 'yarn.lock', manager: 'yarn', marker: path.join('node_modules', '.yarn-integrity'), args: ['install', '--frozen-lockfile'] },
693
+ ];
694
+
695
+ function dependencyState(root) {
696
+ if (!existsSync(path.join(root, 'package.json'))) return { action: 'none', reason: 'no package.json' };
697
+ const lock = LOCKFILES.find((entry) => existsSync(path.join(root, entry.file)));
698
+ if (!lock) return { action: 'none', reason: 'no lockfile' };
699
+ const marker = path.join(root, lock.marker);
700
+ const stale = !existsSync(marker) || statSync(marker).mtimeMs < statSync(path.join(root, lock.file)).mtimeMs;
701
+ return { action: stale ? 'install' : 'present', manager: lock.manager, lockfile: lock.file, args: lock.args };
702
+ }
703
+
704
+ function ensureDependencies() {
705
+ const state = dependencyState(REPO_ROOT);
706
+ if (config.installDependencies === false || state.action !== 'install') {
707
+ return { ...state, action: config.installDependencies === false && state.action === 'install' ? 'disabled' : state.action, args: undefined };
708
+ }
709
+ const started = Date.now();
710
+ console.error(` → dependencies: ${state.manager} ${state.args.join(' ')} (${state.lockfile})`);
711
+ return new Promise((resolve) => {
712
+ const child = spawn(state.manager, state.args, { cwd: REPO_ROOT, stdio: ['ignore', 'pipe', 'pipe'], env: process.env });
713
+ currentChild = child;
714
+ let tail = '';
715
+ const keep = (chunk) => { tail = (tail + chunk).slice(-4000); };
716
+ child.stdout.on('data', keep);
717
+ child.stderr.on('data', keep);
718
+ const done = (code, error) => {
719
+ currentChild = null;
720
+ resolve({ action: code === 0 ? 'installed' : 'failed', manager: state.manager, lockfile: state.lockfile,
721
+ durationMs: Date.now() - started, ...(code === 0 ? {} : { error: error || tail.trim().split('\n').slice(-20).join('\n') }) });
722
+ };
723
+ child.on('error', (err) => done(null, err.message));
724
+ child.on('close', (code) => done(code));
725
+ });
726
+ }
727
+
681
728
  async function main() {
682
729
  const wantTier = TIER_ORDER[TIER];
683
730
  if (!wantTier) fail(`Unknown --tier ${TIER} (expected: fast | full | remote)`);
@@ -709,10 +756,16 @@ async function main() {
709
756
 
710
757
  const startedAt = new Date().toISOString();
711
758
  const startedMs = Date.now();
759
+ const testedHead = gitHead();
712
760
  console.error(` [code-quality] ${config.toolchain} — ${checks.length} check(s) in ${REPO_ROOT}`);
713
761
 
762
+ const dependencies = await ensureDependencies();
763
+ if (dependencies.action === 'failed') {
764
+ console.error(` dependency install failed (${dependencies.manager}, ${dependencies.lockfile}); no gate ran:\n${dependencies.error}`);
765
+ }
766
+
714
767
  const results = [];
715
- for (const check of checks) {
768
+ for (const check of dependencies.action === 'failed' ? [] : checks) {
716
769
  const label = check.kind === 'grep-forbid'
717
770
  ? `grep-forbid (${(check.patterns || []).length} pattern(s))`
718
771
  : (check.cmd || []).join(' ');
@@ -739,15 +792,16 @@ async function main() {
739
792
  tier: TIER,
740
793
  // Coverage is stated explicitly so a partial run can never be mistaken
741
794
  // for a clean full run when someone reads the report later.
742
- ranChecks: checks.map((c) => c.id),
795
+ ranChecks: results.map((r) => r.id),
743
796
  skippedChecks: config.checks.filter((c) => !checks.includes(c)).map((c) => ({ id: c.id, tier: c.tier || 'fast' })),
744
797
  startedAt,
745
798
  finishedAt: new Date().toISOString(),
746
799
  durationMs: Date.now() - startedMs,
747
- gitHead: gitHead(),
800
+ gitHead: testedHead,
748
801
  sourceFileCount: sources.length,
802
+ dependencies,
749
803
  checks: results.map(({ raw, findings: _f, ...meta }) => meta),
750
- infrastructureFailure: results.some((r) => r.spawnError || r.timedOut),
804
+ infrastructureFailure: dependencies.action === 'failed' || results.some((r) => r.spawnError || r.timedOut),
751
805
  totalFindings: findings.length,
752
806
  findings
753
807
  };