patchwarden 1.5.1 → 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.
- package/PatchWarden.cmd +32 -2
- package/README.en.md +152 -25
- package/README.md +120 -23
- package/dist/assessments/agentAssessor.js +21 -12
- package/dist/assessments/assessmentStore.js +42 -34
- package/dist/config.d.ts +15 -0
- package/dist/config.js +66 -13
- package/dist/control/fileManager.d.ts +2 -0
- package/dist/control/fileManager.js +39 -0
- package/dist/control/middleware/auth.d.ts +6 -0
- package/dist/control/middleware/auth.js +14 -5
- package/dist/control/middleware/static.js +16 -3
- package/dist/control/routeTable.d.ts +24 -0
- package/dist/control/routeTable.js +263 -0
- package/dist/control/routes/audit.js +17 -7
- package/dist/control/routes/evidence.js +1 -1
- package/dist/control/routes/lineage.js +1 -1
- package/dist/control/routes/process.d.ts +7 -0
- package/dist/control/routes/process.js +329 -42
- package/dist/control/routes/sessions.d.ts +9 -1
- package/dist/control/routes/sessions.js +84 -21
- package/dist/control/routes/status.d.ts +46 -1
- package/dist/control/routes/status.js +72 -15
- package/dist/control/routes/taskActions.js +24 -60
- package/dist/control/routes/tasks.d.ts +4 -0
- package/dist/control/routes/tasks.js +61 -62
- package/dist/control/routes/workspace.js +50 -23
- package/dist/control/runtime.d.ts +3 -2
- package/dist/control/runtime.js +63 -30
- package/dist/control/server.js +36 -365
- package/dist/control/shared.d.ts +25 -1
- package/dist/control/shared.js +64 -11
- package/dist/direct/directAudit.js +234 -225
- package/dist/direct/directGuards.d.ts +2 -1
- package/dist/direct/directGuards.js +42 -29
- package/dist/direct/directPatch.d.ts +6 -1
- package/dist/direct/directPatch.js +55 -8
- package/dist/direct/directSessionStore.d.ts +16 -6
- package/dist/direct/directSessionStore.js +127 -35
- package/dist/direct/directVerification.js +8 -16
- package/dist/doctor.js +62 -52
- package/dist/goal/acceptanceEngine.d.ts +1 -1
- package/dist/goal/goalProgress.js +75 -69
- package/dist/goal/goalReport.js +5 -12
- package/dist/goal/goalStatus.d.ts +2 -0
- package/dist/goal/goalStatus.js +10 -3
- package/dist/goal/goalStore.d.ts +9 -2
- package/dist/goal/goalStore.js +152 -45
- package/dist/goal/handoffExport.js +6 -6
- package/dist/goal/specKitImport.js +97 -96
- package/dist/goal/subgoalSync.js +11 -9
- package/dist/goal/worktreeManager.d.ts +2 -0
- package/dist/goal/worktreeManager.js +209 -136
- package/dist/httpServer.js +111 -32
- package/dist/logging.js +5 -3
- package/dist/policy/projectPolicy.js +5 -2
- package/dist/release/releaseGate.d.ts +1 -1
- package/dist/release/releaseGate.js +78 -25
- package/dist/runner/agentInvocation.d.ts +14 -0
- package/dist/runner/agentInvocation.js +96 -4
- package/dist/runner/changeCapture.d.ts +13 -5
- package/dist/runner/changeCapture.js +243 -61
- package/dist/runner/postTaskCleanup.js +48 -19
- package/dist/runner/processSecurity.d.ts +56 -0
- package/dist/runner/processSecurity.js +373 -0
- package/dist/runner/runTask.d.ts +1 -1
- package/dist/runner/runTask.js +115 -87
- package/dist/runner/simpleProcess.d.ts +3 -0
- package/dist/runner/simpleProcess.js +50 -36
- package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
- package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
- package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
- package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
- package/dist/runner/taskStatusStore.d.ts +11 -0
- package/dist/runner/taskStatusStore.js +24 -0
- package/dist/runner/watch.d.ts +14 -1
- package/dist/runner/watch.js +256 -48
- package/dist/security/contentRedaction.js +2 -2
- package/dist/security/discoveryTokenStore.d.ts +3 -2
- package/dist/security/discoveryTokenStore.js +17 -2
- package/dist/security/loopbackHost.d.ts +2 -0
- package/dist/security/loopbackHost.js +16 -0
- package/dist/security/pathGuard.js +32 -17
- package/dist/security/planGuard.js +2 -1
- package/dist/security/runtimeGuard.js +9 -6
- package/dist/security/secretComparison.d.ts +3 -0
- package/dist/security/secretComparison.js +11 -0
- package/dist/security/sensitiveGuard.d.ts +5 -0
- package/dist/security/sensitiveGuard.js +27 -8
- package/dist/security/toolInvocationGuard.d.ts +2 -2
- package/dist/security/toolInvocationGuard.js +1 -1
- package/dist/security/workspaceRootGuard.d.ts +8 -0
- package/dist/security/workspaceRootGuard.js +47 -0
- package/dist/smoke-test.js +24 -23
- package/dist/tools/catalog/index.d.ts +4 -0
- package/dist/tools/catalog/index.js +4 -0
- package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
- package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
- package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
- package/dist/tools/definitions/toolDefs.d.ts +18 -0
- package/dist/tools/definitions/toolDefs.js +1177 -0
- package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
- package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
- package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +1 -1
- package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +99 -29
- package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
- package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +7 -7
- package/dist/tools/diagnostics/index.d.ts +6 -0
- package/dist/tools/diagnostics/index.js +6 -0
- package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
- package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
- package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
- package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
- package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
- package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
- package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
- package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
- package/dist/tools/direct/finalizeDirectSession.js +86 -0
- package/dist/tools/direct/index.d.ts +3 -0
- package/dist/tools/direct/index.js +3 -0
- package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
- package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
- package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
- package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
- package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
- package/dist/tools/discovery/index.d.ts +3 -0
- package/dist/tools/discovery/index.js +3 -0
- package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
- package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
- package/dist/tools/dispatch/coreDispatch.js +32 -31
- package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
- package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
- package/dist/tools/dispatch/directDispatch.js +11 -10
- package/dist/tools/dispatch/goalDispatch.js +5 -4
- package/dist/tools/dispatch/releaseDispatch.js +3 -2
- package/dist/tools/dispatch/validation.d.ts +7 -0
- package/dist/tools/dispatch/validation.js +71 -0
- package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
- package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
- package/dist/tools/goals/goalSubgoalTask.js +118 -0
- package/dist/tools/goals/index.d.ts +3 -0
- package/dist/tools/goals/index.js +3 -0
- package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
- package/dist/tools/registry.d.ts +7 -11
- package/dist/tools/registry.js +17 -1178
- package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
- package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
- package/dist/tools/release/index.d.ts +2 -0
- package/dist/tools/release/index.js +2 -0
- package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
- package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
- package/dist/tools/tasks/cancelTask.d.ts +11 -0
- package/dist/tools/tasks/cancelTask.js +85 -0
- package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +10 -2
- package/dist/tools/{createTask.js → tasks/createTask.js} +42 -27
- package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
- package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
- package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
- package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
- package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
- package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
- package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
- package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
- package/dist/tools/tasks/index.d.ts +18 -0
- package/dist/tools/tasks/index.js +18 -0
- package/dist/tools/tasks/killTask.d.ts +1 -0
- package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
- package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
- package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
- package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
- package/dist/tools/tasks/retryTask.js +60 -0
- package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
- package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +58 -30
- package/dist/tools/tasks/runVerification.js +38 -0
- package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
- package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
- package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
- package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
- package/dist/tools/workspace/applyPatch.js +53 -0
- package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
- package/dist/tools/workspace/index.d.ts +10 -0
- package/dist/tools/workspace/index.js +10 -0
- package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
- package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
- package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
- package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
- package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
- package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
- package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
- package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
- package/dist/tools/workspace/syncFile.js +134 -0
- package/dist/utils/atomicFile.d.ts +7 -0
- package/dist/utils/atomicFile.js +48 -0
- package/dist/utils/boundedFile.d.ts +8 -0
- package/dist/utils/boundedFile.js +93 -0
- package/dist/utils/lockedJsonFile.d.ts +15 -0
- package/dist/utils/lockedJsonFile.js +224 -0
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +26 -0
- package/dist/utils/stableJson.d.ts +2 -0
- package/dist/utils/stableJson.js +12 -0
- package/dist/version.d.ts +3 -2
- package/dist/version.js +3 -2
- package/dist/watcherStatus.d.ts +2 -2
- package/dist/watcherStatus.js +18 -0
- package/docs/CODE_WIKI.md +808 -143
- package/docs/architecture.md +15 -55
- package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
- package/docs/control-center/README.md +4 -4
- package/docs/dashboard-overview.md +4 -4
- package/docs/desktop-app.md +175 -0
- package/docs/evidence-pack-schema.md +3 -3
- package/docs/open-source-application.md +17 -11
- package/docs/release-checklist.md +1 -1
- package/docs/release-evidence.md +39 -29
- package/docs/task-safe-review-workflow.md +5 -5
- package/docs/threat-model.md +19 -1
- package/examples/config.example.json +4 -2
- package/package.json +27 -10
- package/scripts/README.md +15 -6
- package/scripts/build.js +11 -0
- package/scripts/checks/brand-check.js +3 -0
- package/scripts/checks/build-output-check.js +37 -0
- package/scripts/checks/control-center-smoke.js +91 -6
- package/scripts/checks/control-smoke.js +19 -8
- package/scripts/checks/http-mcp-smoke.js +82 -4
- package/scripts/checks/lifecycle-smoke.js +10 -10
- package/scripts/checks/mcp-manifest-check.js +1 -1
- package/scripts/checks/mcp-smoke.js +27 -3
- package/scripts/checks/package-manifest-check.js +20 -7
- package/scripts/checks/unit-tests.js +3 -5
- package/scripts/checks/watcher-supervisor-smoke.js +9 -0
- package/scripts/control/get-patchwarden-health.ps1 +1 -1
- package/scripts/control/manage-patchwarden.ps1 +69 -4
- package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
- package/scripts/control/run-background-supervisor.ps1 +41 -0
- package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
- package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
- package/scripts/launchers/PatchWarden-Control.cmd +6 -0
- package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
- package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
- package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
- package/scripts/lib/clean-generated-output.js +11 -0
- package/scripts/lib/file-discovery.js +12 -0
- package/scripts/release/desktop-preflight.js +211 -0
- package/scripts/release/pack-clean.js +59 -14
- package/src/assessments/agentAssessor.ts +26 -12
- package/src/assessments/assessmentStore.ts +52 -40
- package/src/config.ts +70 -13
- package/src/control/fileManager.ts +40 -0
- package/src/control/middleware/auth.ts +22 -4
- package/src/control/middleware/static.ts +16 -3
- package/src/control/routeTable.ts +301 -0
- package/src/control/routes/audit.ts +14 -6
- package/src/control/routes/evidence.ts +1 -1
- package/src/control/routes/lineage.ts +1 -1
- package/src/control/routes/process.ts +341 -40
- package/src/control/routes/sessions.ts +88 -25
- package/src/control/routes/status.ts +97 -17
- package/src/control/routes/taskActions.ts +24 -58
- package/src/control/routes/tasks.ts +66 -58
- package/src/control/routes/workspace.ts +56 -25
- package/src/control/runtime.ts +69 -33
- package/src/control/server.ts +36 -342
- package/src/control/shared.ts +77 -11
- package/src/direct/directAudit.ts +17 -9
- package/src/direct/directGuards.ts +72 -44
- package/src/direct/directPatch.ts +95 -8
- package/src/direct/directSessionStore.ts +211 -55
- package/src/direct/directVerification.ts +9 -23
- package/src/doctor.ts +73 -59
- package/src/goal/acceptanceEngine.ts +1 -1
- package/src/goal/goalProgress.ts +17 -13
- package/src/goal/goalReport.ts +5 -13
- package/src/goal/goalStatus.ts +19 -3
- package/src/goal/goalStore.ts +241 -54
- package/src/goal/handoffExport.ts +6 -6
- package/src/goal/specKitImport.ts +12 -12
- package/src/goal/subgoalSync.ts +11 -8
- package/src/goal/worktreeManager.ts +139 -22
- package/src/httpServer.ts +113 -30
- package/src/logging.ts +10 -3
- package/src/policy/projectPolicy.ts +5 -2
- package/src/release/releaseGate.ts +74 -27
- package/src/runner/agentInvocation.ts +126 -4
- package/src/runner/changeCapture.ts +291 -57
- package/src/runner/postTaskCleanup.ts +41 -17
- package/src/runner/processSecurity.ts +433 -0
- package/src/runner/runTask.ts +138 -91
- package/src/runner/simpleProcess.ts +66 -31
- package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
- package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
- package/src/runner/taskStatusStore.ts +54 -0
- package/src/runner/watch.ts +281 -51
- package/src/security/contentRedaction.ts +2 -2
- package/src/security/discoveryTokenStore.ts +18 -4
- package/src/security/loopbackHost.ts +19 -0
- package/src/security/pathGuard.ts +35 -16
- package/src/security/planGuard.ts +2 -1
- package/src/security/runtimeGuard.ts +9 -6
- package/src/security/secretComparison.ts +13 -0
- package/src/security/sensitiveGuard.ts +27 -8
- package/src/security/toolInvocationGuard.ts +2 -2
- package/src/security/workspaceRootGuard.ts +53 -0
- package/src/smoke-test.ts +25 -24
- package/src/tools/catalog/index.ts +4 -0
- package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
- package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
- package/src/tools/definitions/toolDefs.ts +1264 -0
- package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
- package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +99 -32
- package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +8 -8
- package/src/tools/diagnostics/index.ts +6 -0
- package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
- package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
- package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
- package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
- package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
- package/src/tools/direct/index.ts +3 -0
- package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
- package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
- package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
- package/src/tools/discovery/index.ts +3 -0
- package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
- package/src/tools/dispatch/coreDispatch.ts +36 -31
- package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
- package/src/tools/dispatch/directDispatch.ts +11 -10
- package/src/tools/dispatch/goalDispatch.ts +5 -4
- package/src/tools/dispatch/releaseDispatch.ts +3 -2
- package/src/tools/dispatch/validation.ts +78 -0
- package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
- package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
- package/src/tools/goals/index.ts +3 -0
- package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
- package/src/tools/registry.ts +17 -1261
- package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
- package/src/tools/release/index.ts +2 -0
- package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
- package/src/tools/tasks/cancelTask.ts +109 -0
- package/src/tools/{createTask.ts → tasks/createTask.ts} +49 -26
- package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
- package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
- package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
- package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
- package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
- package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
- package/src/tools/tasks/index.ts +18 -0
- package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
- package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
- package/src/tools/tasks/retryTask.ts +69 -0
- package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +69 -34
- package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
- package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
- package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
- package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
- package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
- package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
- package/src/tools/workspace/index.ts +10 -0
- package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
- package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
- package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
- package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
- package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
- package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
- package/src/tools/workspace/syncFile.ts +292 -0
- package/src/utils/atomicFile.ts +58 -0
- package/src/utils/boundedFile.ts +116 -0
- package/src/utils/lockedJsonFile.ts +251 -0
- package/src/utils/platform.ts +31 -0
- package/src/utils/stableJson.ts +12 -0
- package/src/version.ts +3 -2
- package/src/watcherStatus.ts +19 -1
- package/ui/desktop-bootstrap.js +11 -0
- package/ui/desktop-bridge.js +36 -0
- package/ui/desktop.css +160 -0
- package/ui/getting-started.js +93 -0
- package/ui/i18n.js +484 -0
- package/ui/log-parser.js +30 -0
- package/ui/pages/audit.html +12 -0
- package/ui/pages/dashboard.html +90 -147
- package/ui/pages/direct-sessions.html +123 -26
- package/ui/pages/getting-started.html +43 -0
- package/ui/pages/logs.html +105 -22
- package/ui/pages/settings.html +101 -0
- package/ui/pages/task-detail.html +12 -0
- package/ui/pages/tasks.html +54 -8
- package/ui/pages/workspace.html +38 -1
- package/ui/settings.js +245 -0
- package/PatchWarden-Control.cmd +0 -6
- package/Restart-PatchWarden-Control.cmd +0 -6
- package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
- package/dist/test/unit/acceptance-engine.test.js +0 -228
- package/dist/test/unit/agent-router.test.d.ts +0 -1
- package/dist/test/unit/agent-router.test.js +0 -287
- package/dist/test/unit/android-doctor.test.d.ts +0 -1
- package/dist/test/unit/android-doctor.test.js +0 -118
- package/dist/test/unit/apply-patch.test.d.ts +0 -1
- package/dist/test/unit/apply-patch.test.js +0 -225
- package/dist/test/unit/audit-checks.test.d.ts +0 -1
- package/dist/test/unit/audit-checks.test.js +0 -350
- package/dist/test/unit/chinese-path.test.d.ts +0 -1
- package/dist/test/unit/chinese-path.test.js +0 -91
- package/dist/test/unit/command-guard.test.d.ts +0 -1
- package/dist/test/unit/command-guard.test.js +0 -160
- package/dist/test/unit/create-task.test.d.ts +0 -1
- package/dist/test/unit/create-task.test.js +0 -197
- package/dist/test/unit/diagnose-task.test.d.ts +0 -1
- package/dist/test/unit/diagnose-task.test.js +0 -457
- package/dist/test/unit/direct-guards.test.d.ts +0 -1
- package/dist/test/unit/direct-guards.test.js +0 -328
- package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
- package/dist/test/unit/discovery-token-store.test.js +0 -139
- package/dist/test/unit/evidence-pack.test.d.ts +0 -1
- package/dist/test/unit/evidence-pack.test.js +0 -224
- package/dist/test/unit/get-task-status.test.d.ts +0 -1
- package/dist/test/unit/get-task-status.test.js +0 -174
- package/dist/test/unit/get-task-summary.test.d.ts +0 -1
- package/dist/test/unit/get-task-summary.test.js +0 -146
- package/dist/test/unit/goal-graph.test.d.ts +0 -1
- package/dist/test/unit/goal-graph.test.js +0 -298
- package/dist/test/unit/goal-progress.test.d.ts +0 -1
- package/dist/test/unit/goal-progress.test.js +0 -381
- package/dist/test/unit/goal-report.test.d.ts +0 -1
- package/dist/test/unit/goal-report.test.js +0 -159
- package/dist/test/unit/goal-status.test.d.ts +0 -1
- package/dist/test/unit/goal-status.test.js +0 -215
- package/dist/test/unit/goal-store.test.d.ts +0 -1
- package/dist/test/unit/goal-store.test.js +0 -253
- package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
- package/dist/test/unit/goal-subgoal-task.test.js +0 -55
- package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
- package/dist/test/unit/goal-tools-registry.test.js +0 -192
- package/dist/test/unit/handoff-export.test.d.ts +0 -1
- package/dist/test/unit/handoff-export.test.js +0 -263
- package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
- package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
- package/dist/test/unit/logging.test.d.ts +0 -1
- package/dist/test/unit/logging.test.js +0 -397
- package/dist/test/unit/path-guard.test.d.ts +0 -1
- package/dist/test/unit/path-guard.test.js +0 -133
- package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
- package/dist/test/unit/post-task-cleanup.test.js +0 -48
- package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
- package/dist/test/unit/project-policy-release-mode.test.js +0 -125
- package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
- package/dist/test/unit/reconcile-tasks.test.js +0 -456
- package/dist/test/unit/release-gate.test.d.ts +0 -1
- package/dist/test/unit/release-gate.test.js +0 -242
- package/dist/test/unit/run-task-loop.test.d.ts +0 -1
- package/dist/test/unit/run-task-loop.test.js +0 -380
- package/dist/test/unit/safe-status.test.d.ts +0 -1
- package/dist/test/unit/safe-status.test.js +0 -165
- package/dist/test/unit/safe-views.test.d.ts +0 -1
- package/dist/test/unit/safe-views.test.js +0 -171
- package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
- package/dist/test/unit/schema-drift-check.test.js +0 -176
- package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
- package/dist/test/unit/sensitive-guard.test.js +0 -104
- package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
- package/dist/test/unit/spec-kit-import.test.js +0 -341
- package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
- package/dist/test/unit/subgoal-sync.test.js +0 -183
- package/dist/test/unit/sync-file.test.d.ts +0 -1
- package/dist/test/unit/sync-file.test.js +0 -154
- package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
- package/dist/test/unit/tool-invocation-guard.test.js +0 -432
- package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
- package/dist/test/unit/tool-usage-stats.test.js +0 -300
- package/dist/test/unit/toolSearch.test.d.ts +0 -1
- package/dist/test/unit/toolSearch.test.js +0 -571
- package/dist/test/unit/wait-for-task.test.d.ts +0 -1
- package/dist/test/unit/wait-for-task.test.js +0 -144
- package/dist/test/unit/watcher-status.test.d.ts +0 -1
- package/dist/test/unit/watcher-status.test.js +0 -169
- package/dist/test/unit/worktree-manager.test.d.ts +0 -1
- package/dist/test/unit/worktree-manager.test.js +0 -176
- package/dist/tools/applyPatch.js +0 -41
- package/dist/tools/cancelTask.d.ts +0 -30
- package/dist/tools/cancelTask.js +0 -64
- package/dist/tools/finalizeDirectSession.js +0 -84
- package/dist/tools/goalSubgoalTask.js +0 -110
- package/dist/tools/killTask.d.ts +0 -15
- package/dist/tools/retryTask.js +0 -45
- package/dist/tools/runVerification.js +0 -36
- package/dist/tools/syncFile.js +0 -65
- package/docs/control-center/control-center-daily-driver.md +0 -211
- package/docs/control-center/control-center-mvp.md +0 -205
- package/docs/control-center/control-center-phase2.md +0 -159
- package/docs/execution-plan-2026-07-09.md +0 -315
- package/docs/release-v0.6.0.md +0 -71
- package/docs/release-v0.6.1.md +0 -75
- package/docs/release-v0.6.4.md +0 -45
- package/docs/roadmap-execution-and-acceptance.md +0 -365
- package/src/test/unit/acceptance-engine.test.ts +0 -261
- package/src/test/unit/agent-router.test.ts +0 -342
- package/src/test/unit/android-doctor.test.ts +0 -158
- package/src/test/unit/apply-patch.test.ts +0 -293
- package/src/test/unit/audit-checks.test.ts +0 -567
- package/src/test/unit/chinese-path.test.ts +0 -106
- package/src/test/unit/command-guard.test.ts +0 -221
- package/src/test/unit/create-task.test.ts +0 -255
- package/src/test/unit/diagnose-task.test.ts +0 -544
- package/src/test/unit/direct-guards.test.ts +0 -467
- package/src/test/unit/discovery-token-store.test.ts +0 -181
- package/src/test/unit/evidence-pack.test.ts +0 -251
- package/src/test/unit/get-task-status.test.ts +0 -203
- package/src/test/unit/get-task-summary.test.ts +0 -173
- package/src/test/unit/goal-graph.test.ts +0 -347
- package/src/test/unit/goal-progress.test.ts +0 -538
- package/src/test/unit/goal-report.test.ts +0 -189
- package/src/test/unit/goal-status.test.ts +0 -270
- package/src/test/unit/goal-store.test.ts +0 -318
- package/src/test/unit/goal-subgoal-task.test.ts +0 -72
- package/src/test/unit/goal-tools-registry.test.ts +0 -245
- package/src/test/unit/handoff-export.test.ts +0 -295
- package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
- package/src/test/unit/logging.test.ts +0 -497
- package/src/test/unit/path-guard.test.ts +0 -180
- package/src/test/unit/post-task-cleanup.test.ts +0 -53
- package/src/test/unit/project-policy-release-mode.test.ts +0 -156
- package/src/test/unit/reconcile-tasks.test.ts +0 -551
- package/src/test/unit/release-gate.test.ts +0 -314
- package/src/test/unit/run-task-loop.test.ts +0 -425
- package/src/test/unit/safe-status.test.ts +0 -187
- package/src/test/unit/safe-views.test.ts +0 -184
- package/src/test/unit/schema-drift-check.test.ts +0 -260
- package/src/test/unit/sensitive-guard.test.ts +0 -124
- package/src/test/unit/spec-kit-import.test.ts +0 -429
- package/src/test/unit/subgoal-sync.test.ts +0 -236
- package/src/test/unit/sync-file.test.ts +0 -231
- package/src/test/unit/tool-invocation-guard.test.ts +0 -542
- package/src/test/unit/tool-usage-stats.test.ts +0 -384
- package/src/test/unit/toolSearch.test.ts +0 -756
- package/src/test/unit/wait-for-task.test.ts +0 -176
- package/src/test/unit/watcher-status.test.ts +0 -190
- package/src/test/unit/worktree-manager.test.ts +0 -247
- package/src/tools/cancelTask.ts +0 -70
- package/src/tools/retryTask.ts +0 -53
- package/src/tools/syncFile.ts +0 -122
- /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
- /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
- /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
- /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
- /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
- /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
- /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
- /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
- /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
- /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
- /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
- /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
- /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
- /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
- /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
- /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
- /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
- /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
- /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
- /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
- /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
- /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
- /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
- /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
- /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
- /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
- /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
- /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
- /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
- /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
- /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
- /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mkdirSync,
|
|
3
|
-
writeFileSync,
|
|
4
3
|
existsSync,
|
|
5
|
-
readFileSync,
|
|
6
4
|
} from "node:fs";
|
|
7
5
|
import { createHash, randomBytes } from "node:crypto";
|
|
8
6
|
import { resolve, join } from "node:path";
|
|
@@ -15,10 +13,16 @@ import {
|
|
|
15
13
|
import { guardPath } from "../security/pathGuard.js";
|
|
16
14
|
import { PatchWardenError } from "../errors.js";
|
|
17
15
|
import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../version.js";
|
|
18
|
-
import {
|
|
16
|
+
import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
17
|
+
import { redactSensitiveContent } from "../security/contentRedaction.js";
|
|
18
|
+
import {
|
|
19
|
+
mutateLockedJsonFileSync,
|
|
20
|
+
readJsonObjectFileSync,
|
|
21
|
+
withFileLock,
|
|
22
|
+
withFileLockSync,
|
|
23
|
+
} from "../utils/lockedJsonFile.js";
|
|
24
|
+
import { getLastToolCatalogSnapshot } from "../tools/catalog/toolCatalog.js";
|
|
19
25
|
import {
|
|
20
|
-
captureRepoSnapshot,
|
|
21
|
-
buildChangeArtifacts,
|
|
22
26
|
type RepoSnapshot,
|
|
23
27
|
type ChangeArtifacts,
|
|
24
28
|
} from "../runner/changeCapture.js";
|
|
@@ -29,7 +33,9 @@ export interface DirectSessionOperation {
|
|
|
29
33
|
index: number;
|
|
30
34
|
timestamp: string;
|
|
31
35
|
path: string;
|
|
32
|
-
|
|
36
|
+
operation_type?: "patch" | "sync";
|
|
37
|
+
source_path?: string;
|
|
38
|
+
before_sha256: string | null;
|
|
33
39
|
after_sha256: string;
|
|
34
40
|
operations_applied: number;
|
|
35
41
|
bytes_changed: number;
|
|
@@ -89,23 +95,52 @@ export function generateDirectSessionId(): string {
|
|
|
89
95
|
return `direct_${timestamp}_${randomHex}`;
|
|
90
96
|
}
|
|
91
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Direct session IDs are directory names. Keep one shared, bounded grammar so
|
|
100
|
+
* MCP tools and Control Center routes cannot disagree about traversal input.
|
|
101
|
+
*/
|
|
102
|
+
export function isValidDirectSessionId(sessionId: string): boolean {
|
|
103
|
+
return /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(sessionId);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function guardDirectSessionId(sessionId: string): void {
|
|
107
|
+
if (!isValidDirectSessionId(sessionId)) {
|
|
108
|
+
throw new PatchWardenError(
|
|
109
|
+
"invalid_session_id",
|
|
110
|
+
`Direct session ID "${sessionId}" is invalid.`,
|
|
111
|
+
"Use the session_id returned by create_direct_session.",
|
|
112
|
+
true,
|
|
113
|
+
{ session_id: sessionId }
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
92
118
|
// ── Directory management ───────────────────────────────────────────
|
|
93
119
|
|
|
94
|
-
export function createDirectSessionDir(
|
|
95
|
-
|
|
120
|
+
export function createDirectSessionDir(
|
|
121
|
+
sessionId: string,
|
|
122
|
+
config: PatchWardenConfig = getConfig()
|
|
123
|
+
): string {
|
|
124
|
+
guardDirectSessionId(sessionId);
|
|
96
125
|
const sessionsDir = getDirectSessionsDir(config);
|
|
97
126
|
guardPath(sessionsDir, config.workspaceRoot, config.directSessionsDir);
|
|
98
127
|
mkdirSync(sessionsDir, { recursive: true });
|
|
99
|
-
const dir =
|
|
100
|
-
guardPath(dir, config.workspaceRoot, config.directSessionsDir);
|
|
128
|
+
const dir = getDirectSessionDir(sessionId, config);
|
|
101
129
|
mkdirSync(dir, { recursive: true });
|
|
102
130
|
return dir;
|
|
103
131
|
}
|
|
104
132
|
|
|
105
|
-
export function getDirectSessionDir(
|
|
106
|
-
|
|
133
|
+
export function getDirectSessionDir(
|
|
134
|
+
sessionId: string,
|
|
135
|
+
config: PatchWardenConfig = getConfig()
|
|
136
|
+
): string {
|
|
137
|
+
guardDirectSessionId(sessionId);
|
|
107
138
|
const sessionsDir = getDirectSessionsDir(config);
|
|
108
|
-
return
|
|
139
|
+
return guardPath(
|
|
140
|
+
resolve(sessionsDir, sessionId),
|
|
141
|
+
config.workspaceRoot,
|
|
142
|
+
config.directSessionsDir
|
|
143
|
+
);
|
|
109
144
|
}
|
|
110
145
|
|
|
111
146
|
// ── CRUD ───────────────────────────────────────────────────────────
|
|
@@ -136,7 +171,11 @@ export function createDirectSession(
|
|
|
136
171
|
|
|
137
172
|
const record: DirectSessionRecord = {
|
|
138
173
|
session_id: sessionId,
|
|
139
|
-
title: input.title || ""
|
|
174
|
+
title: redactSensitiveContent(input.title || "")
|
|
175
|
+
.content
|
|
176
|
+
.replace(/[\r\n\t]+/g, " ")
|
|
177
|
+
.trim()
|
|
178
|
+
.slice(0, 500),
|
|
140
179
|
repo_path: input.repo_path,
|
|
141
180
|
resolved_repo_path: input.resolved_repo_path,
|
|
142
181
|
created_at: now.toISOString(),
|
|
@@ -155,21 +194,16 @@ export function createDirectSession(
|
|
|
155
194
|
change_artifacts: null,
|
|
156
195
|
};
|
|
157
196
|
|
|
158
|
-
|
|
159
|
-
join(dir, "session.json"),
|
|
160
|
-
JSON.stringify(record, null, 2),
|
|
161
|
-
"utf-8"
|
|
162
|
-
);
|
|
197
|
+
atomicWriteJsonFileSync(join(dir, "session.json"), record);
|
|
163
198
|
|
|
164
199
|
return record;
|
|
165
200
|
}
|
|
166
201
|
|
|
167
|
-
export function readDirectSession(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const file =
|
|
172
|
-
guardPath(file, config.workspaceRoot, config.directSessionsDir);
|
|
202
|
+
export function readDirectSession(
|
|
203
|
+
sessionId: string,
|
|
204
|
+
config: PatchWardenConfig = getConfig()
|
|
205
|
+
): DirectSessionRecord {
|
|
206
|
+
const file = getDirectSessionFile(sessionId, config);
|
|
173
207
|
|
|
174
208
|
if (!existsSync(file)) {
|
|
175
209
|
throw new PatchWardenError(
|
|
@@ -181,40 +215,69 @@ export function readDirectSession(sessionId: string): DirectSessionRecord {
|
|
|
181
215
|
);
|
|
182
216
|
}
|
|
183
217
|
|
|
184
|
-
|
|
218
|
+
let value: unknown;
|
|
219
|
+
try {
|
|
220
|
+
value = readJsonObjectFileSync(file);
|
|
221
|
+
} catch {
|
|
222
|
+
throw invalidDirectSessionRecord(sessionId, "session.json is not valid JSON");
|
|
223
|
+
}
|
|
224
|
+
return validateDirectSessionRecord(sessionId, value);
|
|
185
225
|
}
|
|
186
226
|
|
|
187
227
|
export function updateDirectSession(
|
|
188
228
|
sessionId: string,
|
|
189
229
|
updates: Partial<DirectSessionRecord>
|
|
190
230
|
): DirectSessionRecord {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
JSON.stringify(updated, null, 2),
|
|
197
|
-
"utf-8"
|
|
198
|
-
);
|
|
199
|
-
return updated;
|
|
231
|
+
return mutateDirectSessionRecord(sessionId, (session) => ({
|
|
232
|
+
...session,
|
|
233
|
+
...updates,
|
|
234
|
+
session_id: session.session_id,
|
|
235
|
+
}));
|
|
200
236
|
}
|
|
201
237
|
|
|
202
238
|
export function appendDirectSessionOperation(
|
|
203
239
|
sessionId: string,
|
|
204
|
-
operation: DirectSessionOperation
|
|
240
|
+
operation: DirectSessionOperation,
|
|
241
|
+
config: PatchWardenConfig = getConfig(),
|
|
205
242
|
): DirectSessionRecord {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
243
|
+
return mutateDirectSessionRecord(sessionId, (session) => ({
|
|
244
|
+
...session,
|
|
245
|
+
operations: [
|
|
246
|
+
...session.operations,
|
|
247
|
+
{
|
|
248
|
+
...operation,
|
|
249
|
+
index: session.operations.reduce((max, entry) => Math.max(max, entry.index), -1) + 1,
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
}), config);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function withDirectSessionMutationLock<R>(
|
|
256
|
+
sessionId: string,
|
|
257
|
+
action: () => R,
|
|
258
|
+
config: PatchWardenConfig = getConfig(),
|
|
259
|
+
): R {
|
|
260
|
+
const lockTarget = join(getDirectSessionDir(sessionId, config), "workspace-mutation");
|
|
261
|
+
return withFileLockSync(lockTarget, action, directSessionBusyOptions(sessionId));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function withDirectSessionMutationLockAsync<R>(
|
|
265
|
+
sessionId: string,
|
|
266
|
+
action: () => Promise<R>,
|
|
267
|
+
config: PatchWardenConfig = getConfig(),
|
|
268
|
+
): Promise<R> {
|
|
269
|
+
const lockTarget = join(getDirectSessionDir(sessionId, config), "workspace-mutation");
|
|
270
|
+
return withFileLock(lockTarget, action, directSessionBusyOptions(sessionId));
|
|
209
271
|
}
|
|
210
272
|
|
|
211
273
|
export function appendDirectSessionVerificationRun(
|
|
212
274
|
sessionId: string,
|
|
213
275
|
run: DirectSessionVerificationRun
|
|
214
276
|
): DirectSessionRecord {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
277
|
+
return mutateDirectSessionRecord(sessionId, (session) => ({
|
|
278
|
+
...session,
|
|
279
|
+
verification_runs: [...session.verification_runs, run],
|
|
280
|
+
}));
|
|
218
281
|
}
|
|
219
282
|
|
|
220
283
|
// ── Validation ─────────────────────────────────────────────────────
|
|
@@ -261,32 +324,25 @@ export function validateDirectSessionFreshness(
|
|
|
261
324
|
|
|
262
325
|
export function finalizeDirectSessionRecord(
|
|
263
326
|
sessionId: string,
|
|
264
|
-
afterSnapshot: RepoSnapshot,
|
|
265
327
|
changeArtifacts: ChangeArtifacts
|
|
266
328
|
): DirectSessionRecord {
|
|
267
329
|
const dir = getDirectSessionDir(sessionId);
|
|
268
330
|
|
|
269
331
|
// Write change artifacts to session directory
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
JSON.stringify(changeArtifacts.changed_files, null, 2),
|
|
273
|
-
"utf-8"
|
|
274
|
-
);
|
|
275
|
-
writeFileSync(join(dir, "diff.patch"), changeArtifacts.diff, "utf-8");
|
|
332
|
+
atomicWriteJsonFileSync(join(dir, "changed-files.json"), changeArtifacts.changed_files);
|
|
333
|
+
atomicWriteFileSync(join(dir, "diff.patch"), changeArtifacts.diff);
|
|
276
334
|
|
|
277
335
|
const summary = {
|
|
278
336
|
changed_files_total: changeArtifacts.changed_files.length,
|
|
279
337
|
additions: changeArtifacts.additions,
|
|
280
338
|
deletions: changeArtifacts.deletions,
|
|
281
339
|
patch_mode: changeArtifacts.patch_mode,
|
|
340
|
+
diff_redacted: changeArtifacts.diff_redacted === true,
|
|
341
|
+
diff_redaction_categories: changeArtifacts.diff_redaction_categories ?? [],
|
|
282
342
|
artifact_hygiene: changeArtifacts.artifact_hygiene,
|
|
283
343
|
};
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
JSON.stringify(summary, null, 2),
|
|
287
|
-
"utf-8"
|
|
288
|
-
);
|
|
289
|
-
writeFileSync(join(dir, "summary.md"), formatSummaryMd(changeArtifacts), "utf-8");
|
|
344
|
+
atomicWriteJsonFileSync(join(dir, "summary.json"), summary);
|
|
345
|
+
atomicWriteFileSync(join(dir, "summary.md"), formatSummaryMd(changeArtifacts));
|
|
290
346
|
|
|
291
347
|
return updateDirectSession(sessionId, {
|
|
292
348
|
finalized: true,
|
|
@@ -319,6 +375,105 @@ function extractReason(error: unknown): string {
|
|
|
319
375
|
return String(error).slice(0, 80);
|
|
320
376
|
}
|
|
321
377
|
|
|
378
|
+
function invalidDirectSessionRecord(
|
|
379
|
+
sessionId: string,
|
|
380
|
+
detail: string
|
|
381
|
+
): PatchWardenError {
|
|
382
|
+
return new PatchWardenError(
|
|
383
|
+
"invalid_session_record",
|
|
384
|
+
`Direct session "${sessionId}" is invalid: ${detail}.`,
|
|
385
|
+
"Create a new direct session and do not modify its internal session.json file.",
|
|
386
|
+
true,
|
|
387
|
+
{ session_id: sessionId }
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function getDirectSessionFile(
|
|
392
|
+
sessionId: string,
|
|
393
|
+
config: PatchWardenConfig = getConfig()
|
|
394
|
+
): string {
|
|
395
|
+
const dir = getDirectSessionDir(sessionId, config);
|
|
396
|
+
return guardPath(
|
|
397
|
+
join(dir, "session.json"),
|
|
398
|
+
config.workspaceRoot,
|
|
399
|
+
config.directSessionsDir
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function mutateDirectSessionRecord(
|
|
404
|
+
sessionId: string,
|
|
405
|
+
mutation: (session: DirectSessionRecord) => DirectSessionRecord,
|
|
406
|
+
config: PatchWardenConfig = getConfig(),
|
|
407
|
+
): DirectSessionRecord {
|
|
408
|
+
const file = getDirectSessionFile(sessionId, config);
|
|
409
|
+
if (!existsSync(file)) {
|
|
410
|
+
throw new PatchWardenError(
|
|
411
|
+
"session_not_found",
|
|
412
|
+
`Direct session "${sessionId}" not found.`,
|
|
413
|
+
"Call create_direct_session first to get a valid session_id.",
|
|
414
|
+
true,
|
|
415
|
+
{ session_id: sessionId }
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
return mutateLockedJsonFileSync<DirectSessionRecord, DirectSessionRecord>(
|
|
419
|
+
file,
|
|
420
|
+
(current) => {
|
|
421
|
+
const session = validateDirectSessionRecord(sessionId, current);
|
|
422
|
+
const next = mutation(session);
|
|
423
|
+
return { next, result: next };
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
// Session-record appends are short serialized writes. Give Windows
|
|
427
|
+
// antivirus-delayed lock detachment enough time to finish instead of
|
|
428
|
+
// dropping concurrent operation or verification evidence.
|
|
429
|
+
waitMs: 10_000,
|
|
430
|
+
busyError: () => new PatchWardenError(
|
|
431
|
+
"direct_session_busy",
|
|
432
|
+
`Direct session "${sessionId}" is currently being updated.`,
|
|
433
|
+
"Retry after the current Direct operation completes.",
|
|
434
|
+
true,
|
|
435
|
+
{ session_id: sessionId }
|
|
436
|
+
),
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function directSessionBusyOptions(sessionId: string) {
|
|
442
|
+
return {
|
|
443
|
+
waitMs: 0,
|
|
444
|
+
busyError: () => new PatchWardenError(
|
|
445
|
+
"direct_session_busy",
|
|
446
|
+
`Direct session "${sessionId}" already has a workspace operation in progress.`,
|
|
447
|
+
"Wait for the active patch, sync, verification, or finalization to finish, then retry.",
|
|
448
|
+
true,
|
|
449
|
+
{ session_id: sessionId },
|
|
450
|
+
),
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function validateDirectSessionRecord(sessionId: string, value: unknown): DirectSessionRecord {
|
|
455
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
456
|
+
throw invalidDirectSessionRecord(sessionId, "session.json must contain an object");
|
|
457
|
+
}
|
|
458
|
+
const record = value as Partial<DirectSessionRecord>;
|
|
459
|
+
if (record.session_id !== sessionId) {
|
|
460
|
+
throw invalidDirectSessionRecord(sessionId, "session_id does not match its directory");
|
|
461
|
+
}
|
|
462
|
+
if (typeof record.resolved_repo_path !== "string" || record.resolved_repo_path.trim() === "") {
|
|
463
|
+
throw invalidDirectSessionRecord(sessionId, "resolved_repo_path is missing");
|
|
464
|
+
}
|
|
465
|
+
if (typeof record.expires_at !== "string" || !Number.isFinite(Date.parse(record.expires_at))) {
|
|
466
|
+
throw invalidDirectSessionRecord(sessionId, "expires_at is invalid");
|
|
467
|
+
}
|
|
468
|
+
if (typeof record.finalized !== "boolean") {
|
|
469
|
+
throw invalidDirectSessionRecord(sessionId, "finalized must be a boolean");
|
|
470
|
+
}
|
|
471
|
+
if (!Array.isArray(record.operations) || !Array.isArray(record.verification_runs)) {
|
|
472
|
+
throw invalidDirectSessionRecord(sessionId, "operation or verification history is invalid");
|
|
473
|
+
}
|
|
474
|
+
return record as DirectSessionRecord;
|
|
475
|
+
}
|
|
476
|
+
|
|
322
477
|
function formatSummaryMd(artifacts: ChangeArtifacts): string {
|
|
323
478
|
const lines: string[] = [
|
|
324
479
|
"# Direct Session Change Summary",
|
|
@@ -327,6 +482,7 @@ function formatSummaryMd(artifacts: ChangeArtifacts): string {
|
|
|
327
482
|
`**Additions:** ${artifacts.additions}`,
|
|
328
483
|
`**Deletions:** ${artifacts.deletions}`,
|
|
329
484
|
`**Patch mode:** ${artifacts.patch_mode}`,
|
|
485
|
+
`**Diff redacted:** ${artifacts.diff_redacted === true ? "yes" : "no"}`,
|
|
330
486
|
"",
|
|
331
487
|
"## Artifact Hygiene",
|
|
332
488
|
"",
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { writeFileSync, mkdirSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
3
2
|
import { getConfig } from "../config.js";
|
|
4
3
|
import { guardDirectCommand } from "../security/commandGuard.js";
|
|
5
4
|
import { redactSensitiveContent } from "../security/contentRedaction.js";
|
|
6
5
|
import { runSimpleProcess } from "../runner/simpleProcess.js";
|
|
6
|
+
import { resolveTrustedCommandLine } from "../runner/processSecurity.js";
|
|
7
7
|
import { getDirectSessionDir } from "./directSessionStore.js";
|
|
8
8
|
import type { DirectSessionVerificationRun } from "./directSessionStore.js";
|
|
9
|
+
import { atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
9
10
|
|
|
10
11
|
const MAX_TAIL_CHARS = 10_000;
|
|
11
12
|
const DEFAULT_TIMEOUT_SECONDS = 120;
|
|
@@ -36,7 +37,7 @@ export async function runDirectVerification(
|
|
|
36
37
|
const timeoutMs = timeoutSeconds * 1000;
|
|
37
38
|
|
|
38
39
|
// Parse command into command + args
|
|
39
|
-
const parts = parseCommand(allowedCommand);
|
|
40
|
+
const parts = parseCommand(allowedCommand, input.resolvedRepoPath);
|
|
40
41
|
if (!parts) {
|
|
41
42
|
throw new Error(`Failed to parse command: "${allowedCommand}"`);
|
|
42
43
|
}
|
|
@@ -104,11 +105,7 @@ export async function runDirectVerification(
|
|
|
104
105
|
spawn_error: result.spawnError,
|
|
105
106
|
};
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
join(sessionDir, "verification.json"),
|
|
109
|
-
JSON.stringify(verificationJson, null, 2),
|
|
110
|
-
"utf-8"
|
|
111
|
-
);
|
|
108
|
+
atomicWriteJsonFileSync(join(sessionDir, "verification.json"), verificationJson);
|
|
112
109
|
|
|
113
110
|
return { run, log_path: logPath };
|
|
114
111
|
}
|
|
@@ -116,26 +113,15 @@ export async function runDirectVerification(
|
|
|
116
113
|
// ── Helpers ────────────────────────────────────────────────────────
|
|
117
114
|
|
|
118
115
|
function parseCommand(
|
|
119
|
-
command: string
|
|
116
|
+
command: string,
|
|
117
|
+
cwd: string,
|
|
120
118
|
): { command: string; args: string[] } | null {
|
|
121
119
|
const trimmed = command.trim();
|
|
122
120
|
if (!trimmed) return null;
|
|
123
|
-
|
|
124
|
-
// Simple split by spaces - sufficient for allowlisted commands like "npm test"
|
|
125
|
-
const parts = trimmed.split(/\s+/);
|
|
126
|
-
|
|
127
|
-
// On Windows, npm/npx are batch files (.cmd) and spawn() cannot find them
|
|
128
|
-
// without shell resolution. Wrap with cmd.exe /c for those commands.
|
|
129
|
-
if (process.platform === "win32" && (parts[0] === "npm" || parts[0] === "npx")) {
|
|
130
|
-
return {
|
|
131
|
-
command: "cmd",
|
|
132
|
-
args: ["/c", ...parts],
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
121
|
+
const invocation = resolveTrustedCommandLine(trimmed, cwd);
|
|
136
122
|
return {
|
|
137
|
-
command:
|
|
138
|
-
args:
|
|
123
|
+
command: invocation.command,
|
|
124
|
+
args: invocation.argsPrefix,
|
|
139
125
|
};
|
|
140
126
|
}
|
|
141
127
|
|
package/src/doctor.ts
CHANGED
|
@@ -10,20 +10,22 @@
|
|
|
10
10
|
|
|
11
11
|
import { existsSync, statSync, readFileSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
12
12
|
import { isAbsolute, resolve, normalize, join } from "node:path";
|
|
13
|
-
import { execSync } from "node:child_process";
|
|
14
13
|
import { createServer } from "node:net";
|
|
15
14
|
import { getConfig, type PatchWardenConfig } from "./config.js";
|
|
16
15
|
import { guardPath, guardWorkspacePath } from "./security/pathGuard.js";
|
|
17
16
|
import { isSensitivePath } from "./security/sensitiveGuard.js";
|
|
18
17
|
import { guardPlanContent } from "./security/planGuard.js";
|
|
19
|
-
import { TASK_READ_ONLY_FILES } from "./tools/getTaskFile.js";
|
|
18
|
+
import { TASK_READ_ONLY_FILES } from "./tools/tasks/getTaskFile.js";
|
|
20
19
|
import { getToolDefs } from "./tools/registry.js";
|
|
21
|
-
import { CHATGPT_CORE_TOOL_NAMES, CHATGPT_DIRECT_TOOL_NAMES, selectToolsForProfile } from "./tools/toolCatalog.js";
|
|
22
|
-
import { buildToolRegistry } from "./tools/toolRegistry.js";
|
|
23
|
-
import { runAllSchemaDriftChecks } from "./tools/schemaDriftCheck.js";
|
|
20
|
+
import { CHATGPT_CORE_TOOL_NAMES, CHATGPT_DIRECT_TOOL_NAMES, selectToolsForProfile } from "./tools/catalog/toolCatalog.js";
|
|
21
|
+
import { buildToolRegistry } from "./tools/catalog/toolRegistry.js";
|
|
22
|
+
import { runAllSchemaDriftChecks } from "./tools/diagnostics/schemaDriftCheck.js";
|
|
24
23
|
import { runReleaseGateCheck } from "./release/releaseGate.js";
|
|
25
24
|
import { PATCHWARDEN_VERSION } from "./version.js";
|
|
26
25
|
import { logger } from "./logging.js";
|
|
26
|
+
import { unsafeWorkspaceRootLabel } from "./security/workspaceRootGuard.js";
|
|
27
|
+
import { runSimpleProcessSync } from "./runner/simpleProcess.js";
|
|
28
|
+
import { resolveAgentLaunch } from "./runner/agentInvocation.js";
|
|
27
29
|
|
|
28
30
|
// ── Types ──────────────────────────────────────────────────────────
|
|
29
31
|
|
|
@@ -50,13 +52,18 @@ export interface DoctorContext {
|
|
|
50
52
|
|
|
51
53
|
// ── Helpers ────────────────────────────────────────────────────────
|
|
52
54
|
|
|
53
|
-
function cmd(
|
|
55
|
+
function cmd(command: string, args: readonly string[] = []): string {
|
|
54
56
|
try {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const result = runSimpleProcessSync({
|
|
58
|
+
command,
|
|
59
|
+
args: [...args],
|
|
60
|
+
cwd: process.cwd(),
|
|
61
|
+
timeoutMs: 5000,
|
|
62
|
+
maxStdoutBytes: 64 * 1024,
|
|
63
|
+
maxStderrBytes: 64 * 1024,
|
|
64
|
+
});
|
|
65
|
+
if (result.exitCode !== 0) return "";
|
|
66
|
+
return `${result.stdout}${result.stderr}`.trim();
|
|
60
67
|
} catch {
|
|
61
68
|
return "";
|
|
62
69
|
}
|
|
@@ -109,7 +116,7 @@ const checkNpmAvailable: DoctorCheck = {
|
|
|
109
116
|
id: "npm-available",
|
|
110
117
|
description: "npm available",
|
|
111
118
|
run() {
|
|
112
|
-
const npmVer = cmd("npm --version");
|
|
119
|
+
const npmVer = cmd(process.platform === "win32" ? "npm.cmd" : "npm", ["--version"]);
|
|
113
120
|
return [checkResult("npm available", npmVer !== "", npmVer || "npm not found in PATH")];
|
|
114
121
|
},
|
|
115
122
|
};
|
|
@@ -144,7 +151,7 @@ const checkGitAvailable: DoctorCheck = {
|
|
|
144
151
|
id: "git-available",
|
|
145
152
|
description: "Git available",
|
|
146
153
|
run() {
|
|
147
|
-
const gitVer = cmd("git --version");
|
|
154
|
+
const gitVer = cmd("git", ["--version"]);
|
|
148
155
|
return [warnCheckResult("Git available", gitVer !== "", gitVer || "git not found — runner git.diff will not work")];
|
|
149
156
|
},
|
|
150
157
|
};
|
|
@@ -199,18 +206,9 @@ const checkWorkspaceRoot: DoctorCheck = {
|
|
|
199
206
|
try { isDir = statSync(ws).isDirectory(); } catch {}
|
|
200
207
|
results.push(checkResult("workspaceRoot is directory", isDir, ws));
|
|
201
208
|
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
{
|
|
205
|
-
{ pattern: /\\Desktop$/, label: "Desktop" },
|
|
206
|
-
{ pattern: /\\Downloads$/, label: "Downloads" },
|
|
207
|
-
{ pattern: /\\Documents$/, label: "Documents" },
|
|
208
|
-
];
|
|
209
|
-
|
|
210
|
-
for (const { pattern, label } of dangerousRoots) {
|
|
211
|
-
if (pattern.test(ws)) {
|
|
212
|
-
results.push(warnResult(`workspaceRoot is ${label}: ${ws} — consider narrowing to a project directory`));
|
|
213
|
-
}
|
|
209
|
+
const unsafeLabel = unsafeWorkspaceRootLabel(ws);
|
|
210
|
+
if (unsafeLabel) {
|
|
211
|
+
results.push(warnResult(`workspaceRoot is ${unsafeLabel}: ${ws} — consider narrowing to a project directory`));
|
|
214
212
|
}
|
|
215
213
|
return results;
|
|
216
214
|
},
|
|
@@ -312,11 +310,13 @@ const checkServerVersion: DoctorCheck = {
|
|
|
312
310
|
packageJson.version === PATCHWARDEN_VERSION,
|
|
313
311
|
`${PATCHWARDEN_VERSION} vs ${packageJson.version}`,
|
|
314
312
|
),
|
|
315
|
-
|
|
316
|
-
"Manifest preflight
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
313
|
+
process.env.PATCHWARDEN_DESKTOP_RUNTIME === "1"
|
|
314
|
+
? okResult("Manifest preflight covered by compiled desktop runtime checks")
|
|
315
|
+
: checkResult(
|
|
316
|
+
"Manifest preflight script exists",
|
|
317
|
+
existsSync(resolve(process.cwd(), "scripts/checks/mcp-manifest-check.js")),
|
|
318
|
+
"scripts/checks/mcp-manifest-check.js",
|
|
319
|
+
),
|
|
320
320
|
];
|
|
321
321
|
},
|
|
322
322
|
};
|
|
@@ -331,8 +331,8 @@ const checkToolProfiles: DoctorCheck = {
|
|
|
331
331
|
process.env.PATCHWARDEN_TOOL_PROFILE = "full";
|
|
332
332
|
const fullTools = getToolDefs();
|
|
333
333
|
const coreTools = selectToolsForProfile(fullTools, "chatgpt_core", context.config?.enableDirectProfile);
|
|
334
|
-
const createSchema = coreTools.find((tool) => tool.name === "create_task")?.inputSchema
|
|
335
|
-
const waitSchema = coreTools.find((tool) => tool.name === "wait_for_task")?.inputSchema
|
|
334
|
+
const createSchema = coreTools.find((tool) => tool.name === "create_task")?.inputSchema;
|
|
335
|
+
const waitSchema = coreTools.find((tool) => tool.name === "wait_for_task")?.inputSchema;
|
|
336
336
|
results.push(checkResult("Full tool profile exposes 66 tools", fullTools.length === 66, `${fullTools.length} tools`));
|
|
337
337
|
results.push(
|
|
338
338
|
checkResult(
|
|
@@ -463,23 +463,23 @@ const checkToolModules: DoctorCheck = {
|
|
|
463
463
|
description: "Tool module checks",
|
|
464
464
|
run() {
|
|
465
465
|
const newTools = [
|
|
466
|
-
"listTasks",
|
|
467
|
-
"listAgents",
|
|
468
|
-
"cancelTask",
|
|
469
|
-
"killTask",
|
|
470
|
-
"retryTask",
|
|
471
|
-
"getTaskProgress",
|
|
472
|
-
"getTaskSummary",
|
|
473
|
-
"waitForTask",
|
|
474
|
-
"getTaskStdoutTail",
|
|
475
|
-
"healthCheck",
|
|
476
|
-
"auditTask",
|
|
466
|
+
{ name: "listTasks", subdir: "tasks" },
|
|
467
|
+
{ name: "listAgents", subdir: "workspace" },
|
|
468
|
+
{ name: "cancelTask", subdir: "tasks" },
|
|
469
|
+
{ name: "killTask", subdir: "tasks" },
|
|
470
|
+
{ name: "retryTask", subdir: "tasks" },
|
|
471
|
+
{ name: "getTaskProgress", subdir: "tasks" },
|
|
472
|
+
{ name: "getTaskSummary", subdir: "tasks" },
|
|
473
|
+
{ name: "waitForTask", subdir: "tasks" },
|
|
474
|
+
{ name: "getTaskStdoutTail", subdir: "tasks" },
|
|
475
|
+
{ name: "healthCheck", subdir: "diagnostics" },
|
|
476
|
+
{ name: "auditTask", subdir: "diagnostics" },
|
|
477
477
|
];
|
|
478
478
|
const results: DoctorCheckResult[] = [];
|
|
479
479
|
for (const t of newTools) {
|
|
480
|
-
const compiled = resolve(process.cwd(), "dist/tools", `${t}.js`);
|
|
480
|
+
const compiled = resolve(process.cwd(), "dist/tools", t.subdir, `${t.name}.js`);
|
|
481
481
|
const exists = existsSync(compiled);
|
|
482
|
-
results.push(checkResult(`Tool module: ${t}`, exists, exists ? "compiled" : "missing"));
|
|
482
|
+
results.push(checkResult(`Tool module: ${t.name}`, exists, exists ? "compiled" : "missing"));
|
|
483
483
|
}
|
|
484
484
|
return results;
|
|
485
485
|
},
|
|
@@ -490,18 +490,18 @@ const checkDirectToolModules: DoctorCheck = {
|
|
|
490
490
|
description: "Direct tool module checks",
|
|
491
491
|
run() {
|
|
492
492
|
const directToolModules = [
|
|
493
|
-
"createDirectSession",
|
|
494
|
-
"searchWorkspace",
|
|
495
|
-
"applyPatch",
|
|
496
|
-
"runVerification",
|
|
497
|
-
"finalizeDirectSession",
|
|
498
|
-
"auditSession",
|
|
493
|
+
{ name: "createDirectSession", subdir: "direct" },
|
|
494
|
+
{ name: "searchWorkspace", subdir: "workspace" },
|
|
495
|
+
{ name: "applyPatch", subdir: "workspace" },
|
|
496
|
+
{ name: "runVerification", subdir: "tasks" },
|
|
497
|
+
{ name: "finalizeDirectSession", subdir: "direct" },
|
|
498
|
+
{ name: "auditSession", subdir: "diagnostics" },
|
|
499
499
|
];
|
|
500
500
|
const results: DoctorCheckResult[] = [];
|
|
501
501
|
for (const t of directToolModules) {
|
|
502
|
-
const compiled = resolve(process.cwd(), "dist/tools", `${t}.js`);
|
|
502
|
+
const compiled = resolve(process.cwd(), "dist/tools", t.subdir, `${t.name}.js`);
|
|
503
503
|
const exists = existsSync(compiled);
|
|
504
|
-
results.push(checkResult(`Direct tool module: ${t}`, exists, exists ? "compiled" : "missing"));
|
|
504
|
+
results.push(checkResult(`Direct tool module: ${t.name}`, exists, exists ? "compiled" : "missing"));
|
|
505
505
|
}
|
|
506
506
|
return results;
|
|
507
507
|
},
|
|
@@ -612,8 +612,7 @@ const checkAgentCommands: DoctorCheck = {
|
|
|
612
612
|
run(context) {
|
|
613
613
|
if (!context.config) return [];
|
|
614
614
|
const results: DoctorCheckResult[] = [];
|
|
615
|
-
const
|
|
616
|
-
for (const [name, agentCfg] of Object.entries(agents) as [string, any][]) {
|
|
615
|
+
for (const [name, agentCfg] of Object.entries(context.config.agents)) {
|
|
617
616
|
const cmdName = agentCfg.command;
|
|
618
617
|
const looksLikePath = isAbsolute(cmdName) || cmdName.includes("/") || cmdName.includes("\\");
|
|
619
618
|
if (looksLikePath) {
|
|
@@ -621,11 +620,26 @@ const checkAgentCommands: DoctorCheck = {
|
|
|
621
620
|
results.push(warnCheckResult(`Agent "${name}" command available`, agentExists, agentExists ? `Found: ${cmdName}` : `"${cmdName}" does not exist — agent tasks will fail`));
|
|
622
621
|
continue;
|
|
623
622
|
}
|
|
624
|
-
//
|
|
623
|
+
// Use an argumentized lookup utility; never interpolate config into a shell.
|
|
625
624
|
const isWin = process.platform === "win32";
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
625
|
+
let found = "";
|
|
626
|
+
if (isWin) {
|
|
627
|
+
try {
|
|
628
|
+
found = resolveAgentLaunch(
|
|
629
|
+
name,
|
|
630
|
+
cmdName,
|
|
631
|
+
process.platform,
|
|
632
|
+
process.env.PATH || "",
|
|
633
|
+
existsSync,
|
|
634
|
+
context.config.workspaceRoot,
|
|
635
|
+
typeof agentCfg.adapter === "string" ? agentCfg.adapter : name,
|
|
636
|
+
).command;
|
|
637
|
+
} catch {
|
|
638
|
+
found = "";
|
|
639
|
+
}
|
|
640
|
+
} else {
|
|
641
|
+
found = cmd("which", [cmdName]);
|
|
642
|
+
}
|
|
629
643
|
results.push(warnCheckResult(`Agent "${name}" command available`, found !== "", found ? `Found: ${found.split("\n")[0]}` : `"${cmdName}" not in PATH — agent tasks will fail`));
|
|
630
644
|
}
|
|
631
645
|
return results;
|