taphound 0.2.0-dev.1 → 0.2.0-dev.12

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 (467) hide show
  1. package/README.md +49 -149
  2. package/README.zh-CN.md +47 -147
  3. package/assets/skills/taphound-case-suite/SKILL.md +363 -0
  4. package/assets/skills/taphound-case-suite/schemas/base-flow-record.schema.json +22 -0
  5. package/assets/skills/taphound-case-suite/schemas/case-catalog.schema.json +26 -0
  6. package/assets/skills/taphound-case-suite/schemas/case-ledger.schema.json +158 -0
  7. package/assets/skills/taphound-case-suite/schemas/suite-input.schema.json +41 -0
  8. package/assets/skills/taphound-case-suite/schemas/transition.schema.json +61 -0
  9. package/assets/skills/taphound-case-suite/scripts/ledger.mjs +1207 -0
  10. package/assets/skills/taphound-case-suite/templates/base-flow-record.example.json +20 -0
  11. package/assets/skills/taphound-case-suite/templates/suite-input.example.json +27 -0
  12. package/assets/skills/taphound-case-suite/templates/transition.example.json +8 -0
  13. package/assets/skills/taphound-flash/SKILL.md +115 -0
  14. package/assets/skills/taphound-flash/scripts/flash.mjs +585 -0
  15. package/assets/skills/taphound-flash/templates/flash-plan.example.json +15 -0
  16. package/assets/skills/taphound-journey-brief-author/CONTEXT-GUIDE.md +471 -0
  17. package/assets/skills/taphound-journey-brief-author/SKILL.md +494 -0
  18. package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.md +96 -0
  19. package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.zh-CN.md +80 -0
  20. package/assets/skills/taphound-journey-brief-author/prompts/context-analyze-project.md +309 -0
  21. package/assets/skills/taphound-journey-brief-author/schemas/context-refresh-result.json +123 -0
  22. package/assets/skills/taphound-journey-brief-author/schemas/project-context-module.json +192 -0
  23. package/assets/skills/taphound-journey-brief-author/schemas/project-context.json +152 -0
  24. package/assets/skills/taphound-journey-brief-author/templates/project-context-module.example.json +58 -0
  25. package/assets/skills/taphound-journey-brief-author/templates/project-context.example.json +51 -0
  26. package/assets/skills/taphound-journey-brief-author/templates/taphound-journey-brief.template.md +63 -0
  27. package/assets/skills/taphound-journey-generator/SKILL.md +507 -0
  28. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/prompts/check-completion.md +3 -0
  29. package/assets/skills/taphound-journey-generator/prompts/consume-journey-brief.md +52 -0
  30. package/assets/skills/taphound-journey-generator/prompts/generate-step.md +211 -0
  31. package/assets/skills/taphound-journey-generator/prompts/select-flow.md +91 -0
  32. package/assets/skills/taphound-journey-generator/references/bridge.md +35 -0
  33. package/assets/skills/taphound-journey-generator/references/mid-session.md +44 -0
  34. package/assets/skills/taphound-journey-generator/schemas/external-flow.json +37 -0
  35. package/assets/skills/taphound-journey-generator/schemas/flow.json +28 -0
  36. package/assets/skills/taphound-journey-generator/schemas/journey-source.json +25 -0
  37. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/observe-output.json +13 -4
  38. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/proposed-step-envelope.json +127 -5
  39. package/assets/skills/taphound-journey-generator/scripts/envelope.mjs +578 -0
  40. package/assets/skills/taphound-journey-generator/templates/external-flow.example.json +22 -0
  41. package/assets/skills/taphound-journey-generator/templates/flow.example.json +22 -0
  42. package/assets/skills/taphound-journey-generator/templates/journey-source.example.json +17 -0
  43. package/assets/skills/taphound-journey-generator/templates/taphound-journey-brief.example.md +70 -0
  44. package/assets/skills/taphound-verify-change/SKILL.md +91 -0
  45. package/assets/skills/taphound-verify-change/references/accept.md +38 -0
  46. package/assets/skills/taphound-verify-change/references/preserve.md +357 -0
  47. package/assets/skills/taphound-verify-change/schemas/workflow-manifest.schema.json +295 -0
  48. package/assets/skills/taphound-verify-change/scripts/handoff.mjs +482 -0
  49. package/assets/skills/taphound-verify-change/scripts/ui-refactor.mjs +505 -0
  50. package/dist/adapters/adb/adb-adapter.d.ts +16 -2
  51. package/dist/adapters/adb/adb-adapter.js +199 -0
  52. package/dist/adapters/adb/system-uiautomator-snapshot-provider.d.ts +13 -0
  53. package/dist/adapters/adb/system-uiautomator-snapshot-provider.js +122 -0
  54. package/dist/adapters/adb/ui-automator-parser.d.ts +3 -0
  55. package/dist/adapters/adb/ui-automator-parser.js +160 -0
  56. package/dist/adapters/adb/window-topology-parser.d.ts +2 -0
  57. package/dist/adapters/adb/window-topology-parser.js +101 -0
  58. package/dist/adapters/android-cli/android-cli-adapter.d.ts +16 -8
  59. package/dist/adapters/android-cli/android-cli-adapter.js +137 -15
  60. package/dist/adapters/android-cli/android-cli-snapshot-provider.d.ts +9 -0
  61. package/dist/adapters/android-cli/android-cli-snapshot-provider.js +99 -0
  62. package/dist/adapters/android-cli/layout-parser.js +12 -8
  63. package/dist/adapters/appium/appium-doctor.d.ts +7 -0
  64. package/dist/adapters/appium/appium-doctor.js +128 -0
  65. package/dist/adapters/appium/appium-ui-snapshot-provider.d.ts +42 -0
  66. package/dist/adapters/appium/appium-ui-snapshot-provider.js +309 -0
  67. package/dist/adapters/camera/camera-probe-adapter.d.ts +18 -0
  68. package/dist/adapters/camera/camera-probe-adapter.js +286 -0
  69. package/dist/adapters/filesystem/artifact-store.js +40 -1
  70. package/dist/adapters/filesystem/context-document-writer.d.ts +15 -0
  71. package/dist/adapters/filesystem/context-document-writer.js +112 -0
  72. package/dist/adapters/filesystem/diagnostics-journal.d.ts +17 -0
  73. package/dist/adapters/filesystem/diagnostics-journal.js +130 -0
  74. package/dist/adapters/filesystem/external-flow-registry.d.ts +17 -0
  75. package/dist/adapters/filesystem/external-flow-registry.js +325 -0
  76. package/dist/adapters/filesystem/generation-session-store.d.ts +17 -11
  77. package/dist/adapters/filesystem/generation-session-store.js +281 -855
  78. package/dist/adapters/filesystem/generation-store/session-lock.d.ts +29 -0
  79. package/dist/adapters/filesystem/generation-store/session-lock.js +202 -0
  80. package/dist/adapters/filesystem/generation-store/session-transitions.d.ts +24 -0
  81. package/dist/adapters/filesystem/generation-store/session-transitions.js +340 -0
  82. package/dist/adapters/filesystem/generation-store/store-files.d.ts +50 -0
  83. package/dist/adapters/filesystem/generation-store/store-files.js +363 -0
  84. package/dist/adapters/filesystem/journey-composition-store.d.ts +23 -0
  85. package/dist/adapters/filesystem/journey-composition-store.js +151 -0
  86. package/dist/adapters/filesystem/knowledge-registry.d.ts +12 -0
  87. package/dist/adapters/filesystem/knowledge-registry.js +223 -0
  88. package/dist/adapters/filesystem/project-bound-file.js +3 -5
  89. package/dist/adapters/filesystem/project-file-inspector.d.ts +2 -0
  90. package/dist/adapters/filesystem/project-file-inspector.js +57 -28
  91. package/dist/adapters/filesystem/project-identity-inspector.d.ts +7 -0
  92. package/dist/adapters/filesystem/project-identity-inspector.js +215 -0
  93. package/dist/adapters/filesystem/project-inventory-inspector.d.ts +8 -0
  94. package/dist/adapters/filesystem/project-inventory-inspector.js +93 -0
  95. package/dist/adapters/filesystem/project-module-discoverer.d.ts +6 -0
  96. package/dist/adapters/filesystem/project-module-discoverer.js +158 -0
  97. package/dist/adapters/filesystem/skill-installer.d.ts +7 -2
  98. package/dist/adapters/filesystem/skill-installer.js +40 -11
  99. package/dist/adapters/filesystem/workspace-layout.d.ts +7 -0
  100. package/dist/adapters/filesystem/workspace-layout.js +57 -0
  101. package/dist/adapters/git/node-git-diff.d.ts +17 -0
  102. package/dist/adapters/git/node-git-diff.js +114 -0
  103. package/dist/adapters/process/node-detached-process-launcher.d.ts +6 -0
  104. package/dist/adapters/process/node-detached-process-launcher.js +96 -0
  105. package/dist/adapters/process/node-process-runner.js +11 -2
  106. package/dist/adapters/prompt/inquirer-align-prompt.d.ts +27 -0
  107. package/dist/adapters/prompt/inquirer-align-prompt.js +65 -0
  108. package/dist/adapters/prompt/inquirer-generation-prompt.js +3 -0
  109. package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +9 -1
  110. package/dist/adapters/prompt/inquirer-recorder-prompt.js +63 -0
  111. package/dist/adapters/runtime/adb-runtime-backend.d.ts +67 -0
  112. package/dist/adapters/runtime/adb-runtime-backend.js +206 -0
  113. package/dist/adapters/runtime/fake-runtime-backend.d.ts +87 -0
  114. package/dist/adapters/runtime/fake-runtime-backend.js +240 -0
  115. package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.d.ts +45 -0
  116. package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.js +153 -0
  117. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.d.ts +23 -0
  118. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.js +39 -0
  119. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.d.ts +20 -0
  120. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.js +180 -0
  121. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.d.ts +78 -0
  122. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.js +379 -0
  123. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-tools.d.ts +46 -0
  124. package/dist/adapters/runtime/session-adb-view.d.ts +53 -0
  125. package/dist/adapters/runtime/session-adb-view.js +193 -0
  126. package/dist/adapters/runtime/session-backed-ports.d.ts +21 -0
  127. package/dist/adapters/runtime/session-backed-ports.js +43 -0
  128. package/dist/adapters/runtime/shared-session-runtime-backend.d.ts +12 -0
  129. package/dist/adapters/runtime/shared-session-runtime-backend.js +64 -0
  130. package/dist/adapters/ui/auto-ui-snapshot-provider.d.ts +8 -0
  131. package/dist/adapters/ui/auto-ui-snapshot-provider.js +48 -0
  132. package/dist/adapters/ui/device-layout-temp.d.ts +6 -0
  133. package/dist/adapters/ui/device-layout-temp.js +18 -0
  134. package/dist/adapters/ui/device-ui-environment.d.ts +13 -0
  135. package/dist/adapters/ui/device-ui-environment.js +81 -0
  136. package/dist/adapters/ui/layout-normalization.d.ts +9 -0
  137. package/dist/adapters/ui/layout-normalization.js +44 -0
  138. package/dist/adapters/ui/ui-snapshot-error.d.ts +11 -0
  139. package/dist/adapters/ui/ui-snapshot-error.js +12 -0
  140. package/dist/adapters/ui/ui-snapshot-support.d.ts +15 -0
  141. package/dist/adapters/ui/ui-snapshot-support.js +12 -0
  142. package/dist/application/align/align-service.d.ts +42 -0
  143. package/dist/application/align/align-service.js +106 -0
  144. package/dist/application/assertion/expectation-evaluator.d.ts +18 -5
  145. package/dist/application/assertion/expectation-evaluator.js +156 -18
  146. package/dist/application/assertion/guarded-expectation.d.ts +45 -0
  147. package/dist/application/assertion/guarded-expectation.js +74 -0
  148. package/dist/application/checkpoint/baseline-capturer.d.ts +26 -0
  149. package/dist/application/checkpoint/baseline-capturer.js +163 -0
  150. package/dist/application/checkpoint/baseline-error.d.ts +5 -0
  151. package/dist/application/checkpoint/baseline-error.js +8 -0
  152. package/dist/application/checkpoint/baseline-service.d.ts +31 -0
  153. package/dist/application/checkpoint/baseline-service.js +147 -0
  154. package/dist/application/checkpoint/checkpoint-evaluator.d.ts +35 -0
  155. package/dist/application/checkpoint/checkpoint-evaluator.js +415 -0
  156. package/dist/application/checkpoint/regression-comparator.d.ts +18 -0
  157. package/dist/application/checkpoint/regression-comparator.js +192 -0
  158. package/dist/application/collector/logcat-collector.d.ts +22 -1
  159. package/dist/application/collector/logcat-collector.js +103 -15
  160. package/dist/application/collector/logcat-event.d.ts +8 -0
  161. package/dist/application/collector/logcat-event.js +79 -0
  162. package/dist/application/collector/logcat-stop.d.ts +2 -0
  163. package/dist/application/collector/logcat-stop.js +11 -0
  164. package/dist/application/context/context-generator.d.ts +45 -0
  165. package/dist/application/context/context-generator.js +228 -0
  166. package/dist/application/context/context-loader.d.ts +40 -0
  167. package/dist/application/context/context-loader.js +223 -0
  168. package/dist/application/context/context-refresher.d.ts +59 -0
  169. package/dist/application/context/context-refresher.js +349 -0
  170. package/dist/application/context/context-rehasher.d.ts +36 -0
  171. package/dist/application/context/context-rehasher.js +110 -0
  172. package/dist/application/context/context-validator.d.ts +23 -1
  173. package/dist/application/context/context-validator.js +116 -9
  174. package/dist/application/context/evidence-hash.d.ts +1 -0
  175. package/dist/application/context/evidence-hash.js +125 -0
  176. package/dist/application/context/evidence-match.d.ts +9 -0
  177. package/dist/application/context/evidence-match.js +8 -0
  178. package/dist/application/context/shard-identity.d.ts +2 -0
  179. package/dist/application/context/shard-identity.js +7 -0
  180. package/dist/application/contract/contract-loader.d.ts +25 -0
  181. package/dist/application/contract/contract-loader.js +68 -0
  182. package/dist/application/contract/contract-review.d.ts +25 -0
  183. package/dist/application/contract/contract-review.js +51 -0
  184. package/dist/application/contract/contract-verifier.d.ts +53 -0
  185. package/dist/application/contract/contract-verifier.js +645 -0
  186. package/dist/application/devices/resolve-device-assignments.d.ts +21 -0
  187. package/dist/application/devices/resolve-device-assignments.js +60 -0
  188. package/dist/application/diagnosis/failure-classifier.d.ts +23 -0
  189. package/dist/application/diagnosis/failure-classifier.js +179 -0
  190. package/dist/application/diagnostics/diagnostics-exporter.d.ts +29 -0
  191. package/dist/application/diagnostics/diagnostics-exporter.js +283 -0
  192. package/dist/application/diagnostics/ui-capture-telemetry.d.ts +16 -0
  193. package/dist/application/diagnostics/ui-capture-telemetry.js +81 -0
  194. package/dist/application/doctor/doctor-service.d.ts +36 -3
  195. package/dist/application/doctor/doctor-service.js +255 -25
  196. package/dist/application/generation/generation-app-preparer.d.ts +20 -0
  197. package/dist/application/generation/generation-app-preparer.js +28 -0
  198. package/dist/application/generation/generation-config-service.d.ts +21 -0
  199. package/dist/application/generation/generation-config-service.js +53 -0
  200. package/dist/application/generation/generation-confirmation-service.d.ts +14 -3
  201. package/dist/application/generation/generation-confirmation-service.js +109 -24
  202. package/dist/application/generation/generation-context-snapshot.d.ts +6 -0
  203. package/dist/application/generation/generation-context-snapshot.js +29 -0
  204. package/dist/application/generation/generation-finalizer.d.ts +15 -9
  205. package/dist/application/generation/generation-finalizer.js +167 -70
  206. package/dist/application/generation/generation-publisher.js +12 -8
  207. package/dist/application/generation/generation-recovery-service.d.ts +35 -0
  208. package/dist/application/generation/generation-recovery-service.js +144 -0
  209. package/dist/application/generation/generation-reopen-service.d.ts +15 -0
  210. package/dist/application/generation/generation-reopen-service.js +37 -0
  211. package/dist/application/generation/generation-replace-service.d.ts +32 -0
  212. package/dist/application/generation/generation-replace-service.js +116 -0
  213. package/dist/application/generation/generation-starter.d.ts +44 -4
  214. package/dist/application/generation/generation-starter.js +175 -8
  215. package/dist/application/generation/generation-step-executor.d.ts +67 -5
  216. package/dist/application/generation/generation-step-executor.js +471 -108
  217. package/dist/application/generation/proposed-step-validator.js +96 -30
  218. package/dist/application/generation/replay-policy-loader.d.ts +16 -0
  219. package/dist/application/generation/replay-policy-loader.js +29 -0
  220. package/dist/application/generation/risk-evaluator.d.ts +4 -1
  221. package/dist/application/generation/risk-evaluator.js +118 -3
  222. package/dist/application/generation/runtime-observer.d.ts +39 -7
  223. package/dist/application/generation/runtime-observer.js +193 -24
  224. package/dist/application/impact/impact-resolver.d.ts +32 -0
  225. package/dist/application/impact/impact-resolver.js +160 -0
  226. package/dist/application/init/init-service.js +26 -13
  227. package/dist/application/interaction/action-executor.d.ts +3 -2
  228. package/dist/application/interaction/action-executor.js +11 -1
  229. package/dist/application/interaction/action-target.d.ts +17 -0
  230. package/dist/application/interaction/action-target.js +40 -0
  231. package/dist/application/interaction/external-step-runner.d.ts +153 -0
  232. package/dist/application/interaction/external-step-runner.js +332 -0
  233. package/dist/application/interaction/fallback-resolver.d.ts +6 -3
  234. package/dist/application/interaction/fallback-resolver.js +7 -5
  235. package/dist/application/interaction/scroll-to-executor.d.ts +17 -3
  236. package/dist/application/interaction/scroll-to-executor.js +118 -36
  237. package/dist/application/journey/external-flow-resolver.d.ts +25 -0
  238. package/dist/application/journey/external-flow-resolver.js +22 -0
  239. package/dist/application/journey/journey-check-service.d.ts +54 -0
  240. package/dist/application/journey/journey-check-service.js +207 -0
  241. package/dist/application/journey/journey-promoter.d.ts +26 -0
  242. package/dist/application/journey/journey-promoter.js +123 -0
  243. package/dist/application/journey/journey-resolver.d.ts +42 -0
  244. package/dist/application/journey/journey-resolver.js +239 -0
  245. package/dist/application/journey/journey-retirer.d.ts +25 -0
  246. package/dist/application/journey/journey-retirer.js +87 -0
  247. package/dist/application/knowledge/anchor-resolver.d.ts +36 -0
  248. package/dist/application/knowledge/anchor-resolver.js +110 -0
  249. package/dist/application/knowledge/knowledge-loader.d.ts +16 -0
  250. package/dist/application/knowledge/knowledge-loader.js +36 -0
  251. package/dist/application/locator/layout-failure-summary.d.ts +2 -0
  252. package/dist/application/locator/layout-failure-summary.js +67 -0
  253. package/dist/application/locator/layout-traversal.d.ts +11 -0
  254. package/dist/application/locator/layout-traversal.js +18 -0
  255. package/dist/application/locator/locator-resolver.d.ts +13 -1
  256. package/dist/application/locator/locator-resolver.js +182 -18
  257. package/dist/application/observe/observe-service.d.ts +20 -0
  258. package/dist/application/observe/observe-service.js +91 -0
  259. package/dist/application/project/project-describer.d.ts +14 -6
  260. package/dist/application/project/project-describer.js +38 -5
  261. package/dist/application/recognition/screen-detector.d.ts +28 -0
  262. package/dist/application/recognition/screen-detector.js +111 -0
  263. package/dist/application/recorder/locator-selector.js +151 -17
  264. package/dist/application/recorder/recorder-service.d.ts +16 -4
  265. package/dist/application/recorder/recorder-service.js +420 -128
  266. package/dist/application/report/report-writer.d.ts +5 -0
  267. package/dist/application/report/report-writer.js +17 -1
  268. package/dist/application/runtime/cold-launch.d.ts +28 -0
  269. package/dist/application/runtime/cold-launch.js +59 -0
  270. package/dist/application/runtime/step-runner.d.ts +49 -4
  271. package/dist/application/runtime/step-runner.js +721 -130
  272. package/dist/application/runtime/verify-runtime.d.ts +57 -7
  273. package/dist/application/runtime/verify-runtime.js +577 -175
  274. package/dist/application/ui/cached-ui-snapshot-provider.d.ts +45 -0
  275. package/dist/application/ui/cached-ui-snapshot-provider.js +111 -0
  276. package/dist/application/ui/observed-ui-snapshot-provider.d.ts +15 -0
  277. package/dist/application/ui/observed-ui-snapshot-provider.js +66 -0
  278. package/dist/application/ui/ui-snapshot-lifecycle.d.ts +2 -0
  279. package/dist/application/ui/ui-snapshot-lifecycle.js +8 -0
  280. package/dist/application/ui/ui-stability-probe.d.ts +9 -0
  281. package/dist/application/ui/ui-stability-probe.js +24 -0
  282. package/dist/application/wait/idle-advice.d.ts +6 -0
  283. package/dist/application/wait/idle-advice.js +27 -0
  284. package/dist/application/wait/idle-profiles.d.ts +15 -0
  285. package/dist/application/wait/idle-profiles.js +75 -0
  286. package/dist/application/wait/idle-waiter.d.ts +28 -4
  287. package/dist/application/wait/idle-waiter.js +201 -15
  288. package/dist/cli/commands/align.d.ts +3 -0
  289. package/dist/cli/commands/align.js +124 -0
  290. package/dist/cli/commands/baseline.d.ts +3 -0
  291. package/dist/cli/commands/baseline.js +130 -0
  292. package/dist/cli/commands/context.js +300 -27
  293. package/dist/cli/commands/contract.d.ts +3 -0
  294. package/dist/cli/commands/contract.js +120 -0
  295. package/dist/cli/commands/diagnose.d.ts +3 -0
  296. package/dist/cli/commands/diagnose.js +84 -0
  297. package/dist/cli/commands/doctor.js +16 -9
  298. package/dist/cli/commands/failure.d.ts +3 -0
  299. package/dist/cli/commands/failure.js +63 -0
  300. package/dist/cli/commands/generation/lifecycle-commands.d.ts +5 -0
  301. package/dist/cli/commands/generation/lifecycle-commands.js +211 -0
  302. package/dist/cli/commands/generation/session-commands.d.ts +11 -0
  303. package/dist/cli/commands/generation/session-commands.js +472 -0
  304. package/dist/cli/commands/generation/shared.d.ts +1414 -0
  305. package/dist/cli/commands/generation/shared.js +291 -0
  306. package/dist/cli/commands/generation/step-commands.d.ts +8 -0
  307. package/dist/cli/commands/generation/step-commands.js +315 -0
  308. package/dist/cli/commands/generation.js +10 -507
  309. package/dist/cli/commands/impact.d.ts +3 -0
  310. package/dist/cli/commands/impact.js +87 -0
  311. package/dist/cli/commands/init.js +13 -6
  312. package/dist/cli/commands/journey.d.ts +3 -0
  313. package/dist/cli/commands/journey.js +382 -0
  314. package/dist/cli/commands/knowledge.d.ts +3 -0
  315. package/dist/cli/commands/knowledge.js +93 -0
  316. package/dist/cli/commands/observe.d.ts +3 -0
  317. package/dist/cli/commands/observe.js +127 -0
  318. package/dist/cli/commands/project.js +2 -1
  319. package/dist/cli/commands/record.js +18 -5
  320. package/dist/cli/commands/verify.js +326 -64
  321. package/dist/cli/dependencies.d.ts +136 -1
  322. package/dist/cli/dependencies.js +504 -52
  323. package/dist/cli/diagnostics-recorder.d.ts +31 -0
  324. package/dist/cli/diagnostics-recorder.js +82 -0
  325. package/dist/cli/diff-verification.d.ts +41 -0
  326. package/dist/cli/diff-verification.js +192 -0
  327. package/dist/cli/main.d.ts +1 -1
  328. package/dist/cli/main.js +107 -2
  329. package/dist/cli/output.d.ts +5 -3
  330. package/dist/cli/output.js +12 -0
  331. package/dist/cli/program.js +21 -1
  332. package/dist/cli/project-root.d.ts +1 -0
  333. package/dist/cli/project-root.js +6 -0
  334. package/dist/cli/runtime-selection.d.ts +22 -0
  335. package/dist/cli/runtime-selection.js +92 -0
  336. package/dist/cli/version.d.ts +1 -0
  337. package/dist/cli/version.js +14 -0
  338. package/dist/cli/workspace-guard.d.ts +2 -0
  339. package/dist/cli/workspace-guard.js +3 -0
  340. package/dist/domain/binding-reference.d.ts +4 -0
  341. package/dist/domain/binding-reference.js +23 -0
  342. package/dist/domain/checkpoint.d.ts +404 -0
  343. package/dist/domain/checkpoint.js +297 -0
  344. package/dist/domain/config.d.ts +107 -1
  345. package/dist/domain/config.js +52 -6
  346. package/dist/domain/contract.d.ts +413 -0
  347. package/dist/domain/contract.js +196 -0
  348. package/dist/domain/diagnostics.d.ts +1396 -0
  349. package/dist/domain/diagnostics.js +193 -0
  350. package/dist/domain/external-flow.d.ts +493 -0
  351. package/dist/domain/external-flow.js +15 -0
  352. package/dist/domain/failure-classification.d.ts +176 -0
  353. package/dist/domain/failure-classification.js +169 -0
  354. package/dist/domain/failure.d.ts +2 -1
  355. package/dist/domain/failure.js +123 -0
  356. package/dist/domain/generation.d.ts +1671 -132
  357. package/dist/domain/generation.js +202 -13
  358. package/dist/domain/geometry.d.ts +13 -0
  359. package/dist/domain/geometry.js +16 -0
  360. package/dist/domain/impact.d.ts +71 -0
  361. package/dist/domain/impact.js +76 -0
  362. package/dist/domain/init.d.ts +12 -4
  363. package/dist/domain/init.js +24 -18
  364. package/dist/domain/journey-composition.d.ts +2285 -0
  365. package/dist/domain/journey-composition.js +85 -0
  366. package/dist/domain/journey-lifecycle.d.ts +10 -0
  367. package/dist/domain/journey-lifecycle.js +9 -0
  368. package/dist/domain/journey.d.ts +2419 -212
  369. package/dist/domain/journey.js +384 -14
  370. package/dist/domain/knowledge.d.ts +165 -0
  371. package/dist/domain/knowledge.js +188 -0
  372. package/dist/domain/layout.d.ts +34 -5
  373. package/dist/domain/layout.js +64 -6
  374. package/dist/domain/locator-evidence.d.ts +3 -0
  375. package/dist/domain/locator-evidence.js +36 -0
  376. package/dist/domain/logcat-event.d.ts +54 -0
  377. package/dist/domain/logcat-event.js +40 -0
  378. package/dist/domain/observation.d.ts +33 -0
  379. package/dist/domain/observation.js +18 -0
  380. package/dist/domain/project-context.d.ts +219 -1
  381. package/dist/domain/project-context.js +209 -6
  382. package/dist/domain/proposed-step.d.ts +340 -60
  383. package/dist/domain/proposed-step.js +23 -3
  384. package/dist/domain/report.d.ts +1455 -28
  385. package/dist/domain/report.js +211 -14
  386. package/dist/domain/runtime-snapshot.d.ts +58 -1
  387. package/dist/domain/runtime-snapshot.js +14 -3
  388. package/dist/domain/runtime.d.ts +50 -0
  389. package/dist/domain/runtime.js +36 -0
  390. package/dist/domain/system-app-profiles.d.ts +6 -0
  391. package/dist/domain/system-app-profiles.js +43 -0
  392. package/dist/domain/ui-backend.d.ts +28 -0
  393. package/dist/domain/ui-backend.js +33 -0
  394. package/dist/domain/ui-cache.d.ts +11 -0
  395. package/dist/domain/ui-cache.js +10 -0
  396. package/dist/domain/verify-receipt.d.ts +17 -0
  397. package/dist/domain/verify-receipt.js +17 -0
  398. package/dist/domain/window-hierarchy.d.ts +84 -0
  399. package/dist/domain/window-hierarchy.js +172 -0
  400. package/dist/domain/workflow-manifest.d.ts +77 -0
  401. package/dist/domain/workflow-manifest.js +136 -0
  402. package/dist/domain/workspace.d.ts +30 -0
  403. package/dist/domain/workspace.js +74 -0
  404. package/dist/ports/adb.d.ts +34 -1
  405. package/dist/ports/align-prompt.d.ts +11 -0
  406. package/dist/ports/align-prompt.js +6 -0
  407. package/dist/ports/anchor-resolver.d.ts +30 -0
  408. package/dist/ports/anchor-resolver.js +1 -0
  409. package/dist/ports/annotated-screen-resolver.d.ts +4 -0
  410. package/dist/ports/annotated-screen-resolver.js +1 -0
  411. package/dist/ports/camera-probe.d.ts +23 -0
  412. package/dist/ports/camera-probe.js +8 -0
  413. package/dist/ports/context-document-writer.d.ts +24 -0
  414. package/dist/ports/context-document-writer.js +1 -0
  415. package/dist/ports/detached-process-launcher.d.ts +12 -0
  416. package/dist/ports/detached-process-launcher.js +1 -0
  417. package/dist/ports/diagnostics.d.ts +15 -0
  418. package/dist/ports/diagnostics.js +1 -0
  419. package/dist/ports/external-flow-registry.d.ts +37 -0
  420. package/dist/ports/external-flow-registry.js +1 -0
  421. package/dist/ports/generation-prompt.d.ts +2 -1
  422. package/dist/ports/generation-session-store.d.ts +12 -2
  423. package/dist/ports/git-diff.d.ts +9 -0
  424. package/dist/ports/git-diff.js +1 -0
  425. package/dist/ports/journey-composition-store.d.ts +17 -0
  426. package/dist/ports/journey-composition-store.js +1 -0
  427. package/dist/ports/knowledge-registry.d.ts +20 -0
  428. package/dist/ports/knowledge-registry.js +1 -0
  429. package/dist/ports/process-runner.d.ts +7 -0
  430. package/dist/ports/project-file-inspector.d.ts +1 -0
  431. package/dist/ports/project-identity-inspector.d.ts +24 -0
  432. package/dist/ports/project-identity-inspector.js +1 -0
  433. package/dist/ports/project-inventory-inspector.d.ts +21 -0
  434. package/dist/ports/project-inventory-inspector.js +1 -0
  435. package/dist/ports/project-module-discoverer.d.ts +26 -0
  436. package/dist/ports/project-module-discoverer.js +1 -0
  437. package/dist/ports/recorder-prompt.d.ts +10 -1
  438. package/dist/ports/runtime-backend.d.ts +89 -0
  439. package/dist/ports/runtime-backend.js +13 -0
  440. package/dist/ports/runtime-capability.d.ts +12 -0
  441. package/dist/ports/runtime-capability.js +20 -0
  442. package/dist/ports/runtime-session-ports.d.ts +25 -0
  443. package/dist/ports/runtime-session-ports.js +1 -0
  444. package/dist/ports/screenshot.d.ts +11 -0
  445. package/dist/ports/screenshot.js +1 -0
  446. package/dist/ports/skill-installer.d.ts +2 -2
  447. package/dist/ports/ui-snapshot.d.ts +35 -0
  448. package/dist/ports/ui-snapshot.js +1 -0
  449. package/dist/ports/ui-stability.d.ts +19 -0
  450. package/dist/ports/ui-stability.js +1 -0
  451. package/dist/ports/workspace-layout.d.ts +4 -0
  452. package/dist/ports/workspace-layout.js +1 -0
  453. package/dist/shared/errors.d.ts +2 -0
  454. package/dist/shared/errors.js +22 -0
  455. package/dist/shared/paths.d.ts +2 -0
  456. package/dist/shared/paths.js +24 -0
  457. package/dist/shared/strings.d.ts +2 -0
  458. package/dist/shared/strings.js +16 -0
  459. package/package.json +7 -2
  460. package/assets/skills/taphound-ai-journey/GUIDE.md +0 -862
  461. package/assets/skills/taphound-ai-journey/SKILL.md +0 -377
  462. package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +0 -526
  463. package/assets/skills/taphound-ai-journey/prompts/generate-step.md +0 -109
  464. package/assets/skills/taphound-ai-journey/schemas/project-context.json +0 -81
  465. package/assets/skills/taphound-ai-journey/templates/project-context.example.json +0 -49
  466. package/dist/ports/android-cli.d.ts +0 -21
  467. /package/dist/{ports/android-cli.js → adapters/runtime/mobile-mcp/mobile-mcp-tools.js} +0 -0
