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
package/src/goal/goalProgress.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
15
15
|
import { isAbsolute, join } from "node:path";
|
|
16
16
|
import { getConfig } from "../config.js";
|
|
17
17
|
import { PatchWardenError } from "../errors.js";
|
|
18
|
-
import {
|
|
18
|
+
import { mutateGoalStatus, readGoalStatus } from "./goalStore.js";
|
|
19
19
|
import { updateSubgoalStatus, type GoalStatus, type Subgoal } from "./goalStatus.js";
|
|
20
20
|
import { getBlockedSubgoals } from "./goalGraph.js";
|
|
21
21
|
|
|
@@ -93,7 +93,7 @@ export function acceptSubgoal(
|
|
|
93
93
|
subgoalId: string,
|
|
94
94
|
workspaceRoot?: string
|
|
95
95
|
): { subgoal_id: string; status: "accepted"; accepted_at: string } {
|
|
96
|
-
|
|
96
|
+
return mutateGoalStatus(goalId, (goalStatus) => {
|
|
97
97
|
|
|
98
98
|
const subgoal = goalStatus.subgoals.find((s) => s.id === subgoalId);
|
|
99
99
|
if (!subgoal) {
|
|
@@ -146,14 +146,16 @@ export function acceptSubgoal(
|
|
|
146
146
|
// 校验 3:通过状态机转换(要求 subgoal 当前为 done_by_agent)
|
|
147
147
|
const updatedGoalStatus = updateSubgoalStatus(goalStatus, subgoalId, "accepted");
|
|
148
148
|
|
|
149
|
-
writeGoalStatus(goalId, updatedGoalStatus, workspaceRoot);
|
|
150
|
-
|
|
151
149
|
const updatedSubgoal = updatedGoalStatus.subgoals.find((s) => s.id === subgoalId)!;
|
|
152
150
|
return {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
next: updatedGoalStatus,
|
|
152
|
+
result: {
|
|
153
|
+
subgoal_id: subgoalId,
|
|
154
|
+
status: "accepted" as const,
|
|
155
|
+
accepted_at: updatedSubgoal.accepted_at!,
|
|
156
|
+
},
|
|
156
157
|
};
|
|
158
|
+
}, workspaceRoot);
|
|
157
159
|
}
|
|
158
160
|
|
|
159
161
|
/**
|
|
@@ -175,7 +177,7 @@ export function rejectSubgoal(
|
|
|
175
177
|
reason: string,
|
|
176
178
|
workspaceRoot?: string
|
|
177
179
|
): { subgoal_id: string; status: "rejected"; rejected_reason: string } {
|
|
178
|
-
|
|
180
|
+
return mutateGoalStatus(goalId, (goalStatus) => {
|
|
179
181
|
|
|
180
182
|
const index = goalStatus.subgoals.findIndex((s) => s.id === subgoalId);
|
|
181
183
|
if (index === -1) {
|
|
@@ -222,13 +224,15 @@ export function rejectSubgoal(
|
|
|
222
224
|
updated_at: new Date().toISOString(),
|
|
223
225
|
};
|
|
224
226
|
|
|
225
|
-
writeGoalStatus(goalId, updatedGoalStatus, workspaceRoot);
|
|
226
|
-
|
|
227
227
|
return {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
228
|
+
next: updatedGoalStatus,
|
|
229
|
+
result: {
|
|
230
|
+
subgoal_id: subgoalId,
|
|
231
|
+
status: "rejected" as const,
|
|
232
|
+
rejected_reason: reason,
|
|
233
|
+
},
|
|
231
234
|
};
|
|
235
|
+
}, workspaceRoot);
|
|
232
236
|
}
|
|
233
237
|
|
|
234
238
|
/**
|
package/src/goal/goalReport.ts
CHANGED
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
* 所有写入操作使用原子写(.tmp + rename),所有字符串内容经 redactSensitiveValue 脱敏。
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { mkdirSync
|
|
9
|
+
import { mkdirSync } from "node:fs";
|
|
10
10
|
import { join, resolve } from "node:path";
|
|
11
11
|
import { getConfig } from "../config.js";
|
|
12
12
|
import { redactSensitiveValue } from "../security/contentRedaction.js";
|
|
13
|
-
import { listEvidencePacks } from "../tools/evidencePack.js";
|
|
13
|
+
import { listEvidencePacks } from "../tools/tasks/evidencePack.js";
|
|
14
|
+
import { atomicWriteFileSync } from "../utils/atomicFile.js";
|
|
14
15
|
import { summarizeGoalProgress, type GoalProgressSummary } from "./goalProgress.js";
|
|
15
16
|
import { readGoalStatus } from "./goalStore.js";
|
|
16
17
|
|
|
@@ -45,15 +46,6 @@ function resolveWorkspaceRoot(workspaceRoot?: string): string {
|
|
|
45
46
|
return workspaceRoot ?? getConfig().workspaceRoot;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
/**
|
|
49
|
-
* 原子写文件:先写到 .tmp 文件,再 renameSync 到目标路径。
|
|
50
|
-
*/
|
|
51
|
-
function atomicWriteFile(filePath: string, content: string): void {
|
|
52
|
-
const tmpPath = filePath + ".tmp";
|
|
53
|
-
writeFileSync(tmpPath, content, "utf-8");
|
|
54
|
-
renameSync(tmpPath, filePath);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
49
|
/**
|
|
58
50
|
* 将百分比字符串(如 "100%")解析为数字。
|
|
59
51
|
*/
|
|
@@ -261,11 +253,11 @@ export function exportGoalReport(
|
|
|
261
253
|
const safeReport = redactSensitiveValue(rawReport).value as SafeGoalReport;
|
|
262
254
|
|
|
263
255
|
// 原子写 JSON(机器可读)
|
|
264
|
-
|
|
256
|
+
atomicWriteFileSync(reportJsonPath, JSON.stringify(safeReport, null, 2) + "\n");
|
|
265
257
|
|
|
266
258
|
// 原子写 Markdown(人类可读,从脱敏后的对象生成)
|
|
267
259
|
const markdown = buildReportMarkdown(safeReport, { noSubgoals, incomplete });
|
|
268
|
-
|
|
260
|
+
atomicWriteFileSync(reportMdPath, markdown);
|
|
269
261
|
|
|
270
262
|
return safeReport;
|
|
271
263
|
}
|
package/src/goal/goalStatus.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { PatchWardenError } from "../errors.js";
|
|
15
|
+
import { redactSensitiveContent } from "../security/contentRedaction.js";
|
|
15
16
|
|
|
16
17
|
// ── 类型定义 ──────────────────────────────────────────────────────
|
|
17
18
|
|
|
@@ -35,6 +36,8 @@ export interface Subgoal {
|
|
|
35
36
|
external_ref?: string;
|
|
36
37
|
/** Spec Kit task.files 提示(作用域文件路径列表) */
|
|
37
38
|
scope_hints?: string[];
|
|
39
|
+
/** Managed git worktree associated with an isolated subgoal task. */
|
|
40
|
+
worktree_id?: string;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
export interface GoalStatus {
|
|
@@ -74,7 +77,7 @@ export function createInitialGoalStatus(
|
|
|
74
77
|
const now = new Date().toISOString();
|
|
75
78
|
return {
|
|
76
79
|
goal_id: goalId,
|
|
77
|
-
title,
|
|
80
|
+
title: redactGoalText(title, 500),
|
|
78
81
|
status: "active",
|
|
79
82
|
repo_path: repoPath,
|
|
80
83
|
created_at: now,
|
|
@@ -94,6 +97,15 @@ export function addSubgoal(
|
|
|
94
97
|
title: string,
|
|
95
98
|
dependsOn: string[] = []
|
|
96
99
|
): { goalStatus: GoalStatus; subgoalId: string } {
|
|
100
|
+
if (goalStatus.status !== "active") {
|
|
101
|
+
throw new PatchWardenError(
|
|
102
|
+
"goal_not_active",
|
|
103
|
+
`Goal "${goalStatus.goal_id}" is ${goalStatus.status} and cannot accept new subgoals.`,
|
|
104
|
+
"Create a new Goal Session for additional work.",
|
|
105
|
+
true,
|
|
106
|
+
{ goal_id: goalStatus.goal_id, status: goalStatus.status },
|
|
107
|
+
);
|
|
108
|
+
}
|
|
97
109
|
const existingIds = new Set(goalStatus.subgoals.map((s) => s.id));
|
|
98
110
|
for (const depId of dependsOn) {
|
|
99
111
|
if (!existingIds.has(depId)) {
|
|
@@ -111,7 +123,7 @@ export function addSubgoal(
|
|
|
111
123
|
const subgoalId = `subgoal-${String(nextNumber).padStart(3, "0")}`;
|
|
112
124
|
const newSubgoal: Subgoal = {
|
|
113
125
|
id: subgoalId,
|
|
114
|
-
title,
|
|
126
|
+
title: redactGoalText(title, 500),
|
|
115
127
|
status: "ready",
|
|
116
128
|
depends_on: [...dependsOn],
|
|
117
129
|
task_ids: [],
|
|
@@ -185,7 +197,7 @@ export function updateSubgoalStatus(
|
|
|
185
197
|
{ subgoal_id: subgoalId, from_status: current.status, to_status: newStatus }
|
|
186
198
|
);
|
|
187
199
|
}
|
|
188
|
-
updatedSubgoal.rejected_reason = options.rejected_reason;
|
|
200
|
+
updatedSubgoal.rejected_reason = redactGoalText(options.rejected_reason, 2000);
|
|
189
201
|
}
|
|
190
202
|
|
|
191
203
|
const newSubgoals = [...goalStatus.subgoals];
|
|
@@ -198,6 +210,10 @@ export function updateSubgoalStatus(
|
|
|
198
210
|
};
|
|
199
211
|
}
|
|
200
212
|
|
|
213
|
+
function redactGoalText(value: string, maxChars: number): string {
|
|
214
|
+
return redactSensitiveContent(String(value || "")).content.slice(0, maxChars);
|
|
215
|
+
}
|
|
216
|
+
|
|
201
217
|
/**
|
|
202
218
|
* 将 taskId 关联到指定 subgoal(去重)。
|
|
203
219
|
* 不可变更新:返回新的 GoalStatus 对象。
|
package/src/goal/goalStore.ts
CHANGED
|
@@ -18,14 +18,20 @@ import {
|
|
|
18
18
|
mkdirSync,
|
|
19
19
|
readdirSync,
|
|
20
20
|
readFileSync,
|
|
21
|
-
renameSync,
|
|
22
|
-
writeFileSync,
|
|
23
21
|
statSync,
|
|
24
22
|
} from "node:fs";
|
|
25
23
|
import { join } from "node:path";
|
|
26
24
|
import { getConfig } from "../config.js";
|
|
27
|
-
import { guardWorkspacePath } from "../security/pathGuard.js";
|
|
25
|
+
import { guardPath, guardWorkspacePath } from "../security/pathGuard.js";
|
|
28
26
|
import { PatchWardenError } from "../errors.js";
|
|
27
|
+
import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
28
|
+
import { redactSensitiveContent } from "../security/contentRedaction.js";
|
|
29
|
+
import {
|
|
30
|
+
mutateLockedJsonFileSync,
|
|
31
|
+
readJsonObjectFileSync,
|
|
32
|
+
withFileLock,
|
|
33
|
+
withFileLockSync,
|
|
34
|
+
} from "../utils/lockedJsonFile.js";
|
|
29
35
|
import {
|
|
30
36
|
type GoalStatus,
|
|
31
37
|
type Subgoal,
|
|
@@ -38,6 +44,26 @@ function resolveWorkspaceRoot(workspaceRoot?: string): string {
|
|
|
38
44
|
return workspaceRoot ?? getConfig().workspaceRoot;
|
|
39
45
|
}
|
|
40
46
|
|
|
47
|
+
const GOALS_PREFIX = join(".patchwarden", "goals");
|
|
48
|
+
const GOAL_ID_MAX_LENGTH = 128;
|
|
49
|
+
const GOAL_ID_PATTERN = /^goal_[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
50
|
+
|
|
51
|
+
/** Validate the storage key before it can participate in path construction. */
|
|
52
|
+
export function assertValidGoalId(goalId: string): void {
|
|
53
|
+
if (
|
|
54
|
+
goalId.length > GOAL_ID_MAX_LENGTH ||
|
|
55
|
+
!GOAL_ID_PATTERN.test(goalId)
|
|
56
|
+
) {
|
|
57
|
+
throw new PatchWardenError(
|
|
58
|
+
"invalid_goal_id",
|
|
59
|
+
`Invalid goal id. Expected "goal_" followed by at most ${GOAL_ID_MAX_LENGTH - 5} ASCII letters, digits, underscores, or hyphens.`,
|
|
60
|
+
"Use the goal_id returned by create_goal or list_goals.",
|
|
61
|
+
true,
|
|
62
|
+
{ goal_id_length: goalId.length }
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
41
67
|
// ── Goal ID 生成 ──────────────────────────────────────────────────
|
|
42
68
|
|
|
43
69
|
/**
|
|
@@ -86,14 +112,17 @@ export function generateGoalId(title: string, existingIds: string[]): string {
|
|
|
86
112
|
* 不自动创建目录。
|
|
87
113
|
*/
|
|
88
114
|
export function getGoalsDir(workspaceRoot?: string): string {
|
|
89
|
-
|
|
115
|
+
const wsRoot = resolveWorkspaceRoot(workspaceRoot);
|
|
116
|
+
return guardPath(join(wsRoot, GOALS_PREFIX), wsRoot, GOALS_PREFIX);
|
|
90
117
|
}
|
|
91
118
|
|
|
92
119
|
/**
|
|
93
120
|
* 返回 `{getGoalsDir()}/{goalId}` 路径。
|
|
94
121
|
*/
|
|
95
122
|
export function getGoalDir(goalId: string, workspaceRoot?: string): string {
|
|
96
|
-
|
|
123
|
+
assertValidGoalId(goalId);
|
|
124
|
+
const wsRoot = resolveWorkspaceRoot(workspaceRoot);
|
|
125
|
+
return guardPath(join(getGoalsDir(wsRoot), goalId), wsRoot, GOALS_PREFIX);
|
|
97
126
|
}
|
|
98
127
|
|
|
99
128
|
// ── CRUD ──────────────────────────────────────────────────────────
|
|
@@ -112,12 +141,26 @@ export function createGoal(
|
|
|
112
141
|
description: string,
|
|
113
142
|
workspaceRoot?: string
|
|
114
143
|
): { goal_id: string; goal_dir: string } {
|
|
144
|
+
const safeTitle = redactSensitiveContent(title)
|
|
145
|
+
.content
|
|
146
|
+
.replace(/[\r\n\t]+/g, " ")
|
|
147
|
+
.trim()
|
|
148
|
+
.slice(0, 500) || "Untitled goal";
|
|
149
|
+
const safeDescription = redactSensitiveContent(description).content;
|
|
150
|
+
if (Buffer.byteLength(safeDescription, "utf-8") > 1024 * 1024) {
|
|
151
|
+
throw new PatchWardenError(
|
|
152
|
+
"goal_description_too_large",
|
|
153
|
+
"Goal description exceeds the 1 MiB limit.",
|
|
154
|
+
"Split the work into smaller Goal Sessions.",
|
|
155
|
+
);
|
|
156
|
+
}
|
|
115
157
|
const wsRoot = resolveWorkspaceRoot(workspaceRoot);
|
|
116
158
|
const guardedRepo = guardWorkspacePath(repoPath, wsRoot);
|
|
117
159
|
|
|
118
160
|
const goalsDir = getGoalsDir(workspaceRoot);
|
|
119
|
-
|
|
120
|
-
|
|
161
|
+
mkdirSync(goalsDir, { recursive: true });
|
|
162
|
+
return withFileLockSync(join(goalsDir, "goal-creation"), () => {
|
|
163
|
+
const existingIds: string[] = [];
|
|
121
164
|
for (const entry of readdirSync(goalsDir)) {
|
|
122
165
|
const entryPath = join(goalsDir, entry);
|
|
123
166
|
try {
|
|
@@ -125,45 +168,38 @@ export function createGoal(
|
|
|
125
168
|
existingIds.push(entry);
|
|
126
169
|
}
|
|
127
170
|
} catch {
|
|
128
|
-
//
|
|
171
|
+
// Skip entries that cannot be inspected.
|
|
129
172
|
}
|
|
130
173
|
}
|
|
131
|
-
}
|
|
132
174
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
""
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// 写入 goal_status.json
|
|
159
|
-
const status = createInitialGoalStatus(goalId, title, guardedRepo);
|
|
160
|
-
writeFileSync(
|
|
161
|
-
join(goalDir, "goal_status.json"),
|
|
162
|
-
JSON.stringify(status, null, 2) + "\n",
|
|
163
|
-
"utf-8"
|
|
164
|
-
);
|
|
175
|
+
const goalId = generateGoalId(safeTitle, existingIds);
|
|
176
|
+
const goalDir = getGoalDir(goalId, wsRoot);
|
|
177
|
+
|
|
178
|
+
mkdirSync(goalDir);
|
|
179
|
+
mkdirSync(join(goalDir, "tasks"));
|
|
180
|
+
mkdirSync(join(goalDir, "artifacts"));
|
|
181
|
+
|
|
182
|
+
const now = new Date().toISOString();
|
|
183
|
+
const goalMd = [
|
|
184
|
+
`# ${safeTitle}`,
|
|
185
|
+
"",
|
|
186
|
+
safeDescription,
|
|
187
|
+
"",
|
|
188
|
+
`- Created: ${now}`,
|
|
189
|
+
`- Repo: ${guardedRepo}`,
|
|
190
|
+
"- Status: active",
|
|
191
|
+
"",
|
|
192
|
+
].join("\n");
|
|
193
|
+
atomicWriteFileSync(join(goalDir, "GOAL.md"), goalMd);
|
|
194
|
+
|
|
195
|
+
const goalsMd = `# Subgoals: ${safeTitle}\n\n_No subgoals yet._\n`;
|
|
196
|
+
atomicWriteFileSync(join(goalDir, "GOALS.md"), goalsMd);
|
|
197
|
+
|
|
198
|
+
const status = createInitialGoalStatus(goalId, safeTitle, guardedRepo);
|
|
199
|
+
atomicWriteJsonFileSync(join(goalDir, "goal_status.json"), status);
|
|
165
200
|
|
|
166
|
-
|
|
201
|
+
return { goal_id: goalId, goal_dir: goalDir };
|
|
202
|
+
}, goalBusyOptions("creation"));
|
|
167
203
|
}
|
|
168
204
|
|
|
169
205
|
/**
|
|
@@ -197,8 +233,8 @@ export function listGoals(
|
|
|
197
233
|
}> = [];
|
|
198
234
|
|
|
199
235
|
for (const entry of readdirSync(goalsDir)) {
|
|
200
|
-
const entryPath = join(goalsDir, entry);
|
|
201
236
|
try {
|
|
237
|
+
const entryPath = getGoalDir(entry, workspaceRoot);
|
|
202
238
|
if (!statSync(entryPath).isDirectory()) {
|
|
203
239
|
continue;
|
|
204
240
|
}
|
|
@@ -254,7 +290,12 @@ export function readGoal(
|
|
|
254
290
|
let goalDescription = "";
|
|
255
291
|
const goalMdPath = join(goalDir, "GOAL.md");
|
|
256
292
|
if (existsSync(goalMdPath)) {
|
|
257
|
-
|
|
293
|
+
const safeGoalMdPath = guardPath(
|
|
294
|
+
goalMdPath,
|
|
295
|
+
resolveWorkspaceRoot(workspaceRoot),
|
|
296
|
+
GOALS_PREFIX,
|
|
297
|
+
);
|
|
298
|
+
goalDescription = readFileSync(safeGoalMdPath, "utf-8");
|
|
258
299
|
}
|
|
259
300
|
|
|
260
301
|
return {
|
|
@@ -270,8 +311,7 @@ export function readGoal(
|
|
|
270
311
|
}
|
|
271
312
|
|
|
272
313
|
/**
|
|
273
|
-
*
|
|
274
|
-
* JSON 序列化用 JSON.stringify(status, null, 2) + 末尾换行。
|
|
314
|
+
* Replace goal_status.json under the shared cross-process mutation lock.
|
|
275
315
|
*/
|
|
276
316
|
export function writeGoalStatus(
|
|
277
317
|
goalId: string,
|
|
@@ -279,10 +319,26 @@ export function writeGoalStatus(
|
|
|
279
319
|
workspaceRoot?: string
|
|
280
320
|
): void {
|
|
281
321
|
const goalDir = getGoalDir(goalId, workspaceRoot);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
322
|
+
if (!existsSync(goalDir)) {
|
|
323
|
+
throw new PatchWardenError(
|
|
324
|
+
"goal_not_found",
|
|
325
|
+
`Goal directory not found: "${goalDir}"`,
|
|
326
|
+
"Ensure the goal has been created before writing its status.",
|
|
327
|
+
true,
|
|
328
|
+
{ goal_id: goalId, goal_dir: goalDir },
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
const statusPath = getGoalStatusPath(goalId, workspaceRoot);
|
|
332
|
+
if (!existsSync(statusPath)) {
|
|
333
|
+
withFileLockSync(statusPath, () => {
|
|
334
|
+
atomicWriteJsonFileSync(
|
|
335
|
+
statusPath,
|
|
336
|
+
normalizeGoalLifecycle(validateGoalStatus(goalId, status, workspaceRoot)),
|
|
337
|
+
);
|
|
338
|
+
}, goalBusyOptions(goalId));
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
mutateGoalStatus(goalId, () => ({ next: status, result: undefined }), workspaceRoot);
|
|
286
342
|
}
|
|
287
343
|
|
|
288
344
|
/**
|
|
@@ -290,8 +346,7 @@ export function writeGoalStatus(
|
|
|
290
346
|
* 如果文件不存在,抛出 PatchWardenError("goal_not_found")。
|
|
291
347
|
*/
|
|
292
348
|
export function readGoalStatus(goalId: string, workspaceRoot?: string): GoalStatus {
|
|
293
|
-
const
|
|
294
|
-
const statusPath = join(goalDir, "goal_status.json");
|
|
349
|
+
const statusPath = getGoalStatusPath(goalId, workspaceRoot);
|
|
295
350
|
if (!existsSync(statusPath)) {
|
|
296
351
|
throw new PatchWardenError(
|
|
297
352
|
"goal_not_found",
|
|
@@ -301,6 +356,138 @@ export function readGoalStatus(goalId: string, workspaceRoot?: string): GoalStat
|
|
|
301
356
|
{ goal_id: goalId, status_path: statusPath }
|
|
302
357
|
);
|
|
303
358
|
}
|
|
304
|
-
|
|
305
|
-
|
|
359
|
+
let parsed: unknown;
|
|
360
|
+
try {
|
|
361
|
+
parsed = readJsonObjectFileSync(statusPath);
|
|
362
|
+
} catch {
|
|
363
|
+
throw invalidGoalStatus(goalId, "goal_status.json is not valid JSON");
|
|
364
|
+
}
|
|
365
|
+
return validateGoalStatus(goalId, parsed, workspaceRoot);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export interface GoalStatusMutation<R> {
|
|
369
|
+
next?: GoalStatus;
|
|
370
|
+
result: R;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export function mutateGoalStatus<R>(
|
|
374
|
+
goalId: string,
|
|
375
|
+
mutation: (current: GoalStatus) => GoalStatusMutation<R>,
|
|
376
|
+
workspaceRoot?: string,
|
|
377
|
+
): R {
|
|
378
|
+
const statusPath = getGoalStatusPath(goalId, workspaceRoot);
|
|
379
|
+
ensureGoalStatusExists(goalId, statusPath);
|
|
380
|
+
return mutateLockedJsonFileSync<GoalStatus, R>(statusPath, (raw) => {
|
|
381
|
+
const current = validateGoalStatus(goalId, raw, workspaceRoot);
|
|
382
|
+
const outcome = mutation(current);
|
|
383
|
+
if (!outcome.next) return outcome;
|
|
384
|
+
return {
|
|
385
|
+
next: normalizeGoalLifecycle(validateGoalStatus(goalId, outcome.next, workspaceRoot)),
|
|
386
|
+
result: outcome.result,
|
|
387
|
+
};
|
|
388
|
+
}, goalBusyOptions(goalId));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export async function mutateGoalStatusAsync<R>(
|
|
392
|
+
goalId: string,
|
|
393
|
+
mutation: (current: GoalStatus) => Promise<GoalStatusMutation<R>>,
|
|
394
|
+
workspaceRoot?: string,
|
|
395
|
+
): Promise<R> {
|
|
396
|
+
const statusPath = getGoalStatusPath(goalId, workspaceRoot);
|
|
397
|
+
ensureGoalStatusExists(goalId, statusPath);
|
|
398
|
+
return withFileLock(statusPath, async () => {
|
|
399
|
+
const current = validateGoalStatus(
|
|
400
|
+
goalId,
|
|
401
|
+
readJsonObjectFileSync(statusPath),
|
|
402
|
+
workspaceRoot,
|
|
403
|
+
);
|
|
404
|
+
const outcome = await mutation(current);
|
|
405
|
+
if (outcome.next) {
|
|
406
|
+
atomicWriteJsonFileSync(
|
|
407
|
+
statusPath,
|
|
408
|
+
normalizeGoalLifecycle(validateGoalStatus(goalId, outcome.next, workspaceRoot)),
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
return outcome.result;
|
|
412
|
+
}, goalBusyOptions(goalId));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function getGoalStatusPath(goalId: string, workspaceRoot?: string): string {
|
|
416
|
+
const wsRoot = resolveWorkspaceRoot(workspaceRoot);
|
|
417
|
+
return guardPath(
|
|
418
|
+
join(getGoalDir(goalId, wsRoot), "goal_status.json"),
|
|
419
|
+
wsRoot,
|
|
420
|
+
GOALS_PREFIX,
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function ensureGoalStatusExists(goalId: string, statusPath: string): void {
|
|
425
|
+
if (!existsSync(statusPath)) {
|
|
426
|
+
throw new PatchWardenError(
|
|
427
|
+
"goal_not_found",
|
|
428
|
+
`goal_status.json not found for goal "${goalId}" at "${statusPath}"`,
|
|
429
|
+
"Ensure the goal has been created via createGoal before updating it.",
|
|
430
|
+
true,
|
|
431
|
+
{ goal_id: goalId, status_path: statusPath },
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function validateGoalStatus(
|
|
437
|
+
goalId: string,
|
|
438
|
+
value: unknown,
|
|
439
|
+
workspaceRoot?: string,
|
|
440
|
+
): GoalStatus {
|
|
441
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
442
|
+
throw invalidGoalStatus(goalId, "expected a JSON object");
|
|
443
|
+
}
|
|
444
|
+
const candidate = value as Partial<GoalStatus>;
|
|
445
|
+
if (candidate.goal_id !== goalId) {
|
|
446
|
+
throw invalidGoalStatus(goalId, "goal_id does not match its directory");
|
|
447
|
+
}
|
|
448
|
+
if (typeof candidate.title !== "string" || typeof candidate.repo_path !== "string") {
|
|
449
|
+
throw invalidGoalStatus(goalId, "title or repo_path is missing");
|
|
450
|
+
}
|
|
451
|
+
if (candidate.status !== "active" && candidate.status !== "completed" && candidate.status !== "abandoned") {
|
|
452
|
+
throw invalidGoalStatus(goalId, "status is invalid");
|
|
453
|
+
}
|
|
454
|
+
if (!Array.isArray(candidate.subgoals)) {
|
|
455
|
+
throw invalidGoalStatus(goalId, "subgoals must be an array");
|
|
456
|
+
}
|
|
457
|
+
guardWorkspacePath(candidate.repo_path, resolveWorkspaceRoot(workspaceRoot));
|
|
458
|
+
return candidate as GoalStatus;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function normalizeGoalLifecycle(status: GoalStatus): GoalStatus {
|
|
462
|
+
if (
|
|
463
|
+
status.status === "active" &&
|
|
464
|
+
status.subgoals.length > 0 &&
|
|
465
|
+
status.subgoals.every((subgoal) => subgoal.status === "accepted")
|
|
466
|
+
) {
|
|
467
|
+
return { ...status, status: "completed", updated_at: new Date().toISOString() };
|
|
468
|
+
}
|
|
469
|
+
return status;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function invalidGoalStatus(goalId: string, detail: string): PatchWardenError {
|
|
473
|
+
return new PatchWardenError(
|
|
474
|
+
"invalid_goal_status",
|
|
475
|
+
`Invalid goal status for "${goalId}": ${detail}.`,
|
|
476
|
+
"Recreate the Goal Session or restore a valid goal_status.json artifact.",
|
|
477
|
+
true,
|
|
478
|
+
{ goal_id: goalId },
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function goalBusyOptions(goalId: string) {
|
|
483
|
+
return {
|
|
484
|
+
waitMs: 0,
|
|
485
|
+
busyError: () => new PatchWardenError(
|
|
486
|
+
"goal_busy",
|
|
487
|
+
`Goal "${goalId}" is currently being updated.`,
|
|
488
|
+
"Retry after the active Goal operation finishes.",
|
|
489
|
+
true,
|
|
490
|
+
{ goal_id: goalId },
|
|
491
|
+
),
|
|
492
|
+
};
|
|
306
493
|
}
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
import type { GoalStatus } from "./goalStatus.js";
|
|
11
11
|
import { suggestNextSubgoal } from "./goalGraph.js";
|
|
12
|
-
import {
|
|
13
|
-
import { mkdirSync
|
|
14
|
-
import { join
|
|
12
|
+
import { getGoalDir } from "./goalStore.js";
|
|
13
|
+
import { mkdirSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { atomicWriteFileSync } from "../utils/atomicFile.js";
|
|
15
16
|
|
|
16
17
|
// ── Markdown 生成 ────────────────────────────────────────────────
|
|
17
18
|
|
|
@@ -195,12 +196,11 @@ export function exportHandoff(
|
|
|
195
196
|
): { handoff_path: string; content_preview: string } {
|
|
196
197
|
const content = generateHandoff(goalId, goalStatus);
|
|
197
198
|
|
|
198
|
-
const
|
|
199
|
-
const goalDir = resolve(root, ".patchwarden", "goals", goalId);
|
|
199
|
+
const goalDir = getGoalDir(goalId, workspaceRoot);
|
|
200
200
|
mkdirSync(goalDir, { recursive: true });
|
|
201
201
|
|
|
202
202
|
const handoffPath = join(goalDir, "handoff.md");
|
|
203
|
-
|
|
203
|
+
atomicWriteFileSync(handoffPath, content);
|
|
204
204
|
|
|
205
205
|
const preview = content.length > 500 ? content.slice(0, 500) + "..." : content;
|
|
206
206
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { PatchWardenError } from "../errors.js";
|
|
13
13
|
import { addSubgoal, type GoalStatus } from "./goalStatus.js";
|
|
14
|
-
import {
|
|
14
|
+
import { mutateGoalStatus } from "./goalStore.js";
|
|
15
15
|
|
|
16
16
|
// ── 类型定义 ──────────────────────────────────────────────────────
|
|
17
17
|
|
|
@@ -207,8 +207,8 @@ export function importSpecKitTasks(
|
|
|
207
207
|
|
|
208
208
|
const workspaceRoot = options?.workspaceRoot;
|
|
209
209
|
|
|
210
|
-
|
|
211
|
-
let goalStatus: GoalStatus =
|
|
210
|
+
return mutateGoalStatus(goalId, (currentGoalStatus) => {
|
|
211
|
+
let goalStatus: GoalStatus = currentGoalStatus;
|
|
212
212
|
|
|
213
213
|
// ── 构建 external_ref → subgoal_id 映射(现有 subgoal)─────
|
|
214
214
|
const refToSubgoalId = new Map<string, string>();
|
|
@@ -341,15 +341,15 @@ export function importSpecKitTasks(
|
|
|
341
341
|
};
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
-
// ── 持久化(原子写)────────────────────────────────────────
|
|
345
|
-
writeGoalStatus(goalId, goalStatus, workspaceRoot);
|
|
346
|
-
|
|
347
|
-
// ── 返回结果 ───────────────────────────────────────────────
|
|
348
344
|
return {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
345
|
+
next: goalStatus,
|
|
346
|
+
result: {
|
|
347
|
+
goal_id: goalId,
|
|
348
|
+
spec_name: input.spec,
|
|
349
|
+
created_count: createdIds.length,
|
|
350
|
+
existing_count: existingCount,
|
|
351
|
+
subgoal_ids: createdIds,
|
|
352
|
+
},
|
|
354
353
|
};
|
|
354
|
+
}, workspaceRoot);
|
|
355
355
|
}
|