total-recall-brain 3.44.0 → 3.45.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 (212) hide show
  1. package/bin/total-recall.mjs +6 -4
  2. package/docs/ARCHITECTURE.md +10 -7
  3. package/docs/reference/cli-reference.md +13 -8
  4. package/frontend/dist/assets/index-DACh1dhx.js +5613 -0
  5. package/frontend/dist/index.html +1 -1
  6. package/frontend/src/App.tsx +2 -2
  7. package/frontend/src/pages/PluginsPage.tsx +1 -1
  8. package/package.json +1 -1
  9. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +7 -7
  10. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -1
  11. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +1 -1
  12. package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +20 -1
  13. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-store.mjs +12 -1
  14. package/scaffold/.agent/skills/total-recall/plugins/automations/BRICK.md +7 -0
  15. package/scaffold/.agent/skills/total-recall/plugins/automations/LICENSE +21 -0
  16. package/scaffold/.agent/skills/total-recall/plugins/automations/README.md +66 -0
  17. package/scaffold/.agent/skills/total-recall/plugins/automations/adapters.mjs +231 -0
  18. package/scaffold/.agent/skills/total-recall/plugins/automations/capability/index.mjs +16 -0
  19. package/scaffold/.agent/skills/total-recall/plugins/automations/cli.mjs +121 -0
  20. package/{src/core/plugin-cron.mjs → scaffold/.agent/skills/total-recall/plugins/automations/cron.mjs} +1 -3
  21. package/scaffold/.agent/skills/total-recall/plugins/automations/inventory.mjs +88 -0
  22. package/scaffold/.agent/skills/total-recall/plugins/automations/package-lock.json +7661 -0
  23. package/scaffold/.agent/skills/total-recall/plugins/automations/package.json +35 -0
  24. package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +167 -0
  25. package/scaffold/.agent/skills/total-recall/plugins/automations/scheduler.mjs +246 -0
  26. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/DESIGN.md +42 -0
  27. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/automations.js +2 -0
  28. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/card.js +273 -0
  29. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +1 -1
  30. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +57 -5
  31. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +40 -14
  32. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +15 -40
  33. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +7 -7
  34. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +1 -1
  35. package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +1 -1
  36. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +7 -7
  37. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
  38. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +1 -1
  39. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +7 -7
  40. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +1 -1
  41. package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +1 -1
  42. package/scaffold/.agent/skills/total-recall/plugins/git/README.md +112 -0
  43. package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/cli.mjs +135 -23
  44. package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package-lock.json +7 -7
  45. package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package.json +2 -1
  46. package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +161 -0
  47. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/context.mjs +94 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/phase3-spec-support.mjs +50 -0
  49. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/runtime.mjs +40 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/spec-support.mjs +51 -0
  51. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/_support.mjs +195 -0
  52. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/auth.mjs +201 -0
  53. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/bisect.mjs +40 -0
  54. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/cli-meta.mjs +159 -0
  55. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit-memory.mjs +30 -0
  56. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit.mjs +46 -0
  57. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/fleet.mjs +156 -0
  58. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/force-push.mjs +58 -0
  59. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/group.mjs +119 -0
  60. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/handoff.mjs +59 -0
  61. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/message.mjs +22 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/resolve.mjs +60 -0
  63. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/rules.mjs +81 -0
  64. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/secret-firewall.mjs +66 -0
  65. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/undo.mjs +132 -0
  66. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/why.mjs +16 -0
  67. package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/ui/card.js +2 -2
  68. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +7 -7
  69. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +1 -1
  70. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +1 -1
  71. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +25 -9
  72. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +1 -1
  73. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +18 -6
  74. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +124 -24
  75. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +7 -7
  76. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +2 -2
  77. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +147 -14
  78. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +17 -3
  79. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adoption.mjs +2 -2
  80. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +3 -3
  81. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +92 -18
  82. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/control.mjs +145 -0
  83. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +42 -21
  84. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch-request.mjs +4 -2
  85. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -9
  86. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +55 -11
  87. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +26 -4
  88. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +29 -1
  89. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +1 -1
  90. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/included-usage.mjs +1 -1
  91. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/jobs.mjs +84 -0
  92. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +3 -12
  93. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/maintenance.mjs +17 -0
  94. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/mcp-server.mjs +228 -0
  95. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/process-manager.mjs +25 -2
  96. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reasoning.mjs +1 -1
  97. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/remote-operation.mjs +5 -1
  98. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +34 -3
  99. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reviews.mjs +25 -0
  100. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +1 -1
  101. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/claude.mjs +319 -0
  102. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/codex.mjs +387 -0
  103. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/result.mjs +67 -0
  104. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/resume.mjs +373 -0
  105. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/run.mjs +223 -0
  106. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session-jobs.mjs +72 -0
  107. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +6 -3
  108. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/termination.mjs +16 -12
  109. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +32 -1
  110. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/usage-queue.mjs +1 -1
  111. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/verify-ssss.mjs +3 -3
  112. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +285 -40
  113. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +76 -9
  114. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +5 -5
  115. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +3 -3
  116. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +47 -18
  117. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +7 -7
  118. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +1 -1
  119. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +1 -1
  120. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh-diagnostics.mjs +44 -0
  121. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +120 -113
  122. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +7 -7
  123. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +1 -1
  124. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +1 -1
  125. package/scaffold/.agent/skills/total-recall/references/operations.md +1 -1
  126. package/src/cli/agent.mjs +1 -1
  127. package/src/cli/command.mjs +41 -2
  128. package/src/cli/dream.mjs +93 -69
  129. package/src/cli/harness.mjs +4 -4
  130. package/src/cli/help.mjs +1 -1
  131. package/src/cli/init.mjs +0 -25
  132. package/src/cli/lessons.mjs +51 -0
  133. package/src/cli/memory-plugin.mjs +1 -1
  134. package/src/cli/plugin/copies.mjs +64 -14
  135. package/src/cli/plugin/create.mjs +6 -7
  136. package/src/cli/plugin/index.mjs +11 -7
  137. package/src/cli/plugin/install.mjs +10 -15
  138. package/src/cli/plugin/remove.mjs +1 -1
  139. package/src/cli/plugin/search.mjs +2 -2
  140. package/src/cli/preflight.mjs +19 -6
  141. package/src/cli/proposals.mjs +1 -1
  142. package/src/cli/recall.mjs +66 -23
  143. package/src/cli/remember.mjs +3 -14
  144. package/src/cli/server.mjs +18 -0
  145. package/src/cli/startup.mjs +1 -0
  146. package/src/cli/task.mjs +9 -2
  147. package/src/cli/update.mjs +6 -2
  148. package/src/core/access-ledger.mjs +78 -0
  149. package/src/core/agent-manager.mjs +2 -2
  150. package/src/core/brain-locate.mjs +15 -0
  151. package/src/core/brain-updates.mjs +31 -11
  152. package/src/core/claude-hooks.mjs +3 -1
  153. package/src/core/cleanup-task.mjs +15 -0
  154. package/src/core/cli-commands.mjs +2 -0
  155. package/src/core/cli-maintenance.mjs +4 -1
  156. package/src/core/cli-semantic-index.mjs +23 -3
  157. package/src/core/context-compiler.mjs +20 -0
  158. package/src/core/daemon-loop.mjs +39 -62
  159. package/src/core/default-plugins.mjs +1 -1
  160. package/src/core/dream.mjs +33 -105
  161. package/src/core/ensure-dependencies.mjs +148 -0
  162. package/src/core/fast-recall.mjs +2 -2
  163. package/src/core/field-evaluation.mjs +23 -0
  164. package/src/core/gate-mode.mjs +1 -1
  165. package/src/core/harness-hooks.mjs +14 -2
  166. package/src/core/lesson-hooks.mjs +74 -0
  167. package/src/core/lesson-memory.mjs +24 -0
  168. package/src/core/lesson-sessions.mjs +52 -0
  169. package/src/core/lessons.mjs +96 -0
  170. package/src/core/meta-harness.mjs +2 -2
  171. package/src/core/optimizer.mjs +66 -165
  172. package/src/core/plugin-contracts/card.mjs +3 -0
  173. package/src/core/plugin-loader.mjs +12 -3
  174. package/src/core/plugin-runtime.mjs +40 -1
  175. package/src/core/plugin-store.mjs +35 -23
  176. package/src/core/post-mortem.mjs +52 -36
  177. package/src/core/preflight-gate.mjs +23 -2
  178. package/src/core/proposal-applier.mjs +31 -2
  179. package/src/core/repo-sync.mjs +4 -6
  180. package/src/core/required-plugins.mjs +26 -4
  181. package/src/core/rule-length.mjs +23 -0
  182. package/src/core/runtime.mjs +1 -1
  183. package/src/core/scheduler.mjs +35 -43
  184. package/src/core/schema.mjs +1 -2
  185. package/src/core/search.mjs +4 -3
  186. package/src/core/server-service.mjs +148 -0
  187. package/src/core/session-watcher.mjs +19 -1
  188. package/src/core/skill-gap.mjs +16 -0
  189. package/src/core/ssss-kernel-bridge.mjs +32 -4
  190. package/src/core/startup-health.mjs +6 -1
  191. package/src/core/surface.mjs +65 -54
  192. package/src/core/task-executors.mjs +54 -106
  193. package/src/core/validated-write.mjs +1 -0
  194. package/src/core/vector-field.mjs +134 -192
  195. package/src/server/invoke-core-child.mjs +24 -0
  196. package/src/server/invoke-core-operations.mjs +102 -0
  197. package/src/server/route-manifest.json +0 -56
  198. package/src/server/routes/brains.mjs +0 -18
  199. package/src/server/routes/context.mjs +0 -41
  200. package/src/server/routes/invoke.mjs +26 -1
  201. package/src/server/routes/ssss.mjs +0 -169
  202. package/src/server/routes/system.mjs +0 -15
  203. package/src/server/routes/tasks.mjs +0 -26
  204. package/src/server/routes/vault.mjs +2 -23
  205. package/frontend/dist/assets/index-DD4qONvH.js +0 -5607
  206. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +0 -53
  207. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +0 -129
  208. package/src/core/append-log.mjs +0 -131
  209. package/src/core/plugin-tasks.mjs +0 -173
  210. package/templates/onboarding-interview.md +0 -82
  211. /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/LICENSE +0 -0
  212. /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/ui/DESIGN.md +0 -0
