patchwarden 1.5.0 → 1.6.1

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 (597) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +194 -26
  3. package/README.md +160 -22
  4. package/dist/assessments/agentAssessor.d.ts +1 -1
  5. package/dist/assessments/agentAssessor.js +24 -15
  6. package/dist/assessments/assessmentStore.d.ts +1 -1
  7. package/dist/assessments/assessmentStore.js +44 -36
  8. package/dist/assessments/confirmCli.js +5 -4
  9. package/dist/config.d.ts +22 -0
  10. package/dist/config.js +98 -11
  11. package/dist/control/fileManager.d.ts +2 -0
  12. package/dist/control/fileManager.js +39 -0
  13. package/dist/control/middleware/auth.d.ts +16 -0
  14. package/dist/control/middleware/auth.js +17 -0
  15. package/dist/control/middleware/static.d.ts +3 -0
  16. package/dist/control/middleware/static.js +78 -0
  17. package/dist/control/routeTable.d.ts +24 -0
  18. package/dist/control/routeTable.js +263 -0
  19. package/dist/control/routes/audit.d.ts +15 -0
  20. package/dist/control/routes/audit.js +287 -0
  21. package/dist/control/routes/evidence.d.ts +4 -0
  22. package/dist/control/routes/evidence.js +96 -0
  23. package/dist/control/routes/lineage.d.ts +3 -0
  24. package/dist/control/routes/lineage.js +71 -0
  25. package/dist/control/routes/policy.d.ts +3 -0
  26. package/dist/control/routes/policy.js +81 -0
  27. package/dist/control/routes/process.d.ts +12 -0
  28. package/dist/control/routes/process.js +487 -0
  29. package/dist/control/routes/sessions.d.ts +30 -0
  30. package/dist/control/routes/sessions.js +287 -0
  31. package/dist/control/routes/status.d.ts +51 -0
  32. package/dist/control/routes/status.js +307 -0
  33. package/dist/control/routes/taskActions.d.ts +21 -0
  34. package/dist/control/routes/taskActions.js +197 -0
  35. package/dist/control/routes/tasks.d.ts +24 -0
  36. package/dist/control/routes/tasks.js +309 -0
  37. package/dist/control/routes/workspace.d.ts +15 -0
  38. package/dist/control/routes/workspace.js +220 -0
  39. package/dist/control/runtime.d.ts +92 -0
  40. package/dist/control/runtime.js +425 -0
  41. package/dist/control/server.d.ts +13 -0
  42. package/dist/control/server.js +150 -0
  43. package/dist/control/shared.d.ts +59 -0
  44. package/dist/control/shared.js +341 -0
  45. package/dist/controlCenter.d.ts +6 -0
  46. package/dist/controlCenter.js +7 -2197
  47. package/dist/direct/directAudit.js +234 -225
  48. package/dist/direct/directGuards.d.ts +2 -1
  49. package/dist/direct/directGuards.js +72 -37
  50. package/dist/direct/directPatch.d.ts +6 -1
  51. package/dist/direct/directPatch.js +55 -8
  52. package/dist/direct/directSessionStore.d.ts +16 -6
  53. package/dist/direct/directSessionStore.js +127 -35
  54. package/dist/direct/directVerification.js +8 -16
  55. package/dist/doctor.d.ts +18 -1
  56. package/dist/doctor.js +614 -373
  57. package/dist/goal/acceptanceEngine.d.ts +1 -1
  58. package/dist/goal/goalProgress.js +75 -69
  59. package/dist/goal/goalReport.d.ts +54 -0
  60. package/dist/goal/goalReport.js +197 -0
  61. package/dist/goal/goalStatus.d.ts +8 -0
  62. package/dist/goal/goalStatus.js +10 -3
  63. package/dist/goal/goalStore.d.ts +9 -2
  64. package/dist/goal/goalStore.js +152 -45
  65. package/dist/goal/handoffExport.js +6 -6
  66. package/dist/goal/specKitImport.d.ts +63 -0
  67. package/dist/goal/specKitImport.js +221 -0
  68. package/dist/goal/subgoalSync.js +13 -10
  69. package/dist/goal/worktreeManager.d.ts +2 -0
  70. package/dist/goal/worktreeManager.js +209 -136
  71. package/dist/httpServer.js +126 -44
  72. package/dist/index.js +7 -4
  73. package/dist/logging.d.ts +7 -1
  74. package/dist/logging.js +13 -3
  75. package/dist/policy/projectPolicy.js +5 -2
  76. package/dist/release/releaseGate.d.ts +1 -1
  77. package/dist/release/releaseGate.js +78 -25
  78. package/dist/runner/agentInvocation.d.ts +14 -0
  79. package/dist/runner/agentInvocation.js +96 -4
  80. package/dist/runner/changeCapture.d.ts +14 -6
  81. package/dist/runner/changeCapture.js +287 -81
  82. package/dist/runner/cli.js +7 -6
  83. package/dist/runner/postTaskCleanup.js +60 -7
  84. package/dist/runner/processSecurity.d.ts +56 -0
  85. package/dist/runner/processSecurity.js +373 -0
  86. package/dist/runner/runTask.d.ts +1 -1
  87. package/dist/runner/runTask.js +339 -287
  88. package/dist/runner/simpleProcess.d.ts +3 -0
  89. package/dist/runner/simpleProcess.js +52 -38
  90. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  91. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  92. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
  93. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  94. package/dist/runner/taskStatusStore.d.ts +11 -0
  95. package/dist/runner/taskStatusStore.js +24 -0
  96. package/dist/runner/watch.d.ts +14 -1
  97. package/dist/runner/watch.js +265 -54
  98. package/dist/security/contentRedaction.d.ts +6 -0
  99. package/dist/security/contentRedaction.js +24 -2
  100. package/dist/security/discoveryTokenStore.d.ts +3 -2
  101. package/dist/security/discoveryTokenStore.js +17 -2
  102. package/dist/security/loopbackHost.d.ts +2 -0
  103. package/dist/security/loopbackHost.js +16 -0
  104. package/dist/security/pathGuard.js +32 -17
  105. package/dist/security/planGuard.js +2 -1
  106. package/dist/security/runtimeGuard.js +9 -6
  107. package/dist/security/secretComparison.d.ts +3 -0
  108. package/dist/security/secretComparison.js +11 -0
  109. package/dist/security/sensitiveGuard.d.ts +5 -0
  110. package/dist/security/sensitiveGuard.js +27 -8
  111. package/dist/security/toolInvocationGuard.d.ts +2 -2
  112. package/dist/security/toolInvocationGuard.js +1 -1
  113. package/dist/security/workspaceRootGuard.d.ts +8 -0
  114. package/dist/security/workspaceRootGuard.js +47 -0
  115. package/dist/smoke-test.js +280 -273
  116. package/dist/tools/catalog/index.d.ts +4 -0
  117. package/dist/tools/catalog/index.js +4 -0
  118. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  119. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  120. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +28 -15
  121. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  122. package/dist/tools/definitions/toolDefs.js +1177 -0
  123. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  124. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  125. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +8 -63
  126. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +111 -37
  127. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
  128. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +10 -10
  129. package/dist/tools/diagnostics/index.d.ts +6 -0
  130. package/dist/tools/diagnostics/index.js +6 -0
  131. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  132. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  133. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +14 -10
  134. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +25 -15
  135. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  136. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  137. package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +1 -1
  138. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +8 -8
  139. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +2 -2
  140. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  141. package/dist/tools/direct/index.d.ts +3 -0
  142. package/dist/tools/direct/index.js +3 -0
  143. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  144. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  145. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  146. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  147. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  148. package/dist/tools/discovery/index.d.ts +3 -0
  149. package/dist/tools/discovery/index.js +3 -0
  150. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  151. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  152. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  153. package/dist/tools/dispatch/coreDispatch.js +283 -0
  154. package/dist/tools/dispatch/diagnosticDispatch.d.ts +12 -0
  155. package/dist/tools/dispatch/diagnosticDispatch.js +80 -0
  156. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  157. package/dist/tools/dispatch/directDispatch.js +116 -0
  158. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  159. package/dist/tools/dispatch/goalDispatch.js +92 -0
  160. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  161. package/dist/tools/dispatch/releaseDispatch.js +46 -0
  162. package/dist/tools/dispatch/types.d.ts +23 -0
  163. package/dist/tools/dispatch/types.js +15 -0
  164. package/dist/tools/dispatch/validation.d.ts +7 -0
  165. package/dist/tools/dispatch/validation.js +71 -0
  166. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  167. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +2 -2
  168. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  169. package/dist/tools/goals/index.d.ts +3 -0
  170. package/dist/tools/goals/index.js +3 -0
  171. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  172. package/dist/tools/registry.d.ts +10 -14
  173. package/dist/tools/registry.js +47 -1651
  174. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  175. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  176. package/dist/tools/release/index.d.ts +2 -0
  177. package/dist/tools/release/index.js +2 -0
  178. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  179. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  180. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  181. package/dist/tools/tasks/cancelTask.js +85 -0
  182. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  183. package/dist/tools/{createTask.js → tasks/createTask.js} +46 -31
  184. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  185. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
  186. package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +6 -0
  187. package/dist/tools/tasks/evidencePack.js +387 -0
  188. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  189. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  190. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
  191. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  192. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
  193. package/dist/tools/tasks/index.d.ts +18 -0
  194. package/dist/tools/tasks/index.js +18 -0
  195. package/dist/tools/tasks/killTask.d.ts +1 -0
  196. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  197. package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
  198. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
  199. package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
  200. package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +2 -2
  201. package/dist/tools/tasks/retryTask.js +60 -0
  202. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  203. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +59 -31
  204. package/dist/tools/tasks/runVerification.js +38 -0
  205. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  206. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  207. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  208. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  209. package/dist/tools/workspace/applyPatch.js +53 -0
  210. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  211. package/dist/tools/workspace/index.d.ts +10 -0
  212. package/dist/tools/workspace/index.js +10 -0
  213. package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
  214. package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
  215. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  216. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  217. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  218. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  219. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  220. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  221. package/dist/tools/workspace/syncFile.js +134 -0
  222. package/dist/utils/atomicFile.d.ts +7 -0
  223. package/dist/utils/atomicFile.js +48 -0
  224. package/dist/utils/boundedFile.d.ts +8 -0
  225. package/dist/utils/boundedFile.js +93 -0
  226. package/dist/utils/lockedJsonFile.d.ts +15 -0
  227. package/dist/utils/lockedJsonFile.js +224 -0
  228. package/dist/utils/platform.d.ts +11 -0
  229. package/dist/utils/platform.js +26 -0
  230. package/dist/utils/stableJson.d.ts +2 -0
  231. package/dist/utils/stableJson.js +12 -0
  232. package/dist/version.d.ts +3 -2
  233. package/dist/version.js +3 -2
  234. package/dist/watcherStatus.d.ts +2 -2
  235. package/dist/watcherStatus.js +18 -0
  236. package/docs/CODE_WIKI.md +1648 -0
  237. package/docs/agentseal-integration.md +150 -0
  238. package/docs/architecture.md +23 -0
  239. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  240. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  241. package/docs/control-center/README.md +4 -4
  242. package/docs/dashboard-overview.md +86 -0
  243. package/docs/demo.md +8 -0
  244. package/docs/desktop-app.md +175 -0
  245. package/docs/direct-session-workflow.md +98 -0
  246. package/docs/evidence-pack-schema.md +215 -0
  247. package/docs/lineage-evidence-pack-workflow.md +127 -0
  248. package/docs/mcp-inspector-testing.md +200 -0
  249. package/docs/open-source-application.md +168 -0
  250. package/docs/opencode-worker.md +151 -0
  251. package/docs/openhands-worker.md +181 -0
  252. package/docs/release-checklist.md +1 -1
  253. package/docs/release-evidence.md +82 -0
  254. package/docs/spec-kit-integration.md +131 -0
  255. package/docs/task-safe-review-workflow.md +98 -0
  256. package/docs/threat-model.md +97 -0
  257. package/docs/user-feedback.md +40 -0
  258. package/docs/why-patchwarden.md +110 -0
  259. package/examples/config.example.json +4 -2
  260. package/package.json +28 -11
  261. package/scripts/README.md +15 -6
  262. package/scripts/build.js +11 -0
  263. package/scripts/checks/brand-check.js +3 -0
  264. package/scripts/checks/build-output-check.js +37 -0
  265. package/scripts/checks/control-center-smoke.js +447 -6
  266. package/scripts/checks/control-smoke.js +19 -8
  267. package/scripts/checks/http-mcp-smoke.js +82 -4
  268. package/scripts/checks/lifecycle-smoke.js +39 -33
  269. package/scripts/checks/mcp-manifest-check.js +1 -1
  270. package/scripts/checks/mcp-smoke.js +30 -4
  271. package/scripts/checks/package-manifest-check.js +20 -7
  272. package/scripts/checks/unit-tests.js +3 -5
  273. package/scripts/checks/watcher-supervisor-smoke.js +9 -0
  274. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  275. package/scripts/control/manage-patchwarden.ps1 +69 -4
  276. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  277. package/scripts/control/run-background-supervisor.ps1 +41 -0
  278. package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
  279. package/scripts/generate-demo-gif.py +320 -0
  280. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  281. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  282. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  283. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  284. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  285. package/scripts/lib/clean-generated-output.js +11 -0
  286. package/scripts/lib/file-discovery.js +12 -0
  287. package/scripts/release/desktop-preflight.js +211 -0
  288. package/scripts/release/pack-clean.js +59 -14
  289. package/src/assessments/agentAssessor.ts +29 -15
  290. package/src/assessments/assessmentStore.ts +55 -43
  291. package/src/assessments/confirmCli.ts +5 -4
  292. package/src/config.ts +105 -11
  293. package/src/control/fileManager.ts +40 -0
  294. package/src/control/middleware/auth.ts +35 -0
  295. package/src/control/middleware/static.ts +84 -0
  296. package/src/control/routeTable.ts +301 -0
  297. package/src/control/routes/audit.ts +329 -0
  298. package/src/control/routes/evidence.ts +107 -0
  299. package/src/control/routes/lineage.ts +92 -0
  300. package/src/control/routes/policy.ts +81 -0
  301. package/src/control/routes/process.ts +505 -0
  302. package/src/control/routes/sessions.ts +314 -0
  303. package/src/control/routes/status.ts +405 -0
  304. package/src/control/routes/taskActions.ts +214 -0
  305. package/src/control/routes/tasks.ts +331 -0
  306. package/src/control/routes/workspace.ts +234 -0
  307. package/src/control/runtime.ts +508 -0
  308. package/src/control/server.ts +165 -0
  309. package/src/control/shared.ts +360 -0
  310. package/src/controlCenter.ts +7 -2347
  311. package/src/direct/directAudit.ts +17 -9
  312. package/src/direct/directGuards.ts +100 -51
  313. package/src/direct/directPatch.ts +95 -8
  314. package/src/direct/directSessionStore.ts +211 -55
  315. package/src/direct/directVerification.ts +9 -23
  316. package/src/doctor.ts +766 -492
  317. package/src/goal/acceptanceEngine.ts +1 -1
  318. package/src/goal/goalProgress.ts +17 -13
  319. package/src/goal/goalReport.ts +263 -0
  320. package/src/goal/goalStatus.ts +25 -3
  321. package/src/goal/goalStore.ts +241 -54
  322. package/src/goal/handoffExport.ts +6 -6
  323. package/src/goal/specKitImport.ts +355 -0
  324. package/src/goal/subgoalSync.ts +15 -10
  325. package/src/goal/worktreeManager.ts +139 -22
  326. package/src/httpServer.ts +130 -44
  327. package/src/index.ts +7 -4
  328. package/src/logging.ts +20 -4
  329. package/src/policy/projectPolicy.ts +5 -2
  330. package/src/release/releaseGate.ts +74 -27
  331. package/src/runner/agentInvocation.ts +126 -4
  332. package/src/runner/changeCapture.ts +342 -80
  333. package/src/runner/cli.ts +7 -6
  334. package/src/runner/postTaskCleanup.ts +55 -7
  335. package/src/runner/processSecurity.ts +433 -0
  336. package/src/runner/runTask.ts +441 -292
  337. package/src/runner/simpleProcess.ts +68 -33
  338. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  339. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
  340. package/src/runner/taskStatusStore.ts +54 -0
  341. package/src/runner/watch.ts +290 -57
  342. package/src/security/contentRedaction.ts +31 -2
  343. package/src/security/discoveryTokenStore.ts +18 -4
  344. package/src/security/loopbackHost.ts +19 -0
  345. package/src/security/pathGuard.ts +35 -16
  346. package/src/security/planGuard.ts +2 -1
  347. package/src/security/runtimeGuard.ts +9 -6
  348. package/src/security/secretComparison.ts +13 -0
  349. package/src/security/sensitiveGuard.ts +27 -8
  350. package/src/security/toolInvocationGuard.ts +2 -2
  351. package/src/security/workspaceRootGuard.ts +53 -0
  352. package/src/smoke-test.ts +276 -273
  353. package/src/tools/catalog/index.ts +4 -0
  354. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  355. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +29 -16
  356. package/src/tools/definitions/toolDefs.ts +1264 -0
  357. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  358. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +196 -89
  359. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +11 -11
  360. package/src/tools/diagnostics/index.ts +6 -0
  361. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  362. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +29 -19
  363. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  364. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +9 -9
  365. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +11 -9
  366. package/src/tools/direct/index.ts +3 -0
  367. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  368. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  369. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  370. package/src/tools/discovery/index.ts +3 -0
  371. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  372. package/src/tools/dispatch/coreDispatch.ts +379 -0
  373. package/src/tools/dispatch/diagnosticDispatch.ts +106 -0
  374. package/src/tools/dispatch/directDispatch.ts +168 -0
  375. package/src/tools/dispatch/goalDispatch.ts +128 -0
  376. package/src/tools/dispatch/releaseDispatch.ts +66 -0
  377. package/src/tools/dispatch/types.ts +24 -0
  378. package/src/tools/dispatch/validation.ts +78 -0
  379. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  380. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +43 -28
  381. package/src/tools/goals/index.ts +3 -0
  382. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  383. package/src/tools/registry.ts +54 -1858
  384. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  385. package/src/tools/release/index.ts +2 -0
  386. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  387. package/src/tools/tasks/cancelTask.ts +109 -0
  388. package/src/tools/{createTask.ts → tasks/createTask.ts} +56 -33
  389. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
  390. package/src/tools/tasks/evidencePack.ts +504 -0
  391. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  392. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
  393. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  394. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
  395. package/src/tools/tasks/index.ts +18 -0
  396. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
  397. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
  398. package/src/tools/tasks/retryTask.ts +69 -0
  399. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +70 -35
  400. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  401. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  402. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  403. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  404. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  405. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  406. package/src/tools/workspace/index.ts +10 -0
  407. package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
  408. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  409. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  410. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  411. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  412. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  413. package/src/tools/workspace/syncFile.ts +292 -0
  414. package/src/utils/atomicFile.ts +58 -0
  415. package/src/utils/boundedFile.ts +116 -0
  416. package/src/utils/lockedJsonFile.ts +251 -0
  417. package/src/utils/platform.ts +31 -0
  418. package/src/utils/stableJson.ts +12 -0
  419. package/src/version.ts +3 -2
  420. package/src/watcherStatus.ts +19 -1
  421. package/ui/desktop-bootstrap.js +11 -0
  422. package/ui/desktop-bridge.js +36 -0
  423. package/ui/desktop.css +160 -0
  424. package/ui/getting-started.js +93 -0
  425. package/ui/i18n.js +484 -0
  426. package/ui/log-parser.js +30 -0
  427. package/ui/pages/audit.html +204 -3
  428. package/ui/pages/dashboard.html +937 -79
  429. package/ui/pages/direct-sessions.html +609 -60
  430. package/ui/pages/getting-started.html +43 -0
  431. package/ui/pages/logs.html +105 -22
  432. package/ui/pages/settings.html +101 -0
  433. package/ui/pages/task-detail.html +468 -438
  434. package/ui/pages/tasks.html +648 -65
  435. package/ui/pages/workspace.html +38 -1
  436. package/ui/settings.js +245 -0
  437. package/PatchWarden-Control.cmd +0 -6
  438. package/Restart-PatchWarden-Control.cmd +0 -6
  439. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  440. package/dist/test/unit/acceptance-engine.test.js +0 -228
  441. package/dist/test/unit/agent-router.test.d.ts +0 -1
  442. package/dist/test/unit/agent-router.test.js +0 -287
  443. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  444. package/dist/test/unit/android-doctor.test.js +0 -118
  445. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  446. package/dist/test/unit/audit-checks.test.js +0 -350
  447. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  448. package/dist/test/unit/chinese-path.test.js +0 -91
  449. package/dist/test/unit/command-guard.test.d.ts +0 -1
  450. package/dist/test/unit/command-guard.test.js +0 -160
  451. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  452. package/dist/test/unit/diagnose-task.test.js +0 -457
  453. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  454. package/dist/test/unit/direct-guards.test.js +0 -213
  455. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  456. package/dist/test/unit/discovery-token-store.test.js +0 -139
  457. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  458. package/dist/test/unit/evidence-pack.test.js +0 -130
  459. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  460. package/dist/test/unit/goal-graph.test.js +0 -298
  461. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  462. package/dist/test/unit/goal-progress.test.js +0 -381
  463. package/dist/test/unit/goal-status.test.d.ts +0 -1
  464. package/dist/test/unit/goal-status.test.js +0 -215
  465. package/dist/test/unit/goal-store.test.d.ts +0 -1
  466. package/dist/test/unit/goal-store.test.js +0 -253
  467. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  468. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  469. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  470. package/dist/test/unit/goal-tools-registry.test.js +0 -190
  471. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  472. package/dist/test/unit/handoff-export.test.js +0 -263
  473. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  474. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  475. package/dist/test/unit/logging.test.d.ts +0 -1
  476. package/dist/test/unit/logging.test.js +0 -397
  477. package/dist/test/unit/path-guard.test.d.ts +0 -1
  478. package/dist/test/unit/path-guard.test.js +0 -109
  479. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  480. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  481. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  482. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  483. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  484. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  485. package/dist/test/unit/release-gate.test.d.ts +0 -1
  486. package/dist/test/unit/release-gate.test.js +0 -242
  487. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  488. package/dist/test/unit/run-task-loop.test.js +0 -380
  489. package/dist/test/unit/safe-status.test.d.ts +0 -1
  490. package/dist/test/unit/safe-status.test.js +0 -165
  491. package/dist/test/unit/safe-views.test.d.ts +0 -1
  492. package/dist/test/unit/safe-views.test.js +0 -171
  493. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  494. package/dist/test/unit/schema-drift-check.test.js +0 -176
  495. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  496. package/dist/test/unit/sensitive-guard.test.js +0 -104
  497. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  498. package/dist/test/unit/subgoal-sync.test.js +0 -183
  499. package/dist/test/unit/sync-file.test.d.ts +0 -1
  500. package/dist/test/unit/sync-file.test.js +0 -154
  501. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  502. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  503. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  504. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  505. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  506. package/dist/test/unit/toolSearch.test.js +0 -571
  507. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  508. package/dist/test/unit/watcher-status.test.js +0 -169
  509. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  510. package/dist/test/unit/worktree-manager.test.js +0 -176
  511. package/dist/tools/applyPatch.js +0 -41
  512. package/dist/tools/cancelTask.d.ts +0 -30
  513. package/dist/tools/cancelTask.js +0 -64
  514. package/dist/tools/evidencePack.js +0 -168
  515. package/dist/tools/finalizeDirectSession.js +0 -84
  516. package/dist/tools/goalSubgoalTask.js +0 -110
  517. package/dist/tools/killTask.d.ts +0 -15
  518. package/dist/tools/retryTask.js +0 -45
  519. package/dist/tools/runVerification.js +0 -36
  520. package/dist/tools/syncFile.js +0 -65
  521. package/docs/control-center/control-center-daily-driver.md +0 -211
  522. package/docs/control-center/control-center-mvp.md +0 -205
  523. package/docs/control-center/control-center-phase2.md +0 -159
  524. package/docs/release-v0.6.0.md +0 -71
  525. package/docs/release-v0.6.1.md +0 -75
  526. package/docs/release-v0.6.4.md +0 -45
  527. package/src/test/unit/acceptance-engine.test.ts +0 -261
  528. package/src/test/unit/agent-router.test.ts +0 -342
  529. package/src/test/unit/android-doctor.test.ts +0 -158
  530. package/src/test/unit/audit-checks.test.ts +0 -567
  531. package/src/test/unit/chinese-path.test.ts +0 -106
  532. package/src/test/unit/command-guard.test.ts +0 -221
  533. package/src/test/unit/diagnose-task.test.ts +0 -544
  534. package/src/test/unit/direct-guards.test.ts +0 -297
  535. package/src/test/unit/discovery-token-store.test.ts +0 -181
  536. package/src/test/unit/evidence-pack.test.ts +0 -142
  537. package/src/test/unit/goal-graph.test.ts +0 -347
  538. package/src/test/unit/goal-progress.test.ts +0 -538
  539. package/src/test/unit/goal-status.test.ts +0 -270
  540. package/src/test/unit/goal-store.test.ts +0 -318
  541. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  542. package/src/test/unit/goal-tools-registry.test.ts +0 -243
  543. package/src/test/unit/handoff-export.test.ts +0 -295
  544. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  545. package/src/test/unit/logging.test.ts +0 -497
  546. package/src/test/unit/path-guard.test.ts +0 -150
  547. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  548. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  549. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  550. package/src/test/unit/release-gate.test.ts +0 -314
  551. package/src/test/unit/run-task-loop.test.ts +0 -425
  552. package/src/test/unit/safe-status.test.ts +0 -187
  553. package/src/test/unit/safe-views.test.ts +0 -184
  554. package/src/test/unit/schema-drift-check.test.ts +0 -260
  555. package/src/test/unit/sensitive-guard.test.ts +0 -124
  556. package/src/test/unit/subgoal-sync.test.ts +0 -236
  557. package/src/test/unit/sync-file.test.ts +0 -231
  558. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  559. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  560. package/src/test/unit/toolSearch.test.ts +0 -756
  561. package/src/test/unit/watcher-status.test.ts +0 -190
  562. package/src/test/unit/worktree-manager.test.ts +0 -247
  563. package/src/tools/cancelTask.ts +0 -70
  564. package/src/tools/evidencePack.ts +0 -205
  565. package/src/tools/retryTask.ts +0 -53
  566. package/src/tools/syncFile.ts +0 -122
  567. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  568. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  569. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  570. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  571. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  572. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  573. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  574. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  575. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  576. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  577. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  578. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  579. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  580. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  581. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  582. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  583. /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
  584. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  585. /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
  586. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  587. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  588. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  589. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  590. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  591. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  592. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  593. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  594. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  595. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  596. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
  597. /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
