create-walle 0.9.21 → 0.9.23

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 (500) hide show
  1. package/README.md +27 -5
  2. package/package.json +2 -2
  3. package/template/CLAUDE.md +2 -2
  4. package/template/LICENSE +1 -1
  5. package/template/bin/ctm-dev-cleanup.js +24 -3
  6. package/template/bin/ctm-launch.sh +13 -0
  7. package/template/bin/dev.sh +156 -18
  8. package/template/bin/node-bin.sh +84 -0
  9. package/template/bin/pin-node.sh +51 -0
  10. package/template/claude-task-manager/api-prompts.js +1203 -182
  11. package/template/claude-task-manager/api-reviews.js +109 -15
  12. package/template/claude-task-manager/approval-agent.js +1360 -280
  13. package/template/claude-task-manager/bin/restart-ctm.sh +64 -23
  14. package/template/claude-task-manager/bin/storage-migration-supervisor.js +338 -0
  15. package/template/claude-task-manager/db.js +4417 -295
  16. package/template/claude-task-manager/docs/app-update-refresh-protocol.md +69 -0
  17. package/template/claude-task-manager/docs/approval-ai-refinement.md +138 -0
  18. package/template/claude-task-manager/docs/approval-rescue-loop.md +74 -0
  19. package/template/claude-task-manager/docs/codex-operational-warning-health.md +107 -0
  20. package/template/claude-task-manager/docs/codex-resume-state-guard-design.md +17 -12
  21. package/template/claude-task-manager/docs/codex-terminal-render-controller-handoff.md +311 -0
  22. package/template/claude-task-manager/docs/coding-agent-hooks-architecture.md +418 -0
  23. package/template/claude-task-manager/docs/conversation-import-freshness.md +20 -0
  24. package/template/claude-task-manager/docs/google-workspace-auth-health.md +77 -0
  25. package/template/claude-task-manager/docs/image-paste-ux.md +13 -0
  26. package/template/claude-task-manager/docs/ipad-web-preview.md +88 -0
  27. package/template/claude-task-manager/docs/main-loop-offload-architecture.md +66 -0
  28. package/template/claude-task-manager/docs/microsoft-dev-tunnel-phone-access-design.md +274 -519
  29. package/template/claude-task-manager/docs/mobile-live-streaming.md +27 -5
  30. package/template/claude-task-manager/docs/mobile-remote-submission-lifecycle.md +69 -0
  31. package/template/claude-task-manager/docs/phone-access-design.md +53 -15
  32. package/template/claude-task-manager/docs/phone-passkey-identity.md +122 -0
  33. package/template/claude-task-manager/docs/phone-setup.md +3 -0
  34. package/template/claude-task-manager/docs/prompt-editing-tree-design.md +25 -1
  35. package/template/claude-task-manager/docs/remote-desktop-access-design.md +268 -0
  36. package/template/claude-task-manager/docs/restart-lifecycle-architecture.md +95 -0
  37. package/template/claude-task-manager/docs/runtime-work-control-plane.md +53 -0
  38. package/template/claude-task-manager/docs/session-interactive-wait-surfaces.md +38 -0
  39. package/template/claude-task-manager/docs/session-needs-you-dismissal.md +84 -0
  40. package/template/claude-task-manager/docs/session-render-state-management-design.md +91 -3
  41. package/template/claude-task-manager/docs/session-standup-command-center-design.md +25 -1
  42. package/template/claude-task-manager/docs/session-title-authority.md +32 -0
  43. package/template/claude-task-manager/docs/session-workspace-binding.md +33 -0
  44. package/template/claude-task-manager/docs/skill-intent-resolution-design.md +72 -0
  45. package/template/claude-task-manager/docs/walle-mcp-supervisor-health.md +86 -0
  46. package/template/claude-task-manager/docs/walle-relay-phone-access-design.md +24 -15
  47. package/template/claude-task-manager/docs/walle-session-history-hydration.md +114 -0
  48. package/template/claude-task-manager/docs/walle-session-input-queue.md +104 -0
  49. package/template/claude-task-manager/docs/walle-session-model-catalog.md +90 -0
  50. package/template/claude-task-manager/docs/walle-session-model-preferences.md +15 -6
  51. package/template/claude-task-manager/git-utils.js +897 -27
  52. package/template/claude-task-manager/lib/agent-capabilities.js +33 -0
  53. package/template/claude-task-manager/lib/agent-cli-cache.js +37 -7
  54. package/template/claude-task-manager/lib/agent-hooks-installer.js +26 -2
  55. package/template/claude-task-manager/lib/agent-presets.js +17 -1
  56. package/template/claude-task-manager/lib/all-sessions-query.js +108 -0
  57. package/template/claude-task-manager/lib/approval-ai-refinement.js +488 -0
  58. package/template/claude-task-manager/lib/approval-self-adapt.js +168 -0
  59. package/template/claude-task-manager/lib/async-semaphore.js +44 -0
  60. package/template/claude-task-manager/lib/auth-context.js +5 -0
  61. package/template/claude-task-manager/lib/auth-rate-limit.js +47 -4
  62. package/template/claude-task-manager/lib/auth-rules.js +29 -2
  63. package/template/claude-task-manager/lib/auto-approval-verifier.js +129 -16
  64. package/template/claude-task-manager/lib/background-llm.js +144 -17
  65. package/template/claude-task-manager/lib/branch-inventory.js +212 -0
  66. package/template/claude-task-manager/lib/claude-desktop-sessions.js +15 -3
  67. package/template/claude-task-manager/lib/coalesce-sync-frames.js +151 -0
  68. package/template/claude-task-manager/lib/codex-launch-health.js +762 -0
  69. package/template/claude-task-manager/lib/codex-transcript-pager.js +51 -0
  70. package/template/claude-task-manager/lib/codex-zst.js +124 -0
  71. package/template/claude-task-manager/lib/coding-agent-models.js +233 -30
  72. package/template/claude-task-manager/lib/connection-health.js +232 -0
  73. package/template/claude-task-manager/lib/conversation-blob-parser.js +42 -0
  74. package/template/claude-task-manager/lib/conversation-tail-merge.js +89 -26
  75. package/template/claude-task-manager/lib/ctm-session-context-api.js +39 -10
  76. package/template/claude-task-manager/lib/cursor-conversation-store.js +354 -0
  77. package/template/claude-task-manager/lib/db-owner-worker-client.js +315 -0
  78. package/template/claude-task-manager/lib/document-review.js +141 -6
  79. package/template/claude-task-manager/lib/escalation-review.js +152 -0
  80. package/template/claude-task-manager/lib/graceful-shutdown.js +159 -0
  81. package/template/claude-task-manager/lib/headless-term-service.js +678 -0
  82. package/template/claude-task-manager/lib/heavy-worker-fallback.js +38 -0
  83. package/template/claude-task-manager/lib/jsonl-conversation-parser.js +542 -0
  84. package/template/claude-task-manager/lib/jsonl-range-reader.js +112 -0
  85. package/template/claude-task-manager/lib/main-db-census.js +216 -0
  86. package/template/claude-task-manager/lib/message-pagination.js +106 -4
  87. package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +750 -26
  88. package/template/claude-task-manager/lib/mobile-auth-api.js +274 -7
  89. package/template/claude-task-manager/lib/mobile-auth-store.js +592 -10
  90. package/template/claude-task-manager/lib/mobile-notification-dispatcher.js +15 -0
  91. package/template/claude-task-manager/lib/model-overview-brain-fallback.js +311 -0
  92. package/template/claude-task-manager/lib/model-overview-cache.js +141 -0
  93. package/template/claude-task-manager/lib/models-health-routing-notice.js +126 -0
  94. package/template/claude-task-manager/lib/node-pin-guard.js +93 -0
  95. package/template/claude-task-manager/lib/perf-tracker.js +242 -6
  96. package/template/claude-task-manager/lib/permission-match.js +76 -0
  97. package/template/claude-task-manager/lib/permission-sync.js +133 -20
  98. package/template/claude-task-manager/lib/process-title.js +35 -0
  99. package/template/claude-task-manager/lib/prompt-executions-query.js +25 -0
  100. package/template/claude-task-manager/lib/prompt-index-disk-cache.js +44 -0
  101. package/template/claude-task-manager/lib/prompt-intent.js +132 -0
  102. package/template/claude-task-manager/lib/provider-user-context.js +34 -0
  103. package/template/claude-task-manager/lib/read-pool-client.js +313 -0
  104. package/template/claude-task-manager/lib/readpool-breaker.js +31 -0
  105. package/template/claude-task-manager/lib/recent-sessions-breaker.js +12 -0
  106. package/template/claude-task-manager/lib/remote-feedback-client.js +72 -0
  107. package/template/claude-task-manager/lib/remote-relay-protocol.js +37 -4
  108. package/template/claude-task-manager/lib/remote-relay-store.js +159 -0
  109. package/template/claude-task-manager/lib/remote-submission-observer.js +278 -0
  110. package/template/claude-task-manager/lib/restart-guard.js +109 -0
  111. package/template/claude-task-manager/lib/restore-interruption-detector.js +439 -0
  112. package/template/claude-task-manager/lib/restore-policy.js +13 -0
  113. package/template/claude-task-manager/lib/restore-resume-batch.js +74 -0
  114. package/template/claude-task-manager/lib/restore-runtime.js +68 -0
  115. package/template/claude-task-manager/lib/restore-storm.js +34 -0
  116. package/template/claude-task-manager/lib/resume-cwd.js +36 -0
  117. package/template/claude-task-manager/lib/resume-preflight.js +313 -0
  118. package/template/claude-task-manager/lib/runtime-work-registry.js +444 -0
  119. package/template/claude-task-manager/lib/sanitize-openai-auth.js +31 -0
  120. package/template/claude-task-manager/lib/scheduler.js +21 -1
  121. package/template/claude-task-manager/lib/scrollback-snapshot-store.js +159 -0
  122. package/template/claude-task-manager/lib/serial-task-queue.js +64 -0
  123. package/template/claude-task-manager/lib/server-listeners.js +239 -0
  124. package/template/claude-task-manager/lib/session-capture.js +42 -7
  125. package/template/claude-task-manager/lib/session-content-backfill.js +131 -0
  126. package/template/claude-task-manager/lib/session-history.js +388 -43
  127. package/template/claude-task-manager/lib/session-host-manager.js +287 -0
  128. package/template/claude-task-manager/lib/session-image-refs.js +209 -0
  129. package/template/claude-task-manager/lib/session-jobs.js +399 -59
  130. package/template/claude-task-manager/lib/session-prompt-index.js +137 -0
  131. package/template/claude-task-manager/lib/session-restore.js +53 -0
  132. package/template/claude-task-manager/lib/session-standup.js +123 -23
  133. package/template/claude-task-manager/lib/session-state-bus.js +14 -0
  134. package/template/claude-task-manager/lib/session-stream.js +64 -16
  135. package/template/claude-task-manager/lib/session-timeline-summary.js +260 -0
  136. package/template/claude-task-manager/lib/session-token-usage.js +494 -0
  137. package/template/claude-task-manager/lib/session-workspace-binding.js +356 -0
  138. package/template/claude-task-manager/lib/setup-network-config.js +9 -0
  139. package/template/claude-task-manager/lib/size-cap.js +45 -0
  140. package/template/claude-task-manager/lib/size-cap.test.js +62 -0
  141. package/template/claude-task-manager/lib/skill-autocomplete.js +180 -1
  142. package/template/claude-task-manager/lib/skill-intent-resolver.js +304 -0
  143. package/template/claude-task-manager/lib/sqlite-driver.js +19 -3
  144. package/template/claude-task-manager/lib/standup-attention.js +7 -3
  145. package/template/claude-task-manager/lib/status-authority.js +39 -0
  146. package/template/claude-task-manager/lib/status-hooks.js +4 -0
  147. package/template/claude-task-manager/lib/storage-migration.js +235 -0
  148. package/template/claude-task-manager/lib/structured-capture.js +298 -0
  149. package/template/claude-task-manager/lib/sync-io-census.js +163 -0
  150. package/template/claude-task-manager/lib/tailscale-setup.js +6 -0
  151. package/template/claude-task-manager/lib/terminal-activity-evidence.js +33 -0
  152. package/template/claude-task-manager/lib/terminal-choice.js +364 -0
  153. package/template/claude-task-manager/lib/terminal-control-sanitize.js +17 -0
  154. package/template/claude-task-manager/lib/terminal-fingerprint.js +48 -0
  155. package/template/claude-task-manager/lib/terminal-output-flush.js +84 -0
  156. package/template/claude-task-manager/lib/timeline-order.js +122 -0
  157. package/template/claude-task-manager/lib/transcript-store.js +348 -43
  158. package/template/claude-task-manager/lib/transport-security.js +84 -1
  159. package/template/claude-task-manager/lib/wait-state.js +184 -0
  160. package/template/claude-task-manager/lib/walle-client.js +47 -5
  161. package/template/claude-task-manager/lib/walle-ctm-history.js +564 -4
  162. package/template/claude-task-manager/lib/walle-external-actions.js +135 -16
  163. package/template/claude-task-manager/lib/walle-history-hydration.js +46 -0
  164. package/template/claude-task-manager/lib/walle-native-health.js +403 -0
  165. package/template/claude-task-manager/lib/walle-repair.js +701 -0
  166. package/template/claude-task-manager/lib/walle-session-cache.js +109 -0
  167. package/template/claude-task-manager/lib/walle-session-context.js +57 -21
  168. package/template/claude-task-manager/lib/walle-session-model-catalog.js +34 -0
  169. package/template/claude-task-manager/lib/walle-supervisor.js +539 -63
  170. package/template/claude-task-manager/lib/walle-transcript.js +52 -0
  171. package/template/claude-task-manager/lib/worktree-active-sync.js +11 -7
  172. package/template/claude-task-manager/lib/worktree-cwd.js +32 -1
  173. package/template/claude-task-manager/package.json +1 -1
  174. package/template/claude-task-manager/prompt-harvest.js +89 -66
  175. package/template/claude-task-manager/providers/claude-code.js +51 -3
  176. package/template/claude-task-manager/providers/cursor.js +140 -45
  177. package/template/claude-task-manager/public/css/reviews.css +551 -61
  178. package/template/claude-task-manager/public/css/setup.css +191 -0
  179. package/template/claude-task-manager/public/css/walle-session.css +865 -10
  180. package/template/claude-task-manager/public/css/walle.css +154 -0
  181. package/template/claude-task-manager/public/designs/ai-providers-consolidation-v2.html +830 -0
  182. package/template/claude-task-manager/public/index.html +18516 -2058
  183. package/template/claude-task-manager/public/ipad.html +363 -0
  184. package/template/claude-task-manager/public/js/document-review-links.js +301 -0
  185. package/template/claude-task-manager/public/js/image-normalize.js +69 -36
  186. package/template/claude-task-manager/public/js/message-renderer.js +1265 -77
  187. package/template/claude-task-manager/public/js/prompts.js +66 -29
  188. package/template/claude-task-manager/public/js/reviews.js +901 -133
  189. package/template/claude-task-manager/public/js/session-activity-utils.js +11 -1
  190. package/template/claude-task-manager/public/js/session-search-utils.js +94 -10
  191. package/template/claude-task-manager/public/js/session-status-precedence.js +23 -5
  192. package/template/claude-task-manager/public/js/setup.js +1273 -176
  193. package/template/claude-task-manager/public/js/stream-view.js +691 -73
  194. package/template/claude-task-manager/public/js/terminal-reconciler.js +210 -0
  195. package/template/claude-task-manager/public/js/walle-session.js +2455 -158
  196. package/template/claude-task-manager/public/js/walle.js +455 -28
  197. package/template/claude-task-manager/public/m/app.css +2909 -262
  198. package/template/claude-task-manager/public/m/app.js +6601 -398
  199. package/template/claude-task-manager/public/m/claim.html +224 -17
  200. package/template/claude-task-manager/public/m/index.html +117 -21
  201. package/template/claude-task-manager/public/m/sw.js +3 -1
  202. package/template/claude-task-manager/public/manifest.json +2 -2
  203. package/template/claude-task-manager/public/prompts.html +30 -14
  204. package/template/claude-task-manager/queue-engine.js +507 -28
  205. package/template/claude-task-manager/scripts/repair-claude-session-images.js +27 -8
  206. package/template/claude-task-manager/server.js +14341 -2197
  207. package/template/claude-task-manager/session-integrity.js +160 -18
  208. package/template/claude-task-manager/session-search-ranking.js +1 -0
  209. package/template/claude-task-manager/session-utils.js +25 -5
  210. package/template/claude-task-manager/workers/approval-blocklist.js +96 -6
  211. package/template/claude-task-manager/workers/approval-widget-validator.js +14 -8
  212. package/template/claude-task-manager/workers/conversation-import-worker.js +11 -50
  213. package/template/claude-task-manager/workers/db-owner-worker.js +386 -0
  214. package/template/claude-task-manager/workers/harvest-worker.js +9 -55
  215. package/template/claude-task-manager/workers/headless-term-worker.js +9 -530
  216. package/template/claude-task-manager/workers/read-pool-worker.js +387 -0
  217. package/template/claude-task-manager/workers/scrollback-worker.js +11 -72
  218. package/template/claude-task-manager/workers/session-host-process.js +146 -0
  219. package/template/claude-task-manager/workers/session-integrity-worker.js +10 -54
  220. package/template/claude-task-manager/workers/state-detectors/base.js +18 -1
  221. package/template/claude-task-manager/workers/state-detectors/claude-code.js +182 -9
  222. package/template/claude-task-manager/workers/state-detectors/codex.js +150 -2
  223. package/template/claude-task-manager/workers/state-detectors/cursor.js +127 -0
  224. package/template/claude-task-manager/workers/state-detectors/gemini.js +21 -0
  225. package/template/claude-task-manager/workers/state-detectors/index.js +29 -0
  226. package/template/claude-task-manager/workers/state-detectors/opencode.js +103 -0
  227. package/template/docs/design/markdown-review-pane.md +206 -0
  228. package/template/docs/designs/2026-05-17-portkey-gateway-provider-ux.md +129 -38
  229. package/template/docs/designs/2026-05-20-mobile-worktree-finish-command.md +27 -0
  230. package/template/docs/designs/2026-05-22-ai-configuration-consolidation.md +248 -0
  231. package/template/docs/designs/ai-configuration-consolidation-mock.html +812 -0
  232. package/template/docs/private-memory-and-pii-policy.md +69 -0
  233. package/template/package.json +2 -1
  234. package/template/scripts/check-private-data.js +201 -0
  235. package/template/shared/sqlite-owner-guard.js +30 -0
  236. package/template/shared/sqlite-owner-write-queue.js +225 -0
  237. package/template/shared/sqlite-storage-policy.js +111 -0
  238. package/template/shared/sqlite-write-lock.js +428 -0
  239. package/template/wall-e/agent-runners/claude-code.js +5 -0
  240. package/template/wall-e/agent.js +166 -22
  241. package/template/wall-e/api-walle.js +524 -70
  242. package/template/wall-e/auth/provider-flows.js +11 -1
  243. package/template/wall-e/bin/walle-mcp-stdio.js +341 -17
  244. package/template/wall-e/brain.js +1614 -141
  245. package/template/wall-e/chat/attachment-blocks.js +96 -0
  246. package/template/wall-e/chat/attachments.js +2 -1
  247. package/template/wall-e/chat/capability-resolver.js +7 -7
  248. package/template/wall-e/chat/context-messages.js +28 -0
  249. package/template/wall-e/chat/conversation-frame.js +630 -0
  250. package/template/wall-e/chat/provider-messages.js +125 -0
  251. package/template/wall-e/chat.js +1002 -233
  252. package/template/wall-e/coding/acceptance-contract.js +170 -0
  253. package/template/wall-e/coding/acp-adapter.js +1 -1
  254. package/template/wall-e/coding/agent-catalog.js +3 -0
  255. package/template/wall-e/coding/artifact-store.js +93 -0
  256. package/template/wall-e/coding/capability-router.js +120 -0
  257. package/template/wall-e/coding/coding-run-controller.js +423 -0
  258. package/template/wall-e/coding/compaction-service.js +157 -12
  259. package/template/wall-e/coding/frontend-verification.js +258 -0
  260. package/template/wall-e/coding/lifecycle-hooks.js +75 -0
  261. package/template/wall-e/coding/local-preview-contract.js +157 -0
  262. package/template/wall-e/coding/permission-service.js +57 -13
  263. package/template/wall-e/coding/prompt-bundle.js +19 -1
  264. package/template/wall-e/coding/prompt-section-registry.js +227 -0
  265. package/template/wall-e/coding/provider-compat.js +15 -0
  266. package/template/wall-e/coding/runtime-events.js +224 -0
  267. package/template/wall-e/coding/runtime-mode.js +3 -0
  268. package/template/wall-e/coding/side-git-snapshot.js +160 -4
  269. package/template/wall-e/coding/snapshot-service.js +143 -1
  270. package/template/wall-e/coding/stream-processor.js +388 -34
  271. package/template/wall-e/coding/task-tool.js +141 -4
  272. package/template/wall-e/coding/tool-execution-controller.js +365 -0
  273. package/template/wall-e/coding/tool-registry.js +43 -5
  274. package/template/wall-e/coding/user-hooks.js +217 -0
  275. package/template/wall-e/coding-orchestrator.js +1330 -221
  276. package/template/wall-e/coding-prompts.js +20 -4
  277. package/template/wall-e/context/context-builder.js +15 -2
  278. package/template/wall-e/decision/confidence.js +1 -1
  279. package/template/wall-e/docs/coding-acceptance-contract.md +41 -0
  280. package/template/wall-e/docs/external-action-controller.md +26 -6
  281. package/template/wall-e/docs/telemetry-lifecycle.md +8 -2
  282. package/template/wall-e/embeddings.js +591 -53
  283. package/template/wall-e/external-action-controller.js +12 -0
  284. package/template/wall-e/http/auth.js +1 -0
  285. package/template/wall-e/http/chat-api.js +46 -11
  286. package/template/wall-e/http/model-admin.js +836 -34
  287. package/template/wall-e/lib/boot-profile.js +88 -0
  288. package/template/wall-e/lib/event-loop-monitor.js +93 -0
  289. package/template/wall-e/lib/service-health.js +194 -0
  290. package/template/wall-e/llm/anthropic.js +130 -5
  291. package/template/wall-e/llm/client.js +266 -63
  292. package/template/wall-e/llm/default-fallback.js +382 -0
  293. package/template/wall-e/llm/health.js +19 -0
  294. package/template/wall-e/llm/message-guard.js +78 -0
  295. package/template/wall-e/llm/model-catalog.js +252 -1
  296. package/template/wall-e/llm/openai.js +26 -4
  297. package/template/wall-e/llm/portkey-sync.js +654 -0
  298. package/template/wall-e/llm/provider-error.js +30 -2
  299. package/template/wall-e/llm/registry.js +5 -1
  300. package/template/wall-e/llm/request-compat.js +67 -0
  301. package/template/wall-e/loops/backfill.js +79 -23
  302. package/template/wall-e/loops/brain-optimize.js +67 -0
  303. package/template/wall-e/loops/ingest.js +25 -10
  304. package/template/wall-e/loops/question-digest.js +160 -0
  305. package/template/wall-e/loops/reflect.js +6 -4
  306. package/template/wall-e/loops/think.js +39 -12
  307. package/template/wall-e/mcp-server.js +318 -36
  308. package/template/wall-e/memory/ctm-context-client.js +52 -14
  309. package/template/wall-e/memory/ctm-operational-context.js +237 -0
  310. package/template/wall-e/memory/ctm-prompt-executions-client.js +128 -0
  311. package/template/wall-e/memory/ctm-session-context.js +111 -63
  312. package/template/wall-e/prompts/coding/deepseek.txt +3 -0
  313. package/template/wall-e/prompts/coding/gemini.txt +6 -0
  314. package/template/wall-e/prompts/coding/gpt.txt +6 -0
  315. package/template/wall-e/prompts/coding/local.txt +7 -0
  316. package/template/wall-e/runtime/decision-hooks.js +115 -0
  317. package/template/wall-e/runtime/devbox-gateway.js +82 -8
  318. package/template/wall-e/runtime/prompt-manifest.js +86 -0
  319. package/template/wall-e/runtime/tool-executor.js +269 -0
  320. package/template/wall-e/runtime/tool-result-envelope.js +138 -0
  321. package/template/wall-e/runtime/transcript-projection.js +60 -0
  322. package/template/wall-e/runtime/walle-runtime.js +224 -0
  323. package/template/wall-e/scripts/db-optimize/migrate.js +162 -0
  324. package/template/wall-e/scripts/db-optimize/recall-eval.js +117 -0
  325. package/template/wall-e/server.js +15 -0
  326. package/template/wall-e/session-files.js +9 -0
  327. package/template/wall-e/skills/_bundled/google-calendar/run.js +1 -1
  328. package/template/wall-e/skills/_bundled/gws-workspace/run.js +1 -1
  329. package/template/wall-e/skills/_bundled/slack-mentions/run.js +76 -6
  330. package/template/wall-e/skills/claude-code-reader.js +7 -3
  331. package/template/wall-e/skills/script-skill-runner.js +10 -0
  332. package/template/wall-e/skills/skill-planner.js +38 -0
  333. package/template/wall-e/tools/builtin-middleware.js +19 -9
  334. package/template/wall-e/tools/local-tools.js +1428 -16
  335. package/template/wall-e/tools/permission-checker.js +73 -5
  336. package/template/wall-e/tools/question-manager.js +117 -7
  337. package/template/wall-e/training/harvester.js +12 -28
  338. package/template/wall-e/training/replay.js +25 -80
  339. package/template/website/index.html +10 -10
  340. package/template/wall-e/eval/ab-test.js +0 -203
  341. package/template/wall-e/eval/agent-runner.js +0 -772
  342. package/template/wall-e/eval/agent-scorer.js +0 -461
  343. package/template/wall-e/eval/aggregator.js +0 -414
  344. package/template/wall-e/eval/allowed-test-commands.js +0 -34
  345. package/template/wall-e/eval/benchmark-generator.js +0 -113
  346. package/template/wall-e/eval/benchmarks/chat-eval.json +0 -1662
  347. package/template/wall-e/eval/benchmarks/chat.json +0 -82
  348. package/template/wall-e/eval/benchmarks/coding-agent-real.json +0 -1
  349. package/template/wall-e/eval/benchmarks/coding-agent.json +0 -1581
  350. package/template/wall-e/eval/benchmarks/coding.json +0 -122
  351. package/template/wall-e/eval/benchmarks/memory-retrieval.json +0 -234
  352. package/template/wall-e/eval/benchmarks/reasoning.json +0 -82
  353. package/template/wall-e/eval/benchmarks/swebench-lite-30.json +0 -212
  354. package/template/wall-e/eval/benchmarks.js +0 -669
  355. package/template/wall-e/eval/cc-replay.js +0 -719
  356. package/template/wall-e/eval/chat-eval.js +0 -525
  357. package/template/wall-e/eval/check-keys.js +0 -15
  358. package/template/wall-e/eval/check-providers.js +0 -42
  359. package/template/wall-e/eval/codex-cli-baseline.js +0 -669
  360. package/template/wall-e/eval/coding-agent-real.js +0 -570
  361. package/template/wall-e/eval/context-compactor.js +0 -251
  362. package/template/wall-e/eval/debug-agent003.js +0 -68
  363. package/template/wall-e/eval/diagnostics.js +0 -216
  364. package/template/wall-e/eval/eval-orchestrator.js +0 -642
  365. package/template/wall-e/eval/evaluate.js +0 -202
  366. package/template/wall-e/eval/evaluator.js +0 -373
  367. package/template/wall-e/eval/exporter.js +0 -212
  368. package/template/wall-e/eval/fixtures/express-basic/package.json +0 -9
  369. package/template/wall-e/eval/fixtures/express-basic/server.js +0 -115
  370. package/template/wall-e/eval/fixtures/express-basic/test.js +0 -83
  371. package/template/wall-e/eval/fixtures/express-buggy/package.json +0 -9
  372. package/template/wall-e/eval/fixtures/express-buggy/server.js +0 -113
  373. package/template/wall-e/eval/fixtures/express-buggy/test.js +0 -83
  374. package/template/wall-e/eval/fixtures/express-buggy-items/package.json +0 -9
  375. package/template/wall-e/eval/fixtures/express-buggy-items/server.js +0 -112
  376. package/template/wall-e/eval/fixtures/express-buggy-items/test.js +0 -83
  377. package/template/wall-e/eval/fixtures/express-buggy-search/package.json +0 -9
  378. package/template/wall-e/eval/fixtures/express-buggy-search/server.js +0 -121
  379. package/template/wall-e/eval/fixtures/express-buggy-search/test.js +0 -83
  380. package/template/wall-e/eval/fixtures/express-rename-data/data.js +0 -34
  381. package/template/wall-e/eval/fixtures/express-rename-data/package.json +0 -9
  382. package/template/wall-e/eval/fixtures/express-rename-data/server.js +0 -97
  383. package/template/wall-e/eval/fixtures/express-rename-data/test.js +0 -88
  384. package/template/wall-e/eval/fixtures/express-xss/package.json +0 -12
  385. package/template/wall-e/eval/fixtures/express-xss/server.js +0 -90
  386. package/template/wall-e/eval/fixtures/express-xss/test.js +0 -67
  387. package/template/wall-e/eval/fixtures/express-xss/views/profile.ejs +0 -9
  388. package/template/wall-e/eval/fixtures/fullstack-app/config/default.js +0 -9
  389. package/template/wall-e/eval/fixtures/fullstack-app/config/test.js +0 -13
  390. package/template/wall-e/eval/fixtures/fullstack-app/package.json +0 -11
  391. package/template/wall-e/eval/fixtures/fullstack-app/public/css/style.css +0 -137
  392. package/template/wall-e/eval/fixtures/fullstack-app/public/index.html +0 -46
  393. package/template/wall-e/eval/fixtures/fullstack-app/public/js/app.js +0 -121
  394. package/template/wall-e/eval/fixtures/fullstack-app/public/js/auth.js +0 -71
  395. package/template/wall-e/eval/fixtures/fullstack-app/public/js/items.js +0 -80
  396. package/template/wall-e/eval/fixtures/fullstack-app/public/js/users.js +0 -46
  397. package/template/wall-e/eval/fixtures/fullstack-app/public/login.html +0 -45
  398. package/template/wall-e/eval/fixtures/fullstack-app/public/register.html +0 -38
  399. package/template/wall-e/eval/fixtures/fullstack-app/scripts/migrate.js +0 -23
  400. package/template/wall-e/eval/fixtures/fullstack-app/scripts/seed.js +0 -46
  401. package/template/wall-e/eval/fixtures/fullstack-app/server/db.js +0 -99
  402. package/template/wall-e/eval/fixtures/fullstack-app/server/index.js +0 -94
  403. package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/auth.js +0 -19
  404. package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/logger.js +0 -19
  405. package/template/wall-e/eval/fixtures/fullstack-app/server/router.js +0 -50
  406. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/auth.js +0 -69
  407. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/health.js +0 -23
  408. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/items.js +0 -88
  409. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/users.js +0 -75
  410. package/template/wall-e/eval/fixtures/fullstack-app/server/test.js +0 -198
  411. package/template/wall-e/eval/fixtures/fullstack-app/server/utils/response.js +0 -34
  412. package/template/wall-e/eval/fixtures/fullstack-app/server/utils/validate.js +0 -26
  413. package/template/wall-e/eval/fixtures/fullstack-app/server.js +0 -8
  414. package/template/wall-e/eval/fixtures/fullstack-app/test.js +0 -12
  415. package/template/wall-e/eval/fixtures/monorepo-basic/package.json +0 -8
  416. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/data.js +0 -58
  417. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/middleware.js +0 -46
  418. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/package.json +0 -8
  419. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/routes.js +0 -64
  420. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/server.js +0 -56
  421. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/test.js +0 -116
  422. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/commands.js +0 -61
  423. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/index.js +0 -62
  424. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/output.js +0 -43
  425. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/package.json +0 -11
  426. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/test.js +0 -44
  427. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/formatters.js +0 -43
  428. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/index.js +0 -12
  429. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/package.json +0 -5
  430. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/test.js +0 -55
  431. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/validators.js +0 -29
  432. package/template/wall-e/eval/fixtures/monorepo-basic/test.js +0 -46
  433. package/template/wall-e/eval/fixtures/node-cli/index.js +0 -78
  434. package/template/wall-e/eval/fixtures/node-cli/package.json +0 -10
  435. package/template/wall-e/eval/fixtures/node-cli/test.js +0 -57
  436. package/template/wall-e/eval/fixtures/node-typed/package.json +0 -8
  437. package/template/wall-e/eval/fixtures/node-typed/src/handlers.js +0 -31
  438. package/template/wall-e/eval/fixtures/node-typed/src/utils.js +0 -33
  439. package/template/wall-e/eval/fixtures/node-typed/test.js +0 -36
  440. package/template/wall-e/eval/fixtures/python-flask/app.py +0 -14
  441. package/template/wall-e/eval/fixtures/python-flask/requirements.txt +0 -2
  442. package/template/wall-e/eval/fixtures/python-flask/test_app.py +0 -25
  443. package/template/wall-e/eval/fixtures/wall-e-subset/brain.js +0 -105
  444. package/template/wall-e/eval/fixtures/wall-e-subset/eval/aggregator.js +0 -101
  445. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/chat.json +0 -20
  446. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/coding.json +0 -32
  447. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks.js +0 -64
  448. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/package.json +0 -6
  449. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/server.js +0 -31
  450. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/test.js +0 -18
  451. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/utils.js +0 -34
  452. package/template/wall-e/eval/fixtures/wall-e-subset/eval/runner.js +0 -104
  453. package/template/wall-e/eval/fixtures/wall-e-subset/eval/scorer.js +0 -73
  454. package/template/wall-e/eval/fixtures/wall-e-subset/eval/test.js +0 -134
  455. package/template/wall-e/eval/fixtures/wall-e-subset/llm/client.js +0 -99
  456. package/template/wall-e/eval/fixtures/wall-e-subset/llm/providers.js +0 -63
  457. package/template/wall-e/eval/fixtures/wall-e-subset/llm/test.js +0 -70
  458. package/template/wall-e/eval/fixtures/wall-e-subset/package.json +0 -10
  459. package/template/wall-e/eval/fixtures/wall-e-subset/test.js +0 -86
  460. package/template/wall-e/eval/harvester.js +0 -685
  461. package/template/wall-e/eval/head-to-head.js +0 -388
  462. package/template/wall-e/eval/humaneval-adapter.js +0 -321
  463. package/template/wall-e/eval/list-models.js +0 -31
  464. package/template/wall-e/eval/livecodebench-adapter.js +0 -291
  465. package/template/wall-e/eval/mail-integration.js +0 -443
  466. package/template/wall-e/eval/manifest.js +0 -186
  467. package/template/wall-e/eval/meta-harness/adapters/coding-agent.js +0 -57
  468. package/template/wall-e/eval/meta-harness/bootstrap-snapshot.js +0 -149
  469. package/template/wall-e/eval/meta-harness/candidate-store.js +0 -117
  470. package/template/wall-e/eval/meta-harness/cli.js +0 -86
  471. package/template/wall-e/eval/meta-harness/domain-spec.js +0 -154
  472. package/template/wall-e/eval/meta-harness/domains/coding-agent.domain.json +0 -84
  473. package/template/wall-e/eval/meta-harness/examples/env-bootstrap-candidate.js +0 -29
  474. package/template/wall-e/eval/meta-harness/experience-store.js +0 -174
  475. package/template/wall-e/eval/meta-harness/frontier.js +0 -96
  476. package/template/wall-e/eval/meta-harness/harness-interface.js +0 -90
  477. package/template/wall-e/eval/meta-harness/leakage-guard.js +0 -80
  478. package/template/wall-e/eval/meta-harness/optimizer.js +0 -207
  479. package/template/wall-e/eval/meta-harness/proposer-runner.js +0 -110
  480. package/template/wall-e/eval/meta-harness/reporting.js +0 -58
  481. package/template/wall-e/eval/meta-harness/telemetry.js +0 -27
  482. package/template/wall-e/eval/meta-harness/validation.js +0 -81
  483. package/template/wall-e/eval/promoter.js +0 -228
  484. package/template/wall-e/eval/provider-normalizer.js +0 -33
  485. package/template/wall-e/eval/replay.js +0 -395
  486. package/template/wall-e/eval/run-agent-benchmarks.js +0 -386
  487. package/template/wall-e/eval/run-codex-cli-baseline.js +0 -177
  488. package/template/wall-e/eval/run-coding-agent-real.js +0 -187
  489. package/template/wall-e/eval/run-eval.js +0 -435
  490. package/template/wall-e/eval/run-model-comparison.js +0 -142
  491. package/template/wall-e/eval/session-evaluator.js +0 -187
  492. package/template/wall-e/eval/session-miner.js +0 -207
  493. package/template/wall-e/eval/session-retrieval-benchmark.js +0 -150
  494. package/template/wall-e/eval/session-transcripts.js +0 -509
  495. package/template/wall-e/eval/shadow.js +0 -161
  496. package/template/wall-e/eval/swebench-adapter.js +0 -345
  497. package/template/wall-e/eval/swebench-docker.js +0 -192
  498. package/template/wall-e/eval/train.py +0 -320
  499. package/template/wall-e/eval/trainer.js +0 -232
  500. package/template/wall-e/eval/weekly-eval-loop.js +0 -241
