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,112 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { mkdir, realpath, rename, rm, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
+ import { isContained } from "../../shared/paths.js";
5
+ function errorMessage(error) {
6
+ return error instanceof Error ? error.message : "Unknown write failure";
7
+ }
8
+ export class FileSystemContextDocumentWriter {
9
+ writeContextDocument = async (input) => {
10
+ const normalizedPath = input.relativePath.replaceAll("\\", "/");
11
+ if (normalizedPath.length === 0
12
+ || normalizedPath.startsWith("/")
13
+ || /^[A-Za-z]:/.test(normalizedPath)
14
+ || normalizedPath.split("/").includes("..")) {
15
+ return { status: "escape" };
16
+ }
17
+ let canonicalRoot;
18
+ try {
19
+ canonicalRoot = await realpath(input.projectRoot);
20
+ }
21
+ catch (error) {
22
+ return { status: "unwritable", message: errorMessage(error) };
23
+ }
24
+ const target = resolve(canonicalRoot, normalizedPath);
25
+ if (!isContained(canonicalRoot, target)) {
26
+ return { status: "escape" };
27
+ }
28
+ const bytes = `${JSON.stringify(input.document, null, 2)}\n`;
29
+ const directory = dirname(target);
30
+ const temporaryPath = join(directory, `.${basename(target)}.${randomUUID()}.tmp`);
31
+ try {
32
+ await mkdir(directory, { recursive: true });
33
+ await writeFile(temporaryPath, bytes, "utf8");
34
+ await rename(temporaryPath, target);
35
+ }
36
+ catch (error) {
37
+ await rm(temporaryPath, { force: true }).catch(() => undefined);
38
+ return { status: "unwritable", message: errorMessage(error) };
39
+ }
40
+ return {
41
+ status: "written",
42
+ sha256: createHash("sha256").update(bytes).digest("hex")
43
+ };
44
+ };
45
+ writeContextDocumentBatch = async (input) => {
46
+ let canonicalRoot;
47
+ try {
48
+ canonicalRoot = await realpath(input.projectRoot);
49
+ }
50
+ catch (error) {
51
+ const failure = {
52
+ status: "unwritable",
53
+ message: errorMessage(error)
54
+ };
55
+ return input.documents.map(() => failure);
56
+ }
57
+ const prepared = [];
58
+ for (const doc of input.documents) {
59
+ const normalizedPath = doc.relativePath.replaceAll("\\", "/");
60
+ if (normalizedPath.length === 0
61
+ || normalizedPath.startsWith("/")
62
+ || /^[A-Za-z]:/.test(normalizedPath)
63
+ || normalizedPath.split("/").includes("..")) {
64
+ for (const item of prepared) {
65
+ await rm(item.tempPath, { force: true }).catch(() => undefined);
66
+ }
67
+ const failure = { status: "escape" };
68
+ return input.documents.map(() => failure);
69
+ }
70
+ const target = resolve(canonicalRoot, normalizedPath);
71
+ if (!isContained(canonicalRoot, target)) {
72
+ for (const item of prepared) {
73
+ await rm(item.tempPath, { force: true }).catch(() => undefined);
74
+ }
75
+ const failure = { status: "escape" };
76
+ return input.documents.map(() => failure);
77
+ }
78
+ const bytes = `${JSON.stringify(doc.document, null, 2)}\n`;
79
+ const directory = dirname(target);
80
+ const tempPath = join(directory, `.${basename(target)}.${randomUUID()}.tmp`);
81
+ prepared.push({
82
+ target,
83
+ tempPath,
84
+ bytes,
85
+ sha256: createHash("sha256").update(bytes).digest("hex")
86
+ });
87
+ }
88
+ try {
89
+ for (const item of prepared) {
90
+ await mkdir(dirname(item.target), { recursive: true });
91
+ await writeFile(item.tempPath, item.bytes, "utf8");
92
+ }
93
+ for (const item of prepared) {
94
+ await rename(item.tempPath, item.target);
95
+ }
96
+ }
97
+ catch (error) {
98
+ for (const item of prepared) {
99
+ await rm(item.tempPath, { force: true }).catch(() => undefined);
100
+ }
101
+ const failure = {
102
+ status: "unwritable",
103
+ message: errorMessage(error)
104
+ };
105
+ return input.documents.map(() => failure);
106
+ }
107
+ return prepared.map((item) => ({
108
+ status: "written",
109
+ sha256: item.sha256
110
+ }));
111
+ };
112
+ }
@@ -0,0 +1,17 @@
1
+ import { type CommandEvent } from "../../domain/diagnostics.js";
2
+ import type { DiagnosticsJournal } from "../../ports/diagnostics.js";
3
+ /**
4
+ * Appends one JSON line per invocation under `.taphound/build/log/`. It only
5
+ * writes where a command already initialized the ignored build layout (the
6
+ * build directory and `.taphound/.gitignore`), so read-only commands never
7
+ * create project files, and keeps the current file under `maxBytes` with one
8
+ * rotated predecessor.
9
+ */
10
+ export declare class FileSystemDiagnosticsJournal implements DiagnosticsJournal {
11
+ private readonly maxBytes;
12
+ constructor(maxBytes?: number);
13
+ append(projectRoot: string, event: CommandEvent): Promise<void>;
14
+ readLines(projectRoot: string): Promise<readonly string[]>;
15
+ salt(projectRoot: string): Promise<Buffer>;
16
+ private ensureLogDirectory;
17
+ }
@@ -0,0 +1,130 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { constants } from "node:fs";
3
+ import { lstat, mkdir, open, readFile, rename } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { CommandEventSchema, DIAGNOSTICS_EVENTS_FILE, DIAGNOSTICS_EVENTS_ROTATED_FILE, DIAGNOSTICS_SALT_FILE } from "../../domain/diagnostics.js";
6
+ import { BUILD_DIR, BUILD_IGNORE_FILE, DIAGNOSTICS_LOG_DIR, TAPHOUND_DIR } from "../../domain/workspace.js";
7
+ import { ensureBuildLayout } from "./workspace-layout.js";
8
+ const DEFAULT_MAX_BYTES = 1024 * 1024;
9
+ function isMissing(error) {
10
+ return error?.code === "ENOENT";
11
+ }
12
+ async function isDirectory(path) {
13
+ try {
14
+ const stats = await lstat(path);
15
+ return stats.isDirectory() && !stats.isSymbolicLink();
16
+ }
17
+ catch (error) {
18
+ if (isMissing(error))
19
+ return false;
20
+ throw error;
21
+ }
22
+ }
23
+ async function sizeOf(path) {
24
+ try {
25
+ return (await lstat(path)).size;
26
+ }
27
+ catch (error) {
28
+ if (isMissing(error))
29
+ return 0;
30
+ throw error;
31
+ }
32
+ }
33
+ async function readOptional(path) {
34
+ try {
35
+ return await readFile(path, "utf8");
36
+ }
37
+ catch (error) {
38
+ if (isMissing(error))
39
+ return "";
40
+ throw error;
41
+ }
42
+ }
43
+ async function isFile(path) {
44
+ try {
45
+ const stats = await lstat(path);
46
+ return stats.isFile();
47
+ }
48
+ catch (error) {
49
+ if (isMissing(error))
50
+ return false;
51
+ throw error;
52
+ }
53
+ }
54
+ /**
55
+ * Appends one JSON line per invocation under `.taphound/build/log/`. It only
56
+ * writes where a command already initialized the ignored build layout (the
57
+ * build directory and `.taphound/.gitignore`), so read-only commands never
58
+ * create project files, and keeps the current file under `maxBytes` with one
59
+ * rotated predecessor.
60
+ */
61
+ export class FileSystemDiagnosticsJournal {
62
+ maxBytes;
63
+ constructor(maxBytes = DEFAULT_MAX_BYTES) {
64
+ this.maxBytes = maxBytes;
65
+ }
66
+ async append(projectRoot, event) {
67
+ if (!await isDirectory(join(projectRoot, BUILD_DIR))
68
+ || !await isFile(join(projectRoot, BUILD_IGNORE_FILE))) {
69
+ return;
70
+ }
71
+ const line = `${JSON.stringify(CommandEventSchema.parse(event))}\n`;
72
+ const directory = await this.ensureLogDirectory(projectRoot);
73
+ const current = join(directory, DIAGNOSTICS_EVENTS_FILE);
74
+ if (await sizeOf(current) + Buffer.byteLength(line) > this.maxBytes) {
75
+ await rename(current, join(directory, DIAGNOSTICS_EVENTS_ROTATED_FILE))
76
+ .catch((error) => {
77
+ if (!isMissing(error))
78
+ throw error;
79
+ });
80
+ }
81
+ const handle = await open(current, constants.O_WRONLY | constants.O_CREAT | constants.O_APPEND | constants.O_NOFOLLOW, 0o600);
82
+ try {
83
+ await handle.writeFile(line, "utf8");
84
+ }
85
+ finally {
86
+ await handle.close();
87
+ }
88
+ }
89
+ async readLines(projectRoot) {
90
+ const directory = join(projectRoot, DIAGNOSTICS_LOG_DIR);
91
+ const text = await readOptional(join(directory, DIAGNOSTICS_EVENTS_ROTATED_FILE))
92
+ + await readOptional(join(directory, DIAGNOSTICS_EVENTS_FILE));
93
+ return text.split("\n").filter((line) => line.trim().length > 0);
94
+ }
95
+ async salt(projectRoot) {
96
+ if (!await isDirectory(join(projectRoot, TAPHOUND_DIR))) {
97
+ throw Object.assign(new Error(`Not a TapHound project (no ${TAPHOUND_DIR}/ directory): ${projectRoot}`), { code: "CONFIG_INVALID" });
98
+ }
99
+ await ensureBuildLayout(projectRoot);
100
+ const path = join(await this.ensureLogDirectory(projectRoot), DIAGNOSTICS_SALT_FILE);
101
+ try {
102
+ const handle = await open(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
103
+ const salt = randomBytes(32);
104
+ try {
105
+ await handle.writeFile(salt);
106
+ }
107
+ finally {
108
+ await handle.close();
109
+ }
110
+ return salt;
111
+ }
112
+ catch (error) {
113
+ if (error.code !== "EEXIST")
114
+ throw error;
115
+ }
116
+ const salt = await readFile(path);
117
+ if (salt.length < 16) {
118
+ throw new Error(`Diagnostics salt is unreadable: ${DIAGNOSTICS_LOG_DIR}/${DIAGNOSTICS_SALT_FILE}`);
119
+ }
120
+ return salt;
121
+ }
122
+ async ensureLogDirectory(projectRoot) {
123
+ const directory = join(projectRoot, DIAGNOSTICS_LOG_DIR);
124
+ await mkdir(directory, { recursive: true });
125
+ if (!await isDirectory(directory)) {
126
+ throw new Error(`${DIAGNOSTICS_LOG_DIR} is not a directory`);
127
+ }
128
+ return directory;
129
+ }
130
+ }
@@ -0,0 +1,17 @@
1
+ import type { ExternalFlowCatalogEntry, ExternalFlowRecord, ExternalFlowRegistry, WriteExternalFlowInput, WriteExternalFlowResult } from "../../ports/external-flow-registry.js";
2
+ export declare class FileSystemExternalFlowRegistry implements ExternalFlowRegistry {
3
+ private readonly builtinFlowsRoot;
4
+ constructor(builtinFlowsRoot: string);
5
+ readonly read: (input: {
6
+ projectRoot: string;
7
+ name: string;
8
+ }) => Promise<ExternalFlowRecord>;
9
+ readonly list: (projectRoot: string) => Promise<readonly ExternalFlowCatalogEntry[]>;
10
+ readonly write: (input: WriteExternalFlowInput) => Promise<WriteExternalFlowResult>;
11
+ private readonly loadFlow;
12
+ private readonly resolveProjectFlowPath;
13
+ private readonly resolveBuiltinFlowPath;
14
+ private readonly readFlowFile;
15
+ private readonly listBuiltinFlows;
16
+ private readonly listProjectFlows;
17
+ }
@@ -0,0 +1,325 @@
1
+ import { lstat, mkdir, readdir, readFile, realpath, rename, writeFile } from "node:fs/promises";
2
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ import { ExternalFlowSchema } from "../../domain/external-flow.js";
4
+ import { EXTERNAL_FLOWS_DIR } from "../../domain/workspace.js";
5
+ const MAX_EXTERNAL_FLOW_BYTES = 1024 * 1024;
6
+ function contained(root, candidate) {
7
+ const fromRoot = relative(root, candidate);
8
+ return fromRoot === ""
9
+ || (fromRoot !== ".."
10
+ && !fromRoot.startsWith(`..${sep}`)
11
+ && !isAbsolute(fromRoot));
12
+ }
13
+ function flowFileName(name) {
14
+ return `${name}.json`;
15
+ }
16
+ function errorDetails(error) {
17
+ if (error instanceof Error) {
18
+ return {
19
+ code: "EXTERNAL_FLOW_INVALID",
20
+ message: error.message
21
+ };
22
+ }
23
+ return {
24
+ code: "EXTERNAL_FLOW_INVALID",
25
+ message: String(error)
26
+ };
27
+ }
28
+ export class FileSystemExternalFlowRegistry {
29
+ builtinFlowsRoot;
30
+ constructor(builtinFlowsRoot) {
31
+ this.builtinFlowsRoot = builtinFlowsRoot;
32
+ }
33
+ read = async (input) => {
34
+ const loaded = await this.loadFlow(input.projectRoot, input.name);
35
+ return {
36
+ bytes: loaded.bytes,
37
+ flow: loaded.flow,
38
+ source: loaded.source,
39
+ path: loaded.path
40
+ };
41
+ };
42
+ list = async (projectRoot) => {
43
+ const builtinEntries = await this.listBuiltinFlows();
44
+ const projectEntries = await this.listProjectFlows(projectRoot);
45
+ const projectNames = new Set(projectEntries.map((entry) => entry.name));
46
+ const entries = [
47
+ ...projectEntries,
48
+ ...builtinEntries.filter((entry) => !projectNames.has(entry.name))
49
+ ];
50
+ return entries.sort((left, right) => left.name.localeCompare(right.name));
51
+ };
52
+ write = async (input) => {
53
+ const { projectRoot, name, flow, force } = input;
54
+ const canonicalRoot = await realpath(projectRoot);
55
+ const externalRoot = resolve(canonicalRoot, EXTERNAL_FLOWS_DIR);
56
+ const targetPath = join(externalRoot, flowFileName(name));
57
+ const canonicalExternalRoot = await realpath(externalRoot).catch((error) => {
58
+ if (error instanceof Error
59
+ && "code" in error
60
+ && error.code === "ENOENT") {
61
+ return undefined;
62
+ }
63
+ throw error;
64
+ });
65
+ if (canonicalExternalRoot !== undefined
66
+ && !contained(canonicalRoot, canonicalExternalRoot)) {
67
+ throw new Error("External Flow directory escapes the project root");
68
+ }
69
+ const rootStats = await lstat(externalRoot).catch((error) => {
70
+ if (error instanceof Error
71
+ && "code" in error
72
+ && error.code === "ENOENT") {
73
+ return undefined;
74
+ }
75
+ throw error;
76
+ });
77
+ if (rootStats !== undefined && rootStats.isSymbolicLink()) {
78
+ throw new Error("External Flow directory is a symlink");
79
+ }
80
+ const validated = ExternalFlowSchema.parse(flow);
81
+ if (validated.name !== name) {
82
+ throw new Error(`External Flow declares name "${validated.name}", expected "${name}"`);
83
+ }
84
+ let overwritten = false;
85
+ const existingStats = await lstat(targetPath).catch((error) => {
86
+ if (error instanceof Error
87
+ && "code" in error
88
+ && error.code === "ENOENT") {
89
+ return undefined;
90
+ }
91
+ throw error;
92
+ });
93
+ if (existingStats !== undefined) {
94
+ if (existingStats.isSymbolicLink()) {
95
+ throw new Error(`External Flow target is a symlink: ${targetPath}`);
96
+ }
97
+ if (!force) {
98
+ throw new Error(`External Flow already exists: ${name}`);
99
+ }
100
+ overwritten = true;
101
+ }
102
+ const parentDir = name.includes("/")
103
+ ? join(externalRoot, name.split("/").slice(0, -1).join("/"))
104
+ : externalRoot;
105
+ await mkdir(parentDir, { recursive: true });
106
+ const tempPath = `${targetPath}.tmp`;
107
+ await writeFile(tempPath, `${JSON.stringify(validated, null, 2)}\n`);
108
+ await rename(tempPath, targetPath);
109
+ const relativePath = relative(canonicalRoot, targetPath).replaceAll("\\", "/");
110
+ return { path: relativePath, overwritten };
111
+ };
112
+ loadFlow = async (projectRoot, name) => {
113
+ const projectPath = await this.resolveProjectFlowPath(projectRoot, name);
114
+ if (projectPath !== undefined) {
115
+ return this.readFlowFile(projectPath, name, "project");
116
+ }
117
+ const builtinPath = await this.resolveBuiltinFlowPath(name);
118
+ if (builtinPath !== undefined) {
119
+ return this.readFlowFile(builtinPath, name, "builtin");
120
+ }
121
+ throw new Error(`External Flow not found: ${name}`);
122
+ };
123
+ resolveProjectFlowPath = async (projectRoot, name) => {
124
+ const canonicalRoot = await realpath(projectRoot);
125
+ const externalRoot = resolve(canonicalRoot, EXTERNAL_FLOWS_DIR);
126
+ try {
127
+ const canonicalExternalRoot = await realpath(externalRoot);
128
+ if (!contained(canonicalRoot, canonicalExternalRoot)) {
129
+ throw new Error("External Flow directory escapes the project root");
130
+ }
131
+ const rootStats = await lstat(externalRoot);
132
+ if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) {
133
+ return undefined;
134
+ }
135
+ }
136
+ catch (error) {
137
+ if (error instanceof Error
138
+ && "code" in error
139
+ && error.code === "ENOENT") {
140
+ return undefined;
141
+ }
142
+ throw error;
143
+ }
144
+ const filePath = join(externalRoot, flowFileName(name));
145
+ try {
146
+ const stats = await lstat(filePath);
147
+ if (!stats.isFile() || stats.isSymbolicLink()) {
148
+ return undefined;
149
+ }
150
+ return filePath;
151
+ }
152
+ catch (error) {
153
+ if (error instanceof Error
154
+ && "code" in error
155
+ && error.code === "ENOENT") {
156
+ return undefined;
157
+ }
158
+ throw error;
159
+ }
160
+ };
161
+ resolveBuiltinFlowPath = async (name) => {
162
+ const filePath = join(this.builtinFlowsRoot, flowFileName(name));
163
+ try {
164
+ const stats = await lstat(filePath);
165
+ if (!stats.isFile() || stats.isSymbolicLink()) {
166
+ return undefined;
167
+ }
168
+ return filePath;
169
+ }
170
+ catch (error) {
171
+ if (error instanceof Error
172
+ && "code" in error
173
+ && error.code === "ENOENT") {
174
+ return undefined;
175
+ }
176
+ throw error;
177
+ }
178
+ };
179
+ readFlowFile = async (filePath, expectedName, source) => {
180
+ const bytes = await readFile(filePath);
181
+ if (bytes.byteLength > MAX_EXTERNAL_FLOW_BYTES) {
182
+ throw new Error(`External Flow exceeds ${String(MAX_EXTERNAL_FLOW_BYTES)} bytes: ${filePath}`);
183
+ }
184
+ let parsed;
185
+ try {
186
+ parsed = JSON.parse(bytes.toString("utf8"));
187
+ }
188
+ catch (error) {
189
+ throw new Error(`External Flow is not valid JSON: ${filePath}`, { cause: error });
190
+ }
191
+ const flow = ExternalFlowSchema.parse(parsed);
192
+ if (flow.name !== expectedName) {
193
+ throw new Error(`External Flow at ${filePath} declares name "${flow.name}", expected "${expectedName}"`);
194
+ }
195
+ return { bytes, flow, path: filePath, source };
196
+ };
197
+ listBuiltinFlows = async () => {
198
+ let rootStats;
199
+ try {
200
+ rootStats = await lstat(this.builtinFlowsRoot);
201
+ }
202
+ catch (error) {
203
+ if (error instanceof Error
204
+ && "code" in error
205
+ && error.code === "ENOENT") {
206
+ return [];
207
+ }
208
+ throw error;
209
+ }
210
+ if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) {
211
+ return [];
212
+ }
213
+ const paths = [];
214
+ const visit = async (directory) => {
215
+ const entries = await readdir(directory, { withFileTypes: true });
216
+ for (const entry of entries) {
217
+ const path = join(directory, entry.name);
218
+ if (entry.isSymbolicLink()) {
219
+ throw new Error(`Built-in External Flow catalog cannot contain symlinks: ${path}`);
220
+ }
221
+ if (entry.isDirectory()) {
222
+ await visit(path);
223
+ }
224
+ else if (entry.isFile() && entry.name.endsWith(".json")) {
225
+ paths.push(path);
226
+ }
227
+ }
228
+ };
229
+ await visit(this.builtinFlowsRoot);
230
+ paths.sort();
231
+ const results = [];
232
+ for (const filePath of paths) {
233
+ const relativePath = relative(this.builtinFlowsRoot, filePath).replaceAll("\\", "/");
234
+ const name = relativePath.slice(0, -".json".length);
235
+ try {
236
+ const loaded = await this.readFlowFile(filePath, name, "builtin");
237
+ results.push({
238
+ name,
239
+ source: "builtin",
240
+ path: filePath,
241
+ status: "valid",
242
+ escapedPackageName: loaded.flow.escapedPackageName,
243
+ stepCount: loaded.flow.steps.length
244
+ });
245
+ }
246
+ catch (error) {
247
+ results.push({
248
+ name,
249
+ source: "builtin",
250
+ path: filePath,
251
+ status: "invalid",
252
+ failure: errorDetails(error)
253
+ });
254
+ }
255
+ }
256
+ return results;
257
+ };
258
+ listProjectFlows = async (projectRoot) => {
259
+ const canonicalRoot = await realpath(projectRoot);
260
+ const externalRoot = resolve(canonicalRoot, EXTERNAL_FLOWS_DIR);
261
+ try {
262
+ const canonicalExternalRoot = await realpath(externalRoot);
263
+ if (!contained(canonicalRoot, canonicalExternalRoot)) {
264
+ throw new Error("External Flow directory escapes the project root");
265
+ }
266
+ const rootStats = await lstat(externalRoot);
267
+ if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) {
268
+ return [];
269
+ }
270
+ }
271
+ catch (error) {
272
+ if (error instanceof Error
273
+ && "code" in error
274
+ && error.code === "ENOENT") {
275
+ return [];
276
+ }
277
+ throw error;
278
+ }
279
+ const paths = [];
280
+ const visit = async (directory) => {
281
+ const entries = await readdir(directory, { withFileTypes: true });
282
+ for (const entry of entries) {
283
+ const path = join(directory, entry.name);
284
+ if (entry.isSymbolicLink()) {
285
+ throw new Error(`External Flow catalog cannot contain symlinks: ${path}`);
286
+ }
287
+ if (entry.isDirectory()) {
288
+ await visit(path);
289
+ }
290
+ else if (entry.isFile() && entry.name.endsWith(".json")) {
291
+ paths.push(path);
292
+ }
293
+ }
294
+ };
295
+ await visit(externalRoot);
296
+ paths.sort();
297
+ const results = [];
298
+ for (const filePath of paths) {
299
+ const relativePath = relative(canonicalRoot, filePath).replaceAll("\\", "/");
300
+ const name = relativePath
301
+ .slice(`${EXTERNAL_FLOWS_DIR}/`.length, -".json".length);
302
+ try {
303
+ const loaded = await this.readFlowFile(filePath, name, "project");
304
+ results.push({
305
+ name,
306
+ source: "project",
307
+ path: relativePath,
308
+ status: "valid",
309
+ escapedPackageName: loaded.flow.escapedPackageName,
310
+ stepCount: loaded.flow.steps.length
311
+ });
312
+ }
313
+ catch (error) {
314
+ results.push({
315
+ name,
316
+ source: "project",
317
+ path: relativePath,
318
+ status: "invalid",
319
+ failure: errorDetails(error)
320
+ });
321
+ }
322
+ }
323
+ return results;
324
+ };
325
+ }
@@ -1,10 +1,11 @@
1
- import { type GenerationInFlight, type GenerationSession, type PendingConfirmation } from "../../domain/generation.js";
1
+ import { type GenerationInFlight, type GenerationSession, type PendingConfirmation, type VerificationPhase } from "../../domain/generation.js";
2
2
  import { type GenerationSessionStore } from "../../ports/generation-session-store.js";