@@ -0,0 +1,320 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from PIL import Image, ImageDraw, ImageFont
6
+
7
+
8
+ ROOT = Path(__file__).resolve().parents[1]
9
+ OUT = ROOT / "docs" / "assets" / "patchwarden-oss-demo.gif"
10
+
11
+ W, H = 1280, 720
12
+ BG = (246, 248, 245)
13
+ INK = (32, 39, 39)
14
+ MUTED = (91, 101, 101)
15
+ GREEN = (38, 124, 92)
16
+ TEAL = (25, 112, 116)
17
+ BLUE = (48, 92, 160)
18
+ ORANGE = (183, 104, 36)
19
+ RED = (165, 62, 62)
20
+ LINE = (205, 214, 208)
21
+ PANEL = (255, 255, 252)
22
+ SHADOW = (218, 224, 219)
23
+ TERMINAL = (27, 34, 34)
24
+ TERMINAL_TEXT = (208, 230, 218)
25
+
26
+
27
+ def font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont:
28
+ candidates = [
29
+ "C:/Windows/Fonts/segoeuib.ttf" if bold else "C:/Windows/Fonts/segoeui.ttf",
30
+ "C:/Windows/Fonts/arialbd.ttf" if bold else "C:/Windows/Fonts/arial.ttf",
31
+ ]
32
+ for candidate in candidates:
33
+ try:
34
+ return ImageFont.truetype(candidate, size)
35
+ except OSError:
36
+ pass
37
+ return ImageFont.load_default()
38
+
39
+
40
+ F_TITLE = font(44, True)
41
+ F_H2 = font(27, True)
42
+ F_BODY = font(22)
43
+ F_SMALL = font(18)
44
+ F_MONO = font(19)
45
+ F_MONO_SMALL = font(16)
46
+
47
+
48
+ def rounded(draw: ImageDraw.ImageDraw, box, fill, outline=None, radius=18, width=2):
49
+ x0, y0, x1, y1 = box
50
+ draw.rounded_rectangle((x0 + 6, y0 + 8, x1 + 6, y1 + 8), radius, fill=SHADOW)
51
+ draw.rounded_rectangle(box, radius, fill=fill, outline=outline or LINE, width=width)
52
+
53
+
54
+ def label(draw: ImageDraw.ImageDraw, xy, text, fill=INK, fnt=F_BODY, anchor=None):
55
+ draw.text(xy, text, fill=fill, font=fnt, anchor=anchor)
56
+
57
+
58
+ def pill(draw: ImageDraw.ImageDraw, xy, text, fill, text_fill=(255, 255, 255)):
59
+ x, y = xy
60
+ bbox = draw.textbbox((0, 0), text, font=F_SMALL)
61
+ pad_x = 14
62
+ pad_y = 7
63
+ w = bbox[2] - bbox[0] + pad_x * 2
64
+ h = bbox[3] - bbox[1] + pad_y * 2
65
+ draw.rounded_rectangle((x, y, x + w, y + h), h // 2, fill=fill)
66
+ draw.text((x + pad_x, y + pad_y - 1), text, fill=text_fill, font=F_SMALL)
67
+
68
+
69
+ def wrap(draw: ImageDraw.ImageDraw, text: str, width: int, fnt=F_BODY) -> list[str]:
70
+ lines: list[str] = []
71
+ current = ""
72
+ for word in text.split():
73
+ test = f"{current} {word}".strip()
74
+ if draw.textlength(test, font=fnt) <= width:
75
+ current = test
76
+ else:
77
+ if current:
78
+ lines.append(current)
79
+ current = word
80
+ if current:
81
+ lines.append(current)
82
+ return lines
83
+
84
+
85
+ def draw_panel_text(draw, box, title, body, accent):
86
+ x0, y0, x1, y1 = box
87
+ rounded(draw, box, PANEL, LINE)
88
+ draw.rectangle((x0, y0, x0 + 8, y1), fill=accent)
89
+ label(draw, (x0 + 28, y0 + 24), title, fnt=F_H2)
90
+ y = y0 + 68
91
+ for line in wrap(draw, body, x1 - x0 - 56, F_BODY):
92
+ label(draw, (x0 + 28, y), line, fill=MUTED, fnt=F_BODY)
93
+ y += 30
94
+
95
+
96
+ def draw_terminal(draw, box, lines):
97
+ rounded(draw, box, TERMINAL, (66, 78, 78), radius=16)
98
+ x0, y0, x1, _ = box
99
+ draw.ellipse((x0 + 20, y0 + 18, x0 + 32, y0 + 30), fill=(236, 98, 86))
100
+ draw.ellipse((x0 + 42, y0 + 18, x0 + 54, y0 + 30), fill=(241, 190, 76))
101
+ draw.ellipse((x0 + 64, y0 + 18, x0 + 76, y0 + 30), fill=(99, 198, 103))
102
+ y = y0 + 54
103
+ for line, color in lines:
104
+ label(draw, (x0 + 24, y), line, fill=color, fnt=F_MONO_SMALL)
105
+ y += 25
106
+ if y > box[3] - 24:
107
+ break
108
+
109
+
110
+ def draw_progress(draw, active: int):
111
+ steps = [
112
+ ("Plan", BLUE),
113
+ ("Guard", TEAL),
114
+ ("Execute", ORANGE),
115
+ ("Evidence", GREEN),
116
+ ("Review", GREEN),
117
+ ]
118
+ start_x = 165
119
+ y = 650
120
+ gap = 235
121
+ for i, (name, color) in enumerate(steps):
122
+ x = start_x + gap * i
123
+ if i > 0:
124
+ line_color = color if i <= active else LINE
125
+ draw.line((x - gap + 48, y, x - 48, y), fill=line_color, width=5)
126
+ fill = color if i <= active else (226, 232, 228)
127
+ text = (255, 255, 255) if i <= active else MUTED
128
+ draw.ellipse((x - 28, y - 28, x + 28, y + 28), fill=fill)
129
+ label(draw, (x, y - 1), str(i + 1), fill=text, fnt=F_H2, anchor="mm")
130
+ label(draw, (x, y + 44), name, fill=INK if i <= active else MUTED, fnt=F_SMALL, anchor="mm")
131
+
132
+
133
+ def base(title: str, subtitle: str, active: int) -> tuple[Image.Image, ImageDraw.ImageDraw]:
134
+ img = Image.new("RGB", (W, H), BG)
135
+ draw = ImageDraw.Draw(img)
136
+ label(draw, (64, 42), title, fnt=F_TITLE)
137
+ label(draw, (66, 96), subtitle, fill=MUTED, fnt=F_BODY)
138
+ pill(draw, (1040, 48), "privacy-safe demo", GREEN)
139
+ draw_progress(draw, active)
140
+ return img, draw
141
+
142
+
143
+ def frame_intro():
144
+ img, draw = base(
145
+ "PatchWarden OSS Demo",
146
+ "A safe MCP task loop for ChatGPT, Codex, OpenCode, and local maintainers.",
147
+ 0,
148
+ )
149
+ draw_panel_text(
150
+ draw,
151
+ (90, 170, 1190, 530),
152
+ "What this GIF shows",
153
+ "A model plans work, PatchWarden turns it into a bounded task, a registered local agent executes it, and the maintainer reviews redacted evidence before accepting anything.",
154
+ GREEN,
155
+ )
156
+ pill(draw, (130, 420), "no API keys", BLUE)
157
+ pill(draw, (280, 420), "no real account names", TEAL)
158
+ pill(draw, (520, 420), "no unrestricted shell", ORANGE)
159
+ pill(draw, (770, 420), "no auto-publish", RED)
160
+ return img
161
+
162
+
163
+ def frame_plan():
164
+ img, draw = base(
165
+ "1. Model creates a bounded plan",
166
+ "The upstream client asks for a small maintainer task, not a raw shell command.",
167
+ 0,
168
+ )
169
+ draw_panel_text(
170
+ draw,
171
+ (70, 160, 590, 520),
172
+ "ChatGPT / Codex",
173
+ "Goal: update docs for a small OSS workflow. Verify with npm.cmd test. Return summary, diff, and evidence only.",
174
+ BLUE,
175
+ )
176
+ draw_panel_text(
177
+ draw,
178
+ (690, 160, 1210, 520),
179
+ "PatchWarden MCP",
180
+ "Receives structured task input with repo_path, agent, template, and allowlisted verification commands.",
181
+ TEAL,
182
+ )
183
+ draw.line((600, 340, 680, 340), fill=BLUE, width=6)
184
+ draw.polygon([(680, 340), (660, 328), (660, 352)], fill=BLUE)
185
+ return img
186
+
187
+
188
+ def frame_guard():
189
+ img, draw = base(
190
+ "2. PatchWarden checks the safety boundary",
191
+ "Local policy decides what can run and where files may change.",
192
+ 1,
193
+ )
194
+ draw_panel_text(
195
+ draw,
196
+ (70, 160, 1210, 330),
197
+ "Guardrails",
198
+ "repo_path must stay under workspaceRoot. Agent launch commands come from trusted config. Verification commands must exactly match allowedTestCommands.",
199
+ TEAL,
200
+ )
201
+ draw_panel_text(
202
+ draw,
203
+ (70, 380, 580, 540),
204
+ "Blocked by default",
205
+ ".env, tokens, SSH keys, cookies, credential files, out-of-workspace writes, and arbitrary shell.",
206
+ RED,
207
+ )
208
+ draw_panel_text(
209
+ draw,
210
+ (700, 380, 1210, 540),
211
+ "Allowed",
212
+ "A pre-registered local agent can work on the requested repository and produce auditable evidence.",
213
+ GREEN,
214
+ )
215
+ return img
216
+
217
+
218
+ def frame_execute():
219
+ img, draw = base(
220
+ "3. Watcher launches the registered agent",
221
+ "The task is executed locally while PatchWarden records status and scope evidence.",
222
+ 2,
223
+ )
224
+ draw_terminal(
225
+ draw,
226
+ (80, 160, 1200, 540),
227
+ [
228
+ ("> patchwarden watcher", TERMINAL_TEXT),
229
+ ("queued task_20260709_demo", (156, 205, 255)),
230
+ ("repo_path: demo-oss-project", TERMINAL_TEXT),
231
+ ("agent: opencode (registered)", TERMINAL_TEXT),
232
+ ("verify: npm.cmd test (allowlisted)", TERMINAL_TEXT),
233
+ ("scope: inside workspaceRoot", (142, 222, 170)),
234
+ ("status: running -> done", (142, 222, 170)),
235
+ ],
236
+ )
237
+ return img
238
+
239
+
240
+ def frame_evidence():
241
+ img, draw = base(
242
+ "4. Evidence is written for review",
243
+ "The maintainer can inspect safe summaries before opening deeper logs or diffs.",
244
+ 3,
245
+ )
246
+ draw_panel_text(
247
+ draw,
248
+ (80, 150, 410, 540),
249
+ "result.json",
250
+ "status, warnings, changed-file groups, and recommended next actions.",
251
+ GREEN,
252
+ )
253
+ draw_panel_text(
254
+ draw,
255
+ (475, 150, 805, 540),
256
+ "diff.patch",
257
+ "Git diff evidence when available, kept separate from the model plan.",
258
+ BLUE,
259
+ )
260
+ draw_panel_text(
261
+ draw,
262
+ (870, 150, 1200, 540),
263
+ "verify.json",
264
+ "Exact commands, exit codes, and verification status.",
265
+ ORANGE,
266
+ )
267
+ return img
268
+
269
+
270
+ def frame_review():
271
+ img, draw = base(
272
+ "5. Maintainer accepts, fixes, or rejects",
273
+ "PatchWarden supports review. It does not silently push, publish, or restart services.",
274
+ 4,
275
+ )
276
+ draw_panel_text(
277
+ draw,
278
+ (80, 160, 1200, 520),
279
+ "OSS maintainer handoff",
280
+ "Use safe_result, safe_audit, safe_test_summary, and evidence packs to decide the next step. Release actions still require separate human confirmation and GitHub/npm verification.",
281
+ GREEN,
282
+ )
283
+ pill(draw, (165, 410), "reviewable", GREEN)
284
+ pill(draw, (360, 410), "redacted", TEAL)
285
+ pill(draw, (525, 410), "bounded", BLUE)
286
+ pill(draw, (680, 410), "confirmation-gated", ORANGE)
287
+ return img
288
+
289
+
290
+ def hold(frames, image, count):
291
+ for _ in range(count):
292
+ frames.append(image.copy())
293
+
294
+
295
+ def main():
296
+ OUT.parent.mkdir(parents=True, exist_ok=True)
297
+ scenes = [
298
+ frame_intro(),
299
+ frame_plan(),
300
+ frame_guard(),
301
+ frame_execute(),
302
+ frame_evidence(),
303
+ frame_review(),
304
+ ]
305
+ frames: list[Image.Image] = []
306
+ for scene in scenes:
307
+ hold(frames, scene, 12)
308
+ frames[0].save(
309
+ OUT,
310
+ save_all=True,
311
+ append_images=frames[1:],
312
+ duration=120,
313
+ loop=0,
314
+ optimize=True,
315
+ )
316
+ print(OUT)
317
+
318
+
319
+ if __name__ == "__main__":
320
+ main()
@@ -1,11 +1,11 @@
1
1
  @echo off
2
2
  setlocal
3
- cd /d "%~dp0"
3
+ cd /d "%~dp0..\.."
4
4
  if /i "%~1"=="--foreground" (
5
- powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\control\control-center-tray.ps1" %*
5
+ powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0..\control\control-center-tray.ps1" %*
6
6
  set EXITCODE=%ERRORLEVEL%
7
7
  endlocal & exit /b %EXITCODE%
8
8
  )
9
9
 
10
- start "" powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0scripts\control\control-center-tray.ps1" %*
10
+ start "" powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0..\control\control-center-tray.ps1" %*
11
11
  endlocal & exit /b 0
@@ -0,0 +1,6 @@
1
+ @echo off
2
+ setlocal
3
+ cd /d "%~dp0..\.."
4
+ powershell -ExecutionPolicy Bypass -NoProfile -File "%~dp0..\control\start-control-center.ps1" %*
5
+ set EXITCODE=%ERRORLEVEL%
6
+ endlocal & exit /b %EXITCODE%
@@ -1,5 +1,5 @@
1
1
  @echo off
2
2
  setlocal
3
- cd /d "%~dp0"
4
- start "" powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0scripts\control\control-center-tray.ps1" %*
3
+ cd /d "%~dp0..\.."
4
+ start "" powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0..\control\control-center-tray.ps1" %*
5
5
  endlocal & exit /b 0
@@ -0,0 +1,6 @@
1
+ @echo off
2
+ setlocal
3
+ cd /d "%~dp0..\.."
4
+ powershell -ExecutionPolicy Bypass -NoProfile -File "%~dp0..\control\restart-control-center.ps1" %*
5
+ set EXITCODE=%ERRORLEVEL%
6
+ endlocal & exit /b %EXITCODE%
@@ -1,7 +1,7 @@
1
1
  @echo off
2
2
  setlocal
3
- cd /d "%~dp0"
4
- powershell -ExecutionPolicy Bypass -NoProfile -File "%~dp0scripts\control\stop-patchwarden.ps1" %*
3
+ cd /d "%~dp0..\.."
4
+ powershell -ExecutionPolicy Bypass -NoProfile -File "%~dp0..\control\stop-patchwarden.ps1" %*
5
5
  set EXITCODE=%ERRORLEVEL%
6
6
  if not "%EXITCODE%"=="0" (
7
7
  echo.
@@ -0,0 +1,11 @@
1
+ import { rmSync } from "node:fs";
2
+ import { basename, dirname, resolve } from "node:path";
3
+
4
+ export function cleanGeneratedOutput(projectRoot, outputPath) {
5
+ const root = resolve(projectRoot);
6
+ const output = resolve(outputPath);
7
+ if (dirname(output) !== root || basename(output).toLowerCase() !== "dist") {
8
+ throw new Error(`Refusing to clean non-dist output: ${output}`);
9
+ }
10
+ rmSync(output, { recursive: true, force: true });
11
+ }
@@ -0,0 +1,12 @@
1
+ import { readdirSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+
4
+ export function collectMatchingFiles(directory, predicate) {
5
+ return readdirSync(directory, { withFileTypes: true })
6
+ .sort((left, right) => left.name.localeCompare(right.name))
7
+ .flatMap((entry) => {
8
+ const path = resolve(directory, entry.name);
9
+ if (entry.isDirectory()) return collectMatchingFiles(path, predicate);
10
+ return entry.isFile() && predicate(entry.name, path) ? [path] : [];
11
+ });
12
+ }
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env node
2
+ import { createHash } from "node:crypto";
3
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { basename, join, resolve } from "node:path";
6
+ import { spawnSync } from "node:child_process";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ const root = resolve(fileURLToPath(new URL("../..", import.meta.url)));
10
+ const desktopRoot = join(root, "desktop");
11
+ const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
12
+ const argv = process.argv.slice(2);
13
+ const requireClean = argv.includes("--require-clean");
14
+ const skipUiSmoke = argv.includes("--skip-ui-smoke");
15
+
16
+ function option(name) {
17
+ const index = argv.indexOf(name);
18
+ return index >= 0 ? argv[index + 1] : null;
19
+ }
20
+
21
+ function json(path) {
22
+ return JSON.parse(readFileSync(path, "utf8"));
23
+ }
24
+
25
+ function sha256(value) {
26
+ return createHash("sha256").update(value).digest("hex");
27
+ }
28
+
29
+ function directoryStats(directory) {
30
+ let files = 0;
31
+ let bytes = 0;
32
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
33
+ const path = join(directory, entry.name);
34
+ if (entry.isDirectory()) {
35
+ const nested = directoryStats(path);
36
+ files += nested.files;
37
+ bytes += nested.bytes;
38
+ } else if (entry.isFile()) {
39
+ files += 1;
40
+ bytes += statSync(path).size;
41
+ }
42
+ }
43
+ return { files, bytes };
44
+ }
45
+
46
+ function spawnSpec(command, args) {
47
+ if (process.platform === "win32" && /\.cmd$/i.test(command)) {
48
+ return { command: process.env.ComSpec || "cmd.exe", args: ["/d", "/s", "/c", command, ...args] };
49
+ }
50
+ return { command, args };
51
+ }
52
+
53
+ function capture(command, args, cwd = root) {
54
+ const spec = spawnSpec(command, args);
55
+ const result = spawnSync(spec.command, spec.args, { cwd, encoding: "utf8", windowsHide: true, maxBuffer: 64 * 1024 * 1024 });
56
+ if (result.status !== 0) throw new Error(`${command} ${args.join(" ")} failed: ${result.stderr || result.stdout || result.error?.message || "unknown error"}`);
57
+ return String(result.stdout || "").trim();
58
+ }
59
+
60
+ const rootPackage = json(join(root, "package.json"));
61
+ const rootLock = json(join(root, "package-lock.json"));
62
+ const desktopPackage = json(join(desktopRoot, "package.json"));
63
+ const desktopLock = json(join(desktopRoot, "package-lock.json"));
64
+ const versionSource = readFileSync(join(root, "src", "version.ts"), "utf8");
65
+ const sourceVersion = versionSource.match(/PATCHWARDEN_VERSION\s*=\s*"([^"]+)"/)?.[1] || null;
66
+ const versions = [rootPackage.version, rootLock.version, rootLock.packages?.[""]?.version, desktopPackage.version, desktopLock.version, desktopLock.packages?.[""]?.version, sourceVersion];
67
+ if (versions.some((version) => version !== rootPackage.version)) throw new Error(`Version mismatch: ${versions.join(", ")}`);
68
+ if (process.platform !== "win32") throw new Error("Desktop preflight requires Windows.");
69
+
70
+ const gitStatus = capture("git", ["status", "--porcelain=v1", "-z"]);
71
+ const changedFileCount = gitStatus ? gitStatus.split("\0").filter(Boolean).length : 0;
72
+ if (requireClean && changedFileCount > 0) throw new Error(`Release preflight requires a clean worktree; found ${changedFileCount} changed paths.`);
73
+
74
+ const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
75
+ const defaultOutput = join(root, "release", `desktop-preflight-${rootPackage.version}-${stamp}`);
76
+ const outputRoot = resolve(option("--output") || defaultOutput);
77
+ if (existsSync(outputRoot)) throw new Error(`Preflight output already exists: ${outputRoot}`);
78
+ mkdirSync(outputRoot, { recursive: true });
79
+
80
+ const report = {
81
+ schema_version: "patchwarden-desktop-preflight-v1",
82
+ status: "running",
83
+ started_at: new Date().toISOString(),
84
+ finished_at: null,
85
+ version: rootPackage.version,
86
+ output_directory: outputRoot,
87
+ source: {
88
+ branch: capture("git", ["branch", "--show-current"]),
89
+ head: capture("git", ["rev-parse", "HEAD"]),
90
+ dirty: changedFileCount > 0,
91
+ changed_path_count: changedFileCount,
92
+ status_sha256: sha256(gitStatus),
93
+ tracked_diff_sha256: sha256(capture("git", ["diff", "--binary", "HEAD"])),
94
+ },
95
+ environment: {
96
+ platform: process.platform,
97
+ arch: process.arch,
98
+ node: process.version,
99
+ npm: capture(npmCommand, ["--version"]),
100
+ electron: desktopPackage.devDependencies.electron,
101
+ electron_builder: desktopPackage.devDependencies["electron-builder"],
102
+ },
103
+ options: { require_clean: requireClean, skip_ui_smoke: skipUiSmoke },
104
+ checks: [],
105
+ package: null,
106
+ runtime_manifest: null,
107
+ ui_smoke: null,
108
+ artifact: null,
109
+ error: null,
110
+ };
111
+
112
+ function saveReport() {
113
+ writeFileSync(join(outputRoot, "preflight-report.json"), `${JSON.stringify(report, null, 2)}\n`, "utf8");
114
+ const lines = [
115
+ `# PatchWarden Desktop ${report.version} preflight`,
116
+ "",
117
+ `- Status: ${report.status}`,
118
+ `- Source: ${report.source.branch} @ ${report.source.head}`,
119
+ `- Dirty baseline: ${report.source.dirty} (${report.source.changed_path_count} paths, status SHA256 ${report.source.status_sha256})`,
120
+ `- Node/npm: ${report.environment.node} / ${report.environment.npm}`,
121
+ `- Electron: ${report.environment.electron}`,
122
+ `- Output: ${report.output_directory}`,
123
+ "",
124
+ "## Checks",
125
+ "",
126
+ ...report.checks.map((check) => `- ${check.ok ? "PASS" : "FAIL"} ${check.name} (${check.duration_ms} ms)`),
127
+ "",
128
+ report.runtime_manifest ? `Packaged runtime: ${report.runtime_manifest.tool_profile}, ${report.runtime_manifest.tool_count} tools, manifest SHA256 ${report.runtime_manifest.tool_manifest_sha256}.` : "Packaged runtime: not verified.",
129
+ report.ui_smoke ? `UI smoke: ${report.ui_smoke.ok ? "passed" : "failed"}.` : "UI smoke: not run.",
130
+ report.error ? `\nError: ${report.error}` : "",
131
+ ];
132
+ writeFileSync(join(outputRoot, "preflight-report.md"), `${lines.join("\n")}\n`, "utf8");
133
+ }
134
+
135
+ function run(name, command, args, cwd = root) {
136
+ const started = Date.now();
137
+ const spec = spawnSpec(command, args);
138
+ const result = spawnSync(spec.command, spec.args, { cwd, encoding: "utf8", windowsHide: true, maxBuffer: 128 * 1024 * 1024 });
139
+ const stdout = String(result.stdout || "");
140
+ const stderr = String(result.stderr || "");
141
+ if (stdout) process.stdout.write(stdout);
142
+ if (stderr) process.stderr.write(stderr);
143
+ const check = { name, ok: result.status === 0, exit_code: result.status, duration_ms: Date.now() - started };
144
+ report.checks.push(check);
145
+ saveReport();
146
+ if (!check.ok) throw new Error(`${name} failed with exit code ${result.status ?? "unknown"}: ${stderr || result.error?.message || "see command output"}`);
147
+ return stdout;
148
+ }
149
+
150
+ let isolatedRoot = null;
151
+ try {
152
+ run("root clean build", npmCommand, ["run", "build"]);
153
+ run("build output contract", npmCommand, ["run", "check:build-output"]);
154
+ const unitOutput = run("root unit tests", npmCommand, ["run", "test:unit"]);
155
+ const desktopOutput = run("desktop tests", npmCommand, ["run", "desktop:test"]);
156
+ const packageOutput = run("npm package surface", npmCommand, ["run", "verify:package"]);
157
+ run("desktop staging", npmCommand, ["run", "desktop:stage"]);
158
+ run("Electron directory package", npmCommand, ["exec", "electron-builder", "--", "--win", "dir", "--x64", `--config.directories.output=${outputRoot}`], desktopRoot);
159
+
160
+ report.package = {
161
+ npm_file_count: Number(packageOutput.match(/OK:\s+(\d+) package files/)?.[1] || 0),
162
+ root_unit_tests: Number([...unitOutput.matchAll(/tests\s+(\d+)/g)].at(-1)?.[1] || 0),
163
+ desktop_tests: Number([...desktopOutput.matchAll(/tests\s+(\d+)/g)].at(-1)?.[1] || 0),
164
+ };
165
+
166
+ const unpackedRoot = join(outputRoot, "win-unpacked");
167
+ const executable = join(unpackedRoot, "PatchWarden.exe");
168
+ const packagedCore = join(unpackedRoot, "resources", "core");
169
+ if (!existsSync(executable) || !existsSync(packagedCore)) throw new Error("Electron directory package is missing its executable or core runtime.");
170
+ for (const forbidden of ["dist/test", "src/test", "docs/archive", "scripts/checks/doctor-smoke.js"]) {
171
+ if (existsSync(join(packagedCore, forbidden))) throw new Error(`Packaged core contains development-only path: ${forbidden}`);
172
+ }
173
+ const packagedChecks = readdirSync(join(packagedCore, "scripts", "checks")).sort();
174
+ if (JSON.stringify(packagedChecks) !== JSON.stringify(["mcp-manifest-check.js"])) {
175
+ throw new Error(`Packaged core has unexpected check scripts: ${packagedChecks.join(", ")}`);
176
+ }
177
+ const coreStats = directoryStats(packagedCore);
178
+ report.package.packaged_core_file_count = coreStats.files;
179
+ report.package.packaged_core_size_bytes = coreStats.bytes;
180
+
181
+ isolatedRoot = join(tmpdir(), `patchwarden-preflight-${process.pid}-${Date.now()}`);
182
+ cpSync(packagedCore, isolatedRoot, { recursive: true });
183
+ const manifestOutput = run("isolated packaged runtime manifest", process.execPath, ["scripts/checks/mcp-manifest-check.js", "--profile", "chatgpt_core"], isolatedRoot);
184
+ report.runtime_manifest = JSON.parse(manifestOutput);
185
+ if (!report.runtime_manifest.ok || report.runtime_manifest.server_version !== rootPackage.version || report.runtime_manifest.tool_count !== 26) {
186
+ throw new Error("Packaged runtime manifest did not match the 1.6.1 26-tool contract.");
187
+ }
188
+
189
+ if (!skipUiSmoke) {
190
+ const smokeOutput = run("unpacked desktop UI smoke", process.execPath, ["scripts/smoke-unpacked.mjs", "--exe", executable, "--output", join(outputRoot, "ui-smoke")], desktopRoot);
191
+ report.ui_smoke = JSON.parse(smokeOutput.trim().split(/\r?\n/).at(-1));
192
+ if (!report.ui_smoke.ok) throw new Error("Unpacked desktop UI smoke did not pass.");
193
+ }
194
+
195
+ report.artifact = {
196
+ name: basename(executable),
197
+ size_bytes: readFileSync(executable).length,
198
+ sha256: sha256(readFileSync(executable)),
199
+ };
200
+ report.status = "passed";
201
+ } catch (error) {
202
+ report.status = "failed";
203
+ report.error = error instanceof Error ? error.message : String(error);
204
+ throw error;
205
+ } finally {
206
+ if (isolatedRoot) rmSync(isolatedRoot, { recursive: true, force: true });
207
+ report.finished_at = new Date().toISOString();
208
+ saveReport();
209
+ }
210
+
211
+ console.log(`[desktop-preflight] PASS: ${outputRoot}`);