@@ -0,0 +1,93 @@
1
+ 'use strict';
2
+ // Local-only Node.js version guard for the CTM + Wall-E monorepo.
3
+ //
4
+ // Why: the daemon, /ctm-dev, and skill subprocesses share a single compiled
5
+ // better-sqlite3 / sqlite-vec / tree-sitter binary, which is built for ONE Node
6
+ // major (ABI). Launching under a different major corrupts that binary and
7
+ // crash-loops the daemon ("MCP never became ready" + skill-failure storm).
8
+ //
9
+ // This guard activates ONLY inside the owner's repo, identified by a
10
+ // `.node-version` pin file sitting beside `wall-e/` and `claude-task-manager/`.
11
+ // In a shipped create-walle install the pin is excluded from the package
12
+ // (see create-walle/build.sh), so findPinRoot() returns null and this is a
13
+ // silent no-op — it never constrains other users' Node version.
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ // Walk up from startDir until we find the monorepo root: a directory that has a
19
+ // `.node-version` pin AND both project dirs. The triple condition prevents us
20
+ // from hijacking an unrelated `.node-version` somewhere above a user's install.
21
+ function findPinRoot(startDir) {
22
+ let dir = startDir;
23
+ for (;;) {
24
+ if (
25
+ fs.existsSync(path.join(dir, '.node-version')) &&
26
+ fs.existsSync(path.join(dir, 'wall-e')) &&
27
+ fs.existsSync(path.join(dir, 'claude-task-manager'))
28
+ ) {
29
+ return dir;
30
+ }
31
+ const parent = path.dirname(dir);
32
+ if (parent === dir) return null;
33
+ dir = parent;
34
+ }
35
+ }
36
+
37
+ function readPin(root) {
38
+ try {
39
+ return fs.readFileSync(path.join(root, '.node-version'), 'utf8').replace(/[v\s]/g, '');
40
+ } catch {
41
+ return '';
42
+ }
43
+ }
44
+
45
+ // Assert the running Node matches the pinned MAJOR (the ABI-critical part — a
46
+ // patch bump within the line keeps the same NODE_MODULE_VERSION). On mismatch:
47
+ // hard-fail with remediation (default) or throw when opts.throwInstead is set.
48
+ // Returns {pinned:false} as a no-op when there is no pin (shipped / non-owner).
49
+ function assertPinnedNode(opts = {}) {
50
+ const startDir = opts.startDir || __dirname;
51
+ const root = findPinRoot(startDir);
52
+ if (!root) return { pinned: false };
53
+ const pin = readPin(root);
54
+ if (!pin) return { pinned: false };
55
+
56
+ const pinnedMajor = pin.split('.')[0];
57
+ const runningMajor = process.versions.node.split('.')[0];
58
+ if (runningMajor === pinnedMajor) {
59
+ return { pinned: true, ok: true, root, pin, running: process.versions.node };
60
+ }
61
+
62
+ if (opts.throwInstead) {
63
+ const err = new Error(
64
+ `Node version mismatch: pinned v${pin} (major ${pinnedMajor}), running v${process.versions.node}`
65
+ );
66
+ err.code = 'NODE_PIN_MISMATCH';
67
+ throw err;
68
+ }
69
+
70
+ const resolver = path.join(root, 'bin', 'node-bin.sh');
71
+ const devSh = path.join(root, 'bin', 'dev.sh');
72
+ process.stderr.write(
73
+ [
74
+ '',
75
+ ' ✖ Wrong Node.js version for CTM + Wall-E.',
76
+ ` pinned : v${pin} (ABI line ${pinnedMajor}.x)`,
77
+ ` running: v${process.versions.node} (NODE_MODULE_VERSION ${process.versions.modules})`,
78
+ '',
79
+ ' The shared native modules (better-sqlite3, sqlite-vec, tree-sitter) are',
80
+ ' compiled for ONE Node major. Launching under another major corrupts that',
81
+ ' binary and crash-loops the daemon. Start through the pinned launcher:',
82
+ '',
83
+ ` "$(bash ${resolver})" <script> # resolves the pinned node`,
84
+ ` bash ${devSh} # isolated dev instance`,
85
+ ` fnm use ${pin} | nvm use ${pin} # then re-run`,
86
+ '',
87
+ ].join('\n') + '\n'
88
+ );
89
+ process.exit(78); // EX_CONFIG
90
+ return { pinned: true, ok: false }; // unreachable
91
+ }
92
+
93
+ module.exports = { assertPinnedNode, findPinRoot, readPin };
@@ -12,9 +12,177 @@
12
12
  // Cost: two property writes per wrapped call. Negligible.