3
3
  export interface FileSystemGenerationSessionStoreOptions {
4
4
  lockTimeoutMs?: number;
5
5
  lockRetryMs?: number;
6
6
  now?: (() => Date) | undefined;
7
7
  hooks?: FileSystemGenerationSessionStoreHooks;
8
+ generationRoot?: string | undefined;
8
9
  }
9
10
  export interface FileSystemGenerationSessionStoreHooks {
10
11
  beforeLockStagingWrite?: () => Promise<void> | void;
@@ -27,6 +28,8 @@ export declare class FileSystemGenerationSessionStore implements GenerationSessi
27
28
  private readonly options;
28
29
  private readonly now;
29
30
  private readonly hooks;
31
+ private readonly customGenerationRoot;
32
+ private readonly lock;
30
33
  constructor(projectRoot: string, options?: FileSystemGenerationSessionStoreOptions);
31
34
  readonly create: (input: GenerationSession) => Promise<void>;
32
35
  readonly read: (id: string) => Promise<GenerationSession>;
@@ -36,14 +39,24 @@ export declare class FileSystemGenerationSessionStore implements GenerationSessi
36
39
  readonly beginStep: (id: string, expectedRevision: number, inFlightInput: GenerationInFlight, approvedConfirmationInput?: PendingConfirmation) => Promise<GenerationSession>;
37
40
  readonly completeStep: (id: string, expectedRevision: number, expectedInFlightInput: GenerationInFlight, input: GenerationSession) => Promise<void>;
38
41
  readonly recover: (id: string, expectedRevision: number, input: GenerationSession) => Promise<void>;
39
- readonly beginVerification: (id: string, expectedRevision: number, attemptId: string) => Promise<GenerationSession>;
42
+ readonly beginVerification: (id: string, expectedRevision: number, attemptId: string, owner?: {
43
+ pid: number;
44
+ startedAt: string;
45
+ }) => Promise<GenerationSession>;
46
+ readonly updateVerificationPhase: (id: string, attemptId: string, phase: VerificationPhase) => Promise<void>;
47
+ readonly abortVerification: (id: string, expectedRevision: number) => Promise<GenerationSession>;
40
48
  readonly completeVerification: (id: string, expectedRevision: number, input: GenerationSession) => Promise<void>;
41
49
  readonly failVerification: (id: string, expectedRevision: number, input: GenerationSession) => Promise<void>;
50
+ readonly recoverVerification: (id: string, expectedRevision: number, input: GenerationSession) => Promise<void>;
51
+ readonly reopenVerification: (id: string, expectedRevision: number, input: GenerationSession) => Promise<void>;
42
52
  readonly markBundlePublishable: (id: string, expectedRevision: number, input: GenerationSession) => Promise<void>;
53
+ readonly archive: (id: string, expectedRevision: number, input: GenerationSession) => Promise<void>;
54
+ readonly list: () => Promise<readonly GenerationSession[]>;
43
55
  readonly writeEvidence: (id: string, relativePath: string, value: unknown) => Promise<void>;
44
56
  readonly writeTextEvidence: (id: string, relativePath: string, value: string) => Promise<void>;
45
57
  readonly produceEvidence: (id: string, relativePath: string, produce: (temporaryPath: string) => Promise<void>) => Promise<void>;
46
58
  readonly readEvidence: (id: string, relativePath: string) => Promise<Buffer>;
59
+ readonly evidenceReference: (id: string, relativePath: string) => Promise<string>;
47
60
  readonly listEvidence: (id: string) => Promise<readonly {
48
61
  path: string;
49
62
  contentBase64: string;
@@ -54,15 +67,8 @@ export declare class FileSystemGenerationSessionStore implements GenerationSessi
54
67
  private readonly writeVerificationTransition;
55
68
  private readonly activeDirectory;
56
69
  private readonly finalDirectory;
57
- private readonly lockPath;
58
- private readonly syncDirectory;
70
+ private readonly withVerifiedEvidence;
59
71
  private readonly withLock;
60
- private readonly acquireLock;
61
- private readonly tryInstallLock;
62
- private readonly reapDeadOwnerLock;
63
- private readonly releaseLock;
64
- private readonly readLockOwner;
65
- private readonly fileIdentity;
66
- private readonly isProcessAlive;
72
+ private readonly syncDirectory;
67
73
  private assertPublishable;
68
74
  }