codetrellis 0.0.0-stage → 0.2.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 (449) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +27 -2
  3. package/bin/codetrellis.mjs +4 -0
  4. package/package.json +43 -4
  5. package/reader/material-reader.mjs +130346 -0
  6. package/resources/tree-sitter/README.md +150 -0
  7. package/resources/tree-sitter/tree-sitter-c-sharp.wasm +0 -0
  8. package/resources/tree-sitter/tree-sitter-go.wasm +0 -0
  9. package/resources/tree-sitter/tree-sitter-java.wasm +0 -0
  10. package/resources/tree-sitter/tree-sitter-javascript.wasm +0 -0
  11. package/resources/tree-sitter/tree-sitter-kotlin.wasm +0 -0
  12. package/resources/tree-sitter/tree-sitter-php.wasm +0 -0
  13. package/resources/tree-sitter/tree-sitter-python.wasm +0 -0
  14. package/resources/tree-sitter/tree-sitter-ruby.wasm +0 -0
  15. package/resources/tree-sitter/tree-sitter-rust.wasm +0 -0
  16. package/resources/tree-sitter/tree-sitter-swift.wasm +0 -0
  17. package/resources/tree-sitter/tree-sitter-tsx.wasm +0 -0
  18. package/resources/tree-sitter/tree-sitter-typescript.wasm +0 -0
  19. package/resources/tree-sitter/tree-sitter.wasm +0 -0
  20. package/src/backend/agent/claude-code-watcher.js +270 -0
  21. package/src/backend/index.js +8 -0
  22. package/src/backend/lib/sha256-file.js +39 -0
  23. package/src/backend/lib/zip-entries.js +98 -0
  24. package/src/backend/lifecycle.js +102 -0
  25. package/src/backend/mcp/binding-headers.js +54 -0
  26. package/src/backend/mcp/client-identity.js +44 -0
  27. package/src/backend/mcp/connector/command.js +110 -0
  28. package/src/backend/mcp/connector/core.js +278 -0
  29. package/src/backend/mcp/connector/files.js +121 -0
  30. package/src/backend/mcp/connector/hook.js +284 -0
  31. package/src/backend/mcp/connector/main.js +126 -0
  32. package/src/backend/mcp/connector/sse-upstream.js +152 -0
  33. package/src/backend/mcp/helpers.js +43 -0
  34. package/src/backend/mcp/prompt-builders.js +104 -0
  35. package/src/backend/mcp/resources.js +197 -0
  36. package/src/backend/mcp/server.js +813 -0
  37. package/src/backend/mcp/skill-guide.js +1528 -0
  38. package/src/backend/mcp/tools/architecture-tools.js +273 -0
  39. package/src/backend/mcp/tools/audio-tools.js +117 -0
  40. package/src/backend/mcp/tools/awareness-tools.js +623 -0
  41. package/src/backend/mcp/tools/budget-tools.js +185 -0
  42. package/src/backend/mcp/tools/channel-tools.js +205 -0
  43. package/src/backend/mcp/tools/contribution-tools.js +181 -0
  44. package/src/backend/mcp/tools/drift-tools.js +252 -0
  45. package/src/backend/mcp/tools/git-tools.js +316 -0
  46. package/src/backend/mcp/tools/governance-tools.js +175 -0
  47. package/src/backend/mcp/tools/graph-tools.js +181 -0
  48. package/src/backend/mcp/tools/intake-tools.js +244 -0
  49. package/src/backend/mcp/tools/mobile-tools.js +118 -0
  50. package/src/backend/mcp/tools/peer-tools.js +330 -0
  51. package/src/backend/mcp/tools/plan-item-tools.js +1601 -0
  52. package/src/backend/mcp/tools/plan-tools.js +680 -0
  53. package/src/backend/mcp/tools/presence-tools.js +185 -0
  54. package/src/backend/mcp/tools/project-config-tools.js +150 -0
  55. package/src/backend/mcp/tools/review-tools.js +172 -0
  56. package/src/backend/mcp/tools/session-tools.js +554 -0
  57. package/src/backend/mcp/tools/system-docs-tools.js +168 -0
  58. package/src/backend/mcp/tools/terminal-tools.js +186 -0
  59. package/src/backend/mcp/tools/test-tools.js +117 -0
  60. package/src/backend/mcp/tools/ui-tools.js +356 -0
  61. package/src/backend/mcp/types.js +15 -0
  62. package/src/backend/middleware/local-auth.js +116 -0
  63. package/src/backend/server.js +5951 -0
  64. package/src/backend/services/agent-event-log.js +350 -0
  65. package/src/backend/services/architecture-rule.js +584 -0
  66. package/src/backend/services/architecture-rules.js +275 -0
  67. package/src/backend/services/artefact-content-service.js +181 -0
  68. package/src/backend/services/artefact-service.js +305 -0
  69. package/src/backend/services/artefact-watcher.js +167 -0
  70. package/src/backend/services/ast-parser.js +327 -0
  71. package/src/backend/services/audio-buffer-service.js +152 -0
  72. package/src/backend/services/awareness-notices.js +139 -0
  73. package/src/backend/services/awareness-replies.js +194 -0
  74. package/src/backend/services/awareness-service.js +344 -0
  75. package/src/backend/services/awareness-signals.js +267 -0
  76. package/src/backend/services/baseline-store.js +83 -0
  77. package/src/backend/services/branch-workstreams.js +341 -0
  78. package/src/backend/services/breakpoint-service.js +584 -0
  79. package/src/backend/services/brief-service.js +299 -0
  80. package/src/backend/services/budget-service.js +528 -0
  81. package/src/backend/services/callsites/base.js +15 -0
  82. package/src/backend/services/callsites/csharp.js +148 -0
  83. package/src/backend/services/callsites/go.js +163 -0
  84. package/src/backend/services/callsites/index.js +53 -0
  85. package/src/backend/services/callsites/kotlin.js +123 -0
  86. package/src/backend/services/callsites/process-env.js +125 -0
  87. package/src/backend/services/callsites/python.js +91 -0
  88. package/src/backend/services/callsites/ruby.js +164 -0
  89. package/src/backend/services/callsites/shared.js +198 -0
  90. package/src/backend/services/callsites/swift.js +115 -0
  91. package/src/backend/services/callsites/typescript.js +87 -0
  92. package/src/backend/services/capability-token.js +108 -0
  93. package/src/backend/services/change-check.js +156 -0
  94. package/src/backend/services/channel-dispatcher-service.js +235 -0
  95. package/src/backend/services/channel-event-file-service.js +125 -0
  96. package/src/backend/services/channel-event-service.js +297 -0
  97. package/src/backend/services/check-runs.js +177 -0
  98. package/src/backend/services/checkout-identity.js +73 -0
  99. package/src/backend/services/claude-code-parallel.js +221 -0
  100. package/src/backend/services/claude-desktop-config.js +186 -0
  101. package/src/backend/services/clock.js +57 -0
  102. package/src/backend/services/cloud-files.js +191 -0
  103. package/src/backend/services/coalesce.js +50 -0
  104. package/src/backend/services/code-breakpoints.js +291 -0
  105. package/src/backend/services/comment-service.js +168 -0
  106. package/src/backend/services/commit-attribution.js +134 -0
  107. package/src/backend/services/commit-edges.js +99 -0
  108. package/src/backend/services/confined-fs.js +241 -0
  109. package/src/backend/services/conformity-gate.js +132 -0
  110. package/src/backend/services/contribution-service.js +409 -0
  111. package/src/backend/services/coverage-service.js +173 -0
  112. package/src/backend/services/criteria-service.js +721 -0
  113. package/src/backend/services/criterion-checks.js +387 -0
  114. package/src/backend/services/criterion-loop-service.js +411 -0
  115. package/src/backend/services/cross-system-service.js +231 -0
  116. package/src/backend/services/database.js +916 -0
  117. package/src/backend/services/db-schema.js +1358 -0
  118. package/src/backend/services/deviation-service.js +344 -0
  119. package/src/backend/services/diff-engine.js +168 -0
  120. package/src/backend/services/evidence.js +363 -0
  121. package/src/backend/services/external-intake-service.js +269 -0
  122. package/src/backend/services/external-pointer-service.js +195 -0
  123. package/src/backend/services/external-refs-service.js +214 -0
  124. package/src/backend/services/file-history.js +158 -0
  125. package/src/backend/services/file-watcher.js +193 -0
  126. package/src/backend/services/folder-requests.js +118 -0
  127. package/src/backend/services/freeze-service.js +189 -0
  128. package/src/backend/services/gemini-cli-hook.js +172 -0
  129. package/src/backend/services/git-activity-service.js +325 -0
  130. package/src/backend/services/git-blobs.js +70 -0
  131. package/src/backend/services/git-branches.js +357 -0
  132. package/src/backend/services/git-checkout.js +153 -0
  133. package/src/backend/services/git-commit-service.js +91 -0
  134. package/src/backend/services/git-env.js +121 -0
  135. package/src/backend/services/git-identity.js +84 -0
  136. package/src/backend/services/git-refs.js +386 -0
  137. package/src/backend/services/git-safety.js +58 -0
  138. package/src/backend/services/grant-guard.js +67 -0
  139. package/src/backend/services/html-view-policy.js +135 -0
  140. package/src/backend/services/human-decision.js +55 -0
  141. package/src/backend/services/importers.js +159 -0
  142. package/src/backend/services/intent-service.js +105 -0
  143. package/src/backend/services/ipc-dispatcher.js +164 -0
  144. package/src/backend/services/item-git-state.js +208 -0
  145. package/src/backend/services/line-changes.js +232 -0
  146. package/src/backend/services/line-history.js +174 -0
  147. package/src/backend/services/local-api-changes.js +40 -0
  148. package/src/backend/services/logger.js +217 -0
  149. package/src/backend/services/material-footprints.js +203 -0
  150. package/src/backend/services/material-place.js +82 -0
  151. package/src/backend/services/material-reader/child.js +4 -0
  152. package/src/backend/services/material-reader/docx-markdown.js +198 -0
  153. package/src/backend/services/material-reader/fixtures.test-helper.js +169 -0
  154. package/src/backend/services/material-reader/quote.js +44 -0
  155. package/src/backend/services/material-reader/read.js +430 -0
  156. package/src/backend/services/material-reader/reader-host.js +202 -0
  157. package/src/backend/services/material-signals.js +141 -0
  158. package/src/backend/services/mcp-capabilities.js +390 -0
  159. package/src/backend/services/mdns-service.js +304 -0
  160. package/src/backend/services/mobile-api-server.js +281 -0
  161. package/src/backend/services/mobile-approvals.js +306 -0
  162. package/src/backend/services/mobile-awareness.js +180 -0
  163. package/src/backend/services/mobile-breakpoints.js +115 -0
  164. package/src/backend/services/mobile-budget.js +98 -0
  165. package/src/backend/services/mobile-freeze.js +106 -0
  166. package/src/backend/services/mobile-proposals.js +100 -0
  167. package/src/backend/services/mobile-rpc-service.js +1289 -0
  168. package/src/backend/services/mobile-workstreams.js +160 -0
  169. package/src/backend/services/monorepo-detector.js +131 -0
  170. package/src/backend/services/other-work.js +45 -0
  171. package/src/backend/services/pack-seal.js +129 -0
  172. package/src/backend/services/paired-device-service.js +192 -0
  173. package/src/backend/services/pairing-server.js +323 -0
  174. package/src/backend/services/pairing-service.js +200 -0
  175. package/src/backend/services/pantry-resolution-service.js +166 -0
  176. package/src/backend/services/parsers/base.js +124 -0
  177. package/src/backend/services/parsers/csharp.js +224 -0
  178. package/src/backend/services/parsers/go.js +180 -0
  179. package/src/backend/services/parsers/index.js +101 -0
  180. package/src/backend/services/parsers/java.js +103 -0
  181. package/src/backend/services/parsers/kotlin.js +252 -0
  182. package/src/backend/services/parsers/php.js +113 -0
  183. package/src/backend/services/parsers/python.js +189 -0
  184. package/src/backend/services/parsers/ruby.js +212 -0
  185. package/src/backend/services/parsers/rust.js +97 -0
  186. package/src/backend/services/parsers/swift.js +221 -0
  187. package/src/backend/services/parsers/typescript.js +235 -0
  188. package/src/backend/services/pattern-scan.js +65 -0
  189. package/src/backend/services/patterns.js +281 -0
  190. package/src/backend/services/peer-audit-service.js +123 -0
  191. package/src/backend/services/peer-auth.js +153 -0
  192. package/src/backend/services/peer-capabilities.js +209 -0
  193. package/src/backend/services/peer-connection-service.js +450 -0
  194. package/src/backend/services/peer-grants.js +33 -0
  195. package/src/backend/services/persistence.js +116 -0
  196. package/src/backend/services/personal-sync-service.js +232 -0
  197. package/src/backend/services/pipeline-approvals.js +72 -0
  198. package/src/backend/services/pipeline.js +232 -0
  199. package/src/backend/services/plan-arrivals.js +68 -0
  200. package/src/backend/services/plan-changes-service.js +273 -0
  201. package/src/backend/services/plan-conflict-service.js +288 -0
  202. package/src/backend/services/plan-dependencies.js +91 -0
  203. package/src/backend/services/plan-doc-guard.js +90 -0
  204. package/src/backend/services/plan-documents-service.js +275 -0
  205. package/src/backend/services/plan-event-service.js +154 -0
  206. package/src/backend/services/plan-file-refs.js +106 -0
  207. package/src/backend/services/plan-file-service.js +1564 -0
  208. package/src/backend/services/plan-history-service.js +156 -0
  209. package/src/backend/services/plan-import-service.js +261 -0
  210. package/src/backend/services/plan-item-service.js +1223 -0
  211. package/src/backend/services/plan-migrate-service.js +395 -0
  212. package/src/backend/services/plan-overlay-service.js +173 -0
  213. package/src/backend/services/plan-phases-service.js +182 -0
  214. package/src/backend/services/plan-progress-service.js +134 -0
  215. package/src/backend/services/plan-review-service.js +262 -0
  216. package/src/backend/services/plan-service.js +682 -0
  217. package/src/backend/services/plan-status.js +113 -0
  218. package/src/backend/services/plan-template-publish-service.js +217 -0
  219. package/src/backend/services/plan-templates-service.js +242 -0
  220. package/src/backend/services/plan-templates.js +1164 -0
  221. package/src/backend/services/planned-overlap-actions.js +200 -0
  222. package/src/backend/services/plans-home.js +224 -0
  223. package/src/backend/services/play-forward.js +256 -0
  224. package/src/backend/services/playback-service.js +117 -0
  225. package/src/backend/services/power-service.js +167 -0
  226. package/src/backend/services/power-signals.js +81 -0
  227. package/src/backend/services/pr-draft-service.js +142 -0
  228. package/src/backend/services/presence-service.js +122 -0
  229. package/src/backend/services/pricing.js +60 -0
  230. package/src/backend/services/project-config-service.js +474 -0
  231. package/src/backend/services/project-scanner.js +184 -0
  232. package/src/backend/services/projection-service.js +69 -0
  233. package/src/backend/services/push-notification-service.js +288 -0
  234. package/src/backend/services/recent-projects-service.js +204 -0
  235. package/src/backend/services/record-chain.js +162 -0
  236. package/src/backend/services/recurrence-rule.js +63 -0
  237. package/src/backend/services/recurring-agent.js +89 -0
  238. package/src/backend/services/recurring-scheduler.js +77 -0
  239. package/src/backend/services/recurring-service.js +276 -0
  240. package/src/backend/services/reference-service.js +169 -0
  241. package/src/backend/services/release-public-key.js +30 -0
  242. package/src/backend/services/release-signature.js +64 -0
  243. package/src/backend/services/remote-audio-service.js +224 -0
  244. package/src/backend/services/remote-interaction-service.js +275 -0
  245. package/src/backend/services/remote-terminal-service.js +324 -0
  246. package/src/backend/services/rendition/child/main.js +76 -0
  247. package/src/backend/services/rendition/engine-host.js +249 -0
  248. package/src/backend/services/rendition/engine-lock.js +39 -0
  249. package/src/backend/services/rendition/engine-lock.json +14 -0
  250. package/src/backend/services/rendition/engine-manifest.js +97 -0
  251. package/src/backend/services/rendition/network-proof.js +73 -0
  252. package/src/backend/services/rendition/rendition-service.js +175 -0
  253. package/src/backend/services/replay-frames.js +387 -0
  254. package/src/backend/services/replay-state.js +201 -0
  255. package/src/backend/services/resolvers/base.js +108 -0
  256. package/src/backend/services/resolvers/csharp.js +109 -0
  257. package/src/backend/services/resolvers/go.js +105 -0
  258. package/src/backend/services/resolvers/index.js +60 -0
  259. package/src/backend/services/resolvers/java.js +63 -0
  260. package/src/backend/services/resolvers/kotlin.js +109 -0
  261. package/src/backend/services/resolvers/php.js +89 -0
  262. package/src/backend/services/resolvers/python.js +154 -0
  263. package/src/backend/services/resolvers/ruby.js +103 -0
  264. package/src/backend/services/resolvers/rust.js +77 -0
  265. package/src/backend/services/resolvers/swift.js +98 -0
  266. package/src/backend/services/resolvers/typescript.js +71 -0
  267. package/src/backend/services/retention.js +54 -0
  268. package/src/backend/services/review-architecture.js +236 -0
  269. package/src/backend/services/review-attest.js +68 -0
  270. package/src/backend/services/review-bundle.js +226 -0
  271. package/src/backend/services/review-graduation.js +93 -0
  272. package/src/backend/services/review-host/bitbucket.js +79 -0
  273. package/src/backend/services/review-host/detect.js +80 -0
  274. package/src/backend/services/review-host/github.js +97 -0
  275. package/src/backend/services/review-host/gitlab.js +77 -0
  276. package/src/backend/services/review-host/host-state.js +103 -0
  277. package/src/backend/services/review-host/http.js +58 -0
  278. package/src/backend/services/review-host/switch.js +120 -0
  279. package/src/backend/services/review-marks.js +72 -0
  280. package/src/backend/services/review-notes.js +169 -0
  281. package/src/backend/services/review-other-work.js +90 -0
  282. package/src/backend/services/review-queue-service.js +147 -0
  283. package/src/backend/services/review-risk.js +127 -0
  284. package/src/backend/services/review-task.js +121 -0
  285. package/src/backend/services/rule-approvals.js +268 -0
  286. package/src/backend/services/rule-baseline.js +134 -0
  287. package/src/backend/services/rule-changes.js +162 -0
  288. package/src/backend/services/rule-preview.js +47 -0
  289. package/src/backend/services/rule-proposals.js +104 -0
  290. package/src/backend/services/rule-scope.js +76 -0
  291. package/src/backend/services/rulebook.js +286 -0
  292. package/src/backend/services/rules-at.js +56 -0
  293. package/src/backend/services/rules-overview.js +88 -0
  294. package/src/backend/services/schema-reconciler.js +250 -0
  295. package/src/backend/services/secret-store.js +119 -0
  296. package/src/backend/services/section-workstreams.js +95 -0
  297. package/src/backend/services/self-write-tracker.js +75 -0
  298. package/src/backend/services/sensor-bridge-service.js +285 -0
  299. package/src/backend/services/session-service.js +192 -0
  300. package/src/backend/services/settings-service.js +324 -0
  301. package/src/backend/services/signal-breakpoints.js +172 -0
  302. package/src/backend/services/signed-approval-record.js +86 -0
  303. package/src/backend/services/signed-approvals.js +244 -0
  304. package/src/backend/services/signoff-pack.js +328 -0
  305. package/src/backend/services/signoff-rows.js +114 -0
  306. package/src/backend/services/skill-arrival-service.js +138 -0
  307. package/src/backend/services/skill-model.js +193 -0
  308. package/src/backend/services/skill-use-service.js +140 -0
  309. package/src/backend/services/skills-service.js +150 -0
  310. package/src/backend/services/snapshot-compare-service.js +397 -0
  311. package/src/backend/services/source-control.js +189 -0
  312. package/src/backend/services/spec-links-service.js +123 -0
  313. package/src/backend/services/spec-proposal-withdraw.js +50 -0
  314. package/src/backend/services/spec-proposals-service.js +470 -0
  315. package/src/backend/services/sql/embedded.js +95 -0
  316. package/src/backend/services/sql/index.js +63 -0
  317. package/src/backend/services/sql/refs.js +357 -0
  318. package/src/backend/services/sql/schema.js +312 -0
  319. package/src/backend/services/sql/tokenizer.js +195 -0
  320. package/src/backend/services/stack-overlaps.js +117 -0
  321. package/src/backend/services/stack-service.js +225 -0
  322. package/src/backend/services/state-sync-service.js +384 -0
  323. package/src/backend/services/stuck-sensor-service.js +200 -0
  324. package/src/backend/services/system-discovery.js +481 -0
  325. package/src/backend/services/system-docs-service.js +701 -0
  326. package/src/backend/services/task-attachments-service.js +340 -0
  327. package/src/backend/services/task-grounding.js +60 -0
  328. package/src/backend/services/task-records/check-run-record.js +146 -0
  329. package/src/backend/services/task-records/check-runs.js +115 -0
  330. package/src/backend/services/task-records/heads.js +88 -0
  331. package/src/backend/services/task-records/material-reads.js +225 -0
  332. package/src/backend/services/task-records/read-record.js +100 -0
  333. package/src/backend/services/task-records/record.js +173 -0
  334. package/src/backend/services/task-records/run-record.js +136 -0
  335. package/src/backend/services/task-records/shared-state.js +548 -0
  336. package/src/backend/services/task-records/signing.js +140 -0
  337. package/src/backend/services/task-records/split-signals.js +52 -0
  338. package/src/backend/services/task-records/test-runs.js +164 -0
  339. package/src/backend/services/task-records/trust.js +243 -0
  340. package/src/backend/services/task-rules.js +81 -0
  341. package/src/backend/services/task-workstreams.js +90 -0
  342. package/src/backend/services/terminal-history-service.js +266 -0
  343. package/src/backend/services/terminal-service.js +267 -0
  344. package/src/backend/services/test-clock.js +53 -0
  345. package/src/backend/services/tests/grounding.js +266 -0
  346. package/src/backend/services/tests/junit.js +104 -0
  347. package/src/backend/services/tests/teammate-runs.js +134 -0
  348. package/src/backend/services/tests/test-results.js +234 -0
  349. package/src/backend/services/tree-watcher.js +204 -0
  350. package/src/backend/services/trellis-service.js +204 -0
  351. package/src/backend/services/trusted-roots.js +186 -0
  352. package/src/backend/services/update-download-service.js +342 -0
  353. package/src/backend/services/update-service.js +266 -0
  354. package/src/backend/services/watch-ignore.js +76 -0
  355. package/src/backend/services/webhook-egress.js +206 -0
  356. package/src/backend/services/webrtc-service.js +413 -0
  357. package/src/backend/services/work-changes.js +59 -0
  358. package/src/backend/services/workstream-binding.js +82 -0
  359. package/src/backend/services/workstream-commits.js +116 -0
  360. package/src/backend/services/workstream-imports.js +177 -0
  361. package/src/backend/services/workstream-service.js +335 -0
  362. package/src/backend/services/workstream-symbols.js +223 -0
  363. package/src/backend/services/workstream-watch-service.js +391 -0
  364. package/src/backend/services/worktree-service.js +235 -0
  365. package/src/cli/agent.js +100 -0
  366. package/src/cli/args.js +201 -0
  367. package/src/cli/conformity.js +106 -0
  368. package/src/cli/desktop.js +91 -0
  369. package/src/cli/main.js +409 -0
  370. package/src/cli/pipeline.js +109 -0
  371. package/src/cli/plan-verbs.js +259 -0
  372. package/src/cli/review-adapters.js +218 -0
  373. package/src/cli/review-output.js +93 -0
  374. package/src/cli/review-post.js +89 -0
  375. package/src/cli/review-sink.js +183 -0
  376. package/src/cli/review-skill.js +44 -0
  377. package/src/cli/review-verify.js +104 -0
  378. package/src/cli/review.js +386 -0
  379. package/src/cli/sarif.js +148 -0
  380. package/src/cli/verbs.js +359 -0
  381. package/src/shared/build-info.js +39 -0
  382. package/src/shared/lib/agent-review.js +199 -0
  383. package/src/shared/lib/agent-turns.js +140 -0
  384. package/src/shared/lib/awareness-digest.js +120 -0
  385. package/src/shared/lib/branch-name.js +45 -0
  386. package/src/shared/lib/breakpoint-words.js +118 -0
  387. package/src/shared/lib/budget-words.js +56 -0
  388. package/src/shared/lib/burst.js +69 -0
  389. package/src/shared/lib/call-entry.js +105 -0
  390. package/src/shared/lib/check-compare.js +42 -0
  391. package/src/shared/lib/check-words.js +216 -0
  392. package/src/shared/lib/csv.js +62 -0
  393. package/src/shared/lib/folder-entry.js +87 -0
  394. package/src/shared/lib/freeze-words.js +60 -0
  395. package/src/shared/lib/fuzzy.js +84 -0
  396. package/src/shared/lib/git-state-words.js +80 -0
  397. package/src/shared/lib/grep-entry.js +86 -0
  398. package/src/shared/lib/grounding-line.js +61 -0
  399. package/src/shared/lib/import-line.js +71 -0
  400. package/src/shared/lib/item-status.js +249 -0
  401. package/src/shared/lib/line-changes.js +53 -0
  402. package/src/shared/lib/locator.js +71 -0
  403. package/src/shared/lib/matcher.js +120 -0
  404. package/src/shared/lib/merge-order.js +65 -0
  405. package/src/shared/lib/open-findings.js +68 -0
  406. package/src/shared/lib/other-work.js +108 -0
  407. package/src/shared/lib/package-entry.js +142 -0
  408. package/src/shared/lib/plan-vocab.js +40 -0
  409. package/src/shared/lib/pptx-xml.js +86 -0
  410. package/src/shared/lib/proposal-words.js +61 -0
  411. package/src/shared/lib/recurrence.js +183 -0
  412. package/src/shared/lib/references.js +72 -0
  413. package/src/shared/lib/rule-pattern.js +56 -0
  414. package/src/shared/lib/sdp-fingerprint.js +115 -0
  415. package/src/shared/lib/sdp-minimal.js +123 -0
  416. package/src/shared/lib/secret-paths.js +34 -0
  417. package/src/shared/lib/signal-words.js +172 -0
  418. package/src/shared/lib/signoff.js +56 -0
  419. package/src/shared/lib/skills-note.js +60 -0
  420. package/src/shared/lib/spec-sections.js +78 -0
  421. package/src/shared/lib/symbol-entry.js +60 -0
  422. package/src/shared/lib/tool-phrasing.js +338 -0
  423. package/src/shared/lib/turn-gap.js +27 -0
  424. package/src/shared/lib/workstream-words.js +38 -0
  425. package/src/shared/lib/xlsx-xml.js +144 -0
  426. package/src/shared/lib/zip-reader.js +99 -0
  427. package/src/shared/types/agent.js +27 -0
  428. package/src/shared/types/architecture-rules.js +30 -0
  429. package/src/shared/types/ast.js +15 -0
  430. package/src/shared/types/breakpoint.js +33 -0
  431. package/src/shared/types/channel.js +41 -0
  432. package/src/shared/types/criteria.js +15 -0
  433. package/src/shared/types/graph.js +15 -0
  434. package/src/shared/types/index.js +53 -0
  435. package/src/shared/types/peer.js +36 -0
  436. package/src/shared/types/pipeline.js +15 -0
  437. package/src/shared/types/plan.js +15 -0
  438. package/src/shared/types/play-forward.js +15 -0
  439. package/src/shared/types/power.js +15 -0
  440. package/src/shared/types/presence.js +15 -0
  441. package/src/shared/types/project-config.js +35 -0
  442. package/src/shared/types/project.js +15 -0
  443. package/src/shared/types/record.js +15 -0
  444. package/src/shared/types/recurring.js +15 -0
  445. package/src/shared/types/review.js +15 -0
  446. package/src/shared/types/settings.js +114 -0
  447. package/src/shared/types/stack.js +15 -0
  448. package/src/shared/types/system-doc.js +15 -0
  449. package/src/shared/types/system.js +15 -0
