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
@@ -12,7 +12,7 @@
12
12
  * 3. 存在 warn 级检查项或文档不同步 → NEEDS_FIX
13
13
  * 4. 所有检查通过 → ACCEPTED
14
14
  */
15
- import type { AcceptanceStatus } from "../tools/createTask.js";
15
+ import type { AcceptanceStatus } from "../tools/tasks/createTask.js";
16
16
  export type AcceptanceVerdict = "ACCEPTED" | "NEEDS_FIX" | "REJECTED" | "BLOCKED_BY_APPROVAL";
17
17
  export interface AcceptanceCheck {
18
18
  name: string;
@@ -14,7 +14,7 @@ import { existsSync, readFileSync } from "node:fs";
14
14
  import { isAbsolute, join } from "node:path";
15
15
  import { getConfig } from "../config.js";
16
16
  import { PatchWardenError } from "../errors.js";
17
- import { readGoalStatus, writeGoalStatus } from "./goalStore.js";
17
+ import { mutateGoalStatus, readGoalStatus } from "./goalStore.js";
18
18
  import { updateSubgoalStatus } from "./goalStatus.js";
19
19
  import { getBlockedSubgoals } from "./goalGraph.js";
20
20
  // ── 辅助:解析 workspaceRoot ──────────────────────────────────────
@@ -66,42 +66,45 @@ function readTaskStatus(taskId, workspaceRoot) {
66
66
  * @throws {PatchWardenError} reason="goal_not_found" | "subgoal_not_found" | "subgoal_not_ready" | "invalid_status_transition"
67
67
  */
68
68
  export function acceptSubgoal(goalId, subgoalId, workspaceRoot) {
69
- const goalStatus = readGoalStatus(goalId, workspaceRoot);
70
- const subgoal = goalStatus.subgoals.find((s) => s.id === subgoalId);
71
- if (!subgoal) {
72
- throw new PatchWardenError("subgoal_not_found", `Subgoal "${subgoalId}" not found in goal "${goalId}"`, "Ensure the subgoal id exists before accepting.", true, { subgoal_id: subgoalId, goal_id: goalId });
73
- }
74
- // 校验 1:task_ids 不能为空
75
- if (subgoal.task_ids.length === 0) {
76
- throw new PatchWardenError("subgoal_not_ready", `Subgoal "${subgoalId}" has no associated tasks; cannot accept without task verification.`, "Link tasks to the subgoal and run audit_task on each before accepting.", true, { subgoal_id: subgoalId, goal_id: goalId, task_ids: [] });
77
- }
78
- // 校验 2:所有 task 必须为 accepted
79
- const unacceptedTasks = [];
80
- for (const taskId of subgoal.task_ids) {
81
- const taskStatus = readTaskStatus(taskId, workspaceRoot);
82
- if (taskStatus !== "accepted") {
83
- unacceptedTasks.push({
84
- task_id: taskId,
85
- current_status: taskStatus ?? "missing",
69
+ return mutateGoalStatus(goalId, (goalStatus) => {
70
+ const subgoal = goalStatus.subgoals.find((s) => s.id === subgoalId);
71
+ if (!subgoal) {
72
+ throw new PatchWardenError("subgoal_not_found", `Subgoal "${subgoalId}" not found in goal "${goalId}"`, "Ensure the subgoal id exists before accepting.", true, { subgoal_id: subgoalId, goal_id: goalId });
73
+ }
74
+ // 校验 1:task_ids 不能为空
75
+ if (subgoal.task_ids.length === 0) {
76
+ throw new PatchWardenError("subgoal_not_ready", `Subgoal "${subgoalId}" has no associated tasks; cannot accept without task verification.`, "Link tasks to the subgoal and run audit_task on each before accepting.", true, { subgoal_id: subgoalId, goal_id: goalId, task_ids: [] });
77
+ }
78
+ // 校验 2:所有 task 必须为 accepted
79
+ const unacceptedTasks = [];
80
+ for (const taskId of subgoal.task_ids) {
81
+ const taskStatus = readTaskStatus(taskId, workspaceRoot);
82
+ if (taskStatus !== "accepted") {
83
+ unacceptedTasks.push({
84
+ task_id: taskId,
85
+ current_status: taskStatus ?? "missing",
86
+ });
87
+ }
88
+ }
89
+ if (unacceptedTasks.length > 0) {
90
+ throw new PatchWardenError("subgoal_not_ready", `Subgoal "${subgoalId}" cannot be accepted: ${unacceptedTasks.length} task(s) are not in "accepted" status.`, "Run audit_task on each unaccepted task first.", true, {
91
+ subgoal_id: subgoalId,
92
+ goal_id: goalId,
93
+ unaccepted_tasks: unacceptedTasks,
86
94
  });
87
95
  }
88
- }
89
- if (unacceptedTasks.length > 0) {
90
- throw new PatchWardenError("subgoal_not_ready", `Subgoal "${subgoalId}" cannot be accepted: ${unacceptedTasks.length} task(s) are not in "accepted" status.`, "Run audit_task on each unaccepted task first.", true, {
91
- subgoal_id: subgoalId,
92
- goal_id: goalId,
93
- unaccepted_tasks: unacceptedTasks,
94
- });
95
- }
96
- // 校验 3:通过状态机转换(要求 subgoal 当前为 done_by_agent)
97
- const updatedGoalStatus = updateSubgoalStatus(goalStatus, subgoalId, "accepted");
98
- writeGoalStatus(goalId, updatedGoalStatus, workspaceRoot);
99
- const updatedSubgoal = updatedGoalStatus.subgoals.find((s) => s.id === subgoalId);
100
- return {
101
- subgoal_id: subgoalId,
102
- status: "accepted",
103
- accepted_at: updatedSubgoal.accepted_at,
104
- };
96
+ // 校验 3:通过状态机转换(要求 subgoal 当前为 done_by_agent)
97
+ const updatedGoalStatus = updateSubgoalStatus(goalStatus, subgoalId, "accepted");
98
+ const updatedSubgoal = updatedGoalStatus.subgoals.find((s) => s.id === subgoalId);
99
+ return {
100
+ next: updatedGoalStatus,
101
+ result: {
102
+ subgoal_id: subgoalId,
103
+ status: "accepted",
104
+ accepted_at: updatedSubgoal.accepted_at,
105
+ },
106
+ };
107
+ }, workspaceRoot);
105
108
  }
106
109
  /**
107
110
  * 拒绝子目标:从任意非终态(ready/running/done_by_agent/needs_fix)拒绝 subgoal。
@@ -117,40 +120,43 @@ export function acceptSubgoal(goalId, subgoalId, workspaceRoot) {
117
120
  * @throws {PatchWardenError} reason="goal_not_found" | "subgoal_not_found" | "invalid_status_transition"
118
121
  */
119
122
  export function rejectSubgoal(goalId, subgoalId, reason, workspaceRoot) {
120
- const goalStatus = readGoalStatus(goalId, workspaceRoot);
121
- const index = goalStatus.subgoals.findIndex((s) => s.id === subgoalId);
122
- if (index === -1) {
123
- throw new PatchWardenError("subgoal_not_found", `Subgoal "${subgoalId}" not found in goal "${goalId}"`, "Ensure the subgoal id exists before rejecting.", true, { subgoal_id: subgoalId, goal_id: goalId });
124
- }
125
- const current = goalStatus.subgoals[index];
126
- // 终态检查:accepted 和 rejected 不允许再 reject
127
- if (current.status === "accepted" || current.status === "rejected") {
128
- throw new PatchWardenError("invalid_status_transition", `Cannot reject subgoal "${subgoalId}" in terminal state "${current.status}".`, "Reject is only allowed from non-terminal states (ready, running, done_by_agent, needs_fix).", true, {
129
- subgoal_id: subgoalId,
130
- goal_id: goalId,
131
- from_status: current.status,
132
- to_status: "rejected",
133
- });
134
- }
135
- // 手动更新 status 和 rejected_reason(绕过状态机)
136
- const updatedSubgoal = {
137
- ...current,
138
- status: "rejected",
139
- rejected_reason: reason,
140
- };
141
- const newSubgoals = [...goalStatus.subgoals];
142
- newSubgoals[index] = updatedSubgoal;
143
- const updatedGoalStatus = {
144
- ...goalStatus,
145
- subgoals: newSubgoals,
146
- updated_at: new Date().toISOString(),
147
- };
148
- writeGoalStatus(goalId, updatedGoalStatus, workspaceRoot);
149
- return {
150
- subgoal_id: subgoalId,
151
- status: "rejected",
152
- rejected_reason: reason,
153
- };
123
+ return mutateGoalStatus(goalId, (goalStatus) => {
124
+ const index = goalStatus.subgoals.findIndex((s) => s.id === subgoalId);
125
+ if (index === -1) {
126
+ throw new PatchWardenError("subgoal_not_found", `Subgoal "${subgoalId}" not found in goal "${goalId}"`, "Ensure the subgoal id exists before rejecting.", true, { subgoal_id: subgoalId, goal_id: goalId });
127
+ }
128
+ const current = goalStatus.subgoals[index];
129
+ // 终态检查:accepted 和 rejected 不允许再 reject
130
+ if (current.status === "accepted" || current.status === "rejected") {
131
+ throw new PatchWardenError("invalid_status_transition", `Cannot reject subgoal "${subgoalId}" in terminal state "${current.status}".`, "Reject is only allowed from non-terminal states (ready, running, done_by_agent, needs_fix).", true, {
132
+ subgoal_id: subgoalId,
133
+ goal_id: goalId,
134
+ from_status: current.status,
135
+ to_status: "rejected",
136
+ });
137
+ }
138
+ // 手动更新 status 和 rejected_reason(绕过状态机)
139
+ const updatedSubgoal = {
140
+ ...current,
141
+ status: "rejected",
142
+ rejected_reason: reason,
143
+ };
144
+ const newSubgoals = [...goalStatus.subgoals];
145
+ newSubgoals[index] = updatedSubgoal;
146
+ const updatedGoalStatus = {
147
+ ...goalStatus,
148
+ subgoals: newSubgoals,
149
+ updated_at: new Date().toISOString(),
150
+ };
151
+ return {
152
+ next: updatedGoalStatus,
153
+ result: {
154
+ subgoal_id: subgoalId,
155
+ status: "rejected",
156
+ rejected_reason: reason,
157
+ },
158
+ };
159
+ }, workspaceRoot);
154
160
  }
155
161
  /**
156
162
  * 汇总 Goal 进度:统计各状态数量、完成率、阻塞子目标、风险项。
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Goal 最终报告导出 — 将 Goal 完成情况渲染为人类可读 Markdown 和机器可读 JSON。
3
+ *
4
+ * exportGoalReport 读取 GoalStatus 和进度统计,关联 Evidence Pack,
5
+ * 生成 REPORT.md 和 report.json 到 {workspaceRoot}/.patchwarden/goals/{goalId}/report/。
6
+ * 所有写入操作使用原子写(.tmp + rename),所有字符串内容经 redactSensitiveValue 脱敏。
7
+ */
8
+ import { type GoalProgressSummary } from "./goalProgress.js";
9
+ export interface SubgoalReportEntry {
10
+ id: string;
11
+ title: string;
12
+ status: string;
13
+ task_ids: string[];
14
+ evidence_packs: string[];
15
+ accepted_at?: string;
16
+ rejected_reason?: string;
17
+ }
18
+ export interface SafeGoalReport {
19
+ goal_id: string;
20
+ generated_at: string;
21
+ path: string;
22
+ completion_rate: number;
23
+ files: {
24
+ report_md: string;
25
+ report_json: string;
26
+ };
27
+ summary: GoalProgressSummary;
28
+ subgoals: SubgoalReportEntry[];
29
+ risks: string[];
30
+ timeline: {
31
+ created_at: string;
32
+ updated_at: string;
33
+ status: string;
34
+ };
35
+ bounded: true;
36
+ }
37
+ /**
38
+ * 导出 Goal 最终报告,生成 REPORT.md 和 report.json。
39
+ *
40
+ * 流程:
41
+ * 1. 读取 GoalStatus(readGoalStatus)
42
+ * 2. 调用 summarizeGoalProgress 获取完成度统计
43
+ * 3. 遍历 subgoals,构建 SubgoalReportEntry,关联 Evidence Pack
44
+ * 4. 生成 REPORT.md(人类可读)和 report.json(机器可读)
45
+ * 5. 原子写到 {workspaceRoot}/.patchwarden/goals/{goalId}/report/
46
+ * 6. 所有字符串内容经 redactSensitiveValue 脱敏
47
+ *
48
+ * @param goalId Goal 标识
49
+ * @param options 可选配置(workspaceRoot:工作区根目录,默认从 getConfig 获取)
50
+ * @returns SafeGoalReport(脱敏后的报告对象)
51
+ */
52
+ export declare function exportGoalReport(goalId: string, options?: {
53
+ workspaceRoot?: string;
54
+ }): SafeGoalReport;
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Goal 最终报告导出 — 将 Goal 完成情况渲染为人类可读 Markdown 和机器可读 JSON。
3
+ *
4
+ * exportGoalReport 读取 GoalStatus 和进度统计,关联 Evidence Pack,
5
+ * 生成 REPORT.md 和 report.json 到 {workspaceRoot}/.patchwarden/goals/{goalId}/report/。
6
+ * 所有写入操作使用原子写(.tmp + rename),所有字符串内容经 redactSensitiveValue 脱敏。
7
+ */
8
+ import { mkdirSync } from "node:fs";
9
+ import { join, resolve } from "node:path";
10
+ import { getConfig } from "../config.js";
11
+ import { redactSensitiveValue } from "../security/contentRedaction.js";
12
+ import { listEvidencePacks } from "../tools/tasks/evidencePack.js";
13
+ import { atomicWriteFileSync } from "../utils/atomicFile.js";
14
+ import { summarizeGoalProgress } from "./goalProgress.js";
15
+ import { readGoalStatus } from "./goalStore.js";
16
+ // ── 辅助函数 ──────────────────────────────────────────────────────
17
+ function resolveWorkspaceRoot(workspaceRoot) {
18
+ return workspaceRoot ?? getConfig().workspaceRoot;
19
+ }
20
+ /**
21
+ * 将百分比字符串(如 "100%")解析为数字。
22
+ */
23
+ function parseCompletionRate(value) {
24
+ const n = parseInt(value, 10);
25
+ return Number.isFinite(n) ? n : 0;
26
+ }
27
+ /**
28
+ * 查找与 task_ids 关联的 Evidence Pack 路径。
29
+ * 通过 listEvidencePacks 列出所有 pack,匹配 lineage_id 在 task_ids 中的 pack。
30
+ */
31
+ function collectEvidencePacks(taskIds) {
32
+ if (taskIds.length === 0)
33
+ return [];
34
+ const packs = listEvidencePacks({ max_items: 50 });
35
+ const taskIdSet = new Set(taskIds);
36
+ const paths = [];
37
+ for (const pack of packs.evidence_packs) {
38
+ if (taskIdSet.has(pack.lineage_id)) {
39
+ paths.push(pack.path);
40
+ }
41
+ }
42
+ return paths;
43
+ }
44
+ // ── Markdown 生成 ─────────────────────────────────────────────────
45
+ /**
46
+ * 从脱敏后的报告对象生成人类可读 Markdown。
47
+ */
48
+ function buildReportMarkdown(report, flags) {
49
+ const lines = [];
50
+ // 标题
51
+ lines.push("# Goal Report: " + report.summary.title);
52
+ lines.push("");
53
+ // 元信息
54
+ lines.push("## 元信息");
55
+ lines.push("");
56
+ lines.push("- **goal_id**: " + report.goal_id);
57
+ lines.push("- **generated_at**: " + report.generated_at);
58
+ lines.push("- **status**: " + report.timeline.status);
59
+ lines.push("- **completion_rate**: " + report.completion_rate + "%");
60
+ if (flags.noSubgoals) {
61
+ lines.push("- **备注**: 无子目标");
62
+ }
63
+ if (flags.incomplete) {
64
+ lines.push("- **备注**: 未完成");
65
+ }
66
+ lines.push("");
67
+ // 完成度统计表
68
+ lines.push("## 完成度统计");
69
+ lines.push("");
70
+ lines.push("| 指标 | 数值 |");
71
+ lines.push("| --- | --- |");
72
+ lines.push("| total | " + report.summary.total + " |");
73
+ lines.push("| accepted | " + report.summary.accepted + " |");
74
+ lines.push("| rejected | " + report.summary.rejected + " |");
75
+ lines.push("| running | " + report.summary.running + " |");
76
+ lines.push("| ready | " + report.summary.ready + " |");
77
+ lines.push("| needs_fix | " + report.summary.needs_fix + " |");
78
+ lines.push("");
79
+ // 子目标清单
80
+ lines.push("## 子目标清单");
81
+ lines.push("");
82
+ if (report.subgoals.length === 0) {
83
+ lines.push("无子目标");
84
+ lines.push("");
85
+ }
86
+ else {
87
+ for (const sg of report.subgoals) {
88
+ lines.push("### " + sg.id + ": " + sg.title);
89
+ lines.push("");
90
+ lines.push("- **status**: " + sg.status);
91
+ lines.push("- **task_ids**: " + (sg.task_ids.length > 0 ? sg.task_ids.join(", ") : "无"));
92
+ lines.push("- **evidence_packs**: " +
93
+ (sg.evidence_packs.length > 0 ? sg.evidence_packs.join(", ") : "无"));
94
+ if (sg.accepted_at) {
95
+ lines.push("- **accepted_at**: " + sg.accepted_at);
96
+ }
97
+ if (sg.rejected_reason) {
98
+ lines.push("- **rejected_reason**: " + sg.rejected_reason);
99
+ }
100
+ lines.push("");
101
+ }
102
+ }
103
+ // 风险汇总
104
+ lines.push("## 风险汇总");
105
+ lines.push("");
106
+ if (report.risks.length === 0) {
107
+ lines.push("无");
108
+ }
109
+ else {
110
+ for (const r of report.risks) {
111
+ lines.push("- " + r);
112
+ }
113
+ }
114
+ lines.push("");
115
+ // 时间线
116
+ lines.push("## 时间线");
117
+ lines.push("");
118
+ lines.push("- **created_at**: " + report.timeline.created_at);
119
+ lines.push("- **updated_at**: " + report.timeline.updated_at);
120
+ lines.push("");
121
+ return lines.join("\n");
122
+ }
123
+ // ── 公共 API ──────────────────────────────────────────────────────
124
+ /**
125
+ * 导出 Goal 最终报告,生成 REPORT.md 和 report.json。
126
+ *
127
+ * 流程:
128
+ * 1. 读取 GoalStatus(readGoalStatus)
129
+ * 2. 调用 summarizeGoalProgress 获取完成度统计
130
+ * 3. 遍历 subgoals,构建 SubgoalReportEntry,关联 Evidence Pack
131
+ * 4. 生成 REPORT.md(人类可读)和 report.json(机器可读)
132
+ * 5. 原子写到 {workspaceRoot}/.patchwarden/goals/{goalId}/report/
133
+ * 6. 所有字符串内容经 redactSensitiveValue 脱敏
134
+ *
135
+ * @param goalId Goal 标识
136
+ * @param options 可选配置(workspaceRoot:工作区根目录,默认从 getConfig 获取)
137
+ * @returns SafeGoalReport(脱敏后的报告对象)
138
+ */
139
+ export function exportGoalReport(goalId, options) {
140
+ const workspaceRoot = resolveWorkspaceRoot(options?.workspaceRoot);
141
+ const goalStatus = readGoalStatus(goalId, workspaceRoot);
142
+ const summary = summarizeGoalProgress(goalId, workspaceRoot);
143
+ const generatedAt = new Date().toISOString();
144
+ const noSubgoals = goalStatus.subgoals.length === 0;
145
+ const hasIncomplete = goalStatus.subgoals.some((s) => s.status === "running" || s.status === "ready");
146
+ const incomplete = goalStatus.status === "active" && hasIncomplete;
147
+ // 构建 subgoal 报告条目
148
+ const subgoalEntries = goalStatus.subgoals.map((sg) => {
149
+ const entry = {
150
+ id: sg.id,
151
+ title: sg.title,
152
+ status: sg.status,
153
+ task_ids: [...sg.task_ids],
154
+ evidence_packs: collectEvidencePacks(sg.task_ids),
155
+ };
156
+ if (sg.accepted_at)
157
+ entry.accepted_at = sg.accepted_at;
158
+ if (sg.rejected_reason)
159
+ entry.rejected_reason = sg.rejected_reason;
160
+ return entry;
161
+ });
162
+ // 风险汇总
163
+ const risks = summary.risks.map((r) => r.subgoal_id + " (" + r.status + "): " + r.reason);
164
+ if (incomplete) {
165
+ risks.push("未完成:Goal 仍有 running/ready 子目标");
166
+ }
167
+ // 报告目录
168
+ const reportDir = resolve(workspaceRoot, ".patchwarden", "goals", goalId, "report");
169
+ mkdirSync(reportDir, { recursive: true });
170
+ const reportMdPath = join(reportDir, "REPORT.md");
171
+ const reportJsonPath = join(reportDir, "report.json");
172
+ // 构建原始报告对象
173
+ const rawReport = {
174
+ goal_id: goalStatus.goal_id,
175
+ generated_at: generatedAt,
176
+ path: reportDir,
177
+ completion_rate: noSubgoals ? 0 : parseCompletionRate(summary.completion_rate),
178
+ files: { report_md: reportMdPath, report_json: reportJsonPath },
179
+ summary,
180
+ subgoals: subgoalEntries,
181
+ risks,
182
+ timeline: {
183
+ created_at: goalStatus.created_at,
184
+ updated_at: goalStatus.updated_at,
185
+ status: goalStatus.status,
186
+ },
187
+ bounded: true,
188
+ };
189
+ // 脱敏所有字符串内容
190
+ const safeReport = redactSensitiveValue(rawReport).value;
191
+ // 原子写 JSON(机器可读)
192
+ atomicWriteFileSync(reportJsonPath, JSON.stringify(safeReport, null, 2) + "\n");
193
+ // 原子写 Markdown(人类可读,从脱敏后的对象生成)
194
+ const markdown = buildReportMarkdown(safeReport, { noSubgoals, incomplete });
195
+ atomicWriteFileSync(reportMdPath, markdown);
196
+ return safeReport;
197
+ }
@@ -19,6 +19,12 @@ export interface Subgoal {
19
19
  task_ids: string[];
20
20
  accepted_at?: string;
21
21
  rejected_reason?: string;
22
+ /** Spec Kit task id 回溯(用于幂等去重) */
23
+ external_ref?: string;
24
+ /** Spec Kit task.files 提示(作用域文件路径列表) */
25
+ scope_hints?: string[];
26
+ /** Managed git worktree associated with an isolated subgoal task. */
27
+ worktree_id?: string;
22
28
  }
23
29
  export interface GoalStatus {
24
30
  goal_id: string;
@@ -28,6 +34,8 @@ export interface GoalStatus {
28
34
  created_at: string;
29
35
  updated_at: string;
30
36
  subgoals: Subgoal[];
37
+ /** Spec Kit 验收标准列表 */
38
+ acceptance_criteria?: string[];
31
39
  }
32
40
  /**
33
41
  * 创建初始 GoalStatus,用于新 goal。
@@ -11,6 +11,7 @@
11
11
  * 所有更新函数返回新的 GoalStatus 对象,不修改原对象。
12
12
  */
13
13
  import { PatchWardenError } from "../errors.js";
14
+ import { redactSensitiveContent } from "../security/contentRedaction.js";
14
15
  // ── 合法状态转换表 ────────────────────────────────────────────────
15
16
  const LEGAL_TRANSITIONS = {
16
17
  ready: ["running"],
@@ -29,7 +30,7 @@ export function createInitialGoalStatus(goalId, title, repoPath) {
29
30
  const now = new Date().toISOString();
30
31
  return {
31
32
  goal_id: goalId,
32
- title,
33
+ title: redactGoalText(title, 500),
33
34
  status: "active",
34
35
  repo_path: repoPath,
35
36
  created_at: now,
@@ -44,6 +45,9 @@ export function createInitialGoalStatus(goalId, title, repoPath) {
44
45
  * 不可变更新:返回新的 GoalStatus 对象。
45
46
  */
46
47
  export function addSubgoal(goalStatus, title, dependsOn = []) {
48
+ if (goalStatus.status !== "active") {
49
+ throw new PatchWardenError("goal_not_active", `Goal "${goalStatus.goal_id}" is ${goalStatus.status} and cannot accept new subgoals.`, "Create a new Goal Session for additional work.", true, { goal_id: goalStatus.goal_id, status: goalStatus.status });
50
+ }
47
51
  const existingIds = new Set(goalStatus.subgoals.map((s) => s.id));
48
52
  for (const depId of dependsOn) {
49
53
  if (!existingIds.has(depId)) {
@@ -54,7 +58,7 @@ export function addSubgoal(goalStatus, title, dependsOn = []) {
54
58
  const subgoalId = `subgoal-${String(nextNumber).padStart(3, "0")}`;
55
59
  const newSubgoal = {
56
60
  id: subgoalId,
57
- title,
61
+ title: redactGoalText(title, 500),
58
62
  status: "ready",
59
63
  depends_on: [...dependsOn],
60
64
  task_ids: [],
@@ -99,7 +103,7 @@ export function updateSubgoalStatus(goalStatus, subgoalId, newStatus, options) {
99
103
  if (!options || !options.rejected_reason) {
100
104
  throw new PatchWardenError("invalid_status_transition", `Rejecting subgoal "${subgoalId}" requires rejected_reason`, "Provide options.rejected_reason when transitioning to \"rejected\".", true, { subgoal_id: subgoalId, from_status: current.status, to_status: newStatus });
101
105
  }
102
- updatedSubgoal.rejected_reason = options.rejected_reason;
106
+ updatedSubgoal.rejected_reason = redactGoalText(options.rejected_reason, 2000);
103
107
  }
104
108
  const newSubgoals = [...goalStatus.subgoals];
105
109
  newSubgoals[index] = updatedSubgoal;
@@ -109,6 +113,9 @@ export function updateSubgoalStatus(goalStatus, subgoalId, newStatus, options) {
109
113
  updated_at: new Date().toISOString(),
110
114
  };
111
115
  }
116
+ function redactGoalText(value, maxChars) {
117
+ return redactSensitiveContent(String(value || "")).content.slice(0, maxChars);
118
+ }
112
119
  /**
113
120
  * 将 taskId 关联到指定 subgoal(去重)。
114
121
  * 不可变更新:返回新的 GoalStatus 对象。
@@ -13,6 +13,8 @@
13
13
  * 默认从 getConfig().workspaceRoot 读取。
14
14
  */
15
15
  import { type GoalStatus, type Subgoal } from "./goalStatus.js";
16
+ /** Validate the storage key before it can participate in path construction. */
17
+ export declare function assertValidGoalId(goalId: string): void;
16
18
  /**
17
19
  * 生成 `goal_{YYYYMMDD}_{slug}` 格式的 goal id。
18
20
  * 冲突时追加 `_2`、`_3`... 直到唯一。
@@ -68,8 +70,7 @@ export declare function readGoal(goalId: string, workspaceRoot?: string): {
68
70
  subgoals: Subgoal[];
69
71
  };
70
72
  /**
71
- * 原子写入 goal_status.json:先写到 .tmp 文件,再 renameSync。
72
- * JSON 序列化用 JSON.stringify(status, null, 2) + 末尾换行。
73
+ * Replace goal_status.json under the shared cross-process mutation lock.
73
74
  */
74
75
  export declare function writeGoalStatus(goalId: string, status: GoalStatus, workspaceRoot?: string): void;
75
76
  /**
@@ -77,3 +78,9 @@ export declare function writeGoalStatus(goalId: string, status: GoalStatus, work
77
78
  * 如果文件不存在,抛出 PatchWardenError("goal_not_found")。
78
79
  */
79
80
  export declare function readGoalStatus(goalId: string, workspaceRoot?: string): GoalStatus;
81
+ export interface GoalStatusMutation<R> {
82
+ next?: GoalStatus;
83
+ result: R;
84
+ }
85
+ export declare function mutateGoalStatus<R>(goalId: string, mutation: (current: GoalStatus) => GoalStatusMutation<R>, workspaceRoot?: string): R;
86
+ export declare function mutateGoalStatusAsync<R>(goalId: string, mutation: (current: GoalStatus) => Promise<GoalStatusMutation<R>>, workspaceRoot?: string): Promise<R>;