13
13
 
14
14
  let _current = null;
15
+ let _lastCompleted = null;
15
16
  const _debug = process.env.CTM_PERF_DEBUG === '1';
16
17
 
17
- function getOp() { return _current; }
18
+ // Perf diagnostic console output ([perf-lag] / [freeze-probe]) is OFF by default — once the hot
19
+ // paths are tuned, the per-block log lines are noise. The rolling stats are STILL recorded (always
20
+ // queryable at GET /api/diagnostics/event-loop), and CRITICAL freezes (>= PERF_CRITICAL_MS) are
21
+ // ALWAYS logged so a genuine multi-second stall is never silently swallowed. Flip CTM_PERF_LOGS=1
22
+ // (or CTM_PERF_DEBUG=1) to restore full verbose perf logging while debugging.
23
+ const PERF_CRITICAL_MS = (() => {
24
+ const n = Number(process.env.CTM_PERF_CRITICAL_MS);
25
+ return Number.isFinite(n) && n > 0 ? n : 2000;
26
+ })();
27
+ // Re-read the env each call so the flag is runtime-toggleable (and testable) — no restart needed
28
+ // to flip verbose perf logging on/off. Cheap: a couple of process.env property reads.
29
+ function perfLogsEnabled() {
30
+ return process.env.CTM_PERF_LOGS === '1' || process.env.CTM_PERF_DEBUG === '1';
31
+ }
32
+ function shouldLogPerf(durMs) { return perfLogsEnabled() || Number(durMs) >= PERF_CRITICAL_MS; }
33
+
34
+ // Ring buffer of recently-completed spans. `getOp()` only names the single op active
35
+ // (or last completed within 1s) when a [perf-lag] block fires — but a freeze is often
36
+ // the SUM of several sync chunks running back-to-back after awaits, the last of which
37
+ // is `(unknown)`. This trail ("what ran in the last few seconds, and for how long")
38
+ // turns an anonymous block into an attributable one. Cheap: one push per span over a
39
+ // small threshold, capped length.
40
+ const _RECENT_SPAN_MIN_MS = (() => {
41
+ const n = Number(process.env.CTM_PERF_SPAN_MIN_MS);
42
+ return Number.isFinite(n) && n >= 0 ? n : 20;
43
+ })();
44
+ const _RECENT_SPANS_MAX = 24;
45
+ const _recentSpans = []; // {name, dur, end}
46
+ function _recordSpan(name, dur) {
47
+ if (dur < _RECENT_SPAN_MIN_MS) return;
48
+ _recentSpans.push({ name, dur, end: Date.now() });
49
+ if (_recentSpans.length > _RECENT_SPANS_MAX) _recentSpans.shift();
50
+ }
51
+ // Most-recent-first list of spans that ended within maxAgeMs, plus their summed time
52
+ // grouped by name. Read only on the slow path (when a lag line is being emitted).
53
+ function getRecentSpans({ maxAgeMs = 3000, limit = 8 } = {}) {
54
+ const cutoff = Date.now() - maxAgeMs;
55
+ const recent = [];
56
+ const byName = new Map();
57
+ for (let i = _recentSpans.length - 1; i >= 0; i--) {
58
+ const s = _recentSpans[i];
59
+ if (s.end < cutoff) break;
60
+ if (recent.length < limit) recent.push(s);
61
+ byName.set(s.name, (byName.get(s.name) || 0) + s.dur);
62
+ }
63
+ const top = Array.from(byName.entries())
64
+ .sort((a, b) => b[1] - a[1])
65
+ .slice(0, limit)
66
+ .map(([name, ms]) => ({ name, ms }));
67
+ return { recent, top };
68
+ }
69
+
70
+ // Phase 5: continuous event-loop budget. The lag detector feeds every block here so a
71
+ // rolling window of "blocked ms attributed by op" is queryable (GET /api/diagnostics/
72
+ // event-loop) instead of only grep-able after the fact. Cumulative-since-reset (?reset=1
73
+ // for windowed sampling), matching the write-lock diagnostics. Blocks >= the sleep
74
+ // threshold (laptop suspend → a giant wall-clock gap, not a real freeze) are bucketed
75
+ // separately so they don't pollute the named op stats.
76
+ const _SLEEP_BLOCK_MS = (() => {
77
+ const n = Number(process.env.CTM_SLEEP_BLOCK_MS);
78
+ return Number.isFinite(n) && n > 0 ? n : 20000;
79
+ })();
80
+ let _blockSince = Date.now();
81
+ let _blockCount = 0;
82
+ let _blockSumMs = 0;
83
+ let _blockMaxMs = 0;
84
+ let _blockUnknownMs = 0;
85
+ let _sleepCount = 0;
86
+ let _sleepMs = 0;
87
+ const _blockByOp = new Map(); // name -> { count, sumMs, maxMs }
88
+
89
+ // GC-pause attribution. A major GC on CTM's multi-GB heap can pause the loop
90
+ // 100-400ms, and the lag detector blames that pause on whatever synchronous op
91
+ // happened to be running (pty:processData, scrollback-snapshot, ...) — producing
92
+ // scattered ~400ms blocks with no single fixable cause. Observing GC directly
93
+ // lets the diagnostic answer "was that block GC or real CPU?": compare gc.maxMs
94
+ // to maxBlockMs (close → the big block was a GC pause, not code to optimize).
95
+ // Read-only: receiving GC entries does not change GC behavior. The observer is
96
+ // cheap (V8 emits these regardless; the callback only bumps counters). Disable
97
+ // with CTM_TRACK_GC=0 if the observer itself is ever suspected.
98
+ let _gcCount = 0;
99
+ let _gcMs = 0;
100
+ let _gcMaxMs = 0;
101
+ const _gcByKind = new Map(); // kind -> { count, ms, maxMs }
102
+ // performance GC entry `kind` flags (perf_hooks NODE_PERFORMANCE_GC_*).
103
+ const _GC_KIND = { 1: 'minor', 2: 'major', 4: 'incremental', 8: 'weakcb' };
104
+ let _gcObserver = null;
105
+ function _startGcObserver() {
106
+ if (_gcObserver || process.env.CTM_TRACK_GC === '0') return;
107
+ try {
108
+ const { PerformanceObserver } = require('perf_hooks');
109
+ _gcObserver = new PerformanceObserver((list) => {
110
+ for (const entry of list.getEntries()) {
111
+ const ms = Number(entry.duration) || 0;
112
+ _gcCount += 1;
113
+ _gcMs += ms;
114
+ if (ms > _gcMaxMs) _gcMaxMs = ms;
115
+ const flag = entry.detail ? entry.detail.kind : entry.kind;
116
+ const kind = _GC_KIND[flag] || 'gc';
117
+ const e = _gcByKind.get(kind) || { count: 0, ms: 0, maxMs: 0 };
118
+ e.count += 1; e.ms += ms; if (ms > e.maxMs) e.maxMs = ms;
119
+ _gcByKind.set(kind, e);
120
+ }
121
+ });
122
+ _gcObserver.observe({ entryTypes: ['gc'], buffered: false });
123
+ if (typeof _gcObserver.unref === 'function') _gcObserver.unref();
124
+ } catch { _gcObserver = null; }
125
+ }
126
+ _startGcObserver();
127
+ function recordEventLoopBlock(name, blockedMs) {
128
+ const ms = Math.max(0, Number(blockedMs) || 0);
129
+ if (ms <= 0) return;
130
+ if (ms >= _SLEEP_BLOCK_MS) { _sleepCount += 1; _sleepMs += ms; return; }
131
+ _blockCount += 1;
132
+ _blockSumMs += ms;
133
+ if (ms > _blockMaxMs) _blockMaxMs = ms;
134
+ const key = String(name || '(unknown)');
135
+ if (key.includes('(unknown)')) _blockUnknownMs += ms;
136
+ const e = _blockByOp.get(key) || { count: 0, sumMs: 0, maxMs: 0 };
137
+ e.count += 1; e.sumMs += ms; if (ms > e.maxMs) e.maxMs = ms;
138
+ _blockByOp.set(key, e);
139
+ }
140
+ function getEventLoopStats({ reset = false } = {}) {
141
+ const now = Date.now();
142
+ const windowMs = Math.max(1, now - _blockSince);
143
+ const topOps = Array.from(_blockByOp.entries())
144
+ .map(([name, e]) => ({ name, count: e.count, sumMs: e.sumMs, maxMs: e.maxMs }))
145
+ .sort((a, b) => b.sumMs - a.sumMs)
146
+ .slice(0, 15);
147
+ const gcByKind = Array.from(_gcByKind.entries())
148
+ .map(([kind, e]) => ({ kind, count: e.count, ms: Math.round(e.ms), maxMs: Math.round(e.maxMs) }))
149
+ .sort((a, b) => b.ms - a.ms);
150
+ const snap = {
151
+ windowMs,
152
+ blocks: _blockCount,
153
+ totalBlockedMs: _blockSumMs,
154
+ maxBlockMs: _blockMaxMs,
155
+ busyPct: Math.round(1000 * _blockSumMs / windowMs) / 10, // % of wall-clock spent in >threshold blocks
156
+ unknownMs: _blockUnknownMs,
157
+ unknownSharePct: _blockSumMs > 0 ? Math.round(100 * _blockUnknownMs / _blockSumMs) : 0,
158
+ suspectedSleep: { count: _sleepCount, ms: _sleepMs },
159
+ topOps,
160
+ // GC pauses over the same window. Compare gc.maxMs to maxBlockMs: if they're
161
+ // close, the worst block was a GC pause (heap pressure), not a sync op to
162
+ // optimize. enabled:false means the observer was turned off (CTM_TRACK_GC=0).
163
+ gc: {
164
+ enabled: !!_gcObserver,
165
+ count: _gcCount,
166
+ ms: Math.round(_gcMs),
167
+ maxMs: Math.round(_gcMaxMs),
168
+ byKind: gcByKind,
169
+ },
170
+ };
171
+ if (reset) {
172
+ _blockSince = now; _blockCount = 0; _blockSumMs = 0; _blockMaxMs = 0;
173
+ _blockUnknownMs = 0; _sleepCount = 0; _sleepMs = 0; _blockByOp.clear();
174
+ _gcCount = 0; _gcMs = 0; _gcMaxMs = 0; _gcByKind.clear();
175
+ }
176
+ return snap;
177
+ }
178
+
179
+ function getOp() {
180
+ if (_current) return _current;
181
+ if (_lastCompleted && Date.now() - _lastCompleted.end <= 1000) {
182
+ return { name: `${_lastCompleted.name} (last completed)`, start: _lastCompleted.start };
183
+ }
184
+ return null;
185
+ }
18
186
 
