@guanlin4924/devflow-pi 0.1.93 → 0.1.95
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/README.md +10 -10
- package/node_modules/@playwright/test/LICENSE +202 -0
- package/node_modules/@playwright/test/NOTICE +5 -0
- package/node_modules/@playwright/test/README.md +318 -0
- package/node_modules/@playwright/test/cli.js +19 -0
- package/node_modules/@playwright/test/index.d.ts +18 -0
- package/node_modules/@playwright/test/index.js +17 -0
- package/node_modules/@playwright/test/index.mjs +18 -0
- package/node_modules/@playwright/test/package.json +35 -0
- package/node_modules/@playwright/test/reporter.d.ts +17 -0
- package/node_modules/@playwright/test/reporter.js +17 -0
- package/node_modules/@playwright/test/reporter.mjs +17 -0
- package/node_modules/playwright/LICENSE +202 -0
- package/node_modules/playwright/NOTICE +5 -0
- package/node_modules/playwright/README.md +318 -0
- package/node_modules/playwright/ThirdPartyNotices.txt +14 -0
- package/node_modules/playwright/cli.js +19 -0
- package/node_modules/playwright/index.d.ts +17 -0
- package/node_modules/playwright/index.js +17 -0
- package/node_modules/playwright/index.mjs +18 -0
- package/node_modules/playwright/jsx-runtime.js +42 -0
- package/node_modules/playwright/jsx-runtime.mjs +21 -0
- package/node_modules/playwright/lib/agents/agentParser.js +89 -0
- package/node_modules/playwright/lib/agents/copilot-setup-steps.yml +34 -0
- package/node_modules/playwright/lib/agents/generateAgents.js +395 -0
- package/node_modules/playwright/lib/agents/playwright-test-coverage.prompt.md +31 -0
- package/node_modules/playwright/lib/agents/playwright-test-generate.prompt.md +8 -0
- package/node_modules/playwright/lib/agents/playwright-test-generator.agent.md +88 -0
- package/node_modules/playwright/lib/agents/playwright-test-heal.prompt.md +6 -0
- package/node_modules/playwright/lib/agents/playwright-test-healer.agent.md +56 -0
- package/node_modules/playwright/lib/agents/playwright-test-plan.prompt.md +9 -0
- package/node_modules/playwright/lib/agents/playwright-test-planner.agent.md +74 -0
- package/node_modules/playwright/lib/cli/reportActions.js +78 -0
- package/node_modules/playwright/lib/cli/testActions.js +214 -0
- package/node_modules/playwright/lib/common/index.js +2923 -0
- package/node_modules/playwright/lib/common/index.js.txt +34 -0
- package/node_modules/playwright/lib/errorContext.js +129 -0
- package/node_modules/playwright/lib/globals.js +58 -0
- package/node_modules/playwright/lib/index.js +815 -0
- package/node_modules/playwright/lib/isomorphic.js +260 -0
- package/node_modules/playwright/lib/isomorphic.js.txt +9 -0
- package/node_modules/playwright/lib/loader/loaderProcessEntry.js +34 -0
- package/node_modules/playwright/lib/loader/loaderProcessEntry.js.txt +9 -0
- package/node_modules/playwright/lib/matchers/expect.js +13490 -0
- package/node_modules/playwright/lib/matchers/expect.js.LICENSE +640 -0
- package/node_modules/playwright/lib/matchers/expect.js.txt +67 -0
- package/node_modules/playwright/lib/mcp/test/browserBackend.js +135 -0
- package/node_modules/playwright/lib/mcp/test/generatorTools.js +122 -0
- package/node_modules/playwright/lib/mcp/test/plannerTools.js +150 -0
- package/node_modules/playwright/lib/mcp/test/seed.js +82 -0
- package/node_modules/playwright/lib/mcp/test/streams.js +44 -0
- package/node_modules/playwright/lib/mcp/test/testBackend.js +99 -0
- package/node_modules/playwright/lib/mcp/test/testContext.js +306 -0
- package/node_modules/playwright/lib/mcp/test/testTool.js +30 -0
- package/node_modules/playwright/lib/mcp/test/testTools.js +98 -0
- package/node_modules/playwright/lib/package.js +47 -0
- package/node_modules/playwright/lib/program.js +241 -0
- package/node_modules/playwright/lib/runner/index.js +8527 -0
- package/node_modules/playwright/lib/runner/index.js.txt +61 -0
- package/node_modules/playwright/lib/transform/babelBundle.js +72274 -0
- package/node_modules/playwright/lib/transform/babelBundle.js.LICENSE +2384 -0
- package/node_modules/playwright/lib/transform/babelBundle.js.txt +325 -0
- package/node_modules/playwright/lib/transform/esmLoader.js +8054 -0
- package/node_modules/playwright/lib/transform/esmLoader.js.LICENSE +355 -0
- package/node_modules/playwright/lib/transform/esmLoader.js.txt +60 -0
- package/node_modules/playwright/lib/util.js +373 -0
- package/node_modules/playwright/lib/worker/workerProcessEntry.js +1861 -0
- package/node_modules/playwright/lib/worker/workerProcessEntry.js.txt +19 -0
- package/node_modules/playwright/package.json +55 -0
- package/node_modules/playwright/test.d.ts +18 -0
- package/node_modules/playwright/test.js +24 -0
- package/node_modules/playwright/test.mjs +35 -0
- package/node_modules/playwright/types/test.d.ts +11074 -0
- package/node_modules/playwright/types/testReporter.d.ts +950 -0
- package/node_modules/playwright-core/LICENSE +202 -0
- package/node_modules/playwright-core/NOTICE +5 -0
- package/node_modules/playwright-core/README.md +3 -0
- package/node_modules/playwright-core/ThirdPartyNotices.txt +13 -0
- package/node_modules/playwright-core/bin/install_media_pack.ps1 +5 -0
- package/node_modules/playwright-core/bin/install_webkit_wsl.ps1 +33 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +42 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +13 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 +24 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +42 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +12 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 +24 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 +23 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 +23 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 +24 -0
- package/node_modules/playwright-core/browsers.json +52 -0
- package/node_modules/playwright-core/cli.js +21 -0
- package/node_modules/playwright-core/index.d.ts +17 -0
- package/node_modules/playwright-core/index.js +17 -0
- package/node_modules/playwright-core/index.mjs +28 -0
- package/node_modules/playwright-core/lib/bootstrap.js +88 -0
- package/node_modules/playwright-core/lib/coreBundle.js +75813 -0
- package/node_modules/playwright-core/lib/entry/cliDaemon.js +5 -0
- package/node_modules/playwright-core/lib/entry/dashboardApp.js +3 -0
- package/node_modules/playwright-core/lib/entry/mcp.js +10 -0
- package/node_modules/playwright-core/lib/entry/oopBrowserDownload.js +3 -0
- package/node_modules/playwright-core/lib/package.js +50 -0
- package/node_modules/playwright-core/lib/server/chromium/appIcon.png +0 -0
- package/node_modules/playwright-core/lib/server/electron/loader.js +116 -0
- package/node_modules/playwright-core/lib/serverRegistry.js +1939 -0
- package/node_modules/playwright-core/lib/serverRegistry.js.LICENSE +65 -0
- package/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js +141 -0
- package/node_modules/playwright-core/lib/tools/cli-client/cli.js +6 -0
- package/node_modules/playwright-core/lib/tools/cli-client/help.json +721 -0
- package/node_modules/playwright-core/lib/tools/cli-client/minimist.js +128 -0
- package/node_modules/playwright-core/lib/tools/cli-client/output.js +351 -0
- package/node_modules/playwright-core/lib/tools/cli-client/program.js +418 -0
- package/node_modules/playwright-core/lib/tools/cli-client/registry.js +180 -0
- package/node_modules/playwright-core/lib/tools/cli-client/session.js +259 -0
- package/node_modules/playwright-core/lib/tools/dashboard/appIcon.png +0 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/SKILL.md +425 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/element-attributes.md +23 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/playwright-tests.md +39 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/request-mocking.md +87 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/running-code.md +241 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/session-management.md +225 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/storage-state.md +275 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/test-generation.md +433 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/tracing.md +139 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/video-recording.md +143 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/SKILL.md +136 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/migration.md +89 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/react.md +69 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/typing.md +173 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/vue.md +77 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-trace/SKILL.md +174 -0
- package/node_modules/playwright-core/lib/tools/utils/extension.js +112 -0
- package/node_modules/playwright-core/lib/tools/utils/socketConnection.js +108 -0
- package/node_modules/playwright-core/lib/utilsBundle.js +85357 -0
- package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +1890 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule-CQ8RZGBm.js +32 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-AwgwcYie.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-hRvO4yU2.js +12 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/index.html +29 -0
- package/node_modules/playwright-core/lib/vite/dashboard/playwright-logo.svg +24 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/index.html +16 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/report.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-CVQWJZAA.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-BZpYJZ-P.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-hrlLqLtq.js +129 -0
- package/node_modules/playwright-core/lib/vite/recorder/index.html +29 -0
- package/node_modules/playwright-core/lib/vite/recorder/playwright-logo.svg +9 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-BbkfBe3n.js +32 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-Ds6CBOo0.js +182 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-DywYcAf8.js +7 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.Bqk9acqE.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B4cLoZK3.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.html +44 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/manifest.webmanifest +16 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/playwright-logo.svg +9 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +10 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +4 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CU5KtEkS.js +5 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CyMfwkXJ.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +18 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
- package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
- package/node_modules/playwright-core/lib/xdg-open +1267 -0
- package/node_modules/playwright-core/package.json +34 -0
- package/node_modules/playwright-core/types/protocol.d.ts +24645 -0
- package/node_modules/playwright-core/types/structs.d.ts +52 -0
- package/node_modules/playwright-core/types/types.d.ts +26632 -0
- package/package.json +12 -2
- package/plugins/devflow-core/extensions/devflow-core.ts +518 -40
- package/plugins/devflow-core/runtime/hooks/context_sync_gate.py +3 -3
- package/plugins/devflow-core/runtime/hooks/hook_common.py +334 -11
- package/plugins/devflow-core/runtime/hooks/index_gate.py +29 -16
- package/plugins/devflow-core/runtime/hooks/start.py +13 -13
- package/plugins/devflow-core/skills/wireframe-proposal/SKILL.md +2 -2
- package/plugins/devflow-core/skills/wireframe-validate/SKILL.md +4 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +19 -19
- package/plugins/devflow-core/skills/workflow-orchestrator/agents/openai.yaml +2 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/acceptance_test_runner.py +412 -76
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/advance_state.rb +60 -9
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/api_test_matrix.py +2 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/approval_pins.py +433 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/approval_store.py +137 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_assessment.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_brief.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_feasibility.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_invalidation.rb +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_commit.rb +4 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_validate.py +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_source_resolver.rb +4 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/checker_adapter.rb +7 -7
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/control_io.rb +51 -7
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/cross_track_consistency_check.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/dsl_coverage.py +87 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/dsl_v1.py +318 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/environment_profile.py +134 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +7 -6
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_surface.py +138 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_runtime.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_state.rb +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_workflow.rb +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/inspect_state.rb +13 -13
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/machine_evidence.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/manage_lessons.rb +7 -7
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_recover.rb +2 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_run.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/phase_ownership.rb +6 -6
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/playwright_adapter.py +515 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/repair_control_plane.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_coverage.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/review_preflight.py +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/review_run.py +18 -18
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/runtime_context_collector.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/session_rollover.rb +4 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/static_runtime_context_collector.rb +2 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/sync_artifacts.rb +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/tc_hash.py +132 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/tdd_evidence.py +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/test_command_policy.py +104 -10
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/test_task_binding_check.py +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/update_triage_route.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +19 -6
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_commit.rb +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_validate.py +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/acceptance-remediation.yaml +12 -12
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +201 -222
- package/plugins/devflow-core/skills/workflow-pipeline/SKILL.md +6 -4
- package/plugins/devflow-core/skills/workflow-pipeline/references/pipeline-flow.md +68 -140
- package/plugins/devflow-core/skills/workflow-project-context/SKILL.md +14 -14
- package/plugins/devflow-core/skills/workflow-project-context/assets/agents-md-template.md +7 -7
- package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-backend-spec-template.md +4 -4
- package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-frontend-spec-template.md +4 -4
- package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-navigation-template.md +1 -1
- package/plugins/devflow-core/skills/workflow-project-context/assets/context-domain-index-template.md +4 -4
- package/plugins/devflow-core/skills/workflow-project-context/assets/context-spec-index-template.md +10 -10
- package/plugins/devflow-core/skills/workflow-project-context/assets/execution-contract-template.md +2 -2
- package/plugins/devflow-core/skills/workflow-project-context/assets/navigation-index-template.md +18 -18
- package/plugins/devflow-core/skills/workflow-project-context/assets/project-initialization-template.md +32 -32
- package/plugins/devflow-core/skills/workflow-project-context/assets/rules-index-template.md +30 -30
- package/plugins/devflow-core/skills/workflow-project-context/assets/state-template.md +7 -7
- package/plugins/devflow-core/skills/workflow-project-context/references/navigation-guide.md +37 -37
- package/plugins/devflow-core/skills/workflow-project-context/scripts/validate_initialization.py +16 -16
- package/plugins/devflow-core/skills/workflow-project-context/scripts/validate_initialization_database.py +1 -1
- package/plugins/devflow-design/skills/workflow-api-call-guide/SKILL.md +4 -4
- package/plugins/devflow-design/skills/workflow-api-contract/SKILL.md +8 -8
- package/plugins/devflow-design/skills/workflow-api-supplement/SKILL.md +5 -5
- package/plugins/devflow-design/skills/workflow-architecture-feasibility/SKILL.md +1 -1
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/SKILL.md +39 -39
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/assets/backend-spec-template.md +10 -10
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/affected-module-exploration.md +2 -2
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/code-explore-guide.md +1 -1
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/project-rule-contract.md +20 -20
- package/plugins/devflow-design/skills/workflow-design-rule-pack/SKILL.md +8 -8
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/index.md +3 -3
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/accessibility.md +2 -2
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/component-library.md +2 -2
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/design-systems.md +1 -1
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/responsive-grid.md +2 -2
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/stack-guidelines.md +6 -6
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/ux-patterns.md +2 -2
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tokens/project-tokens.md +1 -1
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/SKILL.md +12 -12
- package/plugins/devflow-design/skills/workflow-design-rule-pack/scripts/install.py +2 -2
- package/plugins/devflow-design/skills/workflow-feature-breakdown/SKILL.md +30 -30
- package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/output-template.md +2 -2
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/SKILL.md +41 -41
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/assets/frontend-spec-template.md +15 -15
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/affected-module-exploration.md +2 -2
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/project-rule-contract.md +25 -25
- package/plugins/devflow-design/skills/workflow-integration-verify/SKILL.md +13 -13
- package/plugins/devflow-design/skills/workflow-ui-spec/SKILL.md +23 -23
- package/plugins/devflow-design/skills/workflow-ui-spec/assets/ui-spec-template.md +3 -3
- package/plugins/devflow-implementation/agents/implementation-worker.md +2 -2
- package/plugins/devflow-implementation/skills/workflow-bug-fix/SKILL.md +9 -9
- package/plugins/devflow-implementation/skills/workflow-bug-fix/references/lesson-format.md +2 -2
- package/plugins/devflow-implementation/skills/workflow-bug-fix/references/root-cause-ladder.md +1 -1
- package/plugins/devflow-implementation/skills/workflow-change-plan/SKILL.md +17 -17
- package/plugins/devflow-implementation/skills/workflow-change-plan/references/code-conventions.md +1 -1
- package/plugins/devflow-implementation/skills/workflow-change-plan/references/output-template.md +3 -3
- package/plugins/devflow-implementation/skills/workflow-change-plan/references/project-rule-contract.md +16 -16
- package/plugins/devflow-implementation/skills/workflow-execute-phase/SKILL.md +38 -38
- package/plugins/devflow-implementation/skills/workflow-execute-phase/references/execute-phase.md +4 -2
- package/plugins/devflow-intel/agents/project-scout.md +1 -1
- package/plugins/devflow-intel/extensions/devflow-intel.ts +20 -19
- package/plugins/devflow-intel/skills/code-index-query/SKILL.md +6 -6
- package/plugins/devflow-intel/skills/workflow-code-index/SKILL.md +12 -12
- package/plugins/devflow-intel/skills/workflow-code-index/references/discovery.md +2 -2
- package/plugins/devflow-intel/skills/workflow-code-index/references/query-guide.md +1 -1
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/code_index.py +31 -12
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/discovery.py +2 -2
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/scanner.py +4 -1
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/project_binding_check.py +4 -4
- package/plugins/devflow-intel/skills/workflow-context-sync/SKILL.md +14 -14
- package/plugins/devflow-intel/skills/workflow-context-sync/scripts/context_sync_cursor.py +2 -2
- package/plugins/devflow-intel/skills/workflow-update-navigation/SKILL.md +26 -26
- package/plugins/devflow-intel/skills/workflow-update-navigation/references/incremental-update-guide.md +2 -2
- package/plugins/devflow-quality/runtime/playwright/devflow-test-dsl-v1.schema.json +872 -0
- package/plugins/devflow-quality/runtime/playwright/playwright.config.mjs +46 -0
- package/plugins/devflow-quality/runtime/playwright/reporter.mjs +311 -0
- package/plugins/devflow-quality/runtime/playwright/runner.spec.mjs +1308 -0
- package/plugins/devflow-quality/runtime/playwright/runtime-manifest.json +11 -0
- package/plugins/devflow-quality/skills/code-review/SKILL.md +12 -12
- package/plugins/devflow-quality/skills/code-review/references/spec-checklist.md +1 -1
- package/plugins/devflow-quality/skills/workflow-acceptance-test-design/SKILL.md +11 -8
- package/plugins/devflow-quality/skills/workflow-gen-test-guide/SKILL.md +6 -6
- package/plugins/devflow-quality/skills/workflow-gen-test-guide/legacy/SKILL.md +14 -14
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/SKILL.md +82 -69
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/api-mainflow-rules.md +20 -15
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/codebase-discovery.md +12 -10
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/intermediate-format.md +115 -192
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/mainflow-selection.md +27 -23
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/project-dsl-contract.md +78 -153
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/ui-mainflow-rules.md +36 -83
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/compile_dsl.py +497 -556
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/discover_codebases.py +2 -2
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/export_legacy_testagent.py +647 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/normalize_case_payloads.py +10 -3
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/validate_case_payloads.py +165 -275
- package/plugins/devflow-quality/skills/workflow-human-acceptance/SKILL.md +61 -0
- package/plugins/devflow-quality/skills/workflow-release-closeout/SKILL.md +18 -18
- package/plugins/devflow-quality/skills/workflow-run-acceptance-tests/SKILL.md +20 -14
- package/plugins/devflow-requirements/skills/requirement-feasibility-review/SKILL.md +1 -1
- package/plugins/devflow-requirements/skills/workflow-demand-clarify/SKILL.md +10 -10
- package/plugins/devflow-requirements/skills/workflow-domain-modeling/SKILL.md +25 -25
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/SKILL.md +49 -49
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/assets/baseline-template.md +22 -22
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/antipatterns.md +3 -3
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/challenge-review-guide.md +3 -3
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/project-context-guide.md +11 -11
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/questioning.md +1 -1
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/requirement-review-checklist.md +4 -4
- package/plugins/devflow-requirements/skills/workflow-requirement-triage/SKILL.md +16 -16
- package/plugins/devflow-requirements/skills/workflow-wayfinder/SKILL.md +6 -6
- package/src/approval-store.ts +340 -0
- package/src/cli.ts +15 -0
- package/src/constants.ts +1 -1
- package/src/doctor.ts +259 -2
- package/src/initialization-store.ts +51 -3
- package/src/paths.ts +12 -1
- package/src/project-context.ts +60 -15
- package/src/project-documents.ts +61 -61
- package/src/project-initializer.ts +9 -8
- package/src/project-layout.ts +217 -0
- package/src/runtime.ts +3 -3
- package/templates/commands/start.md +4 -4
- package/templates/omp/AGENTS.md +1 -1
- package/templates/omp/hook-config.json +13 -7
- package/templates/omp/index-policy.json +8 -8
|
@@ -16,6 +16,21 @@ import { runProjectInitialization } from "../../../src/project-initializer.ts";
|
|
|
16
16
|
import { projectControlDatabasePath } from "../../../src/paths.ts";
|
|
17
17
|
import { generateProjectDocuments } from "../../../src/project-documents.ts";
|
|
18
18
|
import { exportInitializationSnapshot, importLegacyInitialization, switchInitializationAuthority } from "../../../src/project-migration.ts";
|
|
19
|
+
import { migrateLegacyProjectLayout } from "../../../src/project-layout.ts";
|
|
20
|
+
import {
|
|
21
|
+
APPROVAL_KINDS,
|
|
22
|
+
confirmApproval,
|
|
23
|
+
databaseAuthority,
|
|
24
|
+
listApprovals,
|
|
25
|
+
listProposals,
|
|
26
|
+
proposalIsExpired,
|
|
27
|
+
proposeApprovals,
|
|
28
|
+
rejectProposal,
|
|
29
|
+
type ApprovalKind,
|
|
30
|
+
type CurrentPins,
|
|
31
|
+
type HumanResultStatus,
|
|
32
|
+
type ProposalRecord,
|
|
33
|
+
} from "../../../src/approval-store.ts";
|
|
19
34
|
|
|
20
35
|
|
|
21
36
|
export type DevflowIntent = "idle" | "explain-state" | "project-init" | "requirement-draft" | "workflow-execute";
|
|
@@ -209,6 +224,28 @@ const STATE_MUTATION_TOOLS = new Set([
|
|
|
209
224
|
"devflow_run_acceptance_tests",
|
|
210
225
|
]);
|
|
211
226
|
|
|
227
|
+
/** Maps devflow_run_acceptance_tests parameters to acceptance_test_runner.py argv; gc_runs is a dry-run unless write=true. */
|
|
228
|
+
export function acceptanceRunnerArgs(params: {
|
|
229
|
+
action: "validate" | "validate_human" | "run" | "validate_results" | "gc_runs";
|
|
230
|
+
feature: string;
|
|
231
|
+
write?: boolean;
|
|
232
|
+
retentionDays?: number;
|
|
233
|
+
unreferencedTtlDays?: number;
|
|
234
|
+
}): string[] {
|
|
235
|
+
const scriptAction = params.action === "validate_results"
|
|
236
|
+
? "validate-results"
|
|
237
|
+
: params.action === "validate_human" ? "validate-human"
|
|
238
|
+
: params.action === "gc_runs" ? "gc-runs" : params.action;
|
|
239
|
+
const args = [scriptAction, "--feature", params.feature, "--json"];
|
|
240
|
+
if (params.action === "run") args.push("--write");
|
|
241
|
+
if (params.action === "gc_runs") {
|
|
242
|
+
if (params.write === true) args.push("--write");
|
|
243
|
+
if (params.retentionDays !== undefined) args.push("--retention-days", String(params.retentionDays));
|
|
244
|
+
if (params.unreferencedTtlDays !== undefined) args.push("--unreferenced-ttl-days", String(params.unreferencedTtlDays));
|
|
245
|
+
}
|
|
246
|
+
return args;
|
|
247
|
+
}
|
|
248
|
+
|
|
212
249
|
export function initialControlState(): SessionControlState {
|
|
213
250
|
return { intent: "idle", phase: "idle", label: "对话(不自动执行)" };
|
|
214
251
|
}
|
|
@@ -351,7 +388,7 @@ export function discoverExistingProjectInitialization(cwd: string, options: { al
|
|
|
351
388
|
}
|
|
352
389
|
}
|
|
353
390
|
if (!options.allowLegacyImport) return null;
|
|
354
|
-
const sources = [".planning/PROJECT.md", ".planning/navigation/INDEX.md"];
|
|
391
|
+
const sources = [".devflow/planning/PROJECT.md", ".devflow/planning/navigation/INDEX.md"];
|
|
355
392
|
const records: Array<ExistingProjectInitialization> = [];
|
|
356
393
|
for (const sourcePath of sources) {
|
|
357
394
|
const absolute = path.join(cwd, sourcePath);
|
|
@@ -531,13 +568,13 @@ type DoctorContext = {
|
|
|
531
568
|
};
|
|
532
569
|
|
|
533
570
|
const CONTROL_PATHS = [
|
|
534
|
-
".planning/STATE.md",
|
|
535
|
-
".planning/.next-intent",
|
|
536
|
-
".planning/.devflow.lock",
|
|
537
|
-
".planning/artifacts/index.yaml",
|
|
571
|
+
".devflow/planning/STATE.md",
|
|
572
|
+
".devflow/planning/.next-intent",
|
|
573
|
+
".devflow/planning/.devflow.lock",
|
|
574
|
+
".devflow/planning/artifacts/index.yaml",
|
|
538
575
|
];
|
|
539
576
|
|
|
540
|
-
const CONTROL_PREFIXES = [".planning/workflows/", ".planning/contracts/", ".planning/evidence/", ".planning/review-runs/"];
|
|
577
|
+
const CONTROL_PREFIXES = [".devflow/planning/workflows/", ".devflow/planning/contracts/", ".devflow/planning/evidence/", ".devflow/planning/review-runs/"];
|
|
541
578
|
|
|
542
579
|
const REQUIRED_EXTENSION_TOOLS = [
|
|
543
580
|
"devflow_doctor",
|
|
@@ -560,6 +597,7 @@ const REQUIRED_EXTENSION_TOOLS = [
|
|
|
560
597
|
"devflow_context_cursor",
|
|
561
598
|
"devflow_design_rule",
|
|
562
599
|
"devflow_mainflow",
|
|
600
|
+
"devflow_approval",
|
|
563
601
|
];
|
|
564
602
|
|
|
565
603
|
const REQUIRED_SKILLS = [
|
|
@@ -583,6 +621,7 @@ const REQUIRED_SKILLS = [
|
|
|
583
621
|
"workflow-frontend-tech-spec",
|
|
584
622
|
"workflow-gen-test-guide",
|
|
585
623
|
"workflow-generate-mainflow-cases",
|
|
624
|
+
"workflow-human-acceptance",
|
|
586
625
|
"workflow-integration-verify",
|
|
587
626
|
"workflow-orchestrator",
|
|
588
627
|
"workflow-pipeline",
|
|
@@ -703,16 +742,63 @@ function assertRuntimeProjectBoundary(cwd: string): void {
|
|
|
703
742
|
if (configured && realpathSync(configured) !== resolvedCwd) {
|
|
704
743
|
throw new Error(`Devflow project-root mismatch: session=${resolvedCwd}, configured=${configured}`);
|
|
705
744
|
}
|
|
706
|
-
const
|
|
745
|
+
const legacyPlanning = path.join(resolvedCwd, ".planning");
|
|
746
|
+
const legacyContext = path.join(resolvedCwd, ".context");
|
|
747
|
+
const planningMarkers = ["STATE.md", "PROJECT.md", ".devflow.lock", "navigation/INDEX.md", "code-index/INDEX.md", "workflows", "contracts", "evidence", "review-runs", "requirements", "features", "handoffs", "artifacts", "phase-ownership.yaml"];
|
|
748
|
+
const contextMarkers = ["spec/index.md", "index.md", "project/index.md", "knowledge/index.md"];
|
|
749
|
+
const legacyMarker = planningMarkers.some(relative => existsSync(path.join(legacyPlanning, relative)))
|
|
750
|
+
|| contextMarkers.some(relative => existsSync(path.join(legacyContext, relative)));
|
|
751
|
+
const activeLegacy = ["STATE.md", "workflows", "contracts", "evidence", "review-runs", "requirements", "features", "handoffs", "artifacts", "phase-ownership.yaml"]
|
|
752
|
+
.find(relative => existsSync(path.join(legacyPlanning, relative)));
|
|
753
|
+
if (legacyMarker) {
|
|
754
|
+
const manifestPath = path.join(resolvedCwd, ".devflow", "project.json");
|
|
755
|
+
let manifestValid = false;
|
|
756
|
+
try {
|
|
757
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as Record<string, unknown>;
|
|
758
|
+
manifestValid = manifest.schemaVersion === 1
|
|
759
|
+
&& typeof manifest.projectId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(manifest.projectId)
|
|
760
|
+
&& typeof manifest.name === "string" && manifest.name.trim().length > 0
|
|
761
|
+
&& typeof manifest.createdAt === "string" && Number.isFinite(Date.parse(manifest.createdAt))
|
|
762
|
+
&& [undefined, 1, 2].includes(manifest.layoutVersion as undefined | number);
|
|
763
|
+
} catch { /* reported below */ }
|
|
764
|
+
if (!manifestValid) throw new Error("DEVFLOW_LEGACY_LAYOUT_CONFIRMATION_REQUIRED: valid .devflow/project.json is required before legacy layout migration");
|
|
765
|
+
let migrationCompletePrecheck = false;
|
|
766
|
+
try {
|
|
767
|
+
const marker = JSON.parse(readFileSync(path.join(resolvedCwd, ".devflow", "layout-migration.json"), "utf8")) as Record<string, unknown>;
|
|
768
|
+
migrationCompletePrecheck = marker.schemaVersion === 1 && marker.status === "complete" && Array.isArray(marker.migrated);
|
|
769
|
+
} catch { /* reported by the normal conflict checks below */ }
|
|
770
|
+
if (activeLegacy && !migrationCompletePrecheck) {
|
|
771
|
+
throw new Error(`DEVFLOW_ACTIVE_LEGACY_LAYOUT: .planning/${activeLegacy} requires dedicated control-plane migration`);
|
|
772
|
+
}
|
|
773
|
+
const canonicalExists = existsSync(path.join(resolvedCwd, ".devflow", "planning")) || existsSync(path.join(resolvedCwd, ".devflow", "context"));
|
|
774
|
+
const migrationComplete = (() => {
|
|
775
|
+
try {
|
|
776
|
+
const marker = JSON.parse(readFileSync(path.join(resolvedCwd, ".devflow", "layout-migration.json"), "utf8")) as Record<string, unknown>;
|
|
777
|
+
return marker.schemaVersion === 1 && marker.status === "complete" && Array.isArray(marker.migrated);
|
|
778
|
+
} catch { return false; }
|
|
779
|
+
})();
|
|
780
|
+
if (manifestValid && (() => {
|
|
781
|
+
try { return (JSON.parse(readFileSync(manifestPath, "utf8")) as Record<string, unknown>).layoutVersion === 2; } catch { return false; }
|
|
782
|
+
})() && migrationComplete) {
|
|
783
|
+
const planningLegacy = planningMarkers.some(relative => existsSync(path.join(legacyPlanning, relative)));
|
|
784
|
+
const contextLegacy = contextMarkers.some(relative => existsSync(path.join(legacyContext, relative)));
|
|
785
|
+
if ((planningLegacy && !existsSync(path.join(resolvedCwd, ".devflow", "planning"))) || (contextLegacy && !existsSync(path.join(resolvedCwd, ".devflow", "context")))) {
|
|
786
|
+
throw new Error("DEVFLOW_LEGACY_REINTRODUCTION: v2 project will not backfill a missing canonical directory");
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (canonicalExists && !migrationComplete) throw new Error("DEVFLOW_LAYOUT_CONFLICT: canonical and legacy layouts coexist without a completed migration marker");
|
|
790
|
+
if (!canonicalExists) throw new Error("DEVFLOW_LAYOUT_MIGRATION_REQUIRED: run project initialization to migrate legacy read-only artifacts");
|
|
791
|
+
}
|
|
792
|
+
const planningRoot = path.join(resolvedCwd, ".devflow/planning");
|
|
707
793
|
if (existsSync(planningRoot) && lstatSync(planningRoot).isSymbolicLink()) {
|
|
708
794
|
throw new Error(`Refusing symlinked Devflow planning root: ${planningRoot}`);
|
|
709
795
|
}
|
|
710
796
|
for (const relative of [
|
|
711
|
-
".planning/STATE.md",
|
|
712
|
-
".planning/.devflow.lock",
|
|
713
|
-
".planning/workflows",
|
|
714
|
-
".planning/artifacts",
|
|
715
|
-
".planning/review-runs",
|
|
797
|
+
".devflow/planning/STATE.md",
|
|
798
|
+
".devflow/planning/.devflow.lock",
|
|
799
|
+
".devflow/planning/workflows",
|
|
800
|
+
".devflow/planning/artifacts",
|
|
801
|
+
".devflow/planning/review-runs",
|
|
716
802
|
]) {
|
|
717
803
|
const target = path.join(resolvedCwd, relative);
|
|
718
804
|
try {
|
|
@@ -744,29 +830,30 @@ function safeControlPath(cwd: string, raw: string | undefined, kind: "state" | "
|
|
|
744
830
|
const relative = normalizedProjectPath(raw, cwd);
|
|
745
831
|
const allowed =
|
|
746
832
|
kind === "state"
|
|
747
|
-
? relative === ".planning/STATE.md"
|
|
748
|
-
: Boolean(relative?.startsWith(".planning/workflows/") && relative.endsWith(".yaml"));
|
|
833
|
+
? relative === ".devflow/planning/STATE.md"
|
|
834
|
+
: Boolean(relative?.startsWith(".devflow/planning/workflows/") && relative.endsWith(".yaml"));
|
|
749
835
|
if (!relative || !allowed) throw new Error(`Invalid ${kind} control path: ${raw}`);
|
|
750
836
|
return relative;
|
|
751
837
|
}
|
|
752
838
|
|
|
753
839
|
function isCanonicalRequirementGraphPath(filePath: string): boolean {
|
|
754
|
-
return /^\.planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:governance|evidence|definitions|behavior-graph|requirement-graph|slices|acceptance|tests|traceability|graph\.meta)\.json$/u.test(filePath)
|
|
755
|
-
|| /^\.planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:context\/[^/\\]+\.json|control-events\.jsonl)$/u.test(filePath);
|
|
840
|
+
return /^\.devflow\/planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:governance|evidence|definitions|behavior-graph|requirement-graph|slices|acceptance|tests|traceability|graph\.meta)\.json$/u.test(filePath)
|
|
841
|
+
|| /^\.devflow\/planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:context\/[^/\\]+\.json|control-events\.jsonl)$/u.test(filePath);
|
|
756
842
|
}
|
|
757
843
|
|
|
758
844
|
function isCoreOwnedGovernancePath(filePath: string): boolean {
|
|
759
845
|
// 规范需求图谱只能由 Core 的 devflow_requirement_graph writer 写入;the
|
|
760
846
|
// same ownership extends to its views, machine evidence, and audit records.
|
|
761
847
|
return isCanonicalRequirementGraphPath(filePath)
|
|
762
|
-
|| /^\.planning\/
|
|
763
|
-
|| /^\.planning\/
|
|
764
|
-
||
|
|
765
|
-
||
|
|
848
|
+
|| /^\.devflow\/planning\/test-runs\/[^/\\\x00-\x1F\x7F]+\/acceptance-results(?:\.meta)?\.json$/u.test(filePath)
|
|
849
|
+
|| /^\.devflow\/planning\/evidence\/[^/\\\x00-\x1F\x7F]+\/[^/\\]+\.machine\.json$/u.test(filePath)
|
|
850
|
+
|| /^\.devflow\/planning\/contracts\/[^/\\\x00-\x1F\x7F]+\/(?:EC-[A-Fa-f0-9]+\.json|control-events\.jsonl)$/u.test(filePath)
|
|
851
|
+
|| filePath.startsWith(".devflow/planning/review-runs/")
|
|
852
|
+
|| /^\.devflow\/planning\/phases\/[^/\\\x00-\x1F\x7F]+\/REVIEW\.md$/u.test(filePath);
|
|
766
853
|
}
|
|
767
854
|
|
|
768
855
|
function planningFeatureFromPath(filePath: string): string | null {
|
|
769
|
-
const match = filePath.match(/^\.planning\/(?:requirements|features)\/([^/\\\x00-\x1F\x7F]+)(?:\/|$)/u);
|
|
856
|
+
const match = filePath.match(/^\.devflow\/planning\/(?:requirements|features)\/([^/\\\x00-\x1F\x7F]+)(?:\/|$)/u);
|
|
770
857
|
return match?.[1] ?? null;
|
|
771
858
|
}
|
|
772
859
|
|
|
@@ -1083,8 +1170,8 @@ function projectInitializationDirective(summary: string): string {
|
|
|
1083
1170
|
return [
|
|
1084
1171
|
"<devflow-project-initialization-directive>",
|
|
1085
1172
|
"用户已批准完整项目初始化。现在处于执行模式;不要再创建 Todo、不要读取或写入 xd://devflow_contract。",
|
|
1086
|
-
"执行 skill://workflow-project-context,按已确认的项目边界建立 .planning/PROJECT.md、.planning/navigation/INDEX.md、.context/spec/index.md,并按用户选择建立 full 代码索引。",
|
|
1087
|
-
"只写项目初始化所需的 .planning/ 与 .context/ 控制产物;不得改动业务源码、依赖或配置。完成后运行项目初始化校验,报告生成文件和验证结果并停止,等待用户审查。",
|
|
1173
|
+
"执行 skill://workflow-project-context,按已确认的项目边界建立 .devflow/planning/PROJECT.md、.devflow/planning/navigation/INDEX.md、.devflow/context/spec/index.md,并按用户选择建立 full 代码索引。",
|
|
1174
|
+
"只写项目初始化所需的 .devflow/planning/ 与 .devflow/context/ 控制产物;不得改动业务源码、依赖或配置。完成后运行项目初始化校验,报告生成文件和验证结果并停止,等待用户审查。",
|
|
1088
1175
|
summary,
|
|
1089
1176
|
"</devflow-project-initialization-directive>",
|
|
1090
1177
|
].join("\n");
|
|
@@ -1177,7 +1264,7 @@ export function isShellControlMutation(toolName: string, input: unknown): boolea
|
|
|
1177
1264
|
/workflow-orchestrator\/scripts\/(?:advance_state|init_state|init_workflow|sync_artifacts|requirement_graph|machine_evidence|execution_contract)\.rb[^\n]*--write/.test(
|
|
1178
1265
|
command,
|
|
1179
1266
|
);
|
|
1180
|
-
const namesControlPath = [...CONTROL_PATHS, ...CONTROL_PREFIXES].some(item => command.includes(item)) || /\.planning\/requirements\//.test(command);
|
|
1267
|
+
const namesControlPath = [...CONTROL_PATHS, ...CONTROL_PREFIXES].some(item => command.includes(item)) || /\.devflow\/planning\/requirements\//.test(command);
|
|
1181
1268
|
// `2>` and `2>>` only redirect stderr. They frequently accompany read-only
|
|
1182
1269
|
// probes of control-plane files and must not be treated as a file mutation.
|
|
1183
1270
|
// Keep bare `>`/`>>` and explicit stdout `1>`/`1>>` guarded.
|
|
@@ -1244,7 +1331,9 @@ function stepScopedMutationOwner(toolName: string, input: unknown): string | nul
|
|
|
1244
1331
|
if (normalized === "devflow_api_test_matrix") return action === "validate" ? undefined : "acceptance-test-design";
|
|
1245
1332
|
if (normalized === "devflow_tdd_evidence") return action === "inspect" ? undefined : "execute";
|
|
1246
1333
|
if (normalized === "devflow_run_acceptance_tests") return action === "run" ? "run-acceptance-tests" : undefined;
|
|
1247
|
-
|
|
1334
|
+
// Mainflow generation is an optional developer handoff inside the explicit
|
|
1335
|
+
// human-acceptance step, not a standalone workflow step.
|
|
1336
|
+
if (normalized === "devflow_mainflow") return "human-acceptance";
|
|
1248
1337
|
if (normalized === "devflow_wireframe" || normalized === "devflow_design_rule") return "ui-spec";
|
|
1249
1338
|
if (normalized.startsWith("devflow_architecture_") || normalized === "devflow_workload_constraint") {
|
|
1250
1339
|
return action === "inspect" ? undefined : "architecture-feasibility";
|
|
@@ -1301,6 +1390,198 @@ export function stepScopedMutationBlockReason(
|
|
|
1301
1390
|
return undefined;
|
|
1302
1391
|
}
|
|
1303
1392
|
|
|
1393
|
+
// ---------------------------------------------------------------------------
|
|
1394
|
+
// Core human approvals (change list P03/P04). The agent may only propose or
|
|
1395
|
+
// list; confirmation exists solely in the /devflow control panel callback,
|
|
1396
|
+
// which writes control.db directly from this extension process.
|
|
1397
|
+
// ---------------------------------------------------------------------------
|
|
1398
|
+
|
|
1399
|
+
export const APPROVAL_TOOL_ACTIONS = ["propose", "list"] as const;
|
|
1400
|
+
|
|
1401
|
+
/** Parameter schema of `devflow_approval`; there is deliberately no confirm action. */
|
|
1402
|
+
export function approvalToolParameters(z: any) {
|
|
1403
|
+
const item = z.object({
|
|
1404
|
+
subject: z.string().min(1).max(128),
|
|
1405
|
+
dsl_file: z.string().min(1).max(512).optional(),
|
|
1406
|
+
evidence_files: z.array(z.string().min(1).max(512)).max(50).optional(),
|
|
1407
|
+
note: z.string().max(4000).optional(),
|
|
1408
|
+
review_issues: z.array(z.string().min(1).max(2000)).max(100).optional(),
|
|
1409
|
+
}).strict();
|
|
1410
|
+
return z.object({
|
|
1411
|
+
action: z.enum(APPROVAL_TOOL_ACTIONS),
|
|
1412
|
+
feature: z.string().min(1).max(200).optional(),
|
|
1413
|
+
kind: z.enum(APPROVAL_KINDS).optional(),
|
|
1414
|
+
items: z.array(item).min(1).max(200).optional(),
|
|
1415
|
+
status: z.enum(["proposed", "consumed", "rejected", "expired"]).optional(),
|
|
1416
|
+
}).strict();
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
export type ApprovalProposeItem = {
|
|
1420
|
+
subject: string;
|
|
1421
|
+
dsl_file?: string;
|
|
1422
|
+
evidence_files?: string[];
|
|
1423
|
+
note?: string;
|
|
1424
|
+
review_issues?: string[];
|
|
1425
|
+
};
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* Splits one agent item into the pin-computation request (re-used verbatim for
|
|
1429
|
+
* the confirm-time CAS) and agent-supplied context that is only displayed.
|
|
1430
|
+
*/
|
|
1431
|
+
export function approvalProposeRequest(kind: ApprovalKind, item: ApprovalProposeItem): { request: Record<string, unknown>; agent: Record<string, unknown> } {
|
|
1432
|
+
if (item.dsl_file !== undefined && kind !== "execution_plan") throw new Error(`dsl_file is only valid for execution_plan proposals`);
|
|
1433
|
+
if (item.review_issues !== undefined && kind !== "execution_plan") throw new Error(`review_issues are only valid for execution_plan proposals`);
|
|
1434
|
+
if (item.evidence_files !== undefined && kind !== "human_result") throw new Error(`evidence_files are only valid for human_result proposals`);
|
|
1435
|
+
const request: Record<string, unknown> = {};
|
|
1436
|
+
if (kind === "execution_plan" && item.dsl_file) request.dsl_file = item.dsl_file;
|
|
1437
|
+
if (kind === "human_result") request.evidence_files = item.evidence_files ?? [];
|
|
1438
|
+
const agent: Record<string, unknown> = {};
|
|
1439
|
+
if (item.note?.trim()) agent.note = item.note.trim();
|
|
1440
|
+
if (item.review_issues?.length) agent.review_issues = item.review_issues;
|
|
1441
|
+
return { request, agent };
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
export function approvalPinsScript(): string {
|
|
1445
|
+
// Always the packaged helper next to this extension: never a project-local
|
|
1446
|
+
// `.skills` copy the agent could edit to forge the confirm-time CAS.
|
|
1447
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../skills/workflow-orchestrator/scripts/approval_pins.py");
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
export function approvalPinsArgv(root: string, feature: string, kind: ApprovalKind, subject: string, request: Record<string, unknown>): string[] {
|
|
1451
|
+
return [
|
|
1452
|
+
"python3", "-I", approvalPinsScript(), "compute",
|
|
1453
|
+
"--root", root, "--feature", feature, "--kind", kind, "--subject", subject,
|
|
1454
|
+
"--payload-json", JSON.stringify(request), "--json",
|
|
1455
|
+
];
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
export type ApprovalPinsResult = { ok: true; pins: Record<string, unknown>; stateRevision: number; display: Record<string, unknown> } | { ok: false; errors: string[] };
|
|
1459
|
+
|
|
1460
|
+
export function parseApprovalPinsOutput(stdout: string, stderr = ""): ApprovalPinsResult {
|
|
1461
|
+
let parsed: Record<string, unknown> | null = null;
|
|
1462
|
+
try { parsed = JSON.parse(stdout) as Record<string, unknown>; } catch { parsed = null; }
|
|
1463
|
+
if (!parsed || typeof parsed !== "object") return { ok: false, errors: [`approval pin helper returned invalid JSON: ${(stderr || stdout).slice(0, 500)}`] };
|
|
1464
|
+
if (parsed.ok !== true) {
|
|
1465
|
+
const errors = Array.isArray(parsed.errors) ? parsed.errors.map(String) : ["approval pin helper failed"];
|
|
1466
|
+
return { ok: false, errors };
|
|
1467
|
+
}
|
|
1468
|
+
if (!parsed.pins || typeof parsed.pins !== "object" || !Number.isInteger(parsed.state_revision)) {
|
|
1469
|
+
return { ok: false, errors: ["approval pin helper returned no pins"] };
|
|
1470
|
+
}
|
|
1471
|
+
return {
|
|
1472
|
+
ok: true,
|
|
1473
|
+
pins: parsed.pins as Record<string, unknown>,
|
|
1474
|
+
stateRevision: Number(parsed.state_revision),
|
|
1475
|
+
display: parsed.display && typeof parsed.display === "object" ? parsed.display as Record<string, unknown> : {},
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
/** Synchronous recompute used inside the confirm transaction. */
|
|
1480
|
+
export function recomputeApprovalPinsSync(root: string, proposal: ProposalRecord): CurrentPins {
|
|
1481
|
+
const request = proposal.payload.request && typeof proposal.payload.request === "object" ? proposal.payload.request as Record<string, unknown> : {};
|
|
1482
|
+
const child = Bun.spawnSync(approvalPinsArgv(root, proposal.feature, proposal.kind, proposal.subjectKey, request), {
|
|
1483
|
+
cwd: root, stdout: "pipe", stderr: "pipe", env: { ...process.env, OMP_PROJECT_DIR: root },
|
|
1484
|
+
});
|
|
1485
|
+
const result = parseApprovalPinsOutput(child.stdout.toString(), child.stderr.toString());
|
|
1486
|
+
if (!result.ok) throw new Error(result.errors.join("; "));
|
|
1487
|
+
return { pins: result.pins, stateRevision: result.stateRevision };
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
export const APPROVAL_KIND_LABELS: Record<ApprovalKind, string> = {
|
|
1491
|
+
execution_plan: "执行计划",
|
|
1492
|
+
environment_profile: "环境配置",
|
|
1493
|
+
mode_override: "降级为人工",
|
|
1494
|
+
human_result: "人工验收结果",
|
|
1495
|
+
};
|
|
1496
|
+
|
|
1497
|
+
export const HUMAN_RESULT_CHOICES: Array<{ label: string; status: HumanResultStatus }> = [
|
|
1498
|
+
{ label: "passed(通过)", status: "passed" },
|
|
1499
|
+
{ label: "failed(失败)", status: "failed" },
|
|
1500
|
+
{ label: "blocked(阻塞)", status: "blocked" },
|
|
1501
|
+
];
|
|
1502
|
+
|
|
1503
|
+
/** Builds a human_result decision only from the user's own selection and input. */
|
|
1504
|
+
export function humanResultDecision(choice: string | undefined, actualResult: string | undefined): { status: HumanResultStatus; actual_result: string } | null {
|
|
1505
|
+
const status = HUMAN_RESULT_CHOICES.find(item => item.label === choice)?.status;
|
|
1506
|
+
const actual = actualResult?.trim();
|
|
1507
|
+
if (!status || !actual) return null;
|
|
1508
|
+
return { status, actual_result: actual };
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
export function pendingApprovalProposals(proposals: ProposalRecord[], now = new Date()): ProposalRecord[] {
|
|
1512
|
+
return proposals.filter(item => item.status === "proposed" && !proposalIsExpired(item, now));
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
export function approvalProposalLabel(proposal: ProposalRecord): string {
|
|
1516
|
+
return `${APPROVAL_KIND_LABELS[proposal.kind] ?? proposal.kind} · ${proposal.feature} · ${proposal.subjectKey} · ${proposal.createdAt.slice(0, 16).replace("T", " ")} · ${proposal.id.slice(0, 8)}`;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
function approvalText(value: unknown, limit = 600): string {
|
|
1520
|
+
const text = typeof value === "string" ? value : value === undefined || value === null ? "(无)" : JSON.stringify(value);
|
|
1521
|
+
return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
function approvalList(values: unknown, empty = "(无)"): string[] {
|
|
1525
|
+
if (!Array.isArray(values) || values.length === 0) return [` ${empty}`];
|
|
1526
|
+
return values.map((value, index) => ` ${index + 1}. ${approvalText(value, 400)}`);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/** Pure display assembly for one pending proposal (tested without a UI). */
|
|
1530
|
+
export function approvalReviewText(proposal: ProposalRecord): string {
|
|
1531
|
+
const display = proposal.payload.display && typeof proposal.payload.display === "object" ? proposal.payload.display as Record<string, any> : {};
|
|
1532
|
+
const agent = proposal.payload.agent && typeof proposal.payload.agent === "object" ? proposal.payload.agent as Record<string, any> : {};
|
|
1533
|
+
const lines = [
|
|
1534
|
+
`类型:${APPROVAL_KIND_LABELS[proposal.kind] ?? proposal.kind}`,
|
|
1535
|
+
`功能:${proposal.feature}`,
|
|
1536
|
+
`对象:${proposal.subjectKey}`,
|
|
1537
|
+
`提案:${proposal.id}(${proposal.createdAt},状态版本 ${proposal.stateRevision})`,
|
|
1538
|
+
"",
|
|
1539
|
+
];
|
|
1540
|
+
const tcLines = () => [
|
|
1541
|
+
`TC 目标:${approvalText(display.target)}`,
|
|
1542
|
+
`Oracle 原文:${approvalText(display.oracle, 2000)}`,
|
|
1543
|
+
"前置条件:", ...approvalList(display.preconditions),
|
|
1544
|
+
"步骤:", ...approvalList(display.steps),
|
|
1545
|
+
];
|
|
1546
|
+
if (proposal.kind === "execution_plan") {
|
|
1547
|
+
lines.push(...tcLines());
|
|
1548
|
+
lines.push(`DSL:${approvalText(display.dsl_file)} sha256=${approvalText(proposal.pins.dsl_sha256)}`);
|
|
1549
|
+
lines.push("覆盖 oracle 的断言:", ...approvalList(display.oracle_assertions, "(无 — 请勿批准)"));
|
|
1550
|
+
const uncovered = [
|
|
1551
|
+
...(Array.isArray(display.uncovered_preconditions) && display.uncovered_preconditions.length ? [`前置条件 ${display.uncovered_preconditions.join(", ")}`] : []),
|
|
1552
|
+
...(Array.isArray(display.uncovered_steps) && display.uncovered_steps.length ? [`步骤 ${display.uncovered_steps.join(", ")}`] : []),
|
|
1553
|
+
];
|
|
1554
|
+
lines.push(`未被覆盖:${uncovered.length ? uncovered.join(";") : "无"}`);
|
|
1555
|
+
if (display.structural_validation === "unavailable") lines.push("注意:Core 结构覆盖校验器不可用,本次只展示覆盖声明。");
|
|
1556
|
+
lines.push("AI 审查问题(建议性):", ...approvalList(agent.review_issues, "(未提供独立 AI 审查结果)"));
|
|
1557
|
+
} else if (proposal.kind === "environment_profile") {
|
|
1558
|
+
lines.push(`Profile:${approvalText(display.profile)}(${approvalText(display.path)})`);
|
|
1559
|
+
lines.push(`base_url:${approvalText(display.base_url)}`);
|
|
1560
|
+
lines.push("允许的页面导航 origin:", ...approvalList(display.navigation_origins));
|
|
1561
|
+
lines.push("允许的 API origin:", ...approvalList(display.api_origins));
|
|
1562
|
+
lines.push(`子资源策略:${approvalText(display.subresources)}`);
|
|
1563
|
+
const secrets = display.secret_env && typeof display.secret_env === "object" ? Object.entries(display.secret_env as Record<string, unknown>) : [];
|
|
1564
|
+
lines.push("密钥环境变量(仅名称):", ...(secrets.length ? secrets.map(([alias, name]) => ` ${alias} ← ${String(name)}`) : [" (无)"]));
|
|
1565
|
+
lines.push(`trace:${approvalText(display.trace)}`);
|
|
1566
|
+
lines.push(`profile sha256:${approvalText(proposal.pins.profile_sha256)}`);
|
|
1567
|
+
} else if (proposal.kind === "mode_override") {
|
|
1568
|
+
lines.push(...tcLines());
|
|
1569
|
+
lines.push(`降级:${approvalText(display.from)} → ${approvalText(display.to)}`);
|
|
1570
|
+
lines.push(`绑定条目:${approvalText(display.binding_entry, 1000)}`);
|
|
1571
|
+
} else if (proposal.kind === "human_result") {
|
|
1572
|
+
lines.push(...tcLines());
|
|
1573
|
+
const evidence = Array.isArray(display.evidence) ? display.evidence as Array<Record<string, unknown>> : [];
|
|
1574
|
+
lines.push("证据文件(批准时固定哈希):", ...(evidence.length ? evidence.map(item => ` ${String(item.path)} sha256=${String(item.sha256)}`) : [" (无证据文件;无法选择 passed)"]));
|
|
1575
|
+
lines.push("结论未预填:请根据你自己的执行结果选择 passed / failed / blocked,并填写实际结果。");
|
|
1576
|
+
}
|
|
1577
|
+
if (agent.note && proposal.kind !== "execution_plan") {
|
|
1578
|
+
lines.push("", `Agent 说明(仅供参考,不是结论):${approvalText(agent.note, 2000)}`);
|
|
1579
|
+
} else if (agent.note) {
|
|
1580
|
+
lines.push("", `Agent 说明:${approvalText(agent.note, 2000)}`);
|
|
1581
|
+
}
|
|
1582
|
+
return lines.join("\n");
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1304
1585
|
export default function devflowCore(pi: ExtensionAPI) {
|
|
1305
1586
|
const { z } = pi.zod;
|
|
1306
1587
|
pi.registerTool({
|
|
@@ -1424,7 +1705,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1424
1705
|
const feature = activeContract.feature;
|
|
1425
1706
|
const step = activeContract.step;
|
|
1426
1707
|
if (!/^[\p{L}\p{N}._-]{1,160}$/u.test(feature) || !/^[\p{L}\p{N}._-]{1,160}$/u.test(step)) return;
|
|
1427
|
-
const directory = path.join(cwd, ".planning", "evidence", feature);
|
|
1708
|
+
const directory = path.join(cwd, ".devflow/planning", "evidence", feature);
|
|
1428
1709
|
const attempt = typeof extra.attempt === "number" && Number.isInteger(extra.attempt) && extra.attempt >= 0
|
|
1429
1710
|
? extra.attempt
|
|
1430
1711
|
: undefined;
|
|
@@ -1619,7 +1900,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1619
1900
|
? (rawStep as { id: string }).id
|
|
1620
1901
|
: undefined;
|
|
1621
1902
|
if (!feature || !step) return undefined;
|
|
1622
|
-
const contractsDir = path.join(cwd, ".planning", "contracts", feature);
|
|
1903
|
+
const contractsDir = path.join(cwd, ".devflow/planning", "contracts", feature);
|
|
1623
1904
|
if (!existsSync(contractsDir)) return undefined;
|
|
1624
1905
|
const candidates = readdirSync(contractsDir)
|
|
1625
1906
|
.filter(name => name.startsWith("EC-") && name.endsWith(".json"))
|
|
@@ -1755,7 +2036,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1755
2036
|
`初始化方式:${modeSummary}`,
|
|
1756
2037
|
].join("\n");
|
|
1757
2038
|
const consequence = mode === "apply"
|
|
1758
|
-
? "确认后会创建 Devflow 的 .planning/ 与 .context/ 初始化产物;不会修改业务源码、依赖或项目配置。"
|
|
2039
|
+
? "确认后会创建 Devflow 的 .devflow/planning/ 与 .devflow/context/ 初始化产物;不会修改业务源码、依赖或项目配置。"
|
|
1759
2040
|
: "确认后只进入只读规划,不会创建工作流或写入项目文件。";
|
|
1760
2041
|
const confirmed = await ctx.ui.confirm("确认项目初始化范围", `${summary}\n\n${consequence}`);
|
|
1761
2042
|
if (!confirmed) {
|
|
@@ -2004,7 +2285,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2004
2285
|
const runtimeBootstrapAuthorizations = new Set<string>();
|
|
2005
2286
|
|
|
2006
2287
|
async function enqueueControlledRolloverRequest(cwd: string, rolloverId: string, sourceSessionId: string) {
|
|
2007
|
-
const requestPath = path.join(cwd, ".planning", "rollover-request.json");
|
|
2288
|
+
const requestPath = path.join(cwd, ".devflow/planning", "rollover-request.json");
|
|
2008
2289
|
const temporary = `${requestPath}.${randomUUID()}.tmp`;
|
|
2009
2290
|
await writeFile(temporary, JSON.stringify({ schema_version: 1, rollover_id: rolloverId, source_session_id: sourceSessionId }) + "\n", "utf8");
|
|
2010
2291
|
await rename(temporary, requestPath);
|
|
@@ -2278,6 +2559,115 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2278
2559
|
ctx.ui.notify(`功能“${feature}”已取消;现有工件已保留。`, "info");
|
|
2279
2560
|
}
|
|
2280
2561
|
|
|
2562
|
+
async function pendingApprovalCount(cwd: string): Promise<number | null> {
|
|
2563
|
+
try {
|
|
2564
|
+
const project = await loadProjectContext(cwd);
|
|
2565
|
+
const store = await InitializationStore.open(project);
|
|
2566
|
+
try {
|
|
2567
|
+
if (databaseAuthority(store.db, project.manifest.projectId) !== "database") return null;
|
|
2568
|
+
return pendingApprovalProposals(listProposals(store.db, project.manifest.projectId, { status: "proposed" })).length;
|
|
2569
|
+
} finally { store.close(); }
|
|
2570
|
+
} catch {
|
|
2571
|
+
return null;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
/**
|
|
2576
|
+
* The only confirmation path for Core approvals. It runs in this extension
|
|
2577
|
+
* process, re-computes pins inside the control.db transaction and never
|
|
2578
|
+
* shells out to a script the agent could also call to confirm.
|
|
2579
|
+
*/
|
|
2580
|
+
async function reviewPendingApprovalsFromPanel(ctx: ProjectControlCommandContext): Promise<void> {
|
|
2581
|
+
let project: Awaited<ReturnType<typeof loadProjectContext>>;
|
|
2582
|
+
try {
|
|
2583
|
+
project = await loadProjectContext(ctx.cwd);
|
|
2584
|
+
} catch (error) {
|
|
2585
|
+
ctx.ui.notify(`无法读取项目上下文:${error instanceof Error ? error.message : String(error)}`, "error");
|
|
2586
|
+
return;
|
|
2587
|
+
}
|
|
2588
|
+
const projectId = project.manifest.projectId;
|
|
2589
|
+
for (;;) {
|
|
2590
|
+
const store = await InitializationStore.open(project);
|
|
2591
|
+
try {
|
|
2592
|
+
if (databaseAuthority(store.db, projectId) !== "database") {
|
|
2593
|
+
ctx.ui.notify("approval_requires_database_authority:只有 database authority 模式支持人工批准。", "error");
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
2596
|
+
const pending = pendingApprovalProposals(listProposals(store.db, projectId, { status: "proposed" }));
|
|
2597
|
+
if (pending.length === 0) {
|
|
2598
|
+
ctx.ui.notify("当前没有待审批的提案。", "info");
|
|
2599
|
+
return;
|
|
2600
|
+
}
|
|
2601
|
+
const labels = pending.map(approvalProposalLabel);
|
|
2602
|
+
const selected = await ctx.ui.select("待审批", [...labels, "返回"]);
|
|
2603
|
+
if (!selected || selected === "返回") return;
|
|
2604
|
+
const proposal = pending[labels.indexOf(selected)];
|
|
2605
|
+
if (!proposal) return;
|
|
2606
|
+
const review = approvalReviewText(proposal);
|
|
2607
|
+
let decision: Record<string, unknown> | null = null;
|
|
2608
|
+
if (proposal.kind === "human_result") {
|
|
2609
|
+
ctx.ui.notify(review.slice(0, 10000), "info");
|
|
2610
|
+
const choice = await ctx.ui.select(`人工验收结论(${proposal.subjectKey})`, [...HUMAN_RESULT_CHOICES.map(item => item.label), "拒绝该提案", "返回"]);
|
|
2611
|
+
if (!choice || choice === "返回") continue;
|
|
2612
|
+
if (choice === "拒绝该提案") {
|
|
2613
|
+
if (await ctx.ui.confirm("拒绝提案", `${approvalProposalLabel(proposal)}
|
|
2614
|
+
|
|
2615
|
+
拒绝后该提案不能再被确认。继续吗?`)) {
|
|
2616
|
+
rejectProposal(store.db, projectId, proposal.id);
|
|
2617
|
+
ctx.ui.notify("已拒绝该提案。", "info");
|
|
2618
|
+
}
|
|
2619
|
+
continue;
|
|
2620
|
+
}
|
|
2621
|
+
const actual = await ctx.ui.input(`实际结果(${proposal.subjectKey})`, "请填写你实际观察到的结果");
|
|
2622
|
+
decision = humanResultDecision(choice, actual);
|
|
2623
|
+
if (!decision) {
|
|
2624
|
+
ctx.ui.notify("必须选择结论并填写实际结果;未写入任何批准。", "error");
|
|
2625
|
+
continue;
|
|
2626
|
+
}
|
|
2627
|
+
const confirmed = await ctx.ui.confirm(
|
|
2628
|
+
"确认人工验收结果",
|
|
2629
|
+
`${proposal.subjectKey}
|
|
2630
|
+
结论:${String(decision.status)}
|
|
2631
|
+
实际结果:${String(decision.actual_result)}
|
|
2632
|
+
|
|
2633
|
+
确认后 Core 将固定当前 TC 与证据哈希并记录该结果。继续吗?`,
|
|
2634
|
+
);
|
|
2635
|
+
if (!confirmed) continue;
|
|
2636
|
+
} else {
|
|
2637
|
+
const action = await ctx.ui.select(`审查:${approvalProposalLabel(proposal)}`, ["查看详情并批准…", "拒绝该提案", "返回"]);
|
|
2638
|
+
if (!action || action === "返回") continue;
|
|
2639
|
+
if (action === "拒绝该提案") {
|
|
2640
|
+
if (await ctx.ui.confirm("拒绝提案", `${approvalProposalLabel(proposal)}
|
|
2641
|
+
|
|
2642
|
+
拒绝后该提案不能再被确认。继续吗?`)) {
|
|
2643
|
+
rejectProposal(store.db, projectId, proposal.id);
|
|
2644
|
+
ctx.ui.notify("已拒绝该提案。", "info");
|
|
2645
|
+
}
|
|
2646
|
+
continue;
|
|
2647
|
+
}
|
|
2648
|
+
const confirmed = await ctx.ui.confirm(`批准${APPROVAL_KIND_LABELS[proposal.kind]}`, `${review.slice(0, 12000)}
|
|
2649
|
+
|
|
2650
|
+
确认批准吗?`);
|
|
2651
|
+
if (!confirmed) continue;
|
|
2652
|
+
decision = { approved: true };
|
|
2653
|
+
}
|
|
2654
|
+
const result = confirmApproval(store.db, {
|
|
2655
|
+
projectId,
|
|
2656
|
+
proposalId: proposal.id,
|
|
2657
|
+
decision,
|
|
2658
|
+
recompute: current => recomputeApprovalPinsSync(project.root, current),
|
|
2659
|
+
});
|
|
2660
|
+
if (result.ok) {
|
|
2661
|
+
ctx.ui.notify(`已批准:${approvalProposalLabel(proposal)}${result.supersededIds.length ? `(替代 ${result.supersededIds.length} 条旧批准)` : ""}`, "info");
|
|
2662
|
+
} else {
|
|
2663
|
+
ctx.ui.notify(`批准失败(${result.code}):${result.message}`, "error");
|
|
2664
|
+
}
|
|
2665
|
+
} finally {
|
|
2666
|
+
store.close();
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2281
2671
|
async function openProjectControlPanel(ctx: ProjectControlCommandContext): Promise<void> {
|
|
2282
2672
|
for (;;) {
|
|
2283
2673
|
// The control panel is a read-only projection. Each action below reads a
|
|
@@ -2293,6 +2683,9 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2293
2683
|
if (snapshot.action === "await_user_confirmation") options.push("审查并批准推进…");
|
|
2294
2684
|
if (hasSafeRecoveryPlan(snapshot)) options.push("执行安全恢复…");
|
|
2295
2685
|
if (typeof snapshot.current_feature === "string" && !["completed", "cancelled", "failed"].includes(String(snapshot.status))) options.push("取消当前功能…");
|
|
2686
|
+
const approvalCount = await pendingApprovalCount(ctx.cwd);
|
|
2687
|
+
const approvalOption = approvalCount === null ? null : `待审批(${approvalCount})…`;
|
|
2688
|
+
if (approvalOption) options.push(approvalOption);
|
|
2296
2689
|
options.push("关闭");
|
|
2297
2690
|
|
|
2298
2691
|
const selected = await ctx.ui.select("项目控制操作", options);
|
|
@@ -2323,6 +2716,10 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2323
2716
|
await cancelFeatureFromPanel(ctx);
|
|
2324
2717
|
return;
|
|
2325
2718
|
}
|
|
2719
|
+
if (approvalOption && selected === approvalOption) {
|
|
2720
|
+
await reviewPendingApprovalsFromPanel(ctx);
|
|
2721
|
+
continue;
|
|
2722
|
+
}
|
|
2326
2723
|
}
|
|
2327
2724
|
}
|
|
2328
2725
|
|
|
@@ -2403,6 +2800,12 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2403
2800
|
pi.on("session_start", async (_event, ctx) => {
|
|
2404
2801
|
projectRoot = ctx.cwd;
|
|
2405
2802
|
sessionId = randomUUID();
|
|
2803
|
+
try {
|
|
2804
|
+
await migrateLegacyProjectLayout(ctx.cwd);
|
|
2805
|
+
} catch (error) {
|
|
2806
|
+
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
2807
|
+
return;
|
|
2808
|
+
}
|
|
2406
2809
|
const result = await runHook(ctx.cwd, "start.py", {
|
|
2407
2810
|
session_id: sessionId,
|
|
2408
2811
|
cwd: ctx.cwd,
|
|
@@ -2736,7 +3139,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2736
3139
|
text: [
|
|
2737
3140
|
"<devflow-planner-directive>",
|
|
2738
3141
|
"用户已确认项目初始化的输入。你现在是 Planner,只允许只读分析和派发 project-scout / requirements-auditor / contract-auditor。",
|
|
2739
|
-
"不得创建 .planning 文件、不得调用状态迁移、不得执行实现。",
|
|
3142
|
+
"不得创建 .devflow/planning 文件、不得调用状态迁移、不得执行实现。",
|
|
2740
3143
|
"先输出:已知事实、待验证点、最少侦察任务和必须由用户决定的问题。没有证据时写 unknown。",
|
|
2741
3144
|
initialization.summary,
|
|
2742
3145
|
"</devflow-planner-directive>",
|
|
@@ -2841,7 +3244,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2841
3244
|
"--root", ctx.cwd,
|
|
2842
3245
|
"--feature", activeContract.feature,
|
|
2843
3246
|
"--step", activeContract.step,
|
|
2844
|
-
"--contract", `.planning/contracts/${activeContract.feature}/${activeContract.contractId}.json`,
|
|
3247
|
+
"--contract", `.devflow/planning/contracts/${activeContract.feature}/${activeContract.contractId}.json`,
|
|
2845
3248
|
"--obligations", "[]",
|
|
2846
3249
|
"--review-agent", "devflow-code-reviewer",
|
|
2847
3250
|
"--stage", "entry",
|
|
@@ -3375,16 +3778,22 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
3375
3778
|
pi.registerTool({
|
|
3376
3779
|
name: "devflow_run_acceptance_tests",
|
|
3377
3780
|
label: "Devflow Run Acceptance Tests",
|
|
3378
|
-
description:
|
|
3781
|
+
description: [
|
|
3782
|
+
"Validate or execute the adapters bound to the frozen TC acceptance suite: command, portable HTTP, and devflow_playwright (the Core-owned Playwright Runtime driven by a pinned DSL).",
|
|
3783
|
+
"Actions: validate (bindings and suite), validate_human (manual TCs), run (executes every automated TC and writes the canonical acceptance-results.json; Core is the only writer), validate_results (re-checks recorded results against current approvals and hashes), gc_runs (prunes .devflow/runs/<feature> execution directories per retention rules; dry-run by default, pass write=true to delete; optional retentionDays / unreferencedTtlDays).",
|
|
3784
|
+
"devflow_playwright bindings require CURRENT human approvals created with devflow_approval and confirmed by the user in /devflow control: an execution_plan approval per TC (pins the DSL, TC and suite) and an environment_profile approval for the target profile; manual or downgraded TCs need a human_result / mode_override approval. You cannot confirm approvals yourself.",
|
|
3785
|
+
"Hard gate: a missing, superseded or expired approval, or a DSL/profile/suite/evidence hash that changed after the approval or the run, makes run and validate_results return ok=false and blocks run-acceptance-tests from completing; there is no soft override and results must never be written by hand.",
|
|
3786
|
+
].join(" "),
|
|
3379
3787
|
parameters: z.object({
|
|
3380
|
-
action: z.enum(["validate", "run", "validate_results"]),
|
|
3788
|
+
action: z.enum(["validate", "validate_human", "run", "validate_results", "gc_runs"]),
|
|
3381
3789
|
feature: z.string().min(1),
|
|
3790
|
+
write: z.boolean().optional().describe("gc_runs only: actually delete expired run directories. Omit for a dry-run."),
|
|
3791
|
+
retentionDays: z.number().int().min(0).optional().describe("gc_runs only: days referenced runs are kept after release-closeout."),
|
|
3792
|
+
unreferencedTtlDays: z.number().int().min(0).optional().describe("gc_runs only: days unreferenced runs are kept."),
|
|
3382
3793
|
}),
|
|
3383
|
-
approval: args => args.action === "run" ? "write" : "read",
|
|
3794
|
+
approval: args => args.action === "run" || (args.action === "gc_runs" && args.write === true) ? "write" : "read",
|
|
3384
3795
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
3385
|
-
const
|
|
3386
|
-
const args = [scriptAction, "--feature", params.feature, "--json"];
|
|
3387
|
-
if (params.action === "run") args.push("--write");
|
|
3796
|
+
const args = acceptanceRunnerArgs(params);
|
|
3388
3797
|
const process = await runPython(ctx.cwd, "workflow-orchestrator/scripts/acceptance_test_runner.py", args);
|
|
3389
3798
|
const payload = parseJsonResult(process);
|
|
3390
3799
|
const failed = payload && typeof payload === "object" && (payload as { ok?: unknown }).ok === false;
|
|
@@ -3395,6 +3804,75 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
3395
3804
|
},
|
|
3396
3805
|
});
|
|
3397
3806
|
|
|
3807
|
+
pi.registerTool({
|
|
3808
|
+
name: "devflow_approval",
|
|
3809
|
+
label: "Devflow Approval",
|
|
3810
|
+
description: "Propose or list Core human approvals (execution_plan, environment_profile, mode_override, human_result). Core computes and pins the current TC/DSL/binding/profile/evidence hashes. Only the user can confirm, in /devflow control → 待审批; this tool has no confirm action. human_result proposals carry evidence only, never a verdict. Requires database authority.",
|
|
3811
|
+
parameters: approvalToolParameters(z),
|
|
3812
|
+
approval: args => (args.action === "list" ? "read" : "write"),
|
|
3813
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
3814
|
+
const fail = (message: string, details: Record<string, unknown> = {}) => ({
|
|
3815
|
+
content: [{ type: "text" as const, text: message }],
|
|
3816
|
+
details: { action: params.action, error: message, ...details },
|
|
3817
|
+
isError: true,
|
|
3818
|
+
});
|
|
3819
|
+
try {
|
|
3820
|
+
if (!(APPROVAL_TOOL_ACTIONS as readonly string[]).includes(params.action)) {
|
|
3821
|
+
return fail(`devflow_approval only supports ${APPROVAL_TOOL_ACTIONS.join("|")}; approvals are confirmed by the user in /devflow control`);
|
|
3822
|
+
}
|
|
3823
|
+
const project = await loadProjectContext(ctx.cwd);
|
|
3824
|
+
const projectId = project.manifest.projectId;
|
|
3825
|
+
const store = await InitializationStore.open(project);
|
|
3826
|
+
try {
|
|
3827
|
+
if (databaseAuthority(store.db, projectId) !== "database") {
|
|
3828
|
+
return fail("approval_requires_database_authority: approvals are only supported when the project uses database authority", { code: "approval_requires_database_authority" });
|
|
3829
|
+
}
|
|
3830
|
+
if (params.action === "list") {
|
|
3831
|
+
const proposals = listProposals(store.db, projectId, { feature: params.feature, kind: params.kind, status: params.status ?? "proposed" })
|
|
3832
|
+
.map(item => ({ proposalId: item.id, feature: item.feature, kind: item.kind, subject: item.subjectKey, status: item.status, createdAt: item.createdAt, resolvedAt: item.resolvedAt, expired: item.status === "proposed" && proposalIsExpired(item) }));
|
|
3833
|
+
const approvals = listApprovals(store.db, projectId, { feature: params.feature, kind: params.kind })
|
|
3834
|
+
.map(item => ({ approvalId: item.id, proposalId: item.proposalId, feature: item.feature, kind: item.kind, subject: item.subjectKey, pins: item.pins, decision: item.decision, approvedAt: item.approvedAt }));
|
|
3835
|
+
const value = { proposals, approvals, note: "Approvals listed here are current records; validity is re-checked against current pins by the reader." };
|
|
3836
|
+
return { content: [{ type: "text" as const, text: JSON.stringify(value, null, 2) }], details: value };
|
|
3837
|
+
}
|
|
3838
|
+
if (!params.feature || !params.kind || !params.items?.length) return fail("propose requires feature, kind and at least one item");
|
|
3839
|
+
const kind = params.kind as ApprovalKind;
|
|
3840
|
+
const subjects = new Set<string>();
|
|
3841
|
+
const prepared: Array<{ subject: string; request: Record<string, unknown>; agent: Record<string, unknown>; pins: Record<string, unknown>; stateRevision: number; display: Record<string, unknown> }> = [];
|
|
3842
|
+
const errors: string[] = [];
|
|
3843
|
+
for (const item of params.items as ApprovalProposeItem[]) {
|
|
3844
|
+
if (subjects.has(item.subject)) { errors.push(`${item.subject}: duplicate subject`); continue; }
|
|
3845
|
+
subjects.add(item.subject);
|
|
3846
|
+
let split: { request: Record<string, unknown>; agent: Record<string, unknown> };
|
|
3847
|
+
try { split = approvalProposeRequest(kind, item); } catch (error) { errors.push(`${item.subject}: ${error instanceof Error ? error.message : String(error)}`); continue; }
|
|
3848
|
+
const result = await runProcess(approvalPinsArgv(project.root, params.feature, kind, item.subject, split.request), project.root, { OMP_PROJECT_DIR: project.root });
|
|
3849
|
+
const pins = parseApprovalPinsOutput(result.stdout, result.stderr);
|
|
3850
|
+
if (!pins.ok) { errors.push(`${item.subject}: ${pins.errors.join("; ")}`); continue; }
|
|
3851
|
+
prepared.push({ subject: item.subject, ...split, pins: pins.pins, stateRevision: pins.stateRevision, display: pins.display });
|
|
3852
|
+
}
|
|
3853
|
+
// All-or-nothing: a partially recorded execution plan would be misleading to review.
|
|
3854
|
+
if (errors.length) return fail(`No proposal was recorded:\n${errors.join("\n")}`, { errors });
|
|
3855
|
+
// One immediate transaction: any store-level error rolls back every insert and every "expire older proposal" update.
|
|
3856
|
+
const recorded = proposeApprovals(store.db, prepared.map(item => ({
|
|
3857
|
+
projectId, feature: params.feature!, kind, subjectKey: item.subject,
|
|
3858
|
+
payload: { request: item.request, agent: item.agent, display: item.display },
|
|
3859
|
+
pins: item.pins, stateRevision: item.stateRevision,
|
|
3860
|
+
})));
|
|
3861
|
+
const created = prepared.map((item, index) => ({
|
|
3862
|
+
proposalId: recorded[index]!.proposalId, subject: item.subject, pins: item.pins, replacedProposals: recorded[index]!.expiredIds,
|
|
3863
|
+
}));
|
|
3864
|
+
const value = {
|
|
3865
|
+
kind, feature: params.feature, proposals: created,
|
|
3866
|
+
next: "Ask the user to review and decide in /devflow control → 待审批. You cannot confirm approvals; do not write verdicts or approval records yourself.",
|
|
3867
|
+
};
|
|
3868
|
+
return { content: [{ type: "text" as const, text: JSON.stringify(value, null, 2) }], details: value };
|
|
3869
|
+
} finally { store.close(); }
|
|
3870
|
+
} catch (error) {
|
|
3871
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
3872
|
+
}
|
|
3873
|
+
},
|
|
3874
|
+
});
|
|
3875
|
+
|
|
3398
3876
|
pi.registerTool({
|
|
3399
3877
|
name: "devflow_runtime_context",
|
|
3400
3878
|
label: "Devflow Runtime Context",
|
|
@@ -3625,7 +4103,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
3625
4103
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
3626
4104
|
const feature = normalizeContractSegment(params.feature, "feature");
|
|
3627
4105
|
const taskId = normalizeContractSegment(params.taskId, "taskId");
|
|
3628
|
-
const root = path.join(ctx.cwd, ".planning", "evidence", feature);
|
|
4106
|
+
const root = path.join(ctx.cwd, ".devflow/planning", "evidence", feature);
|
|
3629
4107
|
const destination = path.join(root, `${taskId}.json`);
|
|
3630
4108
|
const validatePack = (pack: unknown): string[] => {
|
|
3631
4109
|
if (!pack || typeof pack !== "object" || Array.isArray(pack)) return ["pack must be an object"];
|
|
@@ -3718,7 +4196,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
3718
4196
|
const written = await runRuby(ctx.cwd, "workflow-orchestrator/scripts/execution_contract.rb", createArgs);
|
|
3719
4197
|
if (!controlPlaneSucceeded(written)) return renderResult(written, { action: input.action });
|
|
3720
4198
|
const writtenPayload = parseJsonResult(written);
|
|
3721
|
-
const destination = typeof writtenPayload?.path === "string" ? writtenPayload.path : ".planning/contracts/";
|
|
4199
|
+
const destination = typeof writtenPayload?.path === "string" ? writtenPayload.path : ".devflow/planning/contracts/";
|
|
3722
4200
|
contract.contractPath = destination;
|
|
3723
4201
|
activeContract = contract;
|
|
3724
4202
|
contractAgentCalls = 0;
|
|
@@ -4264,7 +4742,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
4264
4742
|
[
|
|
4265
4743
|
"<devflow-planner-directive>",
|
|
4266
4744
|
"用户已确认项目初始化的输入。你现在是 Planner,只允许只读分析和派发 project-scout / requirements-auditor / contract-auditor。",
|
|
4267
|
-
"不得创建 .planning 文件、不得调用状态迁移、不得执行实现。",
|
|
4745
|
+
"不得创建 .devflow/planning 文件、不得调用状态迁移、不得执行实现。",
|
|
4268
4746
|
"先输出:已知事实、待验证点、最少侦察任务和必须由用户决定的问题。没有证据时写 unknown。",
|
|
4269
4747
|
initialization.summary,
|
|
4270
4748
|
"</devflow-planner-directive>",
|