@@ -0,0 +1,223 @@
1
+ import { resolve } from "node:path";
2
+ import { ProjectContextModuleSchema, ProjectContextSchema, ResolvedProjectContextSchema } from "../../domain/project-context.js";
3
+ import { projectRelativePath } from "../../shared/paths.js";
4
+ import { assertProjectPathUnder, CONTEXT_DIR } from "../../domain/workspace.js";
5
+ import { compareStrings } from "../../shared/strings.js";
6
+ import { assertShardIdentity } from "./shard-identity.js";
7
+ export const MAX_CONTEXT_SHARD_BYTES = 4 * 1024 * 1024;
8
+ export class ContextLoadError extends Error {
9
+ code;
10
+ name = "ContextLoadError";
11
+ constructor(code, message) {
12
+ super(message);
13
+ this.code = code;
14
+ }
15
+ }
16
+ function inspectionMessage(label, path, inspection) {
17
+ switch (inspection.status) {
18
+ case "rootNotFound":
19
+ case "rootUnreadable":
20
+ return "Project root does not exist or cannot be read";
21
+ case "rootNotDirectory":
22
+ return "Project root is not a directory";
23
+ case "notFound":
24
+ return `${label} does not exist: ${path}`;
25
+ case "unreadable":
26
+ return `${label} cannot be read: ${path}`;
27
+ case "escape":
28
+ return `${label} resolves outside the project: ${path}`;
29
+ case "changedIdentity":
30
+ return `${label} changed during inspection: ${path}`;
31
+ case "notFile":
32
+ return `${label} path is not a file: ${path}`;
33
+ case "tooLarge":
34
+ return `${label} exceeds ${String(MAX_CONTEXT_SHARD_BYTES)} bytes: ${path}`;
35
+ }
36
+ }
37
+ function selectedReferences(bundle, requestedIds) {
38
+ const byId = new Map(bundle.modules.map((module) => [module.id, module]));
39
+ const selected = new Set(requestedIds === undefined || requestedIds.length === 0
40
+ ? bundle.modules.map((module) => module.id)
41
+ : [
42
+ ...bundle.modules
43
+ .filter((module) => module.kind === "application")
44
+ .map((module) => module.id),
45
+ ...requestedIds
46
+ ]);
47
+ const pending = [...selected];
48
+ while (pending.length > 0) {
49
+ const id = pending.pop();
50
+ if (id === undefined) {
51
+ continue;
52
+ }
53
+ const module = byId.get(id);
54
+ if (module === undefined) {
55
+ throw new ContextLoadError("CONTEXT_MODULE_NOT_FOUND", `Project Context module does not exist: ${id}`);
56
+ }
57
+ for (const dependency of module.dependsOn) {
58
+ if (!selected.has(dependency)) {
59
+ selected.add(dependency);
60
+ pending.push(dependency);
61
+ }
62
+ }
63
+ }
64
+ return bundle.modules.filter((module) => selected.has(module.id));
65
+ }
66
+ function effectiveContext(bundle, modules, indexHash) {
67
+ const evidence = [
68
+ ...bundle.manifest.files,
69
+ ...modules.flatMap((module) => module.manifest.files)
70
+ ];
71
+ const byPath = new Map();
72
+ for (const file of evidence) {
73
+ const existing = byPath.get(file.path);
74
+ if (existing !== undefined && existing.sha256 !== file.sha256) {
75
+ throw new ContextLoadError("CONTEXT_INVALID", `Conflicting Context evidence hash for ${file.path}`);
76
+ }
77
+ byPath.set(file.path, file);
78
+ }
79
+ return ResolvedProjectContextSchema.parse({
80
+ version: 2,
81
+ packageName: bundle.packageName,
82
+ launchActivity: bundle.launchActivity,
83
+ manifest: {
84
+ version: 1,
85
+ files: [...byPath.values()].sort((left, right) => (compareStrings(left.path, right.path)))
86
+ },
87
+ interactionPolicy: bundle.interactionPolicy,
88
+ selection: {
89
+ bundleVersion: 2,
90
+ indexHash,
91
+ modules: modules.map((module) => {
92
+ const reference = bundle.modules.find((candidate) => candidate.id === module.moduleId);
93
+ if (reference === undefined) {
94
+ throw new ContextLoadError("CONTEXT_INVALID", `Context shard is missing from its index: ${module.moduleId}`);
95
+ }
96
+ return {
97
+ id: reference.id,
98
+ sha256: reference.sha256,
99
+ projectDir: module.projectDir,
100
+ inventory: {
101
+ pathSetSha256: module.inventory.pathSetSha256,
102
+ categories: module.inventory.categories
103
+ }
104
+ };
105
+ })
106
+ }
107
+ });
108
+ }
109
+ function sameStrings(left, right) {
110
+ if (left.length !== right.length) {
111
+ return false;
112
+ }
113
+ const sortedLeft = [...left].sort(compareStrings);
114
+ const sortedRight = [...right].sort(compareStrings);
115
+ return sortedLeft.every((value, index) => value === sortedRight[index]);
116
+ }
117
+ export class ContextLoader {
118
+ dependencies;
119
+ constructor(dependencies) {
120
+ this.dependencies = dependencies;
121
+ }
122
+ readStableDocument = async (input) => {
123
+ const before = await this.dependencies.files.inspectProjectFile({
124
+ projectRoot: input.root,
125
+ relativePath: input.relativePath,
126
+ maximumBytes: MAX_CONTEXT_SHARD_BYTES
127
+ });
128
+ if (before.status !== "inspected") {
129
+ throw new ContextLoadError("CONTEXT_INVALID", inspectionMessage(input.label, input.relativePath, before));
130
+ }
131
+ let document;
132
+ try {
133
+ document = await this.dependencies.readJson(resolve(input.root, input.relativePath));
134
+ }
135
+ catch (error) {
136
+ throw new ContextLoadError("CONTEXT_INVALID", error instanceof Error
137
+ ? error.message
138
+ : `Unable to read ${input.label}: ${input.relativePath}`);
139
+ }
140
+ const after = await this.dependencies.files.inspectProjectFile({
141
+ projectRoot: input.root,
142
+ relativePath: input.relativePath,
143
+ maximumBytes: MAX_CONTEXT_SHARD_BYTES
144
+ });
145
+ if (after.status !== "inspected"
146
+ || after.sha256 !== before.sha256) {
147
+ throw new ContextLoadError("CONTEXT_INVALID", `${input.label} changed while loading: ${input.relativePath}`);
148
+ }
149
+ return { document, sha256: before.sha256 };
150
+ };
151
+ readIndex = async (input) => {
152
+ const contextRoot = input.projectRoot;
153
+ const relativeContextPath = projectRelativePath(contextRoot, input.contextPath, (message) => new ContextLoadError("CONTEXT_INVALID", message));
154
+ try {
155
+ assertProjectPathUnder(contextRoot, relativeContextPath, CONTEXT_DIR, "Project Context");
156
+ }
157
+ catch (error) {
158
+ throw new ContextLoadError("CONTEXT_INVALID", error instanceof Error ? error.message : String(error));
159
+ }
160
+ const loaded = await this.readStableDocument({
161
+ root: contextRoot,
162
+ relativePath: relativeContextPath,
163
+ label: "Project Context index"
164
+ });
165
+ const parsedBundle = ProjectContextSchema.safeParse(loaded.document);
166
+ if (!parsedBundle.success) {
167
+ throw new ContextLoadError("CONTEXT_INVALID", "Project Context does not match the version 2 index schema");
168
+ }
169
+ return {
170
+ bundle: parsedBundle.data,
171
+ indexHash: loaded.sha256
172
+ };
173
+ };
174
+ load = async (input) => {
175
+ const { bundle, indexHash } = await this.readIndex(input);
176
+ const references = selectedReferences(bundle, input.moduleIds);
177
+ if (input.allowIncomplete !== true) {
178
+ const incomplete = references.find((module) => module.status === "partial"
179
+ || module.status === "notAnalyzed");
180
+ if (incomplete !== undefined) {
181
+ throw new ContextLoadError("CONTEXT_MODULE_INCOMPLETE", `Project Context module is ${incomplete.status}: ${incomplete.id}`);
182
+ }
183
+ }
184
+ const modules = [];
185
+ for (const reference of references) {
186
+ const loaded = await this.readStableDocument({
187
+ root: input.projectRoot,
188
+ relativePath: reference.contextPath,
189
+ label: "Context shard"
190
+ });
191
+ if (loaded.sha256 !== reference.sha256) {
192
+ throw new ContextLoadError("CONTEXT_STALE", `Context shard changed: ${reference.contextPath}`);
193
+ }
194
+ const module = ProjectContextModuleSchema.safeParse(loaded.document);
195
+ if (!module.success) {
196
+ throw new ContextLoadError("CONTEXT_INVALID", `Context shard does not match the module schema: ${reference.contextPath}`);
197
+ }
198
+ assertShardIdentity(reference, module.data, (message) => new ContextLoadError("CONTEXT_INVALID", message));
199
+ if (!sameStrings(reference.features, module.data.summary.features)
200
+ || !sameStrings(reference.activities, module.data.summary.activities.map((activity) => activity.name))) {
201
+ throw new ContextLoadError("CONTEXT_INVALID", `Context shard routing summary does not match its index: ${reference.id}`);
202
+ }
203
+ const inventory = await this.dependencies.inventory.inspectProjectInventory({
204
+ projectRoot: input.projectRoot,
205
+ projectDir: module.data.projectDir,
206
+ categories: module.data.inventory.categories
207
+ });
208
+ if (inventory.status !== "inspected") {
209
+ throw new ContextLoadError("CONTEXT_INVALID", `Unable to inspect module inventory: ${reference.id}`);
210
+ }
211
+ if (inventory.pathSetSha256 !== module.data.inventory.pathSetSha256) {
212
+ throw new ContextLoadError("CONTEXT_STALE", `Module file inventory changed: ${reference.id}`);
213
+ }
214
+ modules.push(module.data);
215
+ }
216
+ const context = effectiveContext(bundle, modules, indexHash);
217
+ return {
218
+ context,
219
+ bundle,
220
+ modules
221
+ };
222
+ };
223
+ }
@@ -0,0 +1,59 @@
1
+ import type { ContextDocumentWriter } from "../../ports/context-document-writer.js";
2
+ import type { ProjectFileInspector } from "../../ports/project-file-inspector.js";
3
+ import type { ProjectInventoryInspector } from "../../ports/project-inventory-inspector.js";
4
+ import { type ContextLoader } from "./context-loader.js";
5
+ export type ContextRefreshErrorCode = "CONTEXT_INVALID" | "CONTEXT_MODULE_NOT_FOUND" | "CONTEXT_WRITE_FAILED";
6
+ export declare class ContextRefreshError extends Error {
7
+ readonly code: ContextRefreshErrorCode;
8
+ readonly name = "ContextRefreshError";
9
+ constructor(code: ContextRefreshErrorCode, message: string);
10
+ }
11
+ export type ContextRefreshBlockCode = "EVIDENCE_UNRESOLVED" | "EVIDENCE_SEMANTIC_CHANGED" | "MODULE_INVENTORY_CHANGED";
12
+ export type ContextRefreshResolution = "pruneDeleted" | "acceptSourceChanges" | "reanalyze";
13
+ export interface ContextRefreshBlock {
14
+ code: ContextRefreshBlockCode;
15
+ resolution: ContextRefreshResolution;
16
+ message: string;
17
+ }
18
+ export interface ContextRefreshScopeReport {
19
+ scope: "index" | "module";
20
+ id: string;
21
+ contextPath: string;
22
+ written: boolean;
23
+ semanticBackfilled: number;
24
+ formattingRehashed: number;
25
+ semanticChanged: string[];
26
+ unresolved: string[];
27
+ pruned: number;
28
+ droppedIneligible: number;
29
+ inventoryChanged: boolean;
30
+ }
31
+ export interface ContextRefreshResult {
32
+ status: "refreshed" | "unchanged" | "blocked";
33
+ indexHash: string;
34
+ acceptedSourceChanges: boolean;
35
+ scopes: ContextRefreshScopeReport[];
36
+ blocked: ContextRefreshBlock[];
37
+ }
38
+ export interface ContextRefreshInput {
39
+ projectRoot: string;
40
+ contextPath: string;
41
+ moduleIds?: string[] | undefined;
42
+ acceptSourceChanges?: boolean | undefined;
43
+ pruneDeleted?: boolean | undefined;
44
+ }
45
+ export interface ContextRefresherDependencies {
46
+ files: ProjectFileInspector;
47
+ inventory: ProjectInventoryInspector;
48
+ loader: Pick<ContextLoader, "readIndex">;
49
+ writer: ContextDocumentWriter;
50
+ }
51
+ export declare class ContextRefresher {
52
+ private readonly dependencies;
53
+ constructor(dependencies: ContextRefresherDependencies);
54
+ readonly refresh: (input: ContextRefreshInput) => Promise<ContextRefreshResult>;
55
+ private readonly refreshModule;
56
+ private readonly refreshManifest;
57
+ private readonly readShard;
58
+ private readonly writeDocument;
59
+ }
@@ -0,0 +1,349 @@
1
+ import { ProjectContextModuleSchema, ProjectContextSchema } from "../../domain/project-context.js";
2
+ import { projectRelativePath } from "../../shared/paths.js";
3
+ import { MAX_CONTEXT_SHARD_BYTES } from "./context-loader.js";
4
+ import { MAX_CONTEXT_EVIDENCE_BYTES } from "./context-validator.js";
5
+ import { semanticSha256 } from "./evidence-hash.js";
6
+ import { assertShardIdentity } from "./shard-identity.js";
7
+ import { assertProjectPathUnder, CONTEXT_DIR } from "../../domain/workspace.js";
8
+ export class ContextRefreshError extends Error {
9
+ code;
10
+ name = "ContextRefreshError";
11
+ constructor(code, message) {
12
+ super(message);
13
+ this.code = code;
14
+ }
15
+ }
16
+ function selectedReferences(bundle, moduleIds) {
17
+ if (moduleIds === undefined || moduleIds.length === 0) {
18
+ return bundle.modules;
19
+ }
20
+ const requested = new Set(moduleIds);
21
+ for (const id of requested) {
22
+ if (!bundle.modules.some((module) => module.id === id)) {
23
+ throw new ContextRefreshError("CONTEXT_MODULE_NOT_FOUND", `Project Context module does not exist: ${id}`);
24
+ }
25
+ }
26
+ return bundle.modules.filter((module) => requested.has(module.id));
27
+ }
28
+ function scopeBlocks(report, acceptSourceChanges) {
29
+ const blocks = [];
30
+ if (report.unresolved.length > 0) {
31
+ const allNotFound = report.unresolved.every((entry) => entry.endsWith(": notFound"));
32
+ blocks.push({
33
+ code: "EVIDENCE_UNRESOLVED",
34
+ resolution: allNotFound ? "pruneDeleted" : "reanalyze",
35
+ message: `${report.id}: evidence cannot be hashed (${report.unresolved.join(", ")})`
36
+ });
37
+ }
38
+ if (acceptSourceChanges) {
39
+ return blocks;
40
+ }
41
+ if (report.semanticChanged.length > 0) {
42
+ blocks.push({
43
+ code: "EVIDENCE_SEMANTIC_CHANGED",
44
+ resolution: "acceptSourceChanges",
45
+ message: `${report.id}: ${String(report.semanticChanged.length)} evidence files changed semantically (${report.semanticChanged.join(", ")})`
46
+ });
47
+ }
48
+ if (report.inventoryChanged) {
49
+ blocks.push({
50
+ code: "MODULE_INVENTORY_CHANGED",
51
+ resolution: "acceptSourceChanges",
52
+ message: `${report.id}: module file inventory changed`
53
+ });
54
+ }
55
+ return blocks;
56
+ }
57
+ export class ContextRefresher {
58
+ dependencies;
59
+ constructor(dependencies) {
60
+ this.dependencies = dependencies;
61
+ }
62
+ refresh = async (input) => {
63
+ const indexPath = projectRelativePath(input.projectRoot, input.contextPath, (message) => new ContextRefreshError("CONTEXT_INVALID", message));
64
+ try {
65
+ assertProjectPathUnder(input.projectRoot, indexPath, CONTEXT_DIR, "Project Context output");
66
+ }
67
+ catch (error) {
68
+ throw new ContextRefreshError("CONTEXT_INVALID", error instanceof Error ? error.message : String(error));
69
+ }
70
+ const { bundle, indexHash } = await this.dependencies.loader.readIndex({
71
+ projectRoot: input.projectRoot,
72
+ contextPath: input.contextPath
73
+ });
74
+ const acceptSourceChanges = input.acceptSourceChanges === true;
75
+ const pruneDeleted = input.pruneDeleted === true;
76
+ const references = selectedReferences(bundle, input.moduleIds);
77
+ const root = await this.refreshManifest(input.projectRoot, bundle.manifest, acceptSourceChanges, pruneDeleted);
78
+ const rootReport = {
79
+ scope: "index",
80
+ id: "index",
81
+ contextPath: indexPath,
82
+ written: false,
83
+ semanticBackfilled: root.semanticBackfilled,
84
+ formattingRehashed: root.formattingRehashed,
85
+ semanticChanged: root.semanticChanged,
86
+ unresolved: root.unresolved,
87
+ pruned: root.pruned,
88
+ droppedIneligible: root.droppedIneligible,
89
+ inventoryChanged: false
90
+ };
91
+ const modules = [];
92
+ for (const reference of references) {
93
+ modules.push(await this.refreshModule(input.projectRoot, reference, acceptSourceChanges, pruneDeleted));
94
+ }
95
+ const blocked = [
96
+ ...scopeBlocks(rootReport, acceptSourceChanges),
97
+ ...modules.flatMap((module) => scopeBlocks(module.report, acceptSourceChanges))
98
+ ];
99
+ const scopes = [rootReport, ...modules.map((module) => module.report)];
100
+ if (blocked.length > 0) {
101
+ return {
102
+ status: "blocked",
103
+ indexHash,
104
+ acceptedSourceChanges: acceptSourceChanges,
105
+ scopes,
106
+ blocked
107
+ };
108
+ }
109
+ const moduleHashes = new Map();
110
+ for (const module of modules) {
111
+ if (!module.modified) {
112
+ moduleHashes.set(module.reference.id, module.currentSha256);
113
+ }
114
+ }
115
+ const modifiedModules = modules.filter((module) => module.modified);
116
+ if (modifiedModules.length > 0) {
117
+ const batchWriter = this.dependencies.writer.writeContextDocumentBatch;
118
+ if (batchWriter !== undefined) {
119
+ const results = await batchWriter({
120
+ projectRoot: input.projectRoot,
121
+ documents: modifiedModules.map((module) => ({
122
+ relativePath: module.reference.contextPath,
123
+ document: module.document
124
+ }))
125
+ });
126
+ for (let i = 0; i < modifiedModules.length; i++) {
127
+ const module = modifiedModules[i];
128
+ if (module === undefined) {
129
+ continue;
130
+ }
131
+ const result = results[i];
132
+ if (result === undefined || result.status !== "written") {
133
+ let message;
134
+ if (result === undefined) {
135
+ message = `Context document cannot be written: ${module.reference.contextPath}`;
136
+ }
137
+ else if (result.status === "escape") {
138
+ message = `Context document resolves outside the project: ${module.reference.contextPath}`;
139
+ }
140
+ else {
141
+ message = `Context document cannot be written: ${module.reference.contextPath} (${result.message})`;
142
+ }
143
+ throw new ContextRefreshError("CONTEXT_WRITE_FAILED", message);
144
+ }
145
+ moduleHashes.set(module.reference.id, result.sha256);
146
+ module.report.written = true;
147
+ }
148
+ }
149
+ else {
150
+ for (const module of modifiedModules) {
151
+ moduleHashes.set(module.reference.id, await this.writeDocument(input.projectRoot, module.reference.contextPath, module.document));
152
+ module.report.written = true;
153
+ }
154
+ }
155
+ }
156
+ const nextModules = bundle.modules.map((module) => {
157
+ const sha256 = moduleHashes.get(module.id);
158
+ return sha256 === undefined || sha256 === module.sha256
159
+ ? module
160
+ : { ...module, sha256 };
161
+ });
162
+ const indexModified = root.modified
163
+ || nextModules.some((module, index) => module !== bundle.modules[index]);
164
+ if (!indexModified) {
165
+ return {
166
+ status: modules.some((module) => module.report.written)
167
+ ? "refreshed"
168
+ : "unchanged",
169
+ indexHash,
170
+ acceptedSourceChanges: acceptSourceChanges,
171
+ scopes,
172
+ blocked
173
+ };
174
+ }
175
+ const nextBundle = ProjectContextSchema.safeParse({
176
+ ...bundle,
177
+ manifest: { ...bundle.manifest, files: root.files },
178
+ modules: nextModules
179
+ });
180
+ if (!nextBundle.success) {
181
+ throw new ContextRefreshError("CONTEXT_INVALID", "Refreshed Project Context index does not match the version 2 schema");
182
+ }
183
+ const nextIndexHash = await this.writeDocument(input.projectRoot, indexPath, nextBundle.data);
184
+ rootReport.written = true;
185
+ return {
186
+ status: "refreshed",
187
+ indexHash: nextIndexHash,
188
+ acceptedSourceChanges: acceptSourceChanges,
189
+ scopes,
190
+ blocked
191
+ };
192
+ };
193
+ refreshModule = async (projectRoot, reference, acceptSourceChanges, pruneDeleted) => {
194
+ const shard = await this.readShard(projectRoot, reference);
195
+ const inventory = await this.dependencies.inventory.inspectProjectInventory({
196
+ projectRoot,
197
+ projectDir: shard.document.projectDir,
198
+ categories: shard.document.inventory.categories
199
+ });
200
+ if (inventory.status !== "inspected") {
201
+ throw new ContextRefreshError("CONTEXT_INVALID", `Unable to inspect module inventory: ${reference.id}`);
202
+ }
203
+ const inventoryChanged = (inventory.pathSetSha256 !== shard.document.inventory.pathSetSha256);
204
+ const acceptedInventory = inventoryChanged && acceptSourceChanges;
205
+ const manifest = await this.refreshManifest(projectRoot, shard.document.manifest, acceptSourceChanges, pruneDeleted, acceptedInventory ? new Set(inventory.paths) : undefined);
206
+ const document = ProjectContextModuleSchema.safeParse({
207
+ ...shard.document,
208
+ inventory: acceptedInventory
209
+ ? {
210
+ ...shard.document.inventory,
211
+ pathSetSha256: inventory.pathSetSha256
212
+ }
213
+ : shard.document.inventory,
214
+ manifest: { ...shard.document.manifest, files: manifest.files }
215
+ });
216
+ if (!document.success) {
217
+ throw new ContextRefreshError("CONTEXT_INVALID", `Refreshed Context shard does not match the module schema: ${reference.contextPath}`);
218
+ }
219
+ return {
220
+ reference,
221
+ document: document.data,
222
+ currentSha256: shard.sha256,
223
+ modified: manifest.modified || acceptedInventory,
224
+ report: {
225
+ scope: "module",
226
+ id: reference.id,
227
+ contextPath: reference.contextPath,
228
+ written: false,
229
+ semanticBackfilled: manifest.semanticBackfilled,
230
+ formattingRehashed: manifest.formattingRehashed,
231
+ semanticChanged: manifest.semanticChanged,
232
+ unresolved: manifest.unresolved,
233
+ pruned: manifest.pruned,
234
+ droppedIneligible: manifest.droppedIneligible,
235
+ inventoryChanged
236
+ }
237
+ };
238
+ };
239
+ refreshManifest = async (projectRoot, manifest, acceptSourceChanges, pruneDeleted, eligiblePaths) => {
240
+ const files = [];
241
+ const semanticChanged = [];
242
+ const unresolved = [];
243
+ let modified = false;
244
+ let semanticBackfilled = 0;
245
+ let formattingRehashed = 0;
246
+ let pruned = 0;
247
+ let droppedIneligible = 0;
248
+ for (const evidence of manifest.files) {
249
+ const inspection = await this.dependencies.files.inspectProjectFile({
250
+ projectRoot,
251
+ relativePath: evidence.path,
252
+ maximumBytes: MAX_CONTEXT_EVIDENCE_BYTES
253
+ });
254
+ if (inspection.status !== "inspected" || inspection.bytes === undefined) {
255
+ if (pruneDeleted && inspection.status === "notFound") {
256
+ pruned += 1;
257
+ modified = true;
258
+ continue;
259
+ }
260
+ unresolved.push(`${evidence.path}: ${inspection.status}`);
261
+ files.push(evidence);
262
+ continue;
263
+ }
264
+ if (eligiblePaths !== undefined && !eligiblePaths.has(evidence.path)) {
265
+ droppedIneligible += 1;
266
+ modified = true;
267
+ continue;
268
+ }
269
+ const semantic = semanticSha256(inspection.bytes);
270
+ if (inspection.sha256 === evidence.sha256) {
271
+ if (evidence.semanticSha256 === semantic) {
272
+ files.push(evidence);
273
+ continue;
274
+ }
275
+ files.push({ ...evidence, semanticSha256: semantic });
276
+ semanticBackfilled += 1;
277
+ modified = true;
278
+ continue;
279
+ }
280
+ if (evidence.semanticSha256 === semantic) {
281
+ files.push({
282
+ ...evidence,
283
+ sha256: inspection.sha256,
284
+ semanticSha256: semantic
285
+ });
286
+ formattingRehashed += 1;
287
+ modified = true;
288
+ continue;
289
+ }
290
+ semanticChanged.push(evidence.path);
291
+ if (!acceptSourceChanges) {
292
+ files.push(evidence);
293
+ continue;
294
+ }
295
+ files.push({
296
+ ...evidence,
297
+ sha256: inspection.sha256,
298
+ semanticSha256: semantic
299
+ });
300
+ modified = true;
301
+ }
302
+ return {
303
+ files,
304
+ modified,
305
+ semanticBackfilled,
306
+ formattingRehashed,
307
+ semanticChanged,
308
+ unresolved,
309
+ pruned,
310
+ droppedIneligible
311
+ };
312
+ };
313
+ readShard = async (projectRoot, reference) => {
314
+ const inspection = await this.dependencies.files.inspectProjectFile({
315
+ projectRoot,
316
+ relativePath: reference.contextPath,
317
+ maximumBytes: MAX_CONTEXT_SHARD_BYTES
318
+ });
319
+ if (inspection.status !== "inspected" || inspection.bytes === undefined) {
320
+ throw new ContextRefreshError("CONTEXT_INVALID", `Context shard cannot be read: ${reference.contextPath}`);
321
+ }
322
+ let parsedJson;
323
+ try {
324
+ parsedJson = JSON.parse(inspection.bytes.toString("utf8"));
325
+ }
326
+ catch {
327
+ throw new ContextRefreshError("CONTEXT_INVALID", `Context shard is not valid JSON: ${reference.contextPath}`);
328
+ }
329
+ const document = ProjectContextModuleSchema.safeParse(parsedJson);
330
+ if (!document.success) {
331
+ throw new ContextRefreshError("CONTEXT_INVALID", `Context shard does not match the module schema: ${reference.contextPath}`);
332
+ }
333
+ assertShardIdentity(reference, document.data, (message) => new ContextRefreshError("CONTEXT_INVALID", message));
334
+ return { document: document.data, sha256: inspection.sha256 };
335
+ };
336
+ writeDocument = async (projectRoot, relativePath, document) => {
337
+ const written = await this.dependencies.writer.writeContextDocument({
338
+ projectRoot,
339
+ relativePath,
340
+ document
341
+ });
342
+ if (written.status !== "written") {
343
+ throw new ContextRefreshError("CONTEXT_WRITE_FAILED", written.status === "escape"
344
+ ? `Context document resolves outside the project: ${relativePath}`
345
+ : `Context document cannot be written: ${relativePath} (${written.message})`);
346
+ }
347
+ return written.sha256;
348
+ };
349
+ }
@@ -0,0 +1,36 @@
1
+ import type { ContextDocumentWriter } from "../../ports/context-document-writer.js";
2
+ import type { ProjectFileInspector } from "../../ports/project-file-inspector.js";
3
+ import { type ContextLoader } from "./context-loader.js";
4
+ export type ContextRehashErrorCode = "CONTEXT_INVALID" | "CONTEXT_MODULE_NOT_FOUND" | "CONTEXT_WRITE_FAILED";
5
+ export declare class ContextRehashError extends Error {
6
+ readonly code: ContextRehashErrorCode;
7
+ readonly name = "ContextRehashError";
8
+ constructor(code: ContextRehashErrorCode, message: string);
9
+ }
10
+ export interface ContextRehashInput {
11
+ readonly projectRoot: string;
12
+ readonly contextPath: string;
13
+ readonly moduleIds?: readonly string[] | undefined;
14
+ }
15
+ export interface ContextRehashModuleResult {
16
+ readonly id: string;
17
+ readonly previousSha256: string;
18
+ readonly currentSha256: string;
19
+ readonly changed: boolean;
20
+ }
21
+ export interface ContextRehashResult {
22
+ readonly status: "rehashed" | "unchanged";
23
+ readonly modules: readonly ContextRehashModuleResult[];
24
+ readonly previousIndexHash: string;
25
+ readonly indexHash: string;
26
+ }
27
+ export interface ContextRehasherDependencies {
28
+ readonly files: ProjectFileInspector;
29
+ readonly loader: Pick<ContextLoader, "readIndex">;
30
+ readonly writer: ContextDocumentWriter;
31
+ }
32
+ export declare class ContextRehasher {
33
+ private readonly dependencies;
34
+ constructor(dependencies: ContextRehasherDependencies);
35
+ readonly rehash: (input: ContextRehashInput) => Promise<ContextRehashResult>;
36
+ }