zero-workspace 0.0.2
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/CHANGELOG.md +18 -0
- package/README.md +187 -0
- package/config/component-versions.json +16 -0
- package/config/scenarioCapabilities.json +29 -0
- package/config/version-notes.yaml +244 -0
- package/dist/adapters/OutputAdapter.d.ts +79 -0
- package/dist/adapters/OutputAdapter.d.ts.map +1 -0
- package/dist/adapters/OutputAdapter.js +124 -0
- package/dist/adapters/OutputAdapter.js.map +1 -0
- package/dist/cli/check-node-version.d.ts +3 -0
- package/dist/cli/check-node-version.d.ts.map +1 -0
- package/dist/cli/check-node-version.js +153 -0
- package/dist/cli/check-node-version.js.map +1 -0
- package/dist/cli/plugins.d.ts +41 -0
- package/dist/cli/plugins.d.ts.map +1 -0
- package/dist/cli/plugins.js +742 -0
- package/dist/cli/plugins.js.map +1 -0
- package/dist/cli/rebuild.d.ts +63 -0
- package/dist/cli/rebuild.d.ts.map +1 -0
- package/dist/cli/rebuild.js +989 -0
- package/dist/cli/rebuild.js.map +1 -0
- package/dist/cli/repair.d.ts +7 -0
- package/dist/cli/repair.d.ts.map +1 -0
- package/dist/cli/repair.js +925 -0
- package/dist/cli/repair.js.map +1 -0
- package/dist/cli/setup.d.ts +7 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +452 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/update.d.ts +10 -0
- package/dist/cli/update.d.ts.map +1 -0
- package/dist/cli/update.js +426 -0
- package/dist/cli/update.js.map +1 -0
- package/dist/cli/webui.d.ts +6 -0
- package/dist/cli/webui.d.ts.map +1 -0
- package/dist/cli/webui.js +210 -0
- package/dist/cli/webui.js.map +1 -0
- package/dist/http/index.d.ts +3 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +15 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/middleware/errorHandler.d.ts +16 -0
- package/dist/http/middleware/errorHandler.d.ts.map +1 -0
- package/dist/http/middleware/errorHandler.js +79 -0
- package/dist/http/middleware/errorHandler.js.map +1 -0
- package/dist/http/routes/admin.d.ts +3 -0
- package/dist/http/routes/admin.d.ts.map +1 -0
- package/dist/http/routes/admin.js +730 -0
- package/dist/http/routes/admin.js.map +1 -0
- package/dist/http/routes/backup.d.ts +3 -0
- package/dist/http/routes/backup.d.ts.map +1 -0
- package/dist/http/routes/backup.js +172 -0
- package/dist/http/routes/backup.js.map +1 -0
- package/dist/http/routes/config.d.ts +3 -0
- package/dist/http/routes/config.d.ts.map +1 -0
- package/dist/http/routes/config.js +157 -0
- package/dist/http/routes/config.js.map +1 -0
- package/dist/http/routes/context.d.ts +3 -0
- package/dist/http/routes/context.d.ts.map +1 -0
- package/dist/http/routes/context.js +82 -0
- package/dist/http/routes/context.js.map +1 -0
- package/dist/http/routes/log.d.ts +3 -0
- package/dist/http/routes/log.d.ts.map +1 -0
- package/dist/http/routes/log.js +105 -0
- package/dist/http/routes/log.js.map +1 -0
- package/dist/http/routes/memo.d.ts +6 -0
- package/dist/http/routes/memo.d.ts.map +1 -0
- package/dist/http/routes/memo.js +29 -0
- package/dist/http/routes/memo.js.map +1 -0
- package/dist/http/routes/node.d.ts +3 -0
- package/dist/http/routes/node.d.ts.map +1 -0
- package/dist/http/routes/node.js +251 -0
- package/dist/http/routes/node.js.map +1 -0
- package/dist/http/routes/state.d.ts +3 -0
- package/dist/http/routes/state.d.ts.map +1 -0
- package/dist/http/routes/state.js +48 -0
- package/dist/http/routes/state.js.map +1 -0
- package/dist/http/routes/workspace.d.ts +3 -0
- package/dist/http/routes/workspace.d.ts.map +1 -0
- package/dist/http/routes/workspace.js +249 -0
- package/dist/http/routes/workspace.js.map +1 -0
- package/dist/http/server.d.ts +10 -0
- package/dist/http/server.d.ts.map +1 -0
- package/dist/http/server.js +284 -0
- package/dist/http/server.js.map +1 -0
- package/dist/http/services.d.ts +93 -0
- package/dist/http/services.d.ts.map +1 -0
- package/dist/http/services.js +297 -0
- package/dist/http/services.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1073 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/guidanceContent.d.ts +18 -0
- package/dist/prompts/guidanceContent.d.ts.map +1 -0
- package/dist/prompts/guidanceContent.js +814 -0
- package/dist/prompts/guidanceContent.js.map +1 -0
- package/dist/prompts/index.d.ts +2 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +4 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/instructions.d.ts +56 -0
- package/dist/prompts/instructions.d.ts.map +1 -0
- package/dist/prompts/instructions.js +1343 -0
- package/dist/prompts/instructions.js.map +1 -0
- package/dist/services/BackupService.d.ts +104 -0
- package/dist/services/BackupService.d.ts.map +1 -0
- package/dist/services/BackupService.js +549 -0
- package/dist/services/BackupService.js.map +1 -0
- package/dist/services/CapabilityService.d.ts +38 -0
- package/dist/services/CapabilityService.d.ts.map +1 -0
- package/dist/services/CapabilityService.js +256 -0
- package/dist/services/CapabilityService.js.map +1 -0
- package/dist/services/ConfigService.d.ts +35 -0
- package/dist/services/ConfigService.d.ts.map +1 -0
- package/dist/services/ConfigService.js +105 -0
- package/dist/services/ConfigService.js.map +1 -0
- package/dist/services/ContextService.d.ts +65 -0
- package/dist/services/ContextService.d.ts.map +1 -0
- package/dist/services/ContextService.js +503 -0
- package/dist/services/ContextService.js.map +1 -0
- package/dist/services/DetectionService.d.ts +76 -0
- package/dist/services/DetectionService.d.ts.map +1 -0
- package/dist/services/DetectionService.js +262 -0
- package/dist/services/DetectionService.js.map +1 -0
- package/dist/services/DispatchService.d.ts +267 -0
- package/dist/services/DispatchService.d.ts.map +1 -0
- package/dist/services/DispatchService.js +1357 -0
- package/dist/services/DispatchService.js.map +1 -0
- package/dist/services/EventService.d.ts +81 -0
- package/dist/services/EventService.d.ts.map +1 -0
- package/dist/services/EventService.js +187 -0
- package/dist/services/EventService.js.map +1 -0
- package/dist/services/GuidanceService.d.ts +64 -0
- package/dist/services/GuidanceService.d.ts.map +1 -0
- package/dist/services/GuidanceService.js +259 -0
- package/dist/services/GuidanceService.js.map +1 -0
- package/dist/services/HealthService.d.ts +43 -0
- package/dist/services/HealthService.d.ts.map +1 -0
- package/dist/services/HealthService.js +276 -0
- package/dist/services/HealthService.js.map +1 -0
- package/dist/services/InstallationService.d.ts +62 -0
- package/dist/services/InstallationService.d.ts.map +1 -0
- package/dist/services/InstallationService.js +204 -0
- package/dist/services/InstallationService.js.map +1 -0
- package/dist/services/LogService.d.ts +35 -0
- package/dist/services/LogService.d.ts.map +1 -0
- package/dist/services/LogService.js +189 -0
- package/dist/services/LogService.js.map +1 -0
- package/dist/services/MemoService.d.ts +39 -0
- package/dist/services/MemoService.d.ts.map +1 -0
- package/dist/services/MemoService.js +288 -0
- package/dist/services/MemoService.js.map +1 -0
- package/dist/services/NodeService.d.ts +90 -0
- package/dist/services/NodeService.d.ts.map +1 -0
- package/dist/services/NodeService.js +958 -0
- package/dist/services/NodeService.js.map +1 -0
- package/dist/services/OpenSpecParser.d.ts +43 -0
- package/dist/services/OpenSpecParser.d.ts.map +1 -0
- package/dist/services/OpenSpecParser.js +191 -0
- package/dist/services/OpenSpecParser.js.map +1 -0
- package/dist/services/ReferenceService.d.ts +35 -0
- package/dist/services/ReferenceService.d.ts.map +1 -0
- package/dist/services/ReferenceService.js +195 -0
- package/dist/services/ReferenceService.js.map +1 -0
- package/dist/services/RepairService.d.ts +36 -0
- package/dist/services/RepairService.d.ts.map +1 -0
- package/dist/services/RepairService.js +429 -0
- package/dist/services/RepairService.js.map +1 -0
- package/dist/services/SearchService.d.ts +34 -0
- package/dist/services/SearchService.d.ts.map +1 -0
- package/dist/services/SearchService.js +293 -0
- package/dist/services/SearchService.js.map +1 -0
- package/dist/services/SessionService.d.ts +136 -0
- package/dist/services/SessionService.d.ts.map +1 -0
- package/dist/services/SessionService.js +297 -0
- package/dist/services/SessionService.js.map +1 -0
- package/dist/services/StateService.d.ts +97 -0
- package/dist/services/StateService.d.ts.map +1 -0
- package/dist/services/StateService.js +846 -0
- package/dist/services/StateService.js.map +1 -0
- package/dist/services/TutorialService.d.ts +114 -0
- package/dist/services/TutorialService.d.ts.map +1 -0
- package/dist/services/TutorialService.js +1262 -0
- package/dist/services/TutorialService.js.map +1 -0
- package/dist/services/WorkspaceService.d.ts +273 -0
- package/dist/services/WorkspaceService.d.ts.map +1 -0
- package/dist/services/WorkspaceService.js +1764 -0
- package/dist/services/WorkspaceService.js.map +1 -0
- package/dist/services/index.d.ts +15 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +14 -0
- package/dist/services/index.js.map +1 -0
- package/dist/storage/FileSystemAdapter.d.ts +223 -0
- package/dist/storage/FileSystemAdapter.d.ts.map +1 -0
- package/dist/storage/FileSystemAdapter.js +384 -0
- package/dist/storage/FileSystemAdapter.js.map +1 -0
- package/dist/storage/JsonStorage.d.ts +158 -0
- package/dist/storage/JsonStorage.d.ts.map +1 -0
- package/dist/storage/JsonStorage.js +613 -0
- package/dist/storage/JsonStorage.js.map +1 -0
- package/dist/storage/MarkdownStorage.d.ts +178 -0
- package/dist/storage/MarkdownStorage.d.ts.map +1 -0
- package/dist/storage/MarkdownStorage.js +918 -0
- package/dist/storage/MarkdownStorage.js.map +1 -0
- package/dist/storage/SessionBindingStorage.d.ts +69 -0
- package/dist/storage/SessionBindingStorage.d.ts.map +1 -0
- package/dist/storage/SessionBindingStorage.js +131 -0
- package/dist/storage/SessionBindingStorage.js.map +1 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +6 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/tools/capability.d.ts +18 -0
- package/dist/tools/capability.d.ts.map +1 -0
- package/dist/tools/capability.js +73 -0
- package/dist/tools/capability.js.map +1 -0
- package/dist/tools/config.d.ts +14 -0
- package/dist/tools/config.d.ts.map +1 -0
- package/dist/tools/config.js +61 -0
- package/dist/tools/config.js.map +1 -0
- package/dist/tools/context.d.ts +22 -0
- package/dist/tools/context.d.ts.map +1 -0
- package/dist/tools/context.js +139 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/dispatch.d.ts +41 -0
- package/dist/tools/dispatch.d.ts.map +1 -0
- package/dist/tools/dispatch.js +380 -0
- package/dist/tools/dispatch.js.map +1 -0
- package/dist/tools/help.d.ts +44 -0
- package/dist/tools/help.d.ts.map +1 -0
- package/dist/tools/help.js +227 -0
- package/dist/tools/help.js.map +1 -0
- package/dist/tools/import.d.ts +17 -0
- package/dist/tools/import.d.ts.map +1 -0
- package/dist/tools/import.js +96 -0
- package/dist/tools/import.js.map +1 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +13 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/log.d.ts +21 -0
- package/dist/tools/log.d.ts.map +1 -0
- package/dist/tools/log.js +93 -0
- package/dist/tools/log.js.map +1 -0
- package/dist/tools/memo.d.ts +26 -0
- package/dist/tools/memo.d.ts.map +1 -0
- package/dist/tools/memo.js +188 -0
- package/dist/tools/memo.js.map +1 -0
- package/dist/tools/node.d.ts +34 -0
- package/dist/tools/node.d.ts.map +1 -0
- package/dist/tools/node.js +328 -0
- package/dist/tools/node.js.map +1 -0
- package/dist/tools/search.d.ts +14 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +95 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/session.d.ts +22 -0
- package/dist/tools/session.d.ts.map +1 -0
- package/dist/tools/session.js +127 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/state.d.ts +10 -0
- package/dist/tools/state.d.ts.map +1 -0
- package/dist/tools/state.js +79 -0
- package/dist/tools/state.js.map +1 -0
- package/dist/tools/workspace.d.ts +38 -0
- package/dist/tools/workspace.d.ts.map +1 -0
- package/dist/tools/workspace.js +240 -0
- package/dist/tools/workspace.js.map +1 -0
- package/dist/types/capability.d.ts +36 -0
- package/dist/types/capability.d.ts.map +1 -0
- package/dist/types/capability.js +3 -0
- package/dist/types/capability.js.map +1 -0
- package/dist/types/confirmation.d.ts +35 -0
- package/dist/types/confirmation.d.ts.map +1 -0
- package/dist/types/confirmation.js +3 -0
- package/dist/types/confirmation.js.map +1 -0
- package/dist/types/context.d.ts +174 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +3 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/errors.d.ts +81 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +154 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/guidance.d.ts +162 -0
- package/dist/types/guidance.d.ts.map +1 -0
- package/dist/types/guidance.js +4 -0
- package/dist/types/guidance.js.map +1 -0
- package/dist/types/health.d.ts +61 -0
- package/dist/types/health.d.ts.map +1 -0
- package/dist/types/health.js +3 -0
- package/dist/types/health.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/memo.d.ts +132 -0
- package/dist/types/memo.d.ts.map +1 -0
- package/dist/types/memo.js +3 -0
- package/dist/types/memo.js.map +1 -0
- package/dist/types/node.d.ts +316 -0
- package/dist/types/node.d.ts.map +1 -0
- package/dist/types/node.js +3 -0
- package/dist/types/node.js.map +1 -0
- package/dist/types/repair.d.ts +62 -0
- package/dist/types/repair.d.ts.map +1 -0
- package/dist/types/repair.js +4 -0
- package/dist/types/repair.js.map +1 -0
- package/dist/types/search.d.ts +58 -0
- package/dist/types/search.d.ts.map +1 -0
- package/dist/types/search.js +3 -0
- package/dist/types/search.js.map +1 -0
- package/dist/types/settings.d.ts +109 -0
- package/dist/types/settings.d.ts.map +1 -0
- package/dist/types/settings.js +30 -0
- package/dist/types/settings.js.map +1 -0
- package/dist/types/workspace.d.ts +357 -0
- package/dist/types/workspace.d.ts.map +1 -0
- package/dist/types/workspace.js +3 -0
- package/dist/types/workspace.js.map +1 -0
- package/dist/utils/contentValidation.d.ts +47 -0
- package/dist/utils/contentValidation.d.ts.map +1 -0
- package/dist/utils/contentValidation.js +93 -0
- package/dist/utils/contentValidation.js.map +1 -0
- package/dist/utils/devLog.d.ts +43 -0
- package/dist/utils/devLog.d.ts.map +1 -0
- package/dist/utils/devLog.js +94 -0
- package/dist/utils/devLog.js.map +1 -0
- package/dist/utils/errorLogger.d.ts +27 -0
- package/dist/utils/errorLogger.d.ts.map +1 -0
- package/dist/utils/errorLogger.js +105 -0
- package/dist/utils/errorLogger.js.map +1 -0
- package/dist/utils/git.d.ts +123 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +400 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/hash.d.ts +32 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +37 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/id.d.ts +54 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +96 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +42 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +228 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/manualChangeFormatter.d.ts +8 -0
- package/dist/utils/manualChangeFormatter.d.ts.map +1 -0
- package/dist/utils/manualChangeFormatter.js +21 -0
- package/dist/utils/manualChangeFormatter.js.map +1 -0
- package/dist/utils/paramValidator.d.ts +35 -0
- package/dist/utils/paramValidator.d.ts.map +1 -0
- package/dist/utils/paramValidator.js +214 -0
- package/dist/utils/paramValidator.js.map +1 -0
- package/dist/utils/port.d.ts +7 -0
- package/dist/utils/port.d.ts.map +1 -0
- package/dist/utils/port.js +28 -0
- package/dist/utils/port.js.map +1 -0
- package/dist/utils/processManager.d.ts +53 -0
- package/dist/utils/processManager.d.ts.map +1 -0
- package/dist/utils/processManager.js +267 -0
- package/dist/utils/processManager.js.map +1 -0
- package/dist/utils/sessionLogger.d.ts +28 -0
- package/dist/utils/sessionLogger.d.ts.map +1 -0
- package/dist/utils/sessionLogger.js +142 -0
- package/dist/utils/sessionLogger.js.map +1 -0
- package/dist/utils/time.d.ts +15 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +32 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/utils/validation.d.ts +23 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +88 -0
- package/dist/utils/validation.js.map +1 -0
- package/docs//346/227/245/345/277/227/347/263/273/347/273/237.md +389 -0
- package/docs//347/224/250/346/210/267/346/211/213/345/206/214.md +1446 -0
- package/docs//347/224/250/346/210/267/346/211/213/345/206/214/344/270/216/346/212/200/346/234/257/346/214/207/345/215/227.md +873 -0
- package/package.json +94 -0
- package/plugin/README.md +141 -0
- package/plugin/agents/zero-executor.md +114 -0
- package/plugin/agents/zero-reviewer.md +133 -0
- package/plugin/docs/diagnostic-guide.md +128 -0
- package/plugin/hooks/hooks.json.deprecated +70 -0
- package/plugin/scripts/cursor-hook-entry.cjs +217 -0
- package/plugin/scripts/hook-entry.cjs +663 -0
- package/plugin/scripts/openspec-import.cjs +714 -0
- package/plugin/scripts/shared/binding.cjs +98 -0
- package/plugin/scripts/shared/config.cjs +65 -0
- package/plugin/scripts/shared/context.cjs +120 -0
- package/plugin/scripts/shared/index.cjs +34 -0
- package/plugin/scripts/shared/logger.cjs +196 -0
- package/plugin/scripts/shared/reminder.cjs +261 -0
- package/plugin/scripts/shared/utils.cjs +62 -0
- package/plugin/scripts/shared/workspace.cjs +322 -0
- package/plugin/skills/aligning-intent/SKILL.md +275 -0
- package/plugin/skills/analyzing-measurements/SKILL.md +223 -0
- package/plugin/skills/bootstrapping-workspace/SKILL.md +260 -0
- package/plugin/skills/designing-solutions/SKILL.md +363 -0
- package/plugin/skills/diagnosing-issues/SKILL.md +219 -0
- package/plugin/skills/discovering-context/SKILL.md +283 -0
- package/plugin/skills/dispatching-parent/SKILL.md +399 -0
- package/plugin/skills/executing-task/SKILL.md +340 -0
- package/plugin/skills/memo-create/SKILL.md +222 -0
- package/plugin/skills/planning-verification/SKILL.md +245 -0
- package/plugin/skills/preparing-dispatch/SKILL.md +299 -0
- package/plugin/skills/researching-tech/SKILL.md +223 -0
- package/plugin/skills/reviewing-quality/SKILL.md +354 -0
- package/plugin/skills/reviewing-spec/SKILL.md +333 -0
- package/plugin/skills/starting-info-flow/SKILL.md +196 -0
- package/web/README.md +5 -0
- package/web/dist/assets/DocsView-Bls_Vjsr.css +1 -0
- package/web/dist/assets/DocsView-Cxc0B63r.js +1447 -0
- package/web/dist/assets/HomeView-C7df9thb.js +9 -0
- package/web/dist/assets/HomeView-ufUdnfHk.css +1 -0
- package/web/dist/assets/MarkdownContent-DXp6CtSP.js +308 -0
- package/web/dist/assets/MarkdownContent-NFqiOBLH.css +1 -0
- package/web/dist/assets/NotFoundView-BYX1oZAn.css +1 -0
- package/web/dist/assets/NotFoundView-zrc0lT9q.js +1 -0
- package/web/dist/assets/WorkspaceView-BckqgNcX.js +27 -0
- package/web/dist/assets/WorkspaceView-J1dgpYMx.css +1 -0
- package/web/dist/assets/WsConfirmDialog-C1CvL4my.css +1 -0
- package/web/dist/assets/WsConfirmDialog-gLEP7uBD.js +4 -0
- package/web/dist/assets/arc-DPkKTkUT.js +1 -0
- package/web/dist/assets/architectureDiagram-VXUJARFQ-CEGpqUlZ.js +36 -0
- package/web/dist/assets/blockDiagram-VD42YOAC-Bv-mqdQH.js +122 -0
- package/web/dist/assets/c4Diagram-YG6GDRKO-DRyPatZ_.js +10 -0
- package/web/dist/assets/channel-B84mKLDZ.js +1 -0
- package/web/dist/assets/chunk-4BX2VUAB-c7DivX0u.js +1 -0
- package/web/dist/assets/chunk-55IACEB6-CGKTaLlo.js +1 -0
- package/web/dist/assets/chunk-B4BG7PRW-Czhx5Q_P.js +165 -0
- package/web/dist/assets/chunk-DI55MBZ5-CQVA7hcZ.js +220 -0
- package/web/dist/assets/chunk-FMBD7UC4-hEiPmi7V.js +15 -0
- package/web/dist/assets/chunk-QN33PNHL-rL6yYI-E.js +1 -0
- package/web/dist/assets/chunk-QZHKN3VN-BRyHBBzq.js +1 -0
- package/web/dist/assets/chunk-TZMSLE5B-D4PXmTz9.js +1 -0
- package/web/dist/assets/classDiagram-2ON5EDUG-CNn53ohi.js +1 -0
- package/web/dist/assets/classDiagram-v2-WZHVMYZB-CNn53ohi.js +1 -0
- package/web/dist/assets/cose-bilkent-S5V4N54A-BAREnRga.js +1 -0
- package/web/dist/assets/cytoscape.esm-BnkdMOzK.js +321 -0
- package/web/dist/assets/dagre-6UL2VRFP-DaYzb3MT.js +4 -0
- package/web/dist/assets/defaultLocale-C4B-KCzX.js +1 -0
- package/web/dist/assets/diagram-PSM6KHXK-BFltDqvd.js +24 -0
- package/web/dist/assets/diagram-QEK2KX5R-CR4VU2La.js +43 -0
- package/web/dist/assets/diagram-S2PKOQOG-0UfIeT-1.js +24 -0
- package/web/dist/assets/erDiagram-Q2GNP2WA-Bo17Xmng.js +60 -0
- package/web/dist/assets/flowDiagram-NV44I4VS-CzqhQp8s.js +162 -0
- package/web/dist/assets/ganttDiagram-JELNMOA3-TXwXtUcq.js +267 -0
- package/web/dist/assets/gitGraphDiagram-NY62KEGX-CoFQTy9O.js +65 -0
- package/web/dist/assets/graph-CIQcRIVd.js +1 -0
- package/web/dist/assets/index-BgLd_o_M.css +1 -0
- package/web/dist/assets/index-Cd_J3fZn.js +30 -0
- package/web/dist/assets/infoDiagram-WHAUD3N6-Dq0xXfVu.js +2 -0
- package/web/dist/assets/init-Gi6I4Gst.js +1 -0
- package/web/dist/assets/journeyDiagram-XKPGCS4Q-jIg5BOfC.js +139 -0
- package/web/dist/assets/kanban-definition-3W4ZIXB7-D2giu6aZ.js +89 -0
- package/web/dist/assets/katex-XbL3y5x-.js +261 -0
- package/web/dist/assets/layout-Bm-XCM-8.js +1 -0
- package/web/dist/assets/linear-FbekP9OZ.js +1 -0
- package/web/dist/assets/min-BrRCpYmF.js +1 -0
- package/web/dist/assets/mindmap-definition-VGOIOE7T-o-4ubbY9.js +68 -0
- package/web/dist/assets/noto-emoji-0-400-normal-BTQbhB77.woff +0 -0
- package/web/dist/assets/noto-emoji-0-400-normal-DHdy6Uhy.woff2 +0 -0
- package/web/dist/assets/noto-emoji-1-400-normal-0IvkdXBB.woff +0 -0
- package/web/dist/assets/noto-emoji-1-400-normal-BY9OovbM.woff2 +0 -0
- package/web/dist/assets/noto-emoji-10-400-normal-D9w4QCof.woff2 +0 -0
- package/web/dist/assets/noto-emoji-10-400-normal-DtCumcZR.woff +0 -0
- package/web/dist/assets/noto-emoji-11-400-normal-BboTlyvx.woff +0 -0
- package/web/dist/assets/noto-emoji-12-400-normal-BB5pgBKj.woff2 +0 -0
- package/web/dist/assets/noto-emoji-12-400-normal-g186qhiA.woff +0 -0
- package/web/dist/assets/noto-emoji-2-400-normal-BKCR1azW.woff2 +0 -0
- package/web/dist/assets/noto-emoji-2-400-normal-BYH0KhDr.woff +0 -0
- package/web/dist/assets/noto-emoji-3-400-normal-CnPTUeEK.woff +0 -0
- package/web/dist/assets/noto-emoji-3-400-normal-TrTb2VQM.woff2 +0 -0
- package/web/dist/assets/noto-emoji-4-400-normal-BxD0KVdj.woff +0 -0
- package/web/dist/assets/noto-emoji-4-400-normal-s_n9EyG1.woff2 +0 -0
- package/web/dist/assets/noto-emoji-5-400-normal-C190AIxR.woff +0 -0
- package/web/dist/assets/noto-emoji-5-400-normal-Ctfx4xc6.woff2 +0 -0
- package/web/dist/assets/noto-emoji-6-400-normal-DlXlXWt7.woff +0 -0
- package/web/dist/assets/noto-emoji-6-400-normal-NzsjD754.woff2 +0 -0
- package/web/dist/assets/noto-emoji-7-400-normal-BHP8KeA6.woff2 +0 -0
- package/web/dist/assets/noto-emoji-7-400-normal-CtuKhtAZ.woff +0 -0
- package/web/dist/assets/noto-emoji-8-400-normal-DR49ZFe7.woff +0 -0
- package/web/dist/assets/noto-emoji-8-400-normal-Dvmkf6b2.woff2 +0 -0
- package/web/dist/assets/noto-emoji-9-400-normal-BeHJQ2iK.woff2 +0 -0
- package/web/dist/assets/noto-emoji-9-400-normal-BlXmCgeQ.woff +0 -0
- package/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/web/dist/assets/pieDiagram-ADFJNKIX-C45wSpld.js +30 -0
- package/web/dist/assets/quadrantDiagram-AYHSOK5B-tv-_fe-W.js +7 -0
- package/web/dist/assets/reduce-CoLNNlNb.js +1 -0
- package/web/dist/assets/requirementDiagram-UZGBJVZJ-Dn6PDfkL.js +64 -0
- package/web/dist/assets/sankeyDiagram-TZEHDZUN-CbXTZAsG.js +10 -0
- package/web/dist/assets/sequenceDiagram-WL72ISMW-B-o1CUJ5.js +145 -0
- package/web/dist/assets/stateDiagram-FKZM4ZOC-hzrupXQi.js +1 -0
- package/web/dist/assets/stateDiagram-v2-4FDKWEC3-DN-c2M96.js +1 -0
- package/web/dist/assets/timeline-definition-IT6M3QCI-R5SP9GDo.js +61 -0
- package/web/dist/assets/treemap-KMMF4GRG-Pg9KlUOt.js +128 -0
- package/web/dist/assets/xychartDiagram-PRI3JC2R-C3vbJhd1.js +7 -0
- package/web/dist/favicon.svg +13 -0
- package/web/dist/index.html +17 -0
- package//351/205/215/347/275/256/346/226/271/345/274/217.md +330 -0
|
@@ -0,0 +1,846 @@
|
|
|
1
|
+
// src/services/StateService.ts
|
|
2
|
+
import { ZeroError } from "../types/errors.js";
|
|
3
|
+
import { now, formatShort } from "../utils/time.js";
|
|
4
|
+
import { computeConclusionsHash } from "../utils/hash.js";
|
|
5
|
+
import { validateMultilineContent } from "../utils/contentValidation.js";
|
|
6
|
+
import { randomBytes } from "crypto";
|
|
7
|
+
import { GuidanceService } from "./GuidanceService.js";
|
|
8
|
+
import { isGitRepo, getCurrentCommit } from "../utils/git.js";
|
|
9
|
+
import { eventService } from "./EventService.js";
|
|
10
|
+
/**
|
|
11
|
+
* 结论最大长度(字符数)
|
|
12
|
+
* 超过此长度时拒绝写入,要求 AI 创建 memo 记录详情后提交精简结论
|
|
13
|
+
*/
|
|
14
|
+
const CONCLUSION_MAX_LENGTH = 600;
|
|
15
|
+
/**
|
|
16
|
+
* 执行节点状态转换规则表
|
|
17
|
+
*/
|
|
18
|
+
const EXECUTION_TRANSITION_TABLE = {
|
|
19
|
+
pending: { start: "implementing" },
|
|
20
|
+
implementing: { submit: "validating", complete: "completed", fail: "failed" },
|
|
21
|
+
validating: { complete: "completed", fail: "failed" },
|
|
22
|
+
failed: { retry: "implementing" },
|
|
23
|
+
completed: { reopen: "implementing" },
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* 规划节点状态转换规则表
|
|
27
|
+
*/
|
|
28
|
+
const PLANNING_TRANSITION_TABLE = {
|
|
29
|
+
pending: { start: "planning" },
|
|
30
|
+
planning: { complete: "completed", cancel: "cancelled" },
|
|
31
|
+
monitoring: { complete: "completed", cancel: "cancelled" },
|
|
32
|
+
completed: { reopen: "planning" },
|
|
33
|
+
cancelled: { reopen: "planning" },
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* 需要 conclusion 的动作
|
|
37
|
+
*/
|
|
38
|
+
const CONCLUSION_REQUIRED_ACTIONS = ["complete", "fail", "cancel"];
|
|
39
|
+
/**
|
|
40
|
+
* 状态服务
|
|
41
|
+
* 处理节点状态转换
|
|
42
|
+
*/
|
|
43
|
+
export class StateService {
|
|
44
|
+
json;
|
|
45
|
+
md;
|
|
46
|
+
fs;
|
|
47
|
+
/**
|
|
48
|
+
* Token 存储(内存中)
|
|
49
|
+
* key: token, value: PendingConfirmation
|
|
50
|
+
*/
|
|
51
|
+
pendingConfirmations = new Map();
|
|
52
|
+
/**
|
|
53
|
+
* Token 有效期(毫秒)
|
|
54
|
+
*/
|
|
55
|
+
TOKEN_VALIDITY_MS = 30 * 60 * 1000; // 30 分钟
|
|
56
|
+
/**
|
|
57
|
+
* 引导服务
|
|
58
|
+
*/
|
|
59
|
+
guidanceService;
|
|
60
|
+
constructor(json, md, fs) {
|
|
61
|
+
this.json = json;
|
|
62
|
+
this.md = md;
|
|
63
|
+
this.fs = fs;
|
|
64
|
+
this.guidanceService = new GuidanceService();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 根据 workspaceId 获取 projectRoot 和 wsDirName
|
|
68
|
+
*/
|
|
69
|
+
async resolveProjectRoot(workspaceId) {
|
|
70
|
+
const index = await this.json.readIndex();
|
|
71
|
+
const entry = index.workspaces.find(ws => ws.id === workspaceId);
|
|
72
|
+
if (!entry) {
|
|
73
|
+
throw new ZeroError("WORKSPACE_NOT_FOUND", `工作区 "${workspaceId}" 不存在`);
|
|
74
|
+
}
|
|
75
|
+
if (entry.status === "error" && entry.errorInfo) {
|
|
76
|
+
throw new ZeroError("WORKSPACE_ERROR", `工作区 "${workspaceId}" 处于错误状态: ${entry.errorInfo.message}`);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
projectRoot: entry.projectRoot,
|
|
80
|
+
wsDirName: entry.dirName || entry.id // 向后兼容
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 执行状态转换
|
|
85
|
+
*/
|
|
86
|
+
async transition(params) {
|
|
87
|
+
const { workspaceId, nodeId, action, reason, conclusion, confirmation, conclusionsHash } = params;
|
|
88
|
+
// 1. 如果提供了 confirmation,验证 token
|
|
89
|
+
if (confirmation) {
|
|
90
|
+
const validatedConfirmation = this.validateConfirmation(confirmation.token);
|
|
91
|
+
if (!validatedConfirmation) {
|
|
92
|
+
throw new ZeroError("INVALID_CONFIRMATION_TOKEN", "提供的 confirmation token 无效或已过期。请使用最新的 token 重新提交。");
|
|
93
|
+
}
|
|
94
|
+
// 验证 token 对应的工作区和节点是否匹配
|
|
95
|
+
if (validatedConfirmation.workspaceId !== workspaceId || validatedConfirmation.nodeId !== nodeId) {
|
|
96
|
+
throw new ZeroError("CONFIRMATION_MISMATCH", `confirmation token 对应的节点不匹配。Token 属于 ${validatedConfirmation.workspaceId}/${validatedConfirmation.nodeId},但请求的是 ${workspaceId}/${nodeId}`);
|
|
97
|
+
}
|
|
98
|
+
// Token 验证通过,记录用户输入到日志
|
|
99
|
+
const { projectRoot: pRoot, wsDirName: wDir } = await this.resolveProjectRoot(workspaceId);
|
|
100
|
+
const timestamp = formatShort(now());
|
|
101
|
+
await this.md.appendTypedLogEntry(pRoot, wDir, {
|
|
102
|
+
timestamp,
|
|
103
|
+
operator: "Human",
|
|
104
|
+
event: `用户确认: ${confirmation.userInput}`,
|
|
105
|
+
}, nodeId); // 这里 nodeId 需要后续改为 nodeDirName
|
|
106
|
+
}
|
|
107
|
+
// 2. 获取 projectRoot 和 wsDirName
|
|
108
|
+
const { projectRoot, wsDirName } = await this.resolveProjectRoot(workspaceId);
|
|
109
|
+
// 3. 验证节点存在并获取当前状态
|
|
110
|
+
const graph = await this.json.readGraph(projectRoot, wsDirName);
|
|
111
|
+
if (!graph.nodes[nodeId]) {
|
|
112
|
+
throw new ZeroError("NODE_NOT_FOUND", `节点 "${nodeId}" 不存在`);
|
|
113
|
+
}
|
|
114
|
+
const nodeMeta = graph.nodes[nodeId];
|
|
115
|
+
const nodeType = nodeMeta.type;
|
|
116
|
+
const currentStatus = nodeMeta.status;
|
|
117
|
+
// 4. 根据节点类型验证转换合法性
|
|
118
|
+
const newStatus = this.validateTransition(nodeType, currentStatus, action);
|
|
119
|
+
if (!newStatus) {
|
|
120
|
+
const suggestion = this.getTransitionSuggestion(nodeType, currentStatus, action);
|
|
121
|
+
throw new ZeroError("INVALID_TRANSITION", `非法状态转换: ${currentStatus} --[${action}]--> ? (不允许)。${suggestion}`);
|
|
122
|
+
}
|
|
123
|
+
// 5. 验证 conclusion 要求
|
|
124
|
+
if (CONCLUSION_REQUIRED_ACTIONS.includes(action) && !conclusion) {
|
|
125
|
+
throw new ZeroError("CONCLUSION_REQUIRED", `${action} 动作必须提供 conclusion 参数`);
|
|
126
|
+
}
|
|
127
|
+
// 5.0.1 验证 conclusion 格式(必须在状态更新前验证,否则会导致状态已更新但结论写入失败)
|
|
128
|
+
if (conclusion) {
|
|
129
|
+
validateMultilineContent(conclusion, "结论");
|
|
130
|
+
}
|
|
131
|
+
// 5.1 根节点 start 时检查信息收集节点状态(不阻止,但记录用于后续提醒)
|
|
132
|
+
let infoCollectionWarning = null;
|
|
133
|
+
if (nodeId === "root" && action === "start") {
|
|
134
|
+
const infoCollectionCheck = this.checkInfoCollectionNode(graph.nodes, nodeMeta.children);
|
|
135
|
+
if (!infoCollectionCheck.passed) {
|
|
136
|
+
infoCollectionWarning = infoCollectionCheck.message;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// 5.2 规划节点 complete 时验证子节点状态(所有子节点必须处于终态)
|
|
140
|
+
if (nodeType === "planning" && action === "complete") {
|
|
141
|
+
const terminalStatuses = new Set(["completed", "failed", "cancelled"]);
|
|
142
|
+
const childStatuses = nodeMeta.children.map(cid => graph.nodes[cid]?.status);
|
|
143
|
+
const hasIncompleteChildren = childStatuses.some(s => s && !terminalStatuses.has(s));
|
|
144
|
+
if (hasIncompleteChildren) {
|
|
145
|
+
throw new ZeroError("INCOMPLETE_CHILDREN", "规划节点有未完成的子节点,无法直接完成。请先完成所有子节点(completed/failed/cancelled)。");
|
|
146
|
+
}
|
|
147
|
+
// 5.2.1 规划节点 complete 时验证 conclusionsHash(有子节点时必填)
|
|
148
|
+
if (nodeMeta.children.length > 0) {
|
|
149
|
+
if (!conclusionsHash) {
|
|
150
|
+
throw new ZeroError("CONCLUSIONS_HASH_REQUIRED", "规划节点 complete 需要提供 conclusionsHash,请先调用 context_get 获取最新上下文。");
|
|
151
|
+
}
|
|
152
|
+
// 计算当前 conclusionsHash 并验证
|
|
153
|
+
const childConclusions = nodeMeta.children
|
|
154
|
+
.map(cid => {
|
|
155
|
+
const childMeta = graph.nodes[cid];
|
|
156
|
+
return childMeta ? { nodeId: cid, conclusion: childMeta.conclusion || "" } : null;
|
|
157
|
+
})
|
|
158
|
+
.filter((c) => c !== null && !!c.conclusion);
|
|
159
|
+
const currentHash = computeConclusionsHash(childConclusions);
|
|
160
|
+
if (conclusionsHash !== currentHash) {
|
|
161
|
+
throw new ZeroError("CONCLUSIONS_HASH_MISMATCH", "conclusionsHash 不匹配,子节点结论可能已变化。请重新调用 context_get 获取最新上下文后再完成。");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// 4.3 派发模式下的权限检查
|
|
166
|
+
const config = await this.json.readWorkspaceConfig(projectRoot, wsDirName);
|
|
167
|
+
if (config.dispatch?.enabled && nodeType === "execution") {
|
|
168
|
+
// 4.3.1 派发执行中(executing)时阻止状态变更(除非是系统内部调用)
|
|
169
|
+
if (nodeMeta.dispatch?.status === "executing") {
|
|
170
|
+
throw new ZeroError("DISPATCH_IN_PROGRESS", `节点 ${nodeId} 正在派发执行中,请等待 subagent 完成后由系统更新状态。如需强制终止,请使用 dispatch_cleanup。`);
|
|
171
|
+
}
|
|
172
|
+
// 4.3.2 执行节点 start 时,检查是否需要通过 dispatch_node 派发
|
|
173
|
+
// 派发子节点有 dispatch 字段且 status 为 pending,允许直接 start
|
|
174
|
+
const isDispatchChild = nodeMeta.role === "dispatch_exec" || nodeMeta.role === "dispatch_spec" || nodeMeta.role === "dispatch_quality";
|
|
175
|
+
const hasDispatchPending = nodeMeta.dispatch?.status === "pending";
|
|
176
|
+
if (action === "start" && !isDispatchChild && !hasDispatchPending) {
|
|
177
|
+
// 检查上级节点角色,info_collection/info_summary 节点的子节点允许直接 start
|
|
178
|
+
const parent = nodeMeta.parentId ? graph.nodes[nodeMeta.parentId] : null;
|
|
179
|
+
const isParentInfoNode = parent?.role === "info_collection" || parent?.role === "info_summary";
|
|
180
|
+
if (!isParentInfoNode) {
|
|
181
|
+
throw new ZeroError("DISPATCH_REQUIRED", `派发模式已启用,执行节点必须通过 dispatch_node 派发执行,不能直接 start。请先调用 dispatch_node(workspaceId="${workspaceId}", nodeId="${nodeId}")。`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// 4.3.3 派发子节点 start 时,设置 dispatch.startMarker 和 status
|
|
185
|
+
if (action === "start" && (isDispatchChild || hasDispatchPending)) {
|
|
186
|
+
const useGit = config.dispatch.useGit ?? false;
|
|
187
|
+
const startMarker = useGit ? await getCurrentCommit(projectRoot) : Date.now().toString();
|
|
188
|
+
nodeMeta.dispatch = {
|
|
189
|
+
...nodeMeta.dispatch,
|
|
190
|
+
startMarker,
|
|
191
|
+
status: "executing",
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
// 4.3.4 派发子节点 retry 时,重置 dispatch.status 为 pending
|
|
195
|
+
if (action === "retry" && nodeMeta.dispatch?.status === "failed") {
|
|
196
|
+
nodeMeta.dispatch = {
|
|
197
|
+
...nodeMeta.dispatch,
|
|
198
|
+
status: "pending",
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// 4.3.5 派发子节点 reopen 时,重置 dispatch.status 为 pending
|
|
202
|
+
if (action === "reopen" && nodeMeta.dispatch?.status === "passed") {
|
|
203
|
+
nodeMeta.dispatch = {
|
|
204
|
+
...nodeMeta.dispatch,
|
|
205
|
+
status: "pending",
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// 4.4 执行节点 start 时检查同级节点并发(一次只能有一个执行中的节点)
|
|
210
|
+
if (nodeType === "execution" && action === "start" && nodeMeta.parentId) {
|
|
211
|
+
const parentNode = graph.nodes[nodeMeta.parentId];
|
|
212
|
+
if (parentNode) {
|
|
213
|
+
const activeStatuses = new Set(["implementing", "validating"]);
|
|
214
|
+
const activeSiblings = parentNode.children
|
|
215
|
+
.filter(sibId => sibId !== nodeId)
|
|
216
|
+
.map(sibId => graph.nodes[sibId])
|
|
217
|
+
.filter(sib => sib && sib.type === "execution" && activeStatuses.has(sib.status));
|
|
218
|
+
if (activeSiblings.length > 0) {
|
|
219
|
+
const activeIds = activeSiblings.map(s => `${s.id}(${s.status})`).join(", ");
|
|
220
|
+
throw new ZeroError("CONCURRENT_EXECUTION", `同级节点 ${activeIds} 正在执行中。请先完成或暂停当前任务,再开始新任务。遵循"一次一个节点"原则。`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const currentTime = now();
|
|
225
|
+
const timestamp = formatShort(currentTime);
|
|
226
|
+
// 6. 验证 conclusion 格式(在任何写入操作之前验证,避免数据不一致)
|
|
227
|
+
if (conclusion) {
|
|
228
|
+
validateMultilineContent(conclusion, "结论");
|
|
229
|
+
// 6.1 验证 conclusion 长度(仅 complete 时)
|
|
230
|
+
if (action === "complete" && conclusion.length > CONCLUSION_MAX_LENGTH) {
|
|
231
|
+
throw new ZeroError("CONCLUSION_TOO_LONG", `结论过长(${conclusion.length}字符),限制${CONCLUSION_MAX_LENGTH}字符。\n\n` +
|
|
232
|
+
`请按以下步骤处理:\n\n` +
|
|
233
|
+
`1. 创建 memo 记录完整信息:\n` +
|
|
234
|
+
` memo_create({ workspaceId, title, summary, content, tags })\n\n` +
|
|
235
|
+
`2. 提交精简结论完成节点:\n` +
|
|
236
|
+
` node_transition({ action: "complete", conclusion: "精简结论" })\n\n` +
|
|
237
|
+
`3. 添加 memo 引用(可在 WebUI 跳转查看):\n` +
|
|
238
|
+
` node_reference({ nodeId, targetIdOrPath: "memo:memoId", action: "add" })`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// 7. 更新 graph.json 中的节点状态和 conclusion
|
|
242
|
+
nodeMeta.status = newStatus;
|
|
243
|
+
nodeMeta.updatedAt = currentTime;
|
|
244
|
+
if (conclusion) {
|
|
245
|
+
// 将字面量 \\n 转换为真正的换行符(MCP 工具调用时可能传入转义字符串)
|
|
246
|
+
nodeMeta.conclusion = conclusion.replace(/\\n/g, "\n");
|
|
247
|
+
}
|
|
248
|
+
// 7.0.0.1 节点 complete 时,清除自己的 stale 标记
|
|
249
|
+
if (action === "complete" && nodeMeta.conclusionStale) {
|
|
250
|
+
nodeMeta.conclusionStale = undefined;
|
|
251
|
+
}
|
|
252
|
+
// 7.0.1 reopen 时,如果节点有 conclusion,设置 stale
|
|
253
|
+
if (action === "reopen" && nodeMeta.conclusion) {
|
|
254
|
+
nodeMeta.conclusionStale = true;
|
|
255
|
+
}
|
|
256
|
+
// 7.1 父节点状态级联(仅执行节点 start/reopen 时)
|
|
257
|
+
const cascadeMessages = [];
|
|
258
|
+
if (nodeType === "execution" && (action === "start" || action === "reopen")) {
|
|
259
|
+
// 当执行节点开始时,确保父规划节点处于 monitoring 状态
|
|
260
|
+
let parentId = nodeMeta.parentId;
|
|
261
|
+
while (parentId && graph.nodes[parentId]) {
|
|
262
|
+
const parent = graph.nodes[parentId];
|
|
263
|
+
if (parent.type === "planning") {
|
|
264
|
+
if (parent.status === "pending" || parent.status === "planning") {
|
|
265
|
+
parent.status = "monitoring";
|
|
266
|
+
parent.updatedAt = currentTime;
|
|
267
|
+
cascadeMessages.push(`父节点 ${parentId}: ${parent.status} → monitoring`);
|
|
268
|
+
const parentDirName = parent.dirName || parentId; // 向后兼容
|
|
269
|
+
await this.md.updateNodeStatus(projectRoot, wsDirName, parentDirName, "monitoring");
|
|
270
|
+
}
|
|
271
|
+
else if (parent.status === "completed" && action === "reopen") {
|
|
272
|
+
parent.status = "monitoring";
|
|
273
|
+
parent.updatedAt = currentTime;
|
|
274
|
+
cascadeMessages.push(`父节点 ${parentId}: completed → monitoring (级联重开)`);
|
|
275
|
+
const parentDirName = parent.dirName || parentId;
|
|
276
|
+
await this.md.updateNodeStatus(projectRoot, wsDirName, parentDirName, "monitoring");
|
|
277
|
+
}
|
|
278
|
+
else if (parent.status === "cancelled" && action === "reopen") {
|
|
279
|
+
parent.status = "monitoring";
|
|
280
|
+
parent.updatedAt = currentTime;
|
|
281
|
+
cascadeMessages.push(`父节点 ${parentId}: cancelled → monitoring (级联重开)`);
|
|
282
|
+
const parentDirName = parent.dirName || parentId;
|
|
283
|
+
await this.md.updateNodeStatus(projectRoot, wsDirName, parentDirName, "monitoring");
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
parentId = parent.parentId;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// 7.2 自动切换焦点到当前节点(start/reopen 时)
|
|
290
|
+
if (action === "start" || action === "reopen") {
|
|
291
|
+
graph.currentFocus = nodeId;
|
|
292
|
+
}
|
|
293
|
+
await this.json.writeGraph(projectRoot, wsDirName, graph);
|
|
294
|
+
// 8. 更新 Info.md 的 frontmatter 和结论部分
|
|
295
|
+
const nodeDirName = nodeMeta.dirName || nodeId; // 向后兼容
|
|
296
|
+
await this.md.updateNodeStatus(projectRoot, wsDirName, nodeDirName, newStatus);
|
|
297
|
+
if (conclusion) {
|
|
298
|
+
await this.md.updateConclusion(projectRoot, wsDirName, nodeDirName, conclusion);
|
|
299
|
+
}
|
|
300
|
+
// 9. 追加日志记录
|
|
301
|
+
const logEvent = this.buildLogEvent(nodeType, action, currentStatus, newStatus, reason);
|
|
302
|
+
await this.md.appendTypedLogEntry(projectRoot, wsDirName, {
|
|
303
|
+
timestamp,
|
|
304
|
+
operator: "AI",
|
|
305
|
+
event: logEvent,
|
|
306
|
+
}, nodeDirName);
|
|
307
|
+
// 10. 如果是 complete/cancel,清空 Problem.md
|
|
308
|
+
if (action === "complete" || action === "cancel") {
|
|
309
|
+
await this.md.writeProblem(projectRoot, wsDirName, {
|
|
310
|
+
currentProblem: "(暂无)",
|
|
311
|
+
nextStep: "(暂无)",
|
|
312
|
+
}, nodeDirName);
|
|
313
|
+
}
|
|
314
|
+
// 10.0.1 子节点 complete 时,设置父节点 stale
|
|
315
|
+
if (action === "complete" && nodeMeta.parentId) {
|
|
316
|
+
const parentMeta = graph.nodes[nodeMeta.parentId];
|
|
317
|
+
const terminalStatuses = new Set(["completed", "cancelled"]);
|
|
318
|
+
if (parentMeta && parentMeta.conclusion && !terminalStatuses.has(parentMeta.status)) {
|
|
319
|
+
parentMeta.conclusionStale = true;
|
|
320
|
+
await this.json.writeGraph(projectRoot, wsDirName, graph);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// 10.1 信息收集节点 complete 时自动归档规则和文档
|
|
324
|
+
let archiveResult = null;
|
|
325
|
+
if (nodeMeta.role === "info_collection" && action === "complete" && conclusion) {
|
|
326
|
+
archiveResult = await this.archiveInfoCollection(projectRoot, wsDirName, conclusion);
|
|
327
|
+
}
|
|
328
|
+
// 10.2 complete 时获取节点的文档引用(用于提醒更新)
|
|
329
|
+
let nodeDocRefs = [];
|
|
330
|
+
if (action === "complete") {
|
|
331
|
+
const nodeInfo = await this.md.readNodeInfoFull(projectRoot, wsDirName, nodeDirName);
|
|
332
|
+
nodeDocRefs = nodeInfo.docs;
|
|
333
|
+
}
|
|
334
|
+
// 11. 更新工作区配置的 updatedAt(使用前面已读取的 config)
|
|
335
|
+
config.updatedAt = currentTime;
|
|
336
|
+
await this.json.writeWorkspaceConfig(projectRoot, wsDirName, config);
|
|
337
|
+
// 12. 同步更新索引中的 updatedAt(确保 workspace_list 返回正确时间)
|
|
338
|
+
const index = await this.json.readIndex();
|
|
339
|
+
const wsEntry = index.workspaces.find(ws => ws.id === workspaceId);
|
|
340
|
+
if (wsEntry) {
|
|
341
|
+
wsEntry.updatedAt = currentTime;
|
|
342
|
+
await this.json.writeIndex(index);
|
|
343
|
+
}
|
|
344
|
+
// 13. 返回结果
|
|
345
|
+
const result = {
|
|
346
|
+
success: true,
|
|
347
|
+
previousStatus: currentStatus,
|
|
348
|
+
currentStatus: newStatus,
|
|
349
|
+
conclusion: conclusion ?? null,
|
|
350
|
+
};
|
|
351
|
+
// 如果有级联更新,加入返回结果
|
|
352
|
+
if (cascadeMessages.length > 0) {
|
|
353
|
+
result.cascadeUpdates = cascadeMessages;
|
|
354
|
+
}
|
|
355
|
+
// 13. 添加工作流提示(根据节点类型)
|
|
356
|
+
result.hint = this.generateHint(nodeType, action, nodeMeta, graph, archiveResult, infoCollectionWarning, nodeDocRefs);
|
|
357
|
+
// 13.1 生成引导内容
|
|
358
|
+
const guidanceContext = {
|
|
359
|
+
toolName: "node_transition",
|
|
360
|
+
toolInput: { action },
|
|
361
|
+
nodeType,
|
|
362
|
+
nodeStatus: newStatus,
|
|
363
|
+
nodeRole: nodeMeta.role,
|
|
364
|
+
hasChildren: nodeMeta.children.length > 0,
|
|
365
|
+
extra: {
|
|
366
|
+
isRootNode: nodeId === "root",
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
const guidance = this.guidanceService.generateFromContext(guidanceContext, 0);
|
|
370
|
+
result.guidance = guidance.content;
|
|
371
|
+
// 13.2 如果派发模式启用,追加派发相关提示
|
|
372
|
+
if (config.dispatch?.enabled && nodeType === "execution") {
|
|
373
|
+
if (action === "start") {
|
|
374
|
+
result.hint += "\n\n🚀 **派发模式已启用**:请使用 dispatch_node 将任务派发给 subagent 执行,而非直接执行。派发后根据返回的 actionRequired 调用 Task tool。";
|
|
375
|
+
}
|
|
376
|
+
// 注:测试节点附属化后,测试节点作为兄弟节点存在,由父管理节点统一调度
|
|
377
|
+
}
|
|
378
|
+
// 14. 添加 actionRequired(执行节点完成且有文档引用时)
|
|
379
|
+
if (nodeType === "execution" && action === "complete" && nodeDocRefs && nodeDocRefs.length > 0) {
|
|
380
|
+
// 生成 confirmation token
|
|
381
|
+
const confirmation = this.createPendingConfirmation(workspaceId, nodeId, "check_docs", {
|
|
382
|
+
docs: nodeDocRefs,
|
|
383
|
+
});
|
|
384
|
+
result.actionRequired = {
|
|
385
|
+
type: "check_docs",
|
|
386
|
+
message: "执行任务已完成,请向用户确认引用的文档是否需要同步更新。",
|
|
387
|
+
data: {
|
|
388
|
+
docs: nodeDocRefs,
|
|
389
|
+
},
|
|
390
|
+
confirmationToken: confirmation.token,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
// 15. 添加 actionRequired(reopen 时,如果有子节点则需要先查看结构)
|
|
394
|
+
if (action === "reopen" && nodeMeta.children.length > 0) {
|
|
395
|
+
// 收集子节点概览信息
|
|
396
|
+
const childrenOverview = nodeMeta.children.map(childId => {
|
|
397
|
+
const child = graph.nodes[childId];
|
|
398
|
+
return {
|
|
399
|
+
nodeId: childId,
|
|
400
|
+
status: child?.status || "unknown",
|
|
401
|
+
type: child?.type || "unknown",
|
|
402
|
+
};
|
|
403
|
+
});
|
|
404
|
+
// 生成 confirmation token
|
|
405
|
+
const confirmation = this.createPendingConfirmation(workspaceId, nodeId, "review_structure", {
|
|
406
|
+
childCount: nodeMeta.children.length,
|
|
407
|
+
childrenOverview,
|
|
408
|
+
});
|
|
409
|
+
result.actionRequired = {
|
|
410
|
+
type: "review_structure",
|
|
411
|
+
message: "节点已重开,存在已有子节点。请先调用 node_list 查看现有结构,评估是否需要调整现有节点而非创建新节点。",
|
|
412
|
+
data: {
|
|
413
|
+
childCount: nodeMeta.children.length,
|
|
414
|
+
childrenOverview,
|
|
415
|
+
},
|
|
416
|
+
confirmationToken: confirmation.token,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
// 15. 添加 actionRequired(ask_dispatch - 首次执行节点启动时询问是否启用派发)
|
|
420
|
+
if (nodeType === "execution" && action === "start" && !result.actionRequired) {
|
|
421
|
+
// 检查是否应该询问派发:
|
|
422
|
+
// 1. 工作区尚未启用派发
|
|
423
|
+
// 2. 这是第一个开始执行的非信息收集节点
|
|
424
|
+
if (!config.dispatch?.enabled) {
|
|
425
|
+
const isFirstExecution = this.isFirstNonInfoCollectionExecution(graph.nodes, nodeMeta, nodeId);
|
|
426
|
+
if (isFirstExecution) {
|
|
427
|
+
try {
|
|
428
|
+
const isGit = await isGitRepo(projectRoot);
|
|
429
|
+
// 无论是否 git 仓库都询问,但提示不同模式
|
|
430
|
+
if (isGit) {
|
|
431
|
+
result.actionRequired = {
|
|
432
|
+
type: "ask_dispatch",
|
|
433
|
+
message: "检测到项目是 Git 仓库,是否启用派发模式?\n\n派发模式允许将执行节点任务交给独立的 subagent 执行。提供两种模式:\n- **无 Git 模式**(默认,推荐):仅更新元数据,不影响代码,安全\n- **Git 模式**(实验功能):自动创建分支、提交、回滚,支持失败自动恢复,但有一定风险",
|
|
434
|
+
data: {
|
|
435
|
+
projectRoot,
|
|
436
|
+
workspaceId,
|
|
437
|
+
isGitRepo: true,
|
|
438
|
+
},
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
result.actionRequired = {
|
|
443
|
+
type: "ask_dispatch",
|
|
444
|
+
message: "是否启用派发模式(无 Git 模式)?\n\n派发模式允许将执行节点任务交给独立的 subagent 执行。\n当前项目不是 git 仓库,将使用无 Git 模式(仅更新元数据,不影响代码)。",
|
|
445
|
+
data: {
|
|
446
|
+
projectRoot,
|
|
447
|
+
workspaceId,
|
|
448
|
+
isGitRepo: false,
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
// 检测失败,按非 git 仓库处理
|
|
455
|
+
result.actionRequired = {
|
|
456
|
+
type: "ask_dispatch",
|
|
457
|
+
message: "是否启用派发模式(无 Git 模式)?\n\n派发模式允许将执行节点任务交给独立的 subagent 执行。\n将使用无 Git 模式(仅更新元数据,不影响代码)。",
|
|
458
|
+
data: {
|
|
459
|
+
projectRoot,
|
|
460
|
+
workspaceId,
|
|
461
|
+
isGitRepo: false,
|
|
462
|
+
},
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// 推送 SSE 事件通知前端
|
|
469
|
+
eventService.emitNodeUpdate(workspaceId, nodeId);
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* 检查是否是第一个非信息收集的执行节点启动
|
|
474
|
+
*/
|
|
475
|
+
isFirstNonInfoCollectionExecution(nodes, currentNode, currentNodeId) {
|
|
476
|
+
// 如果当前节点有 info_collection 角色,不算
|
|
477
|
+
if (currentNode.role === "info_collection") {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
// 检查是否有其他非信息收集的执行节点已经启动过
|
|
481
|
+
for (const [nodeId, node] of Object.entries(nodes)) {
|
|
482
|
+
if (nodeId === currentNodeId)
|
|
483
|
+
continue;
|
|
484
|
+
if (node.type !== "execution")
|
|
485
|
+
continue;
|
|
486
|
+
if (node.role === "info_collection")
|
|
487
|
+
continue;
|
|
488
|
+
// 如果有其他执行节点不是 pending 状态,说明已经开始过
|
|
489
|
+
if (node.status !== "pending") {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* 生成工作流提示
|
|
497
|
+
*/
|
|
498
|
+
generateHint(nodeType, action, nodeMeta, graph, archiveResult, infoCollectionWarning, nodeDocRefs) {
|
|
499
|
+
// 根节点 start 时如果缺少信息收集节点,优先显示强提醒
|
|
500
|
+
if (infoCollectionWarning) {
|
|
501
|
+
return `⚠️ **重要提醒**\n\n${infoCollectionWarning}\n\n` +
|
|
502
|
+
"这是开始任务前的必要步骤,信息收集的结果会自动归档到工作区规则和文档中,帮助后续任务更好地执行。";
|
|
503
|
+
}
|
|
504
|
+
// 信息收集节点完成时,显示归档结果
|
|
505
|
+
if (nodeMeta.role === "info_collection" && action === "complete" && archiveResult) {
|
|
506
|
+
const parts = ["💡 信息收集已完成,已自动归档到工作区:"];
|
|
507
|
+
if (archiveResult.rules.length > 0) {
|
|
508
|
+
parts.push(`- 新增 ${archiveResult.rules.length} 条规则`);
|
|
509
|
+
}
|
|
510
|
+
if (archiveResult.docs.length > 0) {
|
|
511
|
+
parts.push(`- 新增 ${archiveResult.docs.length} 个文档引用`);
|
|
512
|
+
}
|
|
513
|
+
if (archiveResult.rules.length === 0 && archiveResult.docs.length === 0) {
|
|
514
|
+
parts[0] = "💡 信息收集已完成。未在 conclusion 中发现需要归档的规则或文档。";
|
|
515
|
+
}
|
|
516
|
+
parts.push("建议返回根节点继续规划执行任务。");
|
|
517
|
+
return parts.join("\n");
|
|
518
|
+
}
|
|
519
|
+
if (nodeType === "execution") {
|
|
520
|
+
// 执行节点提示
|
|
521
|
+
if (action === "start" || action === "retry") {
|
|
522
|
+
return "💡 执行任务已开始。请使用 log_append 记录执行过程,完成后调用 complete,如遇问题调用 fail。";
|
|
523
|
+
}
|
|
524
|
+
else if (action === "reopen") {
|
|
525
|
+
const oldConclusion = nodeMeta.conclusion;
|
|
526
|
+
if (oldConclusion) {
|
|
527
|
+
return `💡 执行任务已重开。旧结论:「${oldConclusion}」\n如需修改需求描述,请使用 node_update({ requirement: "新需求" })。\n完成时请将新工作与旧结论合并。`;
|
|
528
|
+
}
|
|
529
|
+
return "💡 执行任务已重开。如需修改需求描述,请使用 node_update({ requirement: \"新需求\" })。";
|
|
530
|
+
}
|
|
531
|
+
else if (action === "complete") {
|
|
532
|
+
const parentId = nodeMeta.parentId;
|
|
533
|
+
let hint = "💡 执行任务已完成。";
|
|
534
|
+
if (parentId && graph.nodes[parentId]) {
|
|
535
|
+
hint = `💡 执行任务已完成。建议切换到父规划节点 ${parentId} 检查是否还有其他任务。`;
|
|
536
|
+
}
|
|
537
|
+
// 如果有文档引用,追加更新提醒
|
|
538
|
+
if (nodeDocRefs && nodeDocRefs.length > 0) {
|
|
539
|
+
hint += `\n\n📄 您在此任务中引用了 ${nodeDocRefs.length} 个文档,请确认是否需要同步更新:`;
|
|
540
|
+
for (const doc of nodeDocRefs) {
|
|
541
|
+
hint += `\n- ${doc.path}${doc.description ? ` (${doc.description})` : ""}`;
|
|
542
|
+
}
|
|
543
|
+
hint += `\n\n💡 提示:如果文档没有元文件(frontmatter),建议添加 YAML 格式的元数据头(以 --- 开头和结尾),便于文档管理和检索。`;
|
|
544
|
+
}
|
|
545
|
+
return hint;
|
|
546
|
+
}
|
|
547
|
+
else if (action === "fail") {
|
|
548
|
+
return "💡 执行任务已标记失败。请切换到父规划节点,根据失败原因决定:重新派发、修改需求后重试、或取消任务。";
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
// 规划节点提示
|
|
553
|
+
if (action === "start") {
|
|
554
|
+
return "💡 进入规划状态。请分析需求,使用 node_create 创建执行节点或子规划节点。";
|
|
555
|
+
}
|
|
556
|
+
else if (action === "reopen") {
|
|
557
|
+
const oldConclusion = nodeMeta.conclusion;
|
|
558
|
+
if (oldConclusion) {
|
|
559
|
+
return `💡 规划节点已重开。旧结论:「${oldConclusion}」\n如需修改需求描述,请使用 node_update({ requirement: "新需求" })。\n完成时请将新工作与旧结论合并,确保结论完整反映所有已完成的工作。`;
|
|
560
|
+
}
|
|
561
|
+
return "💡 规划节点已重开。如需修改需求描述,请使用 node_update({ requirement: \"新需求\" })。";
|
|
562
|
+
}
|
|
563
|
+
else if (action === "complete") {
|
|
564
|
+
const parentId = nodeMeta.parentId;
|
|
565
|
+
let hint = "💡 规划节点已完成。工作区任务完成!";
|
|
566
|
+
if (parentId && graph.nodes[parentId]) {
|
|
567
|
+
hint = `💡 规划节点已完成汇总。建议切换到父节点 ${parentId} 继续。`;
|
|
568
|
+
}
|
|
569
|
+
// 如果有文档引用,追加更新提醒
|
|
570
|
+
if (nodeDocRefs && nodeDocRefs.length > 0) {
|
|
571
|
+
hint += `\n\n📄 您在此任务中引用了 ${nodeDocRefs.length} 个文档,请确认是否需要同步更新:`;
|
|
572
|
+
for (const doc of nodeDocRefs) {
|
|
573
|
+
hint += `\n- ${doc.path}${doc.description ? ` (${doc.description})` : ""}`;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return hint;
|
|
577
|
+
}
|
|
578
|
+
else if (action === "cancel") {
|
|
579
|
+
return "💡 规划节点已取消。如需重新规划请使用 reopen。";
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return "";
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* 验证状态转换合法性
|
|
586
|
+
*/
|
|
587
|
+
validateTransition(nodeType, currentStatus, action) {
|
|
588
|
+
if (nodeType === "execution") {
|
|
589
|
+
return EXECUTION_TRANSITION_TABLE[currentStatus]?.[action] ?? null;
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
return PLANNING_TRANSITION_TABLE[currentStatus]?.[action] ?? null;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* 生成状态转换错误的修复建议
|
|
597
|
+
*/
|
|
598
|
+
getTransitionSuggestion(nodeType, currentStatus, attemptedAction) {
|
|
599
|
+
if (nodeType === "execution") {
|
|
600
|
+
// 执行节点错误建议
|
|
601
|
+
if (currentStatus === "pending" && attemptedAction === "complete") {
|
|
602
|
+
return "请先调用 node_transition(action=\"start\") 开始执行,再进行 complete";
|
|
603
|
+
}
|
|
604
|
+
if (currentStatus === "pending" && attemptedAction === "submit") {
|
|
605
|
+
return "请先调用 node_transition(action=\"start\") 开始执行";
|
|
606
|
+
}
|
|
607
|
+
if (currentStatus === "completed" && attemptedAction === "complete") {
|
|
608
|
+
return "节点已完成,无需重复完成";
|
|
609
|
+
}
|
|
610
|
+
if (currentStatus === "completed" && attemptedAction === "start") {
|
|
611
|
+
return "节点已完成,如需重新执行请使用 node_transition(action=\"reopen\")";
|
|
612
|
+
}
|
|
613
|
+
if (currentStatus === "failed" && attemptedAction === "complete") {
|
|
614
|
+
return "失败的节点无法直接完成,请先 retry 后重新执行";
|
|
615
|
+
}
|
|
616
|
+
if (currentStatus === "implementing" && attemptedAction === "start") {
|
|
617
|
+
return "节点已在执行中,无需重复 start";
|
|
618
|
+
}
|
|
619
|
+
if (attemptedAction === "cancel") {
|
|
620
|
+
return "执行节点不支持 cancel 动作,如需放弃请使用 fail";
|
|
621
|
+
}
|
|
622
|
+
const availableActions = Object.keys(EXECUTION_TRANSITION_TABLE[currentStatus] || {});
|
|
623
|
+
if (availableActions.length > 0) {
|
|
624
|
+
return `执行节点当前状态 ${currentStatus} 可用的动作: ${availableActions.join(", ")}`;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
// 规划节点错误建议
|
|
629
|
+
if (currentStatus === "pending" && attemptedAction === "complete") {
|
|
630
|
+
return "请先调用 node_transition(action=\"start\") 进入规划状态";
|
|
631
|
+
}
|
|
632
|
+
if (currentStatus === "monitoring" && attemptedAction === "start") {
|
|
633
|
+
return "节点已在监控子节点执行,如需重新规划请先 cancel 后 reopen";
|
|
634
|
+
}
|
|
635
|
+
if (currentStatus === "completed" && attemptedAction === "start") {
|
|
636
|
+
return "节点已完成,如需重新规划请使用 node_transition(action=\"reopen\")";
|
|
637
|
+
}
|
|
638
|
+
if (currentStatus === "planning" && attemptedAction === "start") {
|
|
639
|
+
return "节点已在规划中,无需重复 start";
|
|
640
|
+
}
|
|
641
|
+
if (attemptedAction === "fail") {
|
|
642
|
+
return "规划节点不支持 fail 动作,如需放弃请使用 cancel";
|
|
643
|
+
}
|
|
644
|
+
if (attemptedAction === "submit") {
|
|
645
|
+
return "规划节点不支持 submit 动作";
|
|
646
|
+
}
|
|
647
|
+
if (attemptedAction === "retry") {
|
|
648
|
+
return "规划节点不支持 retry 动作,如需重新开始请使用 reopen";
|
|
649
|
+
}
|
|
650
|
+
const availableActions = Object.keys(PLANNING_TRANSITION_TABLE[currentStatus] || {});
|
|
651
|
+
if (availableActions.length > 0) {
|
|
652
|
+
return `规划节点当前状态 ${currentStatus} 可用的动作: ${availableActions.join(", ")}`;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return `当前状态 ${currentStatus} 无可用转换`;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* 构建日志事件描述
|
|
659
|
+
*/
|
|
660
|
+
buildLogEvent(nodeType, action, from, to, reason) {
|
|
661
|
+
const executionDescriptions = {
|
|
662
|
+
start: "开始执行",
|
|
663
|
+
submit: "提交验证",
|
|
664
|
+
complete: "完成执行",
|
|
665
|
+
fail: "执行失败",
|
|
666
|
+
retry: "重新执行",
|
|
667
|
+
reopen: "重新激活",
|
|
668
|
+
};
|
|
669
|
+
const planningDescriptions = {
|
|
670
|
+
start: "开始规划",
|
|
671
|
+
complete: "完成汇总",
|
|
672
|
+
cancel: "取消规划",
|
|
673
|
+
reopen: "重新规划",
|
|
674
|
+
};
|
|
675
|
+
const descriptions = nodeType === "execution" ? executionDescriptions : planningDescriptions;
|
|
676
|
+
let event = `${descriptions[action] || action}: ${from} → ${to}`;
|
|
677
|
+
if (reason) {
|
|
678
|
+
event += ` (${reason})`;
|
|
679
|
+
}
|
|
680
|
+
return event;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* 检查根节点是否有已完成的信息收集节点
|
|
684
|
+
*/
|
|
685
|
+
checkInfoCollectionNode(nodes, childIds) {
|
|
686
|
+
// 查找信息收集节点
|
|
687
|
+
const infoCollectionNodes = childIds
|
|
688
|
+
.map(id => nodes[id])
|
|
689
|
+
.filter(node => node?.role === "info_collection");
|
|
690
|
+
if (infoCollectionNodes.length === 0) {
|
|
691
|
+
return {
|
|
692
|
+
passed: false,
|
|
693
|
+
message: "根节点 start 前必须先创建信息收集节点(role: 'info_collection')。\n" +
|
|
694
|
+
"请先使用 node_create 创建一个 planning 类型、role 为 'info_collection' 的节点," +
|
|
695
|
+
"用于收集项目信息、环境配置、相关文档等,收集完成后信息会自动归档到工作区规则和文档中。",
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
// 检查是否有已完成的信息收集节点
|
|
699
|
+
const completedInfoCollection = infoCollectionNodes.find(node => node.status === "completed");
|
|
700
|
+
if (!completedInfoCollection) {
|
|
701
|
+
const infoNode = infoCollectionNodes[0];
|
|
702
|
+
return {
|
|
703
|
+
passed: false,
|
|
704
|
+
message: `信息收集节点 "${infoNode.id}" 尚未完成(当前状态: ${infoNode.status})。\n` +
|
|
705
|
+
"请先完成信息收集,系统会自动将收集的规则和文档归档到工作区,然后再开始根节点规划。",
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
return { passed: true, message: "" };
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* 归档信息收集节点的 conclusion 到工作区
|
|
712
|
+
* 解析 ## 规则 和 ## 文档 部分
|
|
713
|
+
*/
|
|
714
|
+
async archiveInfoCollection(projectRoot, wsDirName, conclusion) {
|
|
715
|
+
const result = { rules: [], docs: [] };
|
|
716
|
+
// 解析 ## 规则 部分
|
|
717
|
+
const rulesMatch = conclusion.match(/##\s*规则\s*\n([\s\S]*?)(?=\n##|\n*$)/i);
|
|
718
|
+
if (rulesMatch) {
|
|
719
|
+
const rulesSection = rulesMatch[1];
|
|
720
|
+
// 解析列表项(支持 - 或 * 开头)
|
|
721
|
+
const ruleLines = rulesSection.split("\n")
|
|
722
|
+
.map(line => line.trim())
|
|
723
|
+
.filter(line => line.match(/^[-*]\s+/))
|
|
724
|
+
.map(line => line.replace(/^[-*]\s+/, "").trim())
|
|
725
|
+
.filter(line => line.length > 0);
|
|
726
|
+
result.rules = ruleLines;
|
|
727
|
+
}
|
|
728
|
+
// 解析 ## 文档 部分
|
|
729
|
+
const docsMatch = conclusion.match(/##\s*文档\s*\n([\s\S]*?)(?=\n##|\n*$)/i);
|
|
730
|
+
if (docsMatch) {
|
|
731
|
+
const docsSection = docsMatch[1];
|
|
732
|
+
// 解析列表项,格式:- path: description 或 - path(description 可选)
|
|
733
|
+
const docLines = docsSection.split("\n")
|
|
734
|
+
.map(line => line.trim())
|
|
735
|
+
.filter(line => line.match(/^[-*]\s+/))
|
|
736
|
+
.map(line => line.replace(/^[-*]\s+/, "").trim())
|
|
737
|
+
.filter(line => line.length > 0);
|
|
738
|
+
for (const line of docLines) {
|
|
739
|
+
// 尝试匹配 "path: description" 格式
|
|
740
|
+
const colonMatch = line.match(/^([^:]+):\s*(.+)$/);
|
|
741
|
+
if (colonMatch) {
|
|
742
|
+
result.docs.push({
|
|
743
|
+
path: colonMatch[1].trim(),
|
|
744
|
+
description: colonMatch[2].trim(),
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
// 没有描述,只有路径
|
|
749
|
+
result.docs.push({
|
|
750
|
+
path: line,
|
|
751
|
+
description: "",
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
// 如果有解析到内容,追加到工作区
|
|
757
|
+
if (result.rules.length > 0 || result.docs.length > 0) {
|
|
758
|
+
const workspaceMdData = await this.md.readWorkspaceMd(projectRoot, wsDirName);
|
|
759
|
+
// 追加规则(去重)
|
|
760
|
+
const existingRules = new Set(workspaceMdData.rules);
|
|
761
|
+
for (const rule of result.rules) {
|
|
762
|
+
if (!existingRules.has(rule)) {
|
|
763
|
+
workspaceMdData.rules.push(rule);
|
|
764
|
+
existingRules.add(rule);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
// 追加文档(去重,按路径判断)
|
|
768
|
+
const existingDocPaths = new Set(workspaceMdData.docs.map(d => d.path));
|
|
769
|
+
for (const doc of result.docs) {
|
|
770
|
+
if (!existingDocPaths.has(doc.path)) {
|
|
771
|
+
workspaceMdData.docs.push(doc);
|
|
772
|
+
existingDocPaths.add(doc.path);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
// 写回工作区
|
|
776
|
+
await this.md.writeWorkspaceMd(projectRoot, wsDirName, workspaceMdData);
|
|
777
|
+
}
|
|
778
|
+
return result;
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* 创建待确认 Token
|
|
782
|
+
* @param workspaceId 工作区 ID
|
|
783
|
+
* @param nodeId 节点 ID
|
|
784
|
+
* @param actionType 动作类型(如 "check_docs")
|
|
785
|
+
* @param metadata 附加元数据
|
|
786
|
+
* @returns PendingConfirmation 对象
|
|
787
|
+
*/
|
|
788
|
+
createPendingConfirmation(workspaceId, nodeId, actionType, metadata) {
|
|
789
|
+
// 生成随机 token(32 字节,hex 编码为 64 字符)
|
|
790
|
+
const token = randomBytes(32).toString("hex");
|
|
791
|
+
const currentTime = Date.now();
|
|
792
|
+
const confirmation = {
|
|
793
|
+
token,
|
|
794
|
+
workspaceId,
|
|
795
|
+
nodeId,
|
|
796
|
+
actionType,
|
|
797
|
+
createdAt: currentTime,
|
|
798
|
+
expiresAt: currentTime + this.TOKEN_VALIDITY_MS,
|
|
799
|
+
metadata,
|
|
800
|
+
};
|
|
801
|
+
// 存储到内存
|
|
802
|
+
this.pendingConfirmations.set(token, confirmation);
|
|
803
|
+
// 清理过期 token(顺便执行)
|
|
804
|
+
this.clearExpiredTokens();
|
|
805
|
+
return confirmation;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* 验证 Token
|
|
809
|
+
* @param token 待验证的 token
|
|
810
|
+
* @returns 如果 token 有效,返回 PendingConfirmation 对象;否则返回 null
|
|
811
|
+
*/
|
|
812
|
+
validateConfirmation(token) {
|
|
813
|
+
const confirmation = this.pendingConfirmations.get(token);
|
|
814
|
+
if (!confirmation) {
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
// 检查是否过期
|
|
818
|
+
const currentTime = Date.now();
|
|
819
|
+
if (currentTime > confirmation.expiresAt) {
|
|
820
|
+
// 过期,删除并返回 null
|
|
821
|
+
this.pendingConfirmations.delete(token);
|
|
822
|
+
return null;
|
|
823
|
+
}
|
|
824
|
+
// 验证成功,删除 token(一次性使用)
|
|
825
|
+
this.pendingConfirmations.delete(token);
|
|
826
|
+
return confirmation;
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* 清理过期 Token
|
|
830
|
+
* 遍历所有 token,删除已过期的
|
|
831
|
+
*/
|
|
832
|
+
clearExpiredTokens() {
|
|
833
|
+
const currentTime = Date.now();
|
|
834
|
+
const toDelete = [];
|
|
835
|
+
// 使用 Array.from 转换迭代器以兼容 ES5
|
|
836
|
+
for (const [token, confirmation] of Array.from(this.pendingConfirmations.entries())) {
|
|
837
|
+
if (currentTime > confirmation.expiresAt) {
|
|
838
|
+
toDelete.push(token);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
for (const token of toDelete) {
|
|
842
|
+
this.pendingConfirmations.delete(token);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
//# sourceMappingURL=StateService.js.map
|