@@ -0,0 +1,1289 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var mobile_rpc_service_exports = {};
29
+ __export(mobile_rpc_service_exports, {
30
+ mobileConnected: () => mobileConnected,
31
+ navigateMobile: () => navigateMobile,
32
+ requestMobileScreenshot: () => requestMobileScreenshot,
33
+ sendMobileCommand: () => sendMobileCommand,
34
+ startMobileRpc: () => startMobileRpc,
35
+ stopMobileRpc: () => stopMobileRpc
36
+ });
37
+ module.exports = __toCommonJS(mobile_rpc_service_exports);
38
+ var import_types = require("../../shared/types");
39
+ var import_peer_capabilities = require("./peer-capabilities");
40
+ var import_peer_audit_service = require("./peer-audit-service");
41
+ var import_paired_device_service = require("./paired-device-service");
42
+ var import_confined_fs = require("./confined-fs");
43
+ var import_trusted_roots = require("./trusted-roots");
44
+ var import_plan_review_service = require("./plan-review-service");
45
+ var import_review_queue_service = require("./review-queue-service");
46
+ var import_stack_service = require("./stack-service");
47
+ var import_play_forward = require("./play-forward");
48
+ var import_planned_overlap_actions = require("./planned-overlap-actions");
49
+ var import_plan_status = require("./plan-status");
50
+ var import_pr_draft_service = require("./pr-draft-service");
51
+ var import_snapshot_compare_service = require("./snapshot-compare-service");
52
+ var import_webrtc_service = require("./webrtc-service");
53
+ var planService = __toESM(require("./plan-service"));
54
+ var planItemService = __toESM(require("./plan-item-service"));
55
+ var channelEventService = __toESM(require("./channel-event-service"));
56
+ var deviationService = __toESM(require("./deviation-service"));
57
+ var terminalService = __toESM(require("./terminal-service"));
58
+ var recentProjectsService = __toESM(require("./recent-projects-service"));
59
+ var remoteInteractionService = __toESM(require("./remote-interaction-service"));
60
+ var planPhasesService = __toESM(require("./plan-phases-service"));
61
+ var planDocumentsService = __toESM(require("./plan-documents-service"));
62
+ var commentService = __toESM(require("./comment-service"));
63
+ var externalRefsService = __toESM(require("./external-refs-service"));
64
+ var taskAttachmentsService = __toESM(require("./task-attachments-service"));
65
+ var terminalHistoryService = __toESM(require("./terminal-history-service"));
66
+ var import_database = require("./database");
67
+ var import_cross_system_service = require("./cross-system-service");
68
+ var import_diff_engine = require("./diff-engine");
69
+ var import_projection_service = require("./projection-service");
70
+ var import_settings_service = require("./settings-service");
71
+ var import_power_service = require("./power-service");
72
+ var systemDocsService = __toESM(require("./system-docs-service"));
73
+ var planTemplates = __toESM(require("./plan-templates"));
74
+ var planTemplatesService = __toESM(require("./plan-templates-service"));
75
+ var planFileService = __toESM(require("./plan-file-service"));
76
+ var import_server = require("../server");
77
+ var import_mobile_breakpoints = require("./mobile-breakpoints");
78
+ var import_mobile_proposals = require("./mobile-proposals");
79
+ var import_mobile_awareness = require("./mobile-awareness");
80
+ var import_mobile_workstreams = require("./mobile-workstreams");
81
+ var import_plan_vocab = require("../../shared/lib/plan-vocab");
82
+ var import_grant_guard = require("./grant-guard");
83
+ var import_prompt_builders = require("../mcp/prompt-builders");
84
+ var import_mobile_approvals = require("./mobile-approvals");
85
+ var import_mobile_budget = require("./mobile-budget");
86
+ var import_mobile_freeze = require("./mobile-freeze");
87
+ var import_recurring_service = require("./recurring-service");
88
+ var import_recurring_agent = require("./recurring-agent");
89
+ var import_node_fs = __toESM(require("node:fs"));
90
+ var import_node_path = __toESM(require("node:path"));
91
+ var import_node_os = __toESM(require("node:os"));
92
+ let running = false;
93
+ let unsubMessage = null;
94
+ function startMobileRpc() {
95
+ if (running) return;
96
+ running = true;
97
+ unsubMessage = (0, import_webrtc_service.onChannelMessage)(import_types.DATA_CHANNELS.CONTROL, handleControlMessage);
98
+ console.log("[MobileRPC] Started");
99
+ }
100
+ function stopMobileRpc() {
101
+ if (!running) return;
102
+ running = false;
103
+ if (unsubMessage) {
104
+ unsubMessage();
105
+ unsubMessage = null;
106
+ }
107
+ console.log("[MobileRPC] Stopped");
108
+ }
109
+ function phonePerson() {
110
+ return { author: (0, import_settings_service.getAuthorKey)("human"), authorType: "human" };
111
+ }
112
+ function phoneActor() {
113
+ return { actor: (0, import_settings_service.getAuthorKey)("human"), actorType: "human", channel: "phone" };
114
+ }
115
+ function handleControlMessage(fingerprint, data) {
116
+ try {
117
+ const text = typeof data === "string" ? data : data.toString("utf-8");
118
+ const msg = JSON.parse(text);
119
+ if (msg.mcp && msg.cmd === "screenshot.chunk" && msg.id) {
120
+ const pend = pendingScreenshots.get(msg.id);
121
+ if (!pend) return;
122
+ let acc = screenshotChunks.get(msg.id);
123
+ if (!acc) {
124
+ acc = { parts: new Array(msg.total), total: msg.total, received: 0, mime: msg.mime || "image/jpeg" };
125
+ screenshotChunks.set(msg.id, acc);
126
+ }
127
+ if (typeof msg.seq === "number" && acc.parts[msg.seq] === void 0) {
128
+ acc.parts[msg.seq] = typeof msg.data === "string" ? msg.data : "";
129
+ acc.received++;
130
+ }
131
+ if (acc.received >= acc.total) {
132
+ clearTimeout(pend.timer);
133
+ pendingScreenshots.delete(msg.id);
134
+ screenshotChunks.delete(msg.id);
135
+ pend.resolve({ data: acc.parts.join(""), mime: acc.mime });
136
+ }
137
+ return;
138
+ }
139
+ if (msg.mcp && msg.cmd === "screenshot.result" && msg.id) {
140
+ const pend = pendingScreenshots.get(msg.id);
141
+ if (pend) {
142
+ clearTimeout(pend.timer);
143
+ pendingScreenshots.delete(msg.id);
144
+ screenshotChunks.delete(msg.id);
145
+ if (msg.error) pend.reject(new Error(String(msg.error)));
146
+ else pend.resolve({ data: typeof msg.data === "string" ? msg.data : "", mime: msg.mime || "image/png" });
147
+ }
148
+ return;
149
+ }
150
+ if (!msg.rpc || !msg.id) return;
151
+ const req = msg;
152
+ handleRpc(fingerprint, req);
153
+ } catch {
154
+ }
155
+ }
156
+ const pendingScreenshots = /* @__PURE__ */ new Map();
157
+ const screenshotChunks = /* @__PURE__ */ new Map();
158
+ let commandSeq = 0;
159
+ function mobileConnected() {
160
+ return (0, import_webrtc_service.connectedPeerCount)() > 0;
161
+ }
162
+ function sendMobileCommand(cmd, params = {}) {
163
+ return (0, import_webrtc_service.broadcastToAllPeers)(
164
+ import_types.DATA_CHANNELS.CONTROL,
165
+ JSON.stringify({ mcp: true, cmd, ...params })
166
+ );
167
+ }
168
+ function navigateMobile(route) {
169
+ return sendMobileCommand("navigate", { route });
170
+ }
171
+ function requestMobileScreenshot(timeoutMs = 2e4) {
172
+ const id = `mss-${++commandSeq}-${Date.now()}`;
173
+ return new Promise((resolve, reject) => {
174
+ const timer = setTimeout(() => {
175
+ pendingScreenshots.delete(id);
176
+ screenshotChunks.delete(id);
177
+ reject(new Error("Mobile screenshot timed out \u2014 is a phone connected and on screen?"));
178
+ }, timeoutMs);
179
+ pendingScreenshots.set(id, { resolve, reject, timer });
180
+ const reached = (0, import_webrtc_service.broadcastToAllPeers)(
181
+ import_types.DATA_CHANNELS.CONTROL,
182
+ JSON.stringify({ mcp: true, cmd: "screenshot", id })
183
+ );
184
+ if (reached === 0) {
185
+ clearTimeout(timer);
186
+ pendingScreenshots.delete(id);
187
+ reject(new Error("No mobile device connected."));
188
+ }
189
+ });
190
+ }
191
+ async function handleRpc(fingerprint, req) {
192
+ const startedAt = Date.now();
193
+ try {
194
+ const device = (0, import_paired_device_service.getPairedDevice)(fingerprint);
195
+ const capability = (0, import_peer_capabilities.assertPeerMayCall)(req.method, device?.capabilities ?? import_peer_capabilities.DEFAULT_GRANTS, {
196
+ confirmed: Boolean(device?.confirmedAt),
197
+ alias: device?.alias
198
+ });
199
+ if (capability === "terminal") {
200
+ (0, import_peer_audit_service.recordPeerAudit)({
201
+ kind: "terminal-access",
202
+ fingerprint,
203
+ alias: device?.alias ?? "unknown device",
204
+ method: req.method,
205
+ detail: (0, import_peer_audit_service.terminalAuditDetail)(req.params ?? {})
206
+ });
207
+ }
208
+ const result = await routeMethod(req.method, req.params ?? {}, fingerprint);
209
+ sendResponse(fingerprint, { result, id: req.id, rpc: true });
210
+ } catch (err) {
211
+ const message = err instanceof Error ? err.message : String(err);
212
+ if (err instanceof import_peer_capabilities.PeerAuthorizationError) {
213
+ console.warn(
214
+ `[MobileRPC][Authz] REFUSED peer=${fingerprint.slice(0, 12)}\u2026 method=${req.method} \u2014 ${message}`
215
+ );
216
+ const refusedDevice = (0, import_paired_device_service.getPairedDevice)(fingerprint);
217
+ (0, import_peer_audit_service.recordPeerAudit)({
218
+ kind: "refused",
219
+ fingerprint,
220
+ alias: refusedDevice?.alias ?? "unknown device",
221
+ method: req.method,
222
+ detail: message
223
+ });
224
+ }
225
+ const durationMs = Date.now() - startedAt;
226
+ console.warn(
227
+ `[MobileRPC][Lifecycle] rpc_failure peer=${fingerprint.slice(0, 12)}\u2026 method=${req.method} id=${req.id} durationMs=${durationMs} message=${JSON.stringify(message)}`
228
+ );
229
+ sendResponse(fingerprint, { error: message, id: req.id, rpc: true });
230
+ }
231
+ }
232
+ function sendResponse(fingerprint, response) {
233
+ (0, import_webrtc_service.sendToPeer)(fingerprint, import_types.DATA_CHANNELS.CONTROL, JSON.stringify(response));
234
+ }
235
+ function peerProjectRoot(params, opts = {}) {
236
+ const nominated = params.projectPath;
237
+ if (typeof nominated === "string" && nominated.trim().length > 0) {
238
+ return (0, import_trusted_roots.resolveTrustedProjectRoot)(nominated, "projectPath");
239
+ }
240
+ const fallback = (0, import_server.getActiveProjectPath)() || recentProjectsService.listRecentProjects()[0]?.path;
241
+ if (!fallback && opts.required) {
242
+ throw new Error("No active project on the desktop");
243
+ }
244
+ return fallback ?? void 0;
245
+ }
246
+ function planProjectRoot(planUid) {
247
+ const plan = planService.getPlan(planUid);
248
+ if (!plan?.projectPath) {
249
+ throw new Error(`Plan ${planUid} is not associated with a project`);
250
+ }
251
+ return (0, import_trusted_roots.resolveTrustedProjectRoot)(plan.projectPath, "plan.projectPath");
252
+ }
253
+ async function routeMethod(method, params, fingerprint = "") {
254
+ switch (method) {
255
+ // --- Approvals (Phase 31 §12) ---------------------------------------------
256
+ case "criteria.awaiting":
257
+ case "criteria.list":
258
+ case "criterion.decide":
259
+ case "artefact.preview":
260
+ return (0, import_mobile_approvals.handleApprovalMethod)(method, params, {
261
+ fingerprint,
262
+ send: (message) => (0, import_webrtc_service.sendToPeer)(fingerprint, import_types.DATA_CHANNELS.CONTROL, JSON.stringify(message)),
263
+ broadcast: import_server.broadcast
264
+ });
265
+ // --- A plan's budget, and marking an agent's change seen -----------------
266
+ case "budget.get":
267
+ case "budget.acknowledge":
268
+ return (0, import_mobile_budget.handleBudgetMethod)(method, params, {
269
+ fingerprint,
270
+ send: (message) => (0, import_webrtc_service.sendToPeer)(fingerprint, import_types.DATA_CHANNELS.CONTROL, JSON.stringify(message)),
271
+ broadcast: import_server.broadcast
272
+ });
273
+ // --- A project's freeze, and marking an agent's change seen (0.4k) -------
274
+ case "freeze.get":
275
+ case "freeze.acknowledge":
276
+ return (0, import_mobile_freeze.handleFreezeMethod)(method, params, {
277
+ fingerprint,
278
+ send: (message) => (0, import_webrtc_service.sendToPeer)(fingerprint, import_types.DATA_CHANNELS.CONTROL, JSON.stringify(message)),
279
+ broadcast: import_server.broadcast
280
+ });
281
+ // --- What is held for the person, answered from the phone (B4.4) --------
282
+ case "breakpoint.waiting":
283
+ case "breakpoint.answer":
284
+ return (0, import_mobile_breakpoints.handleBreakpointMethod)(method, params, {
285
+ fingerprint,
286
+ send: (message) => (0, import_webrtc_service.sendToPeer)(fingerprint, import_types.DATA_CHANNELS.CONTROL, JSON.stringify(message)),
287
+ broadcast: import_server.broadcast
288
+ }, { who: phonePerson(), projectRoot: (0, import_server.getActiveProjectPath)() });
289
+ // --- A proposed spec change, decided from the phone (B7.6) --------------
290
+ case "proposal.list":
291
+ case "proposal.get":
292
+ case "proposal.decide":
293
+ return (0, import_mobile_proposals.handleProposalMethod)(method, params, {
294
+ fingerprint,
295
+ send: (message) => (0, import_webrtc_service.sendToPeer)(fingerprint, import_types.DATA_CHANNELS.CONTROL, JSON.stringify(message)),
296
+ broadcast: import_server.broadcast
297
+ }, { who: phonePerson(), projectRoot: (0, import_server.getActiveProjectPath)() });
298
+ // --- What overlaps, answered and replied to from the phone (A4.2) -------
299
+ case "awareness.needsYou":
300
+ case "awareness.signal":
301
+ case "awareness.answer":
302
+ case "awareness.reply":
303
+ return (0, import_mobile_awareness.handleAwarenessMethod)(method, params, {
304
+ fingerprint,
305
+ send: (message) => (0, import_webrtc_service.sendToPeer)(fingerprint, import_types.DATA_CHANNELS.CONTROL, JSON.stringify(message)),
306
+ broadcast: import_server.broadcast
307
+ }, { who: phoneActor(), projectRoot: (0, import_server.getActiveProjectPath)(), reply: import_server.replyToSignalAsPerson });
308
+ // --- The lines of work, as the strip shows them (A4.3) ------------------
309
+ case "workstreams.list":
310
+ case "workstreams.detail":
311
+ return (0, import_mobile_workstreams.handleWorkstreamMethod)(method, params, {
312
+ fingerprint,
313
+ send: (message) => (0, import_webrtc_service.sendToPeer)(fingerprint, import_types.DATA_CHANNELS.CONTROL, JSON.stringify(message)),
314
+ broadcast: import_server.broadcast
315
+ }, { projectRoot: (0, import_server.getActiveProjectPath)() });
316
+ // --- Plans ---------------------------------------------------------------
317
+ case "plan.list": {
318
+ const projectPath = typeof params.projectPath === "string" && params.projectPath ? (0, import_trusted_roots.resolveTrustedProjectRoot)(params.projectPath, "projectPath") : void 0;
319
+ return planService.listPlans(projectPath).map((p) => {
320
+ const items = planItemService.listAllItems(p.uid);
321
+ return {
322
+ uid: p.uid,
323
+ title: p.title,
324
+ status: p.status,
325
+ projectPath: p.projectPath,
326
+ itemCount: items.length,
327
+ doneCount: items.filter((i) => i.status === "done").length,
328
+ inProgressCount: items.filter((i) => i.status === "in_progress" || i.status === "assigned").length,
329
+ updatedAt: p.updatedAt,
330
+ createdAt: p.createdAt
331
+ };
332
+ });
333
+ }
334
+ case "plan.get": {
335
+ const uid = requireString(params, "uid");
336
+ const plan = planService.getPlan(uid);
337
+ if (!plan) throw new Error(`Plan not found: ${uid}`);
338
+ const items = planItemService.listAllItems(uid);
339
+ const deviations = deviationService.getDeviations(uid);
340
+ const phases = safe(() => planPhasesService.listPhases(uid), []);
341
+ const documents = safe(() => planDocumentsService.listPlanDocumentSummaries(uid), []);
342
+ const externalRefs = safe(() => externalRefsService.getExternalRefsByPlan(uid), []);
343
+ const comments = safe(() => commentService.getComments(uid), []);
344
+ return { plan, items, deviations, phases, documents, externalRefs, comments };
345
+ }
346
+ case "plan.document": {
347
+ const docUid = requireString(params, "docUid");
348
+ const doc = planDocumentsService.getPlanDocument(docUid);
349
+ if (!doc) throw new Error(`Document not found: ${docUid}`);
350
+ return doc;
351
+ }
352
+ case "plan.items": {
353
+ const planUid = requirePlan(params, "planUid");
354
+ return planItemService.listAllItems(planUid);
355
+ }
356
+ case "plan.update": {
357
+ const uid = requirePlan(params, "uid");
358
+ const updates = {};
359
+ if (params.title !== void 0) updates.title = params.title;
360
+ if (params.status !== void 0) updates.status = params.status;
361
+ if (params.description !== void 0) updates.description = params.description;
362
+ const done = (0, import_server.updatePlanAsPerson)(uid, updates, phonePerson());
363
+ return { ok: true, ...done.plannedOverlaps.length ? { plannedOverlaps: done.plannedOverlaps } : {} };
364
+ }
365
+ case "plan.create": {
366
+ const title = requireString(params, "title");
367
+ const projectPath = peerProjectRoot(params, { required: true });
368
+ const plan = planService.createPlan(
369
+ { title, description: params.description || "", tasks: [] },
370
+ phonePerson().author,
371
+ phonePerson().authorType,
372
+ projectPath
373
+ );
374
+ const exported = planFileService.exportIfSharedByDefault(plan.uid, projectPath);
375
+ (0, import_server.broadcast)("plan-created", { plan, exported });
376
+ return plan;
377
+ }
378
+ case "plan.delete": {
379
+ const uid = requirePlan(params, "uid");
380
+ const { diskRemoved } = (0, import_server.deletePlanAsPerson)(uid);
381
+ return { ok: true, diskRemoved };
382
+ }
383
+ case "plan.copyAsPrompt": {
384
+ const uid = requireString(params, "uid");
385
+ const plan = planService.getPlan(uid);
386
+ if (!plan) throw new Error(`Plan not found: ${uid}`);
387
+ const items = planItemService.listAllItems(uid);
388
+ const refs = safe(() => externalRefsService.getExternalRefsByPlan(uid), []);
389
+ const prompt = (0, import_prompt_builders.buildPlanPrompt)(plan, items, refs);
390
+ return { prompt };
391
+ }
392
+ case "plan.item.get": {
393
+ const uid = requireString(params, "uid");
394
+ const item = planItemService.getItem(uid);
395
+ if (!item) throw new Error(`Item not found: ${uid}`);
396
+ const comments = safe(() => commentService.listItemComments(uid), []);
397
+ const externalRefs = safe(() => externalRefsService.getExternalRefs(uid), []);
398
+ const attachments = safe(() => taskAttachmentsService.listItemAttachments(uid), []);
399
+ return { item, comments, externalRefs, attachments };
400
+ }
401
+ case "plan.item.update": {
402
+ const uid = requireString(params, "uid");
403
+ const updates = {};
404
+ if (params.status !== void 0) updates.status = params.status;
405
+ if (params.title !== void 0) updates.title = params.title;
406
+ if (params.assignee !== void 0) updates.assignee = params.assignee;
407
+ if (params.body !== void 0) updates.body = params.body;
408
+ if (params.blockedReason !== void 0) updates.blockedReason = params.blockedReason;
409
+ if (params.progressPercent !== void 0) updates.progressPercent = params.progressPercent;
410
+ if (updates.status !== void 0 && !(0, import_plan_vocab.isTaskStatus)(updates.status)) {
411
+ throw new Error(`status must be one of: ${import_plan_vocab.TASK_STATUSES.join(", ")}`);
412
+ }
413
+ const updated = planItemService.updateItem(uid, { ...updates, ...phonePerson() });
414
+ if (!updated) throw new Error(`Item not found: ${uid}`);
415
+ (0, import_server.broadcast)("plan-item-updated", { planUid: updated.planUid, itemUid: updated.uid, kind: updated.kind, changes: updates });
416
+ return updated;
417
+ }
418
+ case "plan.item.create": {
419
+ const planUid = requirePlan(params, "planUid");
420
+ const title = requireString(params, "title");
421
+ const kind = params.kind === "object" ? "object" : "action";
422
+ const created = planItemService.createItem({
423
+ planUid,
424
+ kind,
425
+ title,
426
+ parentUid: params.parentUid || null,
427
+ body: params.body || void 0,
428
+ ...phonePerson()
429
+ });
430
+ (0, import_server.broadcast)("plan-item-created", { planUid: created.planUid, item: created });
431
+ return created;
432
+ }
433
+ case "comment.add": {
434
+ const targetUid = requireString(params, "targetUid");
435
+ const body = requireString(params, "body");
436
+ const targetType = params.targetType === "plan" ? "plan" : "item";
437
+ const kind = params.kind || "note";
438
+ const parentUid = params.parentUid || void 0;
439
+ const item = targetType === "item" ? planItemService.getItem(targetUid) : null;
440
+ if (targetType === "item" && !item) throw new Error(`Item not found: ${targetUid}`);
441
+ if (targetType === "plan" && !planService.getPlan(targetUid)) throw new Error(`Plan not found: ${targetUid}`);
442
+ const comment = commentService.addComment(
443
+ targetType,
444
+ targetUid,
445
+ phonePerson().author,
446
+ phonePerson().authorType,
447
+ body,
448
+ { kind, parentUid }
449
+ );
450
+ if (item) (0, import_server.broadcast)("plan-item-comment-added", { planUid: item.planUid, itemUid: item.uid, comment });
451
+ else (0, import_server.broadcast)("comment-added", { comment });
452
+ return comment;
453
+ }
454
+ // --- External references (links) on a plan item --------------------------
455
+ case "item.ref.add": {
456
+ const itemUid = requireString(params, "itemUid");
457
+ const url = requireString(params, "url");
458
+ if (!planItemService.getItem(itemUid)) throw new Error(`Item not found: ${itemUid}`);
459
+ const ref = externalRefsService.createExternalRef({
460
+ itemUid,
461
+ url,
462
+ title: params.title || void 0,
463
+ kind: params.kind || void 0,
464
+ ...phonePerson()
465
+ });
466
+ (0, import_server.broadcast)("external-ref-added", { ref });
467
+ return ref;
468
+ }
469
+ case "item.ref.remove": {
470
+ const uid = requireString(params, "uid");
471
+ if (!externalRefsService.getExternalRef(uid)) throw new Error(`Link not found: ${uid}`);
472
+ externalRefsService.deleteExternalRef(uid);
473
+ (0, import_server.broadcast)("external-ref-deleted", { uid });
474
+ return { ok: true };
475
+ }
476
+ // --- Deviations ----------------------------------------------------------
477
+ case "deviation.list": {
478
+ const planUid = requirePlan(params, "planUid");
479
+ return deviationService.getDeviations(planUid);
480
+ }
481
+ case "deviation.resolve": {
482
+ const id = params.id;
483
+ const resolution = params.resolution;
484
+ if (!id || !resolution) throw new Error("id and resolution required");
485
+ const planUid = deviationService.planOfDeviation(id);
486
+ if (!planUid) throw new Error(`No deviation ${id}`);
487
+ deviationService.reconcileDeviations(planUid, [{ id, action: resolution }], phoneActor());
488
+ return { ok: true };
489
+ }
490
+ // --- Projects ------------------------------------------------------------
491
+ case "project.list": {
492
+ return recentProjectsService.listRecentProjects();
493
+ }
494
+ case "project.open": {
495
+ const projectPath = requireString(params, "projectPath");
496
+ (0, import_server.broadcast)("ui-open-project", { path: projectPath });
497
+ const result = await (0, import_server.scanProject)(projectPath);
498
+ return result;
499
+ }
500
+ case "project.active": {
501
+ const activePath = (0, import_server.getActiveProjectPath)();
502
+ if (!activePath) return null;
503
+ return recentProjectsService.getRecentProject(activePath);
504
+ }
505
+ case "project.close": {
506
+ const projectPath = requireRecentProject(requireString(params, "projectPath"));
507
+ (0, import_server.broadcast)("ui-close-project", { path: projectPath });
508
+ return { ok: true };
509
+ }
510
+ case "project.pin": {
511
+ const projectPath = requireRecentProject(requireString(params, "projectPath"));
512
+ const pinned = params.pinned !== false;
513
+ recentProjectsService.setRecentProjectPinned(projectPath, pinned);
514
+ return recentProjectsService.getRecentProject(projectPath);
515
+ }
516
+ case "project.remove": {
517
+ const projectPath = requireRecentProject(requireString(params, "projectPath"));
518
+ recentProjectsService.removeRecentProject(projectPath);
519
+ return { ok: true };
520
+ }
521
+ case "project.alias": {
522
+ const projectPath = requireRecentProject(requireString(params, "projectPath"));
523
+ const alias = requireString(params, "alias");
524
+ return recentProjectsService.setProjectAlias(projectPath, alias);
525
+ }
526
+ case "project.rescan": {
527
+ const projectPath = peerProjectRoot(params, { required: true });
528
+ const result = await (0, import_server.scanProject)(projectPath);
529
+ return result;
530
+ }
531
+ // --- Settings ------------------------------------------------------------
532
+ case "settings.get": {
533
+ return (0, import_settings_service.getSettings)();
534
+ }
535
+ case "settings.update": {
536
+ const patch = {};
537
+ if (params.identity !== void 0) patch.identity = params.identity;
538
+ if (params.plans !== void 0) patch.plans = params.plans;
539
+ if (params.device !== void 0) patch.device = params.device;
540
+ if (params.power !== void 0) patch.power = params.power;
541
+ const grant = (0, import_grant_guard.grantChange)(patch, (0, import_settings_service.getSettings)());
542
+ if (grant) throw new Error((0, import_grant_guard.grantRefusal)(grant.field, grant.where));
543
+ const updated = (0, import_settings_service.updateSettings)(patch);
544
+ if (params.power !== void 0) (0, import_power_service.notifyPowerSettingsChanged)();
545
+ (0, import_server.broadcast)("settings-changed", { settings: updated });
546
+ return updated;
547
+ }
548
+ // Session-persistence plan / Track A — runtime status (shouldBlock,
549
+ // reason, ac, platform). Phone polls every 4s to render the awake
550
+ // indicator + gate the macOS-only lid-close toggle. Distinct from
551
+ // `settings.get` which returns configuration; this returns the
552
+ // power-service's *current decision* state.
553
+ case "power.status": {
554
+ return (0, import_power_service.getCurrentPowerStatus)();
555
+ }
556
+ // Plan 11.2 — mobile ships its in-RAM diagnostics ring buffer to
557
+ // the desktop logger so cross-side `[Lifecycle]` greps work
558
+ // end-to-end. `installFileLogger` already captures every
559
+ // console.log/warn into the daily log file, so the RPC just
560
+ // routes each entry through console.log with a peer-tag prefix.
561
+ case "diagnostics.flush": {
562
+ const entries = Array.isArray(params.entries) ? params.entries : [];
563
+ const fpTag = `mobile:${fingerprint.slice(0, 8)}`;
564
+ let wrote = 0;
565
+ for (const e of entries) {
566
+ if (typeof e?.text !== "string") continue;
567
+ const tag = typeof e.kind === "string" ? `[${e.kind}]` : "";
568
+ const dataStr = e.data ? " " + JSON.stringify(e.data) : "";
569
+ const tsIso = new Date(e.ts).toISOString();
570
+ console.log(`[${fpTag}] [${tsIso}] [Lifecycle]${tag} ${e.text}${dataStr}`);
571
+ wrote++;
572
+ }
573
+ return { wrote };
574
+ }
575
+ // --- System docs ---------------------------------------------------------
576
+ case "sysdoc.list": {
577
+ const projectPath = peerProjectRoot(params, { required: true });
578
+ const search = params.search;
579
+ return search ? systemDocsService.searchSystemDocs(projectPath, search) : systemDocsService.listSystemDocs(projectPath);
580
+ }
581
+ case "sysdoc.read": {
582
+ const uid = requireString(params, "uid");
583
+ const doc = systemDocsService.getSystemDoc(uid);
584
+ if (!doc) throw new Error(`Doc not found: ${uid}`);
585
+ const freshness = safe(() => systemDocsService.getFreshness(uid), null);
586
+ return { doc, freshness };
587
+ }
588
+ case "sysdoc.create": {
589
+ const projectPath = peerProjectRoot(params, { required: true });
590
+ const title = requireString(params, "title");
591
+ const doc = systemDocsService.createSystemDoc({
592
+ projectPath,
593
+ title,
594
+ body: params.body || "",
595
+ owner: params.owner || null,
596
+ tags: params.tags || [],
597
+ ...phonePerson()
598
+ });
599
+ (0, import_server.broadcast)("system-doc-created", { uid: doc.uid, projectPath: doc.projectPath });
600
+ return doc;
601
+ }
602
+ case "sysdoc.update": {
603
+ const uid = requireString(params, "uid");
604
+ const updates = { ...phonePerson() };
605
+ if (params.title !== void 0) updates.title = params.title;
606
+ if (params.body !== void 0) updates.body = params.body;
607
+ if (params.owner !== void 0) updates.owner = params.owner;
608
+ if (params.tags !== void 0) updates.tags = params.tags;
609
+ const doc = systemDocsService.updateSystemDoc(uid, updates);
610
+ if (!doc) throw new Error(`Doc not found: ${uid}`);
611
+ (0, import_server.broadcast)("system-doc-updated", { uid: doc.uid, projectPath: doc.projectPath });
612
+ return doc;
613
+ }
614
+ case "sysdoc.delete": {
615
+ const uid = requireString(params, "uid");
616
+ if (!systemDocsService.deleteSystemDoc(uid)) throw new Error(`Doc not found: ${uid}`);
617
+ (0, import_server.broadcast)("system-doc-removed", { uid });
618
+ return { ok: true };
619
+ }
620
+ case "sysdoc.verify": {
621
+ const uid = requireString(params, "uid");
622
+ const doc = systemDocsService.verifySystemDoc(uid);
623
+ if (!doc) throw new Error(`Doc not found: ${uid}`);
624
+ (0, import_server.broadcast)("system-doc-verified", { uid: doc.uid, capturedAgainstCommit: doc.capturedAgainstCommit });
625
+ return doc;
626
+ }
627
+ // --- Plan templates + file import/export ---------------------------------
628
+ case "plan.template.list": {
629
+ const projectRoot = peerProjectRoot(params);
630
+ return planTemplates.listTemplates(projectRoot);
631
+ }
632
+ case "plan.template.create": {
633
+ const templateId = requireString(params, "templateId");
634
+ const projectPath = peerProjectRoot(params, { required: true });
635
+ const result = planTemplatesService.applyTemplate({
636
+ templateId,
637
+ projectPath,
638
+ title: params.title || void 0,
639
+ description: params.description || void 0,
640
+ placeholderValues: params.placeholderValues || void 0,
641
+ ...phonePerson()
642
+ });
643
+ (0, import_server.broadcast)("plan-created", { plan: result.plan });
644
+ return { plan: result.plan, itemCount: result.items.length, version: result.version };
645
+ }
646
+ case "plan.file.export": {
647
+ const planUid = requirePlan(params, "planUid");
648
+ const projectRoot = params.projectPath !== void 0 ? peerProjectRoot(params, { required: true }) : planProjectRoot(planUid);
649
+ const result = planFileService.exportPlan(planUid, projectRoot);
650
+ (0, import_server.broadcast)("plan-exported", { planUid, planDir: result.planDir, files: result.files.length });
651
+ return { planDir: result.planDir, fileCount: result.files.length };
652
+ }
653
+ case "plan.file.discover": {
654
+ const projectRoot = peerProjectRoot(params, { required: true });
655
+ const dirs = planFileService.discoverPlanDirs(projectRoot);
656
+ return dirs.map((d) => ({ dir: d, name: import_node_path.default.basename(d) }));
657
+ }
658
+ case "plan.file.import": {
659
+ const planDir = (0, import_trusted_roots.resolveTrustedPlanDir)(requireString(params, "planDir"));
660
+ const result = planFileService.importPlan(planDir);
661
+ if (result.plan) (0, import_server.broadcast)("plan-imported", { planUid: result.plan.uid, source: planDir });
662
+ return { plan: result.plan, warnings: result.warnings };
663
+ }
664
+ // --- Terminals -----------------------------------------------------------
665
+ case "terminal.list": {
666
+ return terminalService.listTerminals();
667
+ }
668
+ case "terminal.create": {
669
+ const preset = params.preset || "shell";
670
+ const cwd = params.cwd || (0, import_server.getActiveProjectPath)() || recentProjectsService.listRecentProjects()[0]?.path || void 0;
671
+ const title = params.title;
672
+ const term = terminalService.createTerminal({
673
+ preset,
674
+ cwd,
675
+ title
676
+ });
677
+ try {
678
+ (0, import_server.broadcast)("terminal-created", { session: term, focus: true });
679
+ } catch {
680
+ }
681
+ return term;
682
+ }
683
+ // A terminal that is not there is an error, not `{ ok: false }` or a
684
+ // null the phone renders as an empty screen: typing into one looked like
685
+ // it worked (0.4j).
686
+ case "terminal.write": {
687
+ const id = requireString(params, "id");
688
+ const data = requireString(params, "data");
689
+ if (!terminalService.writeTerminal(id, data)) throw new Error(`Terminal not found: ${id}`);
690
+ return { ok: true };
691
+ }
692
+ case "terminal.kill": {
693
+ const id = requireString(params, "id");
694
+ if (!terminalService.killTerminal(id)) throw new Error(`Terminal not found: ${id}`);
695
+ try {
696
+ (0, import_server.broadcast)("terminal-killed", { id });
697
+ } catch {
698
+ }
699
+ return { ok: true };
700
+ }
701
+ case "terminal.read": {
702
+ const id = requireString(params, "id");
703
+ const lines = params.lines ?? 100;
704
+ const output = terminalService.readTerminalOutput(id, lines, true);
705
+ if (output === null) throw new Error(`Terminal not found: ${id}`);
706
+ return { output };
707
+ }
708
+ case "terminal.stream": {
709
+ const id = requireString(params, "id");
710
+ const since = typeof params.since === "number" ? params.since : void 0;
711
+ const delta = terminalService.readTerminalDelta(id, since);
712
+ if (delta === null) throw new Error(`Terminal not found: ${id}`);
713
+ return delta;
714
+ }
715
+ case "terminal.resize": {
716
+ const id = requireString(params, "id");
717
+ const cols = params.cols ?? 80;
718
+ const rows = params.rows ?? 24;
719
+ if (!Number.isInteger(cols) || cols < 10 || cols > 500 || !Number.isInteger(rows) || rows < 4 || rows > 300) {
720
+ throw new Error("cols must be a whole number from 10 to 500, and rows from 4 to 300");
721
+ }
722
+ if (!terminalService.resizeTerminal(id, cols, rows)) throw new Error(`Terminal not found: ${id}`);
723
+ return { ok: true };
724
+ }
725
+ // Session-persistence plan §7.6 — paginated read of the
726
+ // persistent on-disk terminal history. Clients pass `before` from
727
+ // the previous response's `prevOffset` to walk backwards through
728
+ // the log; omit it to start at the tail. The data is raw ANSI —
729
+ // safe to feed straight into xterm.
730
+ case "terminal.history": {
731
+ const id = requireString(params, "id");
732
+ const before = typeof params.before === "number" ? params.before : void 0;
733
+ const limit = typeof params.limit === "number" ? params.limit : 64 * 1024;
734
+ return terminalHistoryService.getHistoryChunk(id, before, limit);
735
+ }
736
+ // --- Channel events ------------------------------------------------------
737
+ case "channel.events": {
738
+ const planUid = requireString(params, "planUid");
739
+ const limit = params.limit ?? 50;
740
+ return channelEventService.listChannelEvents(planUid, { limit });
741
+ }
742
+ case "channel.eventsSinceSeq": {
743
+ const planUid = requireString(params, "planUid");
744
+ const sinceSeq = params.sinceSeq ?? 0;
745
+ const limit = params.limit ?? 200;
746
+ return channelEventService.getChannelEventsSinceSeq(planUid, sinceSeq, limit);
747
+ }
748
+ case "channel.post": {
749
+ const planUid = requireString(params, "planUid");
750
+ const eventType = requireString(params, "eventType");
751
+ return (0, import_server.postChannelEventAsPerson)({
752
+ planUid,
753
+ eventType,
754
+ message: typeof params.message === "string" ? params.message : "",
755
+ itemUid: typeof params.itemUid === "string" ? params.itemUid : null,
756
+ respondsTo: params.parentUid || null,
757
+ by: phonePerson().authorType
758
+ });
759
+ }
760
+ case "channel.resolve": {
761
+ const uid = requireString(params, "uid");
762
+ const status = params.status ?? "resolved";
763
+ return (0, import_server.setChannelEventStatusAsPerson)(uid, status);
764
+ }
765
+ case "channel.thread": {
766
+ const rootUid = requireString(params, "rootUid");
767
+ return channelEventService.listThread(rootUid);
768
+ }
769
+ case "channel.get": {
770
+ const uid = requireString(params, "uid");
771
+ const event = channelEventService.getChannelEvent(uid);
772
+ if (!event) throw new Error(`Channel event not found: ${uid}`);
773
+ return event;
774
+ }
775
+ // --- Input requests ------------------------------------------------------
776
+ case "input.respond": {
777
+ const requestId = requireString(params, "requestId");
778
+ const response = requireString(params, "response");
779
+ if (!remoteInteractionService.respondToInputRequest(requestId, response, phoneActor())) {
780
+ throw new Error(`No pending input request ${requestId}`);
781
+ }
782
+ return { ok: true };
783
+ }
784
+ // --- Graph ---------------------------------------------------------------
785
+ case "graph.overview": {
786
+ const summary = (0, import_database.getArchitectureSummary)();
787
+ const d = (0, import_database.getDb)();
788
+ let enrichedMostImported = summary.mostImported;
789
+ try {
790
+ const r = d.exec(`
791
+ SELECT i.resolved_path, COUNT(DISTINCT i.file_id) AS cnt
792
+ FROM imports i
793
+ WHERE i.resolved_path IS NOT NULL
794
+ GROUP BY i.resolved_path
795
+ ORDER BY cnt DESC
796
+ LIMIT 10
797
+ `);
798
+ if (r[0]?.values.length) {
799
+ enrichedMostImported = r[0].values.map((row) => ({
800
+ path: row[0],
801
+ importerCount: row[1]
802
+ }));
803
+ }
804
+ } catch {
805
+ }
806
+ return { ...summary, mostImported: enrichedMostImported };
807
+ }
808
+ case "graph.directory": {
809
+ const dir = requireString(params, "dir");
810
+ return getDirectoryFiles(dir);
811
+ }
812
+ case "graph.file": {
813
+ const filePath = requireString(params, "filePath");
814
+ const symbols = (0, import_database.getFileSymbols)(filePath);
815
+ const deps = (0, import_database.getFileDependencies)(filePath);
816
+ const d = (0, import_database.getDb)();
817
+ let language = "unknown";
818
+ try {
819
+ const r = d.exec(`SELECT language FROM files WHERE path = ?`, [filePath]);
820
+ if (r[0]?.values[0]) language = r[0].values[0][0];
821
+ } catch {
822
+ }
823
+ let crossSystemOut = [];
824
+ let crossSystemIn = [];
825
+ try {
826
+ const edges = (0, import_cross_system_service.listCrossSystemEdges)();
827
+ crossSystemOut = edges.filter((e) => e.source === filePath).map((e) => ({ path: e.target, relativePath: e.targetRelative, protocol: e.protocol, label: e.label }));
828
+ crossSystemIn = edges.filter((e) => e.target === filePath).map((e) => ({ path: e.source, relativePath: e.sourceRelative, protocol: e.protocol, label: e.label }));
829
+ } catch {
830
+ }
831
+ return { filePath, language, symbols, ...deps, crossSystemOut, crossSystemIn };
832
+ }
833
+ case "graph.fileSource": {
834
+ const filePath = requireString(params, "filePath");
835
+ const roots = (0, import_trusted_roots.listTrustedRoots)();
836
+ const owningRoot = roots.find((r) => (0, import_confined_fs.isWithin)(r, filePath));
837
+ if (!owningRoot) {
838
+ throw new Error("File is outside every opened project");
839
+ }
840
+ const confinedBuffer = (0, import_confined_fs.readFileWithin)(owningRoot, filePath, "graph.fileSource");
841
+ const MAX = 200 * 1024;
842
+ const truncated = confinedBuffer.length > MAX;
843
+ const content = (truncated ? confinedBuffer.subarray(0, MAX) : confinedBuffer).toString("utf-8");
844
+ const lineCount = content.split("\n").length;
845
+ let language = "unknown";
846
+ try {
847
+ const r = (0, import_database.getDb)().exec(`SELECT language FROM files WHERE path = ?`, [filePath]);
848
+ if (r[0]?.values[0]) language = r[0].values[0][0];
849
+ } catch {
850
+ }
851
+ const git = safe(
852
+ () => (0, import_server.computeGitLineAnnotations)(owningRoot, import_node_path.default.resolve(filePath), lineCount),
853
+ null
854
+ );
855
+ const lineStatus = git?.annotations ?? null;
856
+ const deletedBefore = git?.deletedBefore ?? null;
857
+ return { content, language, truncated, lineCount, lineStatus, deletedBefore };
858
+ }
859
+ case "graph.fileSearch": {
860
+ const query = requireString(params, "query").toLowerCase();
861
+ const pattern = `%${query.replace(/[\\%_]/g, (c) => `\\${c}`)}%`;
862
+ const d = (0, import_database.getDb)();
863
+ const out = [];
864
+ try {
865
+ const r = d.exec(
866
+ `SELECT path, relative_path FROM files WHERE LOWER(relative_path) LIKE ? ESCAPE '\\' ORDER BY relative_path LIMIT 40`,
867
+ [pattern]
868
+ );
869
+ for (const row of r[0]?.values ?? []) {
870
+ const p = row[0];
871
+ out.push({ path: p, relativePath: row[1], name: import_node_path.default.basename(p) });
872
+ }
873
+ } catch {
874
+ }
875
+ return out;
876
+ }
877
+ // --- Changes / diff -------------------------------------------------------
878
+ // --- Review (Phase 29 mobile flow) ---------------------------------------
879
+ //
880
+ // Phase 25 built the review surface and shipped it REST + MCP only.
881
+ // Phase 29 §4.13 gave it a desktop panel; none of it was ever bridged
882
+ // to the phone, so a user watching an agent from their phone could
883
+ // read the plan and the channel but never ask "did it do what it
884
+ // said". These four methods are that question.
885
+ //
886
+ // All read-only. `buildPrDraft` in particular never touches the
887
+ // repository — CodeTrellis supplies the body an agent cannot write
888
+ // and the agent does the git with its own credentials.
889
+ case "review.get": {
890
+ const planUid = requireString(params, "planUid");
891
+ const projectPath = planProjectRoot(planUid);
892
+ const result = (0, import_plan_review_service.reviewPlan)({
893
+ planUid,
894
+ projectPath,
895
+ before: params.before,
896
+ after: params.after
897
+ });
898
+ if (!result.ok) throw new Error(result.error);
899
+ return (0, import_plan_review_service.withArchitecture)(result.review, projectPath);
900
+ }
901
+ case "review.prDraft": {
902
+ const planUid = requireString(params, "planUid");
903
+ const projectPath = planProjectRoot(planUid);
904
+ const result = (0, import_pr_draft_service.buildPrDraft)({
905
+ planUid,
906
+ projectPath,
907
+ before: params.before,
908
+ after: params.after,
909
+ architecture: await (0, import_pr_draft_service.draftArchitecture)(projectPath, params.before, params.after)
910
+ });
911
+ if (!result.ok) throw new Error(result.error ?? "Could not build a PR draft");
912
+ return result.draft;
913
+ }
914
+ case "review.comparands": {
915
+ const planUid = params.planUid;
916
+ const projectPath = planUid ? planProjectRoot(planUid) : peerProjectRoot(params, { required: true });
917
+ return (0, import_snapshot_compare_service.listComparands)(projectPath, void 0, await (0, import_snapshot_compare_service.comparandBranches)(projectPath));
918
+ }
919
+ case "review.compare": {
920
+ const projectPath = peerProjectRoot(params, { required: true });
921
+ const before = params.before || "baseline";
922
+ const after = params.after || "live";
923
+ const result = (0, import_snapshot_compare_service.compareSnapshots)(before, after, projectPath);
924
+ if (!result.ok) throw new Error(result.error);
925
+ return result.result;
926
+ }
927
+ // The review queue (Phase 32 A5.6, over A5.4): every line of work with plan
928
+ // items, where it stands against the main checkout's branch, and the
929
+ // suggested merge order with its reasons. The same answer as
930
+ // `/api/review-queue`, for an opened project only.
931
+ case "review.queue": {
932
+ const projectPath = peerProjectRoot(params, { required: true });
933
+ return (0, import_review_queue_service.reviewQueue)(projectPath);
934
+ }
935
+ // The stack (Phase 32 B6.6, over B6.2): every plan under way, with who is
936
+ // on what, what waits on what across plans, and where plans meet. The same
937
+ // answer as `/api/stack`, for an opened project only; the phone summarises.
938
+ case "stack.summary": {
939
+ const projectPath = peerProjectRoot(params, { required: true });
940
+ return (0, import_stack_service.buildStack)(projectPath);
941
+ }
942
+ // Play-forward (Phase 32 B9.3b): what every active plan will change and
943
+ // where two will meet, as the desktop's bar says it, and a person on the
944
+ // phone deciding one: re-sequence, tell the agents, or leave it.
945
+ case "playForward.summary": {
946
+ const projectPath = peerProjectRoot(params, { required: true });
947
+ return { ...(0, import_play_forward.buildPlayForward)(projectPath), notices: (0, import_planned_overlap_actions.approvalNotices)(projectPath) };
948
+ }
949
+ case "playForward.decide": {
950
+ const projectPath = peerProjectRoot(params, { required: true });
951
+ const overlapId = requireString(params, "overlapId");
952
+ const action = requireString(params, "action");
953
+ const who = phonePerson();
954
+ let result = {};
955
+ if (action === "resequence") {
956
+ const r = (0, import_planned_overlap_actions.resequence)(projectPath, overlapId, requireString(params, "first"), who);
957
+ for (const t of r.waiting) (0, import_server.broadcast)("plan-item-updated", { planUid: planItemService.getItem(t.uid)?.planUid, itemUid: t.uid, kind: "action", changes: { dependencies: true } });
958
+ result = { waiting: r.waiting };
959
+ } else if (action === "tell") {
960
+ result = (0, import_planned_overlap_actions.tellAgents)(projectPath, overlapId, who);
961
+ } else if (action === "leave") {
962
+ (0, import_planned_overlap_actions.leaveOverlap)(projectPath, overlapId, who);
963
+ } else {
964
+ throw new Error("action must be resequence, tell or leave");
965
+ }
966
+ (0, import_server.broadcast)("play-forward-changed", { project: projectPath });
967
+ return { ...result, playForward: (0, import_play_forward.buildPlayForward)(projectPath) };
968
+ }
969
+ // Recurring playbooks (Phase 32 C4.3a): the series as the plans list shows
970
+ // them, and starting the run due now. Starting is anyone's, as making a
971
+ // plan is, and twice is one run; setting a rule stays the app window's.
972
+ case "recurring.list": {
973
+ const projectPath = peerProjectRoot(params, { required: true });
974
+ return { series: (0, import_recurring_service.seriesFor)(projectPath) };
975
+ }
976
+ case "recurring.start": {
977
+ const projectPath = peerProjectRoot(params, { required: true });
978
+ const ruleId = requireString(params, "ruleId");
979
+ const run = (0, import_recurring_service.startRun)(projectPath, ruleId, phonePerson());
980
+ const mayOpen = ((0, import_paired_device_service.getPairedDevice)(fingerprint)?.capabilities ?? []).includes("terminal") ? true : "this phone is not allowed to open terminals (Settings \u2192 Devices)";
981
+ const agent = run.created ? (0, import_recurring_agent.startRunAgent)(projectPath, ruleId, run.plan, mayOpen, (session) => (0, import_server.broadcast)("terminal-created", { session })) : null;
982
+ if (run.created) {
983
+ (0, import_server.broadcast)("plan-created", { plan: run.plan });
984
+ (0, import_server.broadcast)("recurring-changed", { project: projectPath });
985
+ }
986
+ return { planUid: run.plan.uid, title: run.plan.title, created: run.created, recurrence: run.info, agent, series: (0, import_recurring_service.seriesFor)(projectPath) };
987
+ }
988
+ // The plan's status (Phase 32 C2.4): every item's state with its source,
989
+ // and the one view — progress, waiting, in progress, lineage. The same
990
+ // answer as `/api/plans/:uid/status` and get_plan's `status`.
991
+ case "plan.status": {
992
+ const planUid = requirePlan(params, "planUid");
993
+ const status = await (0, import_plan_status.planStatusFresh)(planUid);
994
+ if (!status) throw new Error(`Plan not found: ${planUid}`);
995
+ return status;
996
+ }
997
+ // What to work on next. `getNextTask` reads plan_items for a V2 plan
998
+ // and falls back to the legacy tasks table (Phase 29 §4.14), so the
999
+ // phone gets the same answer the desktop strip shows.
1000
+ case "plan.nextItem": {
1001
+ const planUid = requireString(params, "planUid");
1002
+ const next = planService.getNextTask(planUid);
1003
+ return next ?? { none: true };
1004
+ }
1005
+ case "changes.summary": {
1006
+ const projectPath = peerProjectRoot(params, { required: true });
1007
+ const git = (0, import_server.getGitWorkingTreeStatus)(projectPath);
1008
+ let arch = null;
1009
+ const baseline = (0, import_diff_engine.getBaseline)();
1010
+ if (baseline) {
1011
+ try {
1012
+ const currentEdges = (0, import_database.getDependencyEdges)();
1013
+ const fileData = (0, import_server.readFilesSnapshot)(projectPath);
1014
+ arch = (0, import_diff_engine.computeDiff)((0, import_diff_engine.captureSnapshot)(fileData, currentEdges));
1015
+ } catch {
1016
+ }
1017
+ }
1018
+ const changed = /* @__PURE__ */ new Set();
1019
+ if (git) {
1020
+ for (const f of [
1021
+ ...git.stagedAdded,
1022
+ ...git.stagedModified,
1023
+ ...git.stagedDeleted,
1024
+ ...git.unstagedModified,
1025
+ ...git.unstagedDeleted,
1026
+ ...git.untracked
1027
+ ]) changed.add(f);
1028
+ }
1029
+ if (arch) {
1030
+ for (const f of [...arch.addedFiles, ...arch.modifiedFiles]) changed.add(f);
1031
+ }
1032
+ return {
1033
+ hasBaseline: !!baseline,
1034
+ git,
1035
+ arch,
1036
+ changedFiles: [...changed]
1037
+ };
1038
+ }
1039
+ case "graph.search": {
1040
+ const query = requireString(params, "query");
1041
+ return (0, import_database.searchSymbols)(query);
1042
+ }
1043
+ // --- Filesystem browse (open a folder as a project from mobile) -----------
1044
+ case "fs.browse": {
1045
+ const dir = params.dir || import_node_os.default.homedir();
1046
+ return browseDirectory(dir);
1047
+ }
1048
+ // --- Renderable graph scene (cluster nodes + edges + planned/diverged) ----
1049
+ case "graph.scene": {
1050
+ const planUid = params.planUid || void 0;
1051
+ const mode = params.mode || "live";
1052
+ const granularity = params.granularity === "file" ? "file" : "cluster";
1053
+ return buildGraphScene(mode, planUid, granularity);
1054
+ }
1055
+ default:
1056
+ throw new Error(`Unknown RPC method: ${method}`);
1057
+ }
1058
+ }
1059
+ function requireRecentProject(projectPath) {
1060
+ if (!recentProjectsService.getRecentProject(projectPath)) {
1061
+ throw new Error(`${projectPath} is not a recent project`);
1062
+ }
1063
+ return projectPath;
1064
+ }
1065
+ function requirePlan(params, key) {
1066
+ const uid = requireString(params, key);
1067
+ if (!planService.getPlan(uid)) throw new Error(`Plan not found: ${uid}`);
1068
+ return uid;
1069
+ }
1070
+ function requireString(params, key) {
1071
+ const val = params[key];
1072
+ if (typeof val !== "string" || !val) {
1073
+ throw new Error(`Missing required string parameter: ${key}`);
1074
+ }
1075
+ return val;
1076
+ }
1077
+ function safe(fn, fallback) {
1078
+ try {
1079
+ return fn();
1080
+ } catch {
1081
+ return fallback;
1082
+ }
1083
+ }
1084
+ const PROJECT_MARKERS = ["package.json", "Cargo.toml", "pom.xml", "go.mod", "pyproject.toml", "composer.json", "build.gradle"];
1085
+ function browseDirectory(dir) {
1086
+ const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "out", ".next", ".cache", "target", ".venv", "venv", "__pycache__", "Library", ".Trash"]);
1087
+ let entries = [];
1088
+ try {
1089
+ const dirents = import_node_fs.default.readdirSync(dir, { withFileTypes: true });
1090
+ entries = dirents.filter((e) => e.isDirectory() && !SKIP.has(e.name) && (!e.name.startsWith(".") || e.name === ".codetrellis")).map((e) => {
1091
+ const full = import_node_path.default.join(dir, e.name);
1092
+ return {
1093
+ name: e.name,
1094
+ path: full,
1095
+ isGitRepo: safe(() => import_node_fs.default.existsSync(import_node_path.default.join(full, ".git")), false),
1096
+ hasProjectMarker: safe(() => PROJECT_MARKERS.some((m) => import_node_fs.default.existsSync(import_node_path.default.join(full, m))), false)
1097
+ };
1098
+ }).sort((a, b) => {
1099
+ const ap = a.isGitRepo || a.hasProjectMarker ? 0 : 1;
1100
+ const bp = b.isGitRepo || b.hasProjectMarker ? 0 : 1;
1101
+ return ap - bp || a.name.localeCompare(b.name);
1102
+ });
1103
+ } catch {
1104
+ }
1105
+ const parent = import_node_path.default.dirname(dir);
1106
+ return {
1107
+ path: dir,
1108
+ parent: parent === dir ? null : parent,
1109
+ home: import_node_os.default.homedir(),
1110
+ entries
1111
+ };
1112
+ }
1113
+ function clusterKey(rel) {
1114
+ const parts = rel.split("/");
1115
+ if (parts.length <= 1) return "(root)";
1116
+ if (parts.length === 2) return parts[0];
1117
+ return `${parts[0]}/${parts[1]}`;
1118
+ }
1119
+ const MAX_FILE_NODES = 140;
1120
+ function buildGraphScene(mode, planUid, granularity = "cluster") {
1121
+ const projectPath = (0, import_server.getActiveProjectPath)() || recentProjectsService.listRecentProjects()[0]?.path || null;
1122
+ const depEdges = safe(() => (0, import_database.getDependencyEdges)(), []);
1123
+ const fileMode = granularity === "file";
1124
+ const keyFn = (rel) => fileMode ? rel : clusterKey(rel);
1125
+ const labelFn = (key) => fileMode ? key.split("/").pop() || key : key;
1126
+ const clusters = /* @__PURE__ */ new Map();
1127
+ const ensure = (key) => {
1128
+ let c = clusters.get(key);
1129
+ if (!c) {
1130
+ c = { fileCount: 0, files: /* @__PURE__ */ new Set(), state: "normal" };
1131
+ clusters.set(key, c);
1132
+ }
1133
+ return c;
1134
+ };
1135
+ const addFile = (rel) => {
1136
+ const c = ensure(keyFn(rel));
1137
+ if (!c.files.has(rel)) {
1138
+ c.files.add(rel);
1139
+ c.fileCount++;
1140
+ }
1141
+ };
1142
+ const edgeAgg = /* @__PURE__ */ new Map();
1143
+ for (const e of depEdges) {
1144
+ addFile(e.sourceRelative);
1145
+ addFile(e.targetRelative);
1146
+ const s = keyFn(e.sourceRelative);
1147
+ const t = keyFn(e.targetRelative);
1148
+ if (s === t) continue;
1149
+ const key = `${s}->${t}`;
1150
+ const agg = edgeAgg.get(key);
1151
+ if (agg) agg.weight++;
1152
+ else edgeAgg.set(key, { source: s, target: t, weight: 1 });
1153
+ }
1154
+ let changedCount = 0;
1155
+ if (mode === "diff" || mode === "live") {
1156
+ const changed = /* @__PURE__ */ new Set();
1157
+ if (projectPath) {
1158
+ const git = safe(() => (0, import_server.getGitWorkingTreeStatus)(projectPath), null);
1159
+ if (git) {
1160
+ for (const f of [
1161
+ ...git.stagedAdded,
1162
+ ...git.stagedModified,
1163
+ ...git.stagedDeleted,
1164
+ ...git.unstagedModified,
1165
+ ...git.unstagedDeleted,
1166
+ ...git.untracked
1167
+ ]) changed.add(f);
1168
+ }
1169
+ const baseline = safe(() => (0, import_diff_engine.getBaseline)(), null);
1170
+ if (baseline) {
1171
+ const arch = safe(() => {
1172
+ const currentEdges = (0, import_database.getDependencyEdges)();
1173
+ const fileData = (0, import_server.readFilesSnapshot)(projectPath);
1174
+ return (0, import_diff_engine.computeDiff)((0, import_diff_engine.captureSnapshot)(fileData, currentEdges));
1175
+ }, null);
1176
+ if (arch) for (const f of [...arch.addedFiles, ...arch.modifiedFiles]) changed.add(f);
1177
+ }
1178
+ }
1179
+ for (const f of changed) {
1180
+ const c = clusters.get(keyFn(f));
1181
+ if (c && c.state === "normal") {
1182
+ c.state = "changed";
1183
+ }
1184
+ }
1185
+ changedCount = changed.size;
1186
+ }
1187
+ let plannedCount = 0;
1188
+ if ((mode === "planned" || planUid) && planUid) {
1189
+ const proj = safe(() => (0, import_projection_service.computeProjection)(planUid), null);
1190
+ if (proj) {
1191
+ const mark = (relPath, state, ghostOk) => {
1192
+ const key = keyFn(relPath);
1193
+ let c = clusters.get(key);
1194
+ if (!c && ghostOk) {
1195
+ c = ensure(key);
1196
+ }
1197
+ if (c) c.state = state;
1198
+ };
1199
+ for (const g of proj.ghostFiles) {
1200
+ ensure(keyFn(g.path)).state = "planned_add";
1201
+ }
1202
+ for (const m of proj.modifiedFiles) mark(m.path, "planned_modify", false);
1203
+ for (const r of proj.removedFiles) mark(r.path, "planned_remove", false);
1204
+ plannedCount = proj.ghostFiles.length + proj.modifiedFiles.length + proj.removedFiles.length;
1205
+ }
1206
+ }
1207
+ let nodes = [...clusters.entries()].map(([id, c]) => ({
1208
+ id,
1209
+ label: labelFn(id),
1210
+ fileCount: c.fileCount,
1211
+ state: c.state
1212
+ }));
1213
+ let edges = [...edgeAgg.values()];
1214
+ let truncated = false;
1215
+ if (fileMode && nodes.length > MAX_FILE_NODES) {
1216
+ const degree = /* @__PURE__ */ new Map();
1217
+ for (const e of edges) {
1218
+ degree.set(e.source, (degree.get(e.source) ?? 0) + e.weight);
1219
+ degree.set(e.target, (degree.get(e.target) ?? 0) + e.weight);
1220
+ }
1221
+ const ranked = [...nodes].sort((a, b) => {
1222
+ const fa = a.state !== "normal" ? 1 : 0;
1223
+ const fb = b.state !== "normal" ? 1 : 0;
1224
+ if (fa !== fb) return fb - fa;
1225
+ return (degree.get(b.id) ?? 0) - (degree.get(a.id) ?? 0);
1226
+ });
1227
+ const keep = new Set(ranked.slice(0, MAX_FILE_NODES).map((n) => n.id));
1228
+ nodes = nodes.filter((n) => keep.has(n.id));
1229
+ edges = edges.filter((e) => keep.has(e.source) && keep.has(e.target));
1230
+ truncated = true;
1231
+ }
1232
+ return {
1233
+ mode,
1234
+ granularity,
1235
+ projectPath,
1236
+ nodes,
1237
+ edges,
1238
+ counts: { nodes: nodes.length, edges: edges.length, changed: changedCount, planned: plannedCount },
1239
+ truncated
1240
+ };
1241
+ }
1242
+ function getDirectoryFiles(dir) {
1243
+ const d = (0, import_database.getDb)();
1244
+ const prefix = dir.endsWith("/") ? dir : `${dir}/`;
1245
+ const filesResult = d.exec(`
1246
+ SELECT f.path, f.relative_path, f.language,
1247
+ (SELECT COUNT(*) FROM symbols s WHERE s.file_id = f.id AND s.parent_symbol_id IS NULL) AS sym_count,
1248
+ (SELECT COUNT(*) FROM imports i WHERE i.file_id = f.id) AS import_count,
1249
+ (SELECT COUNT(DISTINCT i2.file_id) FROM imports i2 WHERE i2.resolved_path = f.path) AS imported_by_count
1250
+ FROM files f
1251
+ WHERE f.relative_path LIKE ? AND f.relative_path NOT LIKE ?
1252
+ ORDER BY f.relative_path
1253
+ `, [`${prefix}%`, `${prefix}%/%`]);
1254
+ const files = (filesResult[0]?.values ?? []).map((row) => ({
1255
+ path: row[0],
1256
+ relativePath: row[1],
1257
+ language: row[2],
1258
+ symbolCount: row[3],
1259
+ importCount: row[4],
1260
+ importedByCount: row[5]
1261
+ }));
1262
+ const subdirResult = d.exec(`
1263
+ SELECT
1264
+ SUBSTR(f.relative_path, LENGTH(?) + 1,
1265
+ INSTR(SUBSTR(f.relative_path, LENGTH(?) + 1), '/') - 1
1266
+ ) AS subdir,
1267
+ COUNT(*) AS cnt
1268
+ FROM files f
1269
+ WHERE f.relative_path LIKE ?
1270
+ AND SUBSTR(f.relative_path, LENGTH(?) + 1) LIKE '%/%'
1271
+ GROUP BY subdir
1272
+ HAVING subdir != ''
1273
+ ORDER BY cnt DESC
1274
+ `, [prefix, prefix, `${prefix}%`, prefix]);
1275
+ const subdirectories = (subdirResult[0]?.values ?? []).map((row) => ({
1276
+ name: row[0],
1277
+ fileCount: row[1]
1278
+ }));
1279
+ return { dir, files, subdirectories };
1280
+ }
1281
+ // Annotate the CommonJS export names for ESM import in node:
1282
+ 0 && (module.exports = {
1283
+ mobileConnected,
1284
+ navigateMobile,
1285
+ requestMobileScreenshot,
1286
+ sendMobileCommand,
1287
+ startMobileRpc,
1288
+ stopMobileRpc
1289
+ });