19
187
  function eventLoopBlockMs(deltaMs, intervalMs = 100) {
20
188
  return Math.max(0, Number(deltaMs || 0) - Number(intervalMs || 0));
@@ -44,17 +212,27 @@ function wrap(name, fn) {
44
212
  if (_debug && Date.now() - t0 > 100) console.warn(`[perf] ${name} threw after ${Date.now() - t0}ms`);
45
213
  throw e;
46
214
  }
215
+ // Synchronous-prefix duration: the time fn() ran on the loop before returning. For an
216
+ // async function this is the only part that BLOCKED the event loop — the awaited time
217
+ // afterward is I/O wait, not a block. Recording wall-clock for async spans made the
218
+ // recent-spans trail over-attribute (e.g. an async health tick showed "1243ms" that was
219
+ // mostly network wait, not a freeze). Record the sync prefix so the trail is faithful.
220
+ const syncDur = Date.now() - t0;
47
221
  // Promise/thenable: the synchronous prefix is done. Clear the tag now so
48
222
  // unrelated timers do not inherit a stale "active op" while this awaits I/O.
49
223
  if (result && typeof result.then === 'function') {
50
224
  _current = prev;
225
+ _recordSpan(name, syncDur); // only the sync prefix counts as a loop block
51
226
  return result.finally(() => {
52
- const dur = Date.now() - t0;
53
- if (_debug && dur > 100) console.warn(`[perf] ${name} took ${dur}ms`);
227
+ const dur = Date.now() - t0; // wall-clock — for the "(last completed)" attribution only
228
+ if (dur > 100) _lastCompleted = { name, start: t0, end: Date.now(), dur };
229
+ if (_debug && dur > 100) console.warn(`[perf] ${name} took ${dur}ms (incl await)`);
54
230
  });
55
231
  }
56
- // Sync: restore immediately and pass through.
57
- const dur = Date.now() - t0;
232
+ // Sync: restore immediately and pass through. The whole call blocked the loop.
233
+ const dur = syncDur;
234
+ if (dur > 100) _lastCompleted = { name, start: t0, end: Date.now(), dur };
235
+ _recordSpan(name, dur);
58
236
  _current = prev;
59
237
  if (_debug && dur > 100) console.warn(`[perf] ${name} took ${dur}ms`);
60
238
  return result;
@@ -64,4 +242,62 @@ function wrap(name, fn) {
64
242
  // Back-compat alias — both behave identically now (smart sync/async detection).
65
243
  const wrapAsync = wrap;
66
244
 
67
- module.exports = { eventLoopBlockMs, getOp, shouldReportLag, wrap, wrapAsync };
245
+ /**
246
+ * Run a synchronous block as a named span and return its result.
247
+ *
248
+ * Why this exists: `wrap()` tags an async function only for its synchronous
249
+ * prefix, then clears the tag at the first `await`. So a heavy synchronous
250
+ * CHUNK that runs *after* an await (a per-row DB loop, a big JSON.parse) is
251
+ * invisible — the event-loop-lag detector fires in a later timer tick and sees
252
+ * `(unknown)`. Wrapping that chunk with `runSync('name', () => {...})` records
253
+ * it into `_lastCompleted`, so `getOp()` can attribute the block within 1s.
254
+ * Use around whole chunks, not per loop iteration (one closure alloc per call).
255
+ */
256
+ function runSync(name, fn) {
257
+ return wrap(name, fn)();
258
+ }
259
+
260
+ // Default freeze-probe threshold (ms). A single synchronous span over this bound
261
+ // is a user-perceptible stall on a single-threaded server. Overridable per call.
262
+ const FREEZE_PROBE_MS = (() => {
263
+ const n = Number(process.env.CTM_FREEZE_PROBE_MS);
264
+ return Number.isFinite(n) && n > 0 ? n : 150;
265
+ })();
266
+
267
+ /**
268
+ * Run a synchronous block as a named span AND emit a one-line `[freeze-probe]`
269
+ * diagnostic when it exceeds a threshold. Builds on runSync() (so the span is
270
+ * still attributed to getOp()), but additionally logs WHY the loop froze with
271
+ * caller-supplied context — turning the next freeze into a self-describing log
272
+ * entry instead of an anonymous `(unknown)` lag block.
273
+ *
274
+ * @param {string} name span name (also the freeze-probe `where`)
275
+ * @param {() => any} fn the synchronous work
276
+ * @param {object} [opts]
277
+ * @param {number} [opts.thresholdMs] override the default threshold
278
+ * @param {() => object} [opts.context] lazily-built context object, only invoked
279
+ * on the slow path (so the common fast path pays nothing). Keys are logged
280
+ * as `k=v` pairs.
281
+ */
282
+ function runSyncProbed(name, fn, opts = {}) {
283
+ const t0 = Date.now();
284
+ try {
285
+ return wrap(name, fn)();
286
+ } finally {
287
+ const dur = Date.now() - t0;
288
+ const threshold = Number(opts.thresholdMs) > 0 ? Number(opts.thresholdMs) : FREEZE_PROBE_MS;
289
+ if (dur >= threshold && shouldLogPerf(dur)) {
290
+ let ctxStr = '';
291
+ try {
292
+ const ctx = typeof opts.context === 'function' ? opts.context() : null;
293
+ if (ctx && typeof ctx === 'object') {
294
+ ctxStr = Object.keys(ctx).map((k) => `${k}=${ctx[k]}`).join(' ');
295
+ }
296
+ } catch { /* context build must never break the probe */ }
297
+ // eslint-disable-next-line no-console
298
+ console.warn(`[freeze-probe] ${name} blocked ${dur}ms${ctxStr ? ' ' + ctxStr : ''}`);
299
+ }
300
+ }
301
+ }
302
+
303
+ module.exports = { eventLoopBlockMs, getOp, getRecentSpans, recordEventLoopBlock, getEventLoopStats, shouldReportLag, wrap, wrapAsync, runSync, runSyncProbed, FREEZE_PROBE_MS, perfLogsEnabled, shouldLogPerf, PERF_CRITICAL_MS };
@@ -0,0 +1,76 @@
1
+ 'use strict';
2
+
3
+ // Match a detected approval command against the user's Permission Manager rules
4
+ // (the `perm_rules` table — Claude-syntax "Bash(node:*)" allow/deny entries).
5
+ //
6
+ // These rules otherwise only configure Claude Code's own settings.json, so they
7
+ // don't apply to Codex/other providers and the shadow approver never honored
8
+ // them. This lets the approver respect the user's explicit allow/deny across ALL
9
+ // providers: an allow match → auto-approve (skip the verifier); a deny match →
10
+ // escalate. An allow rule marked always_ask is treated as "ask" (no auto-approve).
11
+
12
+ // Parse a "Tool(spec)" rule into { tool, wildcard|prefix|exact }.
13
+ function parseRule(rule) {
14
+ const raw = String(rule || '').trim();
15
+ const m = raw.match(/^([A-Za-z_][\w-]*)\((.*)\)$/);
16
+ if (!m) {
17
+ // Bare tool name (e.g. "Read") = the whole tool is covered.
18
+ return raw ? { tool: raw, wildcard: true } : null;
19
+ }
20
+ const tool = m[1];
21
+ const spec = m[2].trim();
22
+ if (spec === '' || spec === '*') return { tool, wildcard: true };
23
+ if (spec.endsWith(':*')) return { tool, prefix: spec.slice(0, -2).trim() };
24
+ return { tool, exact: spec };
25
+ }
26
+
27
+ // Reduce a displayed tool header ("⏺ Bash command", "Bash(...)", "Edit") to a
28
+ // bare tool word ("Bash", "Edit").
29
+ function toolOf(toolName) {
30
+ const t = String(toolName || '').replace(/^[^A-Za-z]+/, '').trim();
31
+ return (t.split(/[\s(]/)[0] || '');
32
+ }
33
+
34
+ // Does `command` fall under a parsed rule's spec? Prefix matches require a word
35
+ // boundary so "Bash(node:*)" does not match "nodejs ...".
36
+ function commandMatchesSpec(command, parsed) {
37
+ if (!parsed) return false;
38
+ if (parsed.wildcard) return true;
39
+ const cmd = String(command || '').trim();
40
+ if (parsed.exact != null) return cmd === parsed.exact;
41
+ const p = parsed.prefix;
42
+ if (!p) return false;
43
+ if (cmd === p) return true;
44
+ if (cmd.startsWith(p)) {
45
+ // Boundary: the next char must not continue a word, so "node" does not match
46
+ // "nodejs" but "node -e require" still matches "node -e require(...)".
47
+ const next = cmd.charAt(p.length);
48
+ return next === '' || !/[A-Za-z0-9_]/.test(next);
49
+ }
50
+ return false;
51
+ }
52
+
53
+ // Evaluate a command against the user's perm_rules.
54
+ // Returns { action: 'deny', rule } | { action: 'allow', rule } | null.
55
+ // Deny wins over allow. allow rules with always_ask are skipped (the user wants
56
+ // to keep being asked).
57
+ function matchPermission({ toolName, command } = {}, rules = []) {
58
+ const tool = toolOf(toolName);
59
+ const looksBash = /^bash$/i.test(tool) || /\bbash\b/i.test(String(toolName || ''));
60
+ let allowHit = null;
61
+ for (const r of rules || []) {
62
+ const parsed = parseRule(r.rule);
63
+ if (!parsed) continue;
64
+ const ruleTool = String(parsed.tool || '').toLowerCase();
65
+ const toolMatch = ruleTool === tool.toLowerCase() || (looksBash && ruleTool === 'bash');
66
+ if (!toolMatch) continue;
67
+ if (!commandMatchesSpec(command, parsed)) continue;
68
+ if (r.list_type === 'deny') return { action: 'deny', rule: r.rule };
69
+ if (r.list_type === 'allow' && !(r.always_ask)) {
70
+ if (!allowHit) allowHit = { action: 'allow', rule: r.rule };
71
+ }
72
+ }
73
+ return allowHit;
74
+ }
75
+
76
+ module.exports = { parseRule, toolOf, commandMatchesSpec, matchPermission };
@@ -19,12 +19,26 @@ function readJsonFile(filePath) {
19
19
  }
20
20
  }
21
21
 
22
+ async function readJsonFileAsync(filePath) {
23
+ try {
24
+ return JSON.parse(await fs.promises.readFile(filePath, 'utf8'));
25
+ } catch {
26
+ return {};
27
+ }
28
+ }
29
+
22
30
  function writeJsonFile(filePath, data) {
23
31
  const dir = path.dirname(filePath);
24
32
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
25
33
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
26
34
  }
27
35
 
36
+ async function writeJsonFileAsync(filePath, data) {
37
+ const dir = path.dirname(filePath);
38
+ await fs.promises.mkdir(dir, { recursive: true });
39
+ await fs.promises.writeFile(filePath, JSON.stringify(data, null, 2));
40
+ }
41
+
28
42
  function getProjectSettingsPath(projectPath) {
29
43
  return path.join(projectPath, '.claude', 'settings.local.json');
30
44
  }
@@ -35,6 +49,21 @@ function isValidPermRule(rule) {
35
49
  return true;
36
50
  }
37
51
 
52
+ function pushSettingsRules(rules, settings, { scope, project }) {
53
+ const permissions = settings && settings.permissions || {};
54
+ for (const tool of permissions.allow || []) {
55
+ if (!isValidPermRule(tool)) {
56
+ console.warn(` Skipping invalid perm rule: ${tool}`);
57
+ continue;
58
+ }
59
+ rules.push({ rule: tool, listType: 'allow', scope, project });
60
+ }
61
+ for (const tool of permissions.deny || []) {
62
+ if (!isValidPermRule(tool)) continue;
63
+ rules.push({ rule: tool, listType: 'deny', scope, project });
64
+ }
65
+ }
66
+
38
67
  function getSettingsAllowList(settingsPath) {
39
68
  const settings = readJsonFile(settingsPath);
40
69
  return (settings.permissions && settings.permissions.allow) || [];
@@ -50,35 +79,40 @@ function collectJsonRules({ homeDir = process.env.HOME } = {}) {
50
79
  const globalSettingsPath = getGlobalSettingsPath(homeDir);
51
80
  const claudeJsonPath = getClaudeJsonPath(homeDir);
52
81
 
53
- for (const tool of getSettingsAllowList(globalSettingsPath)) {
54
- if (!isValidPermRule(tool)) {
55
- console.warn(` Skipping invalid perm rule: ${tool}`);
56
- continue;
57
- }
58
- rules.push({ rule: tool, listType: 'allow', scope: 'global', project: '__global__' });
59
- }
60
- for (const tool of getSettingsDenyList(globalSettingsPath)) {
61
- if (!isValidPermRule(tool)) continue;
62
- rules.push({ rule: tool, listType: 'deny', scope: 'global', project: '__global__' });
63
- }
82
+ pushSettingsRules(rules, readJsonFile(globalSettingsPath), { scope: 'global', project: '__global__' });
64
83
 
65
84
  const claudeJson = readJsonFile(claudeJsonPath);
66
85
  const projectPaths = Object.keys(claudeJson.projects || {});
67
86
  for (const projectPath of projectPaths) {
68
87
  const settingsPath = getProjectSettingsPath(projectPath);
69
- for (const tool of getSettingsAllowList(settingsPath)) {
70
- if (!isValidPermRule(tool)) {
71
- console.warn(` Skipping invalid perm rule: ${tool}`);
72
- continue;
73
- }
74
- rules.push({ rule: tool, listType: 'allow', scope: 'project', project: projectPath });
75
- }
76
- for (const tool of getSettingsDenyList(settingsPath)) {
88
+ pushSettingsRules(rules, readJsonFile(settingsPath), { scope: 'project', project: projectPath });
89
+ }
90
+
91
+ for (const [projectPath, projectData] of Object.entries(claudeJson.projects || {})) {
92
+ const allowedTools = projectData.allowedTools || [];
93
+ for (const tool of allowedTools) {
77
94
  if (!isValidPermRule(tool)) continue;
78
- rules.push({ rule: tool, listType: 'deny', scope: 'project', project: projectPath });
95
+ rules.push({ rule: tool, listType: 'allow', scope: 'project', project: projectPath });
79
96
  }
80
97
  }
81
98
 
99
+ return rules;
100
+ }
101
+
102
+ async function collectJsonRulesAsync({ homeDir = process.env.HOME } = {}) {
103
+ const rules = [];
104
+ const globalSettingsPath = getGlobalSettingsPath(homeDir);
105
+ const claudeJsonPath = getClaudeJsonPath(homeDir);
106
+
107
+ pushSettingsRules(rules, await readJsonFileAsync(globalSettingsPath), { scope: 'global', project: '__global__' });
108
+
109
+ const claudeJson = await readJsonFileAsync(claudeJsonPath);
110
+ const projectPaths = Object.keys(claudeJson.projects || {});
111
+ for (const projectPath of projectPaths) {
112
+ const settingsPath = getProjectSettingsPath(projectPath);
113
+ pushSettingsRules(rules, await readJsonFileAsync(settingsPath), { scope: 'project', project: projectPath });
114
+ }
115
+
82
116
  for (const [projectPath, projectData] of Object.entries(claudeJson.projects || {})) {
83
117
  const allowedTools = projectData.allowedTools || [];
84
118
  for (const tool of allowedTools) {
@@ -101,6 +135,32 @@ function mergeJsonRulesIntoDb(dbApi, options = {}) {
101
135
  return imported;
102
136
  }
103
137
 
138
+ async function mergeJsonRulesIntoDbAsync(dbApi, options = {}) {
139
+ const jsonRules = await collectJsonRulesAsync(options);
140
+ return applyJsonRulesToDb(dbApi, jsonRules, { mergeOnly: true }).imported;
141
+ }
142
+
143
+ function applyJsonRulesToDb(dbApi, jsonRules, { mergeOnly = false } = {}) {
144
+ const rules = Array.isArray(jsonRules) ? jsonRules : [];
145
+ const existing = dbApi.listPermRules();
146
+ if (existing.length > 0 || mergeOnly) {
147
+ const before = existing.length;
148
+ for (const rule of rules) dbApi.addPermRule(rule);
149
+ const imported = dbApi.listPermRules().length - before;
150
+ if (imported > 0) {
151
+ console.log(` Merged ${imported} new permission rules from JSON files into DB`);
152
+ }
153
+ return { imported, mode: 'merge' };
154
+ }
155
+
156
+ if (rules.length > 0) {
157
+ dbApi.bulkSetPermRules(rules);
158
+ console.log(` Imported ${rules.length} permission rules from JSON files into DB`);
159
+ return { imported: rules.length, mode: 'bulk' };
160
+ }
161
+ return { imported: 0, mode: 'empty' };
162
+ }
163
+
104
164
  function syncDbToJsonFiles(dbApi, { homeDir = process.env.HOME } = {}) {
105
165
  for (const rule of collectJsonRules({ homeDir })) dbApi.addPermRule(rule);
106
166
  for (const row of dbApi.listPermRules()) {
@@ -132,6 +192,37 @@ function syncDbToJsonFiles(dbApi, { homeDir = process.env.HOME } = {}) {
132
192
  }
133
193
  }
134
194
 
195
+ async function syncDbToJsonFilesAsync(dbApi, { homeDir = process.env.HOME } = {}) {
196
+ for (const rule of await collectJsonRulesAsync({ homeDir })) dbApi.addPermRule(rule);
197
+ for (const row of dbApi.listPermRules()) {
198
+ if (!isValidPermRule(row.rule)) {
199
+ console.warn(` Removing invalid perm rule from DB: ${row.rule}`);
200
+ dbApi.removePermRule({ rule: row.rule, listType: row.list_type, project: row.project });
201
+ }
202
+ }
203
+
204
+ const byProject = dbApi.getPermRulesByProject();
205
+ const globalSettingsPath = getGlobalSettingsPath(homeDir);
206
+ const globalRules = byProject.__global__ || { allow: [], deny: [] };
207
+ const globalSettings = await readJsonFileAsync(globalSettingsPath);
208
+ if (!globalSettings.permissions) globalSettings.permissions = {};
209
+ globalSettings.permissions.allow = globalRules.allow.filter(isValidPermRule);
210
+ globalSettings.permissions.deny = globalRules.deny.filter(isValidPermRule);
211
+ await writeJsonFileAsync(globalSettingsPath, globalSettings);
212
+
213
+ for (const [project, lists] of Object.entries(byProject)) {
214
+ if (project === '__global__') continue;
215
+ const settingsPath = getProjectSettingsPath(project);
216
+ try {
217
+ const settings = await readJsonFileAsync(settingsPath);
218
+ if (!settings.permissions) settings.permissions = {};
219
+ settings.permissions.allow = lists.allow.filter(isValidPermRule);
220
+ settings.permissions.deny = lists.deny.filter(isValidPermRule);
221
+ await writeJsonFileAsync(settingsPath, settings);
222
+ } catch {}
223
+ }
224
+ }
225
+
135
226
  function importPermissionsToDb(dbApi, { syncBack = true, homeDir = process.env.HOME } = {}) {
136
227
  const existing = dbApi.listPermRules();
137
228
  if (existing.length > 0) {
@@ -148,15 +239,37 @@ function importPermissionsToDb(dbApi, { syncBack = true, homeDir = process.env.H
148
239
  }
149
240
  }
150
241
 
242
+ async function importPermissionsToDbAsync(dbApi, {
243
+ syncBack = true,
244
+ homeDir = process.env.HOME,
245
+ applyWith = null,
246
+ applyRulesWith = null,
247
+ } = {}) {
248
+ const rules = await collectJsonRulesAsync({ homeDir });
249
+ const apply = () => applyJsonRulesToDb(dbApi, rules);
250
+ const result = typeof applyRulesWith === 'function'
251
+ ? await applyRulesWith(rules, apply)
252
+ : (typeof applyWith === 'function' ? await applyWith(apply) : apply());
253
+ if (syncBack) await syncDbToJsonFilesAsync(dbApi, { homeDir });
254
+ return result;
255
+ }
256
+
151
257
  module.exports = {
258
+ applyJsonRulesToDb,
152
259
  collectJsonRules,
260
+ collectJsonRulesAsync,
153
261
  getClaudeJsonPath,
154
262
  getGlobalSettingsPath,
155
263
  getProjectSettingsPath,
156
264
  importPermissionsToDb,
265
+ importPermissionsToDbAsync,
157
266
  isValidPermRule,
158
267
  mergeJsonRulesIntoDb,
268
+ mergeJsonRulesIntoDbAsync,
159
269
  readJsonFile,
270
+ readJsonFileAsync,
160
271
  syncDbToJsonFiles,
272
+ syncDbToJsonFilesAsync,
161
273
  writeJsonFile,
274
+ writeJsonFileAsync,
162
275
  };