@@ -1,53 +0,0 @@
1
- # Git Hygiene
2
-
3
- Reports the state of the git repository you run it in: current branch, staged / unstaged / untracked / conflicted files, commits not yet pushed to upstream, and stashes.
4
-
5
- **Reporting accuracy, verified 2026-10-23.** Two defects reported in the September 30 review are fixed and covered by testing:
6
-
7
- - **Porcelain columns are preserved.** `git status --porcelain` output is never trimmed, so a file whose name begins with a space parses correctly. Verified against a file literally named `" leading-space.txt"`, which reports as `A leading-space.txt`.
8
- - **An unknown upstream is never reported as zero unpushed commits.** `upstreamStatus` distinguishes `tracking`, `none`, `gone`, `detached`, `unborn`, `unknown` and `error`; where no comparison exists, `unpushedCommits` is `null` rather than `0`. Verified in a repository with no remote configured.
9
-
10
- **Use cases:** software-development
11
-
12
- ## Install
13
-
14
- ```bash
15
- npx total-recall plugin install git-sentinel
16
- ```
17
-
18
- ## Use
19
-
20
- ```bash
21
- npx total-recall git-sentinel audit # human summary
22
- npx total-recall git-sentinel audit --json # machine output for agents
23
- npx total-recall git-sentinel diff # diffstat of unstaged changes
24
- npx total-recall git-sentinel identity --json # current commit identity and local history audit
25
- ```
26
-
27
- ## Share with your other nodes
28
-
29
- ```bash
30
- npx total-recall plugin share git-sentinel
31
- # on another node on the mesh:
32
- npx total-recall plugin install peer:<this-node>/git-sentinel
33
- ```
34
-
35
- ## Capability scope
36
-
37
- The plugin runs against the Git worktree selected by the installed Total Recall CLI. Use `diff --patch`, `--cached` and `--base <ref>` for actual Git comparisons. Worktree listing is read-only, creation makes a detached worktree, and removal requires a registered linked worktree. Dirty removal requires `--force`. See the [Git Sentinel project tracker](docs/projects/in-progress/GIT_SENTINEL_CARD/GIT_SENTINEL_CARD_PROJECT_TRACKER.md).
38
-
39
- ## Using this from an agent
40
-
41
- Use the read operations for worktree state, changes, upstream comparison, stashes and commit identity. Worktree creation and removal have separate operation effects.
42
-
43
- ```bash
44
- total-recall git-sentinel audit --json
45
- total-recall git-sentinel diff --json
46
- total-recall git-sentinel diff --patch --cached --json
47
- total-recall git-sentinel identity --json
48
- total-recall git-sentinel worktree-list --json
49
- total-recall git-sentinel worktree-create <path> --base <ref> --json
50
- total-recall git-sentinel worktree-remove <path> --json
51
- ```
52
-
53
- Raw commands this plugin owns: `git status`, `git stash list`, `git diff --stat`, `git config user.*` and author lookups through `git log`.
@@ -1,129 +0,0 @@
1
- {
2
- "$schema": "https://github.com/gregiteen/total-recall/blob/main/metadata.plugin.schema.json",
3
- "id": "git-sentinel",
4
- "name": "Git Sentinel",
5
- "version": "1.1.5",
6
- "description": "Worktree hygiene, branch inspection, unpushed commits, and stash tracker.",
7
- "author": "Total Recall",
8
- "license": "MIT",
9
- "use_cases": [
10
- "software-development"
11
- ],
12
- "gate": {
13
- "tasks": ["Check a repository's state before committing or pushing", "Review what changed in a working tree", "Read the commit identity"],
14
- "owned_commands": [
15
- { "pattern": "git(\\s+-C\\s+\\S+)?\\s+status\\b", "use": "total-recall git-sentinel audit" },
16
- { "pattern": "git(\\s+-C\\s+\\S+)?\\s+stash\\s+list\\b", "use": "total-recall git-sentinel audit" },
17
- { "pattern": "git(\\s+-C\\s+\\S+)?\\s+diff\\s+--stat\\b", "use": "total-recall git-sentinel diff" },
18
- { "pattern": "git(\\s+-C\\s+\\S+)?\\s+config\\s+(--get\\s+)?user\\.(name|email)\\s*($|[;&|])", "use": "total-recall git-sentinel identity" },
19
- { "pattern": "git(\\s+-C\\s+\\S+)?\\s+log\\b[^;&|]*%a[en]", "use": "total-recall git-sentinel identity" }
20
- ]
21
- },
22
- "cli": {
23
- "answers_help": true,
24
- "command": "git-sentinel",
25
- "handler": "./cli.mjs",
26
- "subcommands": [
27
- {
28
- "name": "audit",
29
- "description": "Summarise branch, worktree, upstream and stash state (--json for machine output)",
30
- "effect": "read"
31
- },
32
- {
33
- "name": "diff",
34
- "description": "Read a stat or patch for unstaged, staged or base-relative changes",
35
- "effect": "read",
36
- "args": [
37
- { "name": "patch", "flag": "--patch", "type": "boolean", "label": "Show patch" },
38
- { "name": "cached", "flag": "--cached", "type": "boolean", "label": "Use staged changes" },
39
- { "name": "base", "flag": "--base", "type": "string", "label": "Base ref" }
40
- ]
41
- },
42
- { "name": "worktree-list", "description": "Read registered worktrees for the current repository", "effect": "read" },
43
- { "name": "worktree-create", "description": "Create a detached worktree at a specified path and base ref", "effect": "write", "args": [
44
- { "name": "path", "type": "string", "label": "Worktree path", "required": true, "positional": true },
45
- { "name": "base", "flag": "--base", "type": "string", "label": "Base ref" }
46
- ] },
47
- { "name": "worktree-remove", "description": "Remove an owned linked worktree, with explicit force for dirty content", "effect": "destructive", "args": [
48
- { "name": "path", "type": "string", "label": "Worktree path", "required": true, "positional": true },
49
- { "name": "force", "flag": "--force", "type": "boolean", "label": "Allow removal of dirty content" }
50
- ] },
51
- { "name": "worktree", "description": "Compatibility form: worktree list, create or remove", "effect": "destructive" },
52
- {
53
- "name": "identity",
54
- "description": "Show who commits here and which identities the history publishes; exits 1 on a missing or machine-derived identity (--json for machine output)",
55
- "effect": "read"
56
- }
57
- ]
58
- },
59
- "card_face": {
60
- "skin": "copper-obsidian",
61
- "theme": "copper",
62
- "headline": "Worktree Hygiene & Repository Watch",
63
- "primary_color": "#b45309",
64
- "secondary_color": "#0c0a09",
65
- "accent_color": "#f59e0b",
66
- "touch_layout": "comfortable",
67
- "interactive": true,
68
- "quick_actions": [
69
- {
70
- "subcommand": "audit",
71
- "label": "AUDIT"
72
- },
73
- {
74
- "subcommand": "diff",
75
- "label": "DIFFSTAT"
76
- }
77
- ]
78
- },
79
- "ui": {
80
- "design_tokens": "ui/DESIGN.md",
81
- "elements": [
82
- {
83
- "id": "card",
84
- "kind": "card",
85
- "tag": "tr-git-sentinel-card",
86
- "module": "ui/card.js",
87
- "description": "Git Hygiene Repository Hygiene Card Face"
88
- }
89
- ]
90
- },
91
- "auth": {
92
- "plugin": "auth",
93
- "methods": [
94
- "passkey",
95
- "google-oauth2"
96
- ],
97
- "session_verification": true
98
- },
99
- "deploy": {
100
- "targets": [
101
- "ssss-app",
102
- "nextjs",
103
- "react",
104
- "flask"
105
- ],
106
- "required_ssss_version": ">=0.9.3",
107
- "access_grants": [
108
- "auth:session:verify",
109
- "ssss:vault:read",
110
- "ssss:vault:write",
111
- "ssss:events:append"
112
- ]
113
- },
114
- "branding": {
115
- "name": "Git Hygiene",
116
- "color": "#b45309",
117
- "accent": "#f59e0b",
118
- "badge": "Repository Hygiene",
119
- "font": "space-grotesk"
120
- },
121
- "status": {
122
- "command": "audit",
123
- "refresh_seconds": 60
124
- },
125
- "distribution": {
126
- "tier": "official",
127
- "license": "open"
128
- }
129
- }
@@ -1,131 +0,0 @@
1
- /**
2
- * Append-only JSONL log with last-write-wins semantics and compaction.
3
- *
4
- * Write: O(1) — fs.appendFileSync one line
5
- * Read: O(n) on cold load, cached after first read
6
- * Compact: Rewrites only latest version of each key
7
- *
8
- * File format: One JSON object per line, each with a mandatory `_key` field.
9
- * Later entries for the same `_key` shadow earlier ones.
10
- * Tombstones: { "_key": "slug", "_deleted": true }
11
- */
12
-
13
- import fs from 'node:fs';
14
- import path from 'node:path';
15
- import { atomicWrite } from './vault.mjs';
16
- import { logger } from './logger.mjs';
17
-
18
- /** @type {Map<string, AppendLog>} Registry of all active AppendLog instances */
19
- const activeAppendLogs = new Map();
20
-
21
- export class AppendLog {
22
- #filePath;
23
- #cache; // Map<string, object> — latest value per key
24
- #dirty; // number of appended lines since last compact
25
- #compactThreshold;
26
-
27
- constructor(filePath, { compactThreshold = 500 } = {}) {
28
- this.#filePath = filePath;
29
- this.#cache = null;
30
- this.#dirty = 0;
31
- this.#compactThreshold = compactThreshold;
32
- activeAppendLogs.set(filePath, this);
33
- }
34
-
35
- /** Read the full index. Returns Map<key, value>. Cached after first call. */
36
- entries() {
37
- if (this.#cache) return this.#cache;
38
- this.#cache = new Map();
39
- if (!fs.existsSync(this.#filePath)) return this.#cache;
40
- const raw = fs.readFileSync(this.#filePath, 'utf8');
41
- for (const line of raw.split('\n')) {
42
- if (!line.trim()) continue;
43
- try {
44
- const entry = JSON.parse(line);
45
- if (entry._deleted) this.#cache.delete(entry._key);
46
- else this.#cache.set(entry._key, entry);
47
- } catch { /* skip corrupt lines */ }
48
- }
49
- return this.#cache;
50
- }
51
-
52
- /** Get a single entry by key. */
53
- get(key) {
54
- return this.entries().get(key) ?? null;
55
- }
56
-
57
- /** Append a single entry. O(1) I/O. */
58
- append(key, value) {
59
- const dir = path.dirname(this.#filePath);
60
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
61
- const entry = { _key: key, ...value };
62
- fs.appendFileSync(this.#filePath, JSON.stringify(entry) + '\n');
63
- // Update in-memory cache
64
- this.entries().set(key, entry);
65
- this.#dirty++;
66
- if (this.#dirty >= this.#compactThreshold) {
67
- this.compact();
68
- }
69
- }
70
-
71
- /** Write a tombstone for a key. O(1) I/O. */
72
- remove(key) {
73
- const dir = path.dirname(this.#filePath);
74
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
75
- fs.appendFileSync(this.#filePath, JSON.stringify({ _key: key, _deleted: true }) + '\n');
76
- this.entries().delete(key);
77
- this.#dirty++;
78
- }
79
-
80
- /** Number of live entries. */
81
- get size() {
82
- return this.entries().size;
83
- }
84
-
85
- /** Convert to plain object (for JSON.stringify compatibility). */
86
- toObject() {
87
- const obj = {};
88
- for (const [key, value] of this.entries()) {
89
- const { _key, ...rest } = value;
90
- obj[key] = rest;
91
- }
92
- return obj;
93
- }
94
-
95
- /** Compact: rewrite file with only latest versions. */
96
- compact() {
97
- const entries = this.entries();
98
- const lines = [];
99
- for (const [, value] of entries) {
100
- lines.push(JSON.stringify(value));
101
- }
102
- atomicWrite(this.#filePath, lines.join('\n') + '\n');
103
- this.#dirty = 0;
104
- logger.info('append-log', `Compacted ${this.#filePath}: ${lines.length} entries`);
105
- }
106
-
107
- /** Invalidate the in-memory cache (force reload on next read). */
108
- invalidate() {
109
- this.#cache = null;
110
- this.#dirty = 0;
111
- }
112
- }
113
-
114
- /**
115
- * Compact all active AppendLog instances.
116
- * Returns summary with number of logs compacted and total entries.
117
- */
118
- export function compactAppendLogs() {
119
- let logsCompacted = 0;
120
- let totalEntries = 0;
121
- for (const [filePath, log] of activeAppendLogs) {
122
- try {
123
- log.compact();
124
- logsCompacted++;
125
- totalEntries += log.size;
126
- } catch (err) {
127
- logger.warn('append-log', `Failed to compact ${filePath}: ${err.message}`);
128
- }
129
- }
130
- return { logs_compacted: logsCompacted, total_entries: totalEntries };
131
- }
@@ -1,173 +0,0 @@
1
- /**
2
- * Scheduled plugin tasks.
3
- *
4
- * A manifest task `{ intent, schedule: "<5-field cron>", command }` runs the
5
- * plugin's own CLI subcommand on that schedule, out of process. Tasks are per
6
- * node by default, regardless of leadership. Tasks with selected-node placement
7
- * require an explicit matching node in plugin_record.task_nodes[command].
8
- *
9
- * The last minute-slot each command ran in is kept in the plugin_record, so a
10
- * restart neither repeats a slot nor loses one: when the daemon was busy or
11
- * down, the most recent matching slot since the last run (within a day) runs
12
- * once. A task never runs just because it was installed — the first run is the
13
- * first matching slot after the scheduler first sees it.
14
- */
15
- import { discoverPlugins } from './plugin-loader.mjs';
16
- import { readPluginRecord, patchPluginRecord, ensurePluginRecord } from './plugin-store.mjs';
17
- import { pluginExecutionEnabled } from './plugin-state.mjs';
18
- import { runPluginCommand } from './plugin-runner.mjs';
19
- import { appendVfsEvent } from './ssss-operation-service.mjs';
20
- import { vaultForPluginsDir } from './plugin-loader.mjs';
21
- import { logger } from './logger.mjs';
22
- import { getMeshSelf, meshNodeKey } from './mesh.mjs';
23
-
24
- const TASK_TIMEOUT_MS = 60_000;
25
- const MAX_CATCHUP_MINUTES = 24 * 60;
26
- import { parseCron, cronMatches } from './plugin-cron.mjs';
27
- export { parseCron, cronMatches } from './plugin-cron.mjs';
28
- const pad = (n) => String(n).padStart(2, '0');
29
-
30
- /** A selected-node task stays idle until this node's plugin record selects it. */
31
- export function taskRunsOnNode(task, record, self) {
32
- if (task.placement !== 'selected-node') return true;
33
- const selected = String(record?.task_nodes?.[task.command] || '').trim();
34
- if (!selected || !self?.hostname) return false;
35
- return selected === self.ip || meshNodeKey(selected) === meshNodeKey(self.hostname);
36
- }
37
-
38
- /** Local minute-slot key, e.g. 2026-09-22T14:05. */
39
- export function minuteSlot(date) {
40
- return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
41
- }
42
-
43
- function slotToDate(slot) {
44
- const m = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/.exec(String(slot || ''));
45
- return m ? new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5])) : null;
46
- }
47
-
48
- /**
49
- * The most recent slot matching `cron` in (lastSlot, now], or null.
50
- * @returns {string|null}
51
- */
52
- export function latestDueSlot(cron, lastSlot, now = new Date()) {
53
- const floorNow = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes());
54
- const last = slotToDate(lastSlot);
55
- for (let i = 0; i < MAX_CATCHUP_MINUTES; i++) {
56
- const candidate = new Date(floorNow.getTime() - i * 60_000);
57
- if (last && candidate <= last) return null;
58
- if (cronMatches(cron, candidate)) return minuteSlot(candidate);
59
- }
60
- return null;
61
- }
62
-
63
- /**
64
- * Run every task that is due. Tasks run sequentially so a slow plugin cannot
65
- * multiply into concurrent processes on a constrained host.
66
- * @param {{ projectRoot?: string, now?: Date, runner?: Function, selfNode?: object }} [options]
67
- * @returns {Promise<Array<{ plugin: string, command: string, slot: string, ok: boolean, exitCode: number|null }>>}
68
- */
69
- export async function runDuePluginTasks(options = {}) {
70
- const { projectRoot = process.cwd(), now = new Date(), runner = runPluginCommand } = options;
71
- const selfNode = options.selfNode === undefined ? getMeshSelf() : options.selfNode;
72
- const results = [];
73
-
74
- for (const plugin of discoverPlugins(projectRoot)) {
75
- if (!plugin.valid || !Array.isArray(plugin.manifest.tasks) || plugin.manifest.tasks.length === 0) continue;
76
-
77
- let record = readPluginRecord(plugin);
78
- if (!record) record = await ensurePluginRecord(plugin);
79
- // Scheduled work follows the repository's own setting for a shared plugin.
80
- if (!pluginExecutionEnabled(plugin, { projectRoot })) continue;
81
- const runs = { ...(record?.task_runs || {}) };
82
- let changed = false;
83
-
84
- for (const task of plugin.manifest.tasks) {
85
- if (!taskRunsOnNode(task, record, selfNode)) continue;
86
- let cron;
87
- try {
88
- cron = parseCron(task.schedule);
89
- } catch (err) {
90
- logger.warn('plugin-tasks', `Skipping ${plugin.id}:${task.command} — ${err.message}`);
91
- continue;
92
- }
93
-
94
- if (!runs[task.command]) {
95
- // First sighting: mark the present so the first run is the next real slot.
96
- runs[task.command] = minuteSlot(now);
97
- changed = true;
98
- continue;
99
- }
100
-
101
- const slot = latestDueSlot(cron, runs[task.command], now);
102
- if (!slot) continue;
103
-
104
- const result = await runner(plugin, { subcommand: task.command, cwd: projectRoot, timeoutMs: task.timeout_ms || TASK_TIMEOUT_MS })
105
- .catch((err) => ({ ok: false, exitCode: null, output: err.message, durationMs: 0, timedOut: false }));
106
- runs[task.command] = slot;
107
- changed = true;
108
- results.push({ plugin: plugin.id, command: task.command, slot, ok: result.ok, exitCode: result.exitCode });
109
-
110
- try {
111
- await appendVfsEvent(
112
- `plugins/${plugin.id}/task-${Date.now()}`,
113
- {
114
- kind: 'plugin.task_run',
115
- plugin_id: plugin.id,
116
- command: task.command,
117
- intent: task.intent,
118
- slot,
119
- ok: result.ok,
120
- exit_code: result.exitCode,
121
- timed_out: !!result.timedOut,
122
- duration_ms: result.durationMs,
123
- output_tail: String(result.output || '').slice(-2000)
124
- },
125
- { actorRole: 'system', intent: 'Record plugin task run', vaultRoot: vaultForPluginsDir(plugin.pluginsDir) }
126
- );
127
- } catch (err) {
128
- logger.warn('plugin-tasks', `Could not record task run for ${plugin.id}: ${err.message}`);
129
- }
130
- }
131
-
132
- if (changed) {
133
- try {
134
- await patchPluginRecord(plugin, { task_runs: runs });
135
- } catch (err) {
136
- logger.warn('plugin-tasks', `Could not update task runs for ${plugin.id}: ${err.message}`);
137
- }
138
- }
139
- }
140
-
141
- return results;
142
- }
143
-
144
- let timer = null;
145
- let running = false;
146
-
147
- /** Check for due plugin tasks once a minute. Returns a stop function. */
148
- export function startPluginTaskScheduler(options = {}) {
149
- if (timer) return () => stopPluginTaskScheduler();
150
- const tick = async () => {
151
- if (running) return;
152
- running = true;
153
- try {
154
- const results = await runDuePluginTasks(options);
155
- for (const r of results) {
156
- logger.info('plugin-tasks', `${r.plugin}:${r.command} slot ${r.slot} ${r.ok ? 'ok' : `failed (exit ${r.exitCode})`}`);
157
- }
158
- } catch (err) {
159
- logger.warn('plugin-tasks', `Scheduler tick failed: ${err.message}`);
160
- } finally {
161
- running = false;
162
- }
163
- };
164
- timer = setInterval(tick, 60_000);
165
- timer.unref?.();
166
- tick();
167
- return () => stopPluginTaskScheduler();
168
- }
169
-
170
- export function stopPluginTaskScheduler() {
171
- if (timer) clearInterval(timer);
172
- timer = null;
173
- }
@@ -1,82 +0,0 @@
1
- ---
2
- type: task
3
- slug: onboarding-interview
4
- category: onboarding
5
- title: "Conduct portable-memory onboarding interview"
6
- status: pending
7
- priority: 1
8
- created: {{CREATED_AT}}
9
- updated: {{CREATED_AT}}
10
- ---
11
-
12
- ## Objective
13
-
14
- Conduct a warm, practical onboarding interview so Total Recall can store durable preferences and facts as Markdown memory nodes. This is personal portable memory — not an operating system takeover. Keep the tone helpful and concrete.
15
-
16
- ## Instructions for the Agent
17
-
18
- When the user starts their next chat session (and they have no `user-profile` yet), conduct this interview BEFORE unrelated work. Do NOT dump all questions at once. Ask one at a time. Acknowledge each answer before the next.
19
-
20
- Once the user has answered (or skipped any), write their answers into the vault using tools / CLI (`remember` or direct vault writes). Prefer CLI when available:
21
-
22
- ```bash
23
- npx total-recall remember preference "..." --tags "user,profile,onboarding" --importance 5
24
- ```
25
-
26
- ## Interview Questions (ask one at a time)
27
-
28
- 1. **Who are you?** — "Quick intro: what do you do, and what are you most focused on right now?"
29
-
30
- 2. **What should I remember?** — "What kinds of things matter most for me to keep: decisions, preferences, project facts, people, or something else?"
31
-
32
- 3. **Which tools do you use?** — "Which IDEs or agents should share this memory? (Claude Code, Cursor, Codex, Gemini, Aider, Obsidian, HTTP, etc.) I can guide you with `npx total-recall connect …`."
33
-
34
- 4. **How should I talk?** — "Prefer short answers or detailed ones? Should I push back or mostly support?"
35
-
36
- 5. **Hard rules** — "Any absolute do's or don'ts I must always follow (style, tools, safety)?"
37
-
38
- 6. **Background work** — "Want dream consolidation and optional daemon tasks running in the background, or on-demand only?"
39
-
40
- 7. **Secrets** — "API keys never go in the vault. Use `npx total-recall auth secret set`. Any keys you want to set up next?"
41
-
42
- 8. **Notifications (optional)** — "Want Telegram pings when background tasks finish? If yes, BotFather token + chat ID."
43
-
44
- ## Product context to reinforce (lightly, not as a lecture)
45
-
46
- - **Write** → `remember` / session ingest
47
- - **Sleep** → `dream` (consolidate, conflict-check, recompile)
48
- - **Read** → `recall` + compiled surfaces in any connected IDE
49
- - **Async** → open task queue / daemon under policy
50
- - Hosts are equal — track any repo; no product-specific hardcoding
51
-
52
- ## After the Interview
53
-
54
- Write (or update) these files:
55
-
56
- **`.agent/config/notifications.yml`** — only if they provided Telegram details:
57
- ```yaml
58
- telegram_bot_token: "their-token-here"
59
- telegram_chat_id: "their-chat-id-here"
60
- ```
61
-
62
- **`.agent/memory-vault/preferences/user-profile.md`** — identity + communication style
63
- **`.agent/memory-vault/preferences/user-priorities.md`** — goals and what they want from memory
64
- **`.agent/memory-vault/preferences/user-interests.md`** — topics worth tracking
65
-
66
- Frontmatter template:
67
- ```yaml
68
- ---
69
- type: memory
70
- slug: user-profile
71
- category: preferences
72
- title: "User Profile"
73
- status: active
74
- confidence: 1.0
75
- importance: 5
76
- created: <ISO timestamp>
77
- updated: <ISO timestamp>
78
- tags: [user, profile, onboarding]
79
- ---
80
- ```
81
-
82
- Then mark this task `status: done` and confirm the profile was saved. Point them at the dashboard **Onboarding** page or `init → connect → remember → dream` if they prefer